natplugins/natptraversalcontroller/src/cnattraversalcontroller.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 02 Feb 2010 01:04:58 +0200
changeset 0 1bce908db942
permissions -rw-r--r--
Revision: 201003 Kit: 201005

/*
* Copyright (c) 2007 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/


#include "cnattraversalcontroller.h"
#include "sipnattraversalcontrollerinitparams.h"
#include "sipnatbindingobserver.h"
#include "sipnattraversalrequestobserver.h"
#include "cnattraversalconfig.h"
#include "cnattraversalsession.h"
#include "nattraversalsocketuser.h"
#include "nattraversallog.h"
#include <siperr.h>

_LIT8(KZeroIPAddress, "0.0.0.0");

// -----------------------------------------------------------------------------
// CNATTraversalController::NewL
// -----------------------------------------------------------------------------
//
CNATTraversalController* CNATTraversalController::NewL(TAny* aInitParams)
    {
    __ASSERT_ALWAYS (aInitParams, User::Leave(KErrArgument));
    TSIPNATTraversalControllerInitParams* params =
        static_cast<TSIPNATTraversalControllerInitParams*>(aInitParams);
    CNATTraversalController* self =
        new(ELeave)CNATTraversalController(params->iSocketServ);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::CNATTraversalController
// -----------------------------------------------------------------------------
//
CNATTraversalController::CNATTraversalController(RSocketServ& aSocketServ)
 : iSocketServ(aSocketServ),
   iRequestOrder(TNATTraversalPendingRequest::RequestOrder)
    {
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::ConstructL
// -----------------------------------------------------------------------------
//
void CNATTraversalController::ConstructL()
    {
    iDeltaTimer = CDeltaTimer::NewL(CActive::EPriorityStandard);
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::~CNATTraversalController
// -----------------------------------------------------------------------------
//
CNATTraversalController::~CNATTraversalController()
    {
    iSessions.ResetAndDestroy();
    for (TInt i=0; i < iPendingRequests.Count(); i++)
        {
        if (iDeltaTimer)
            {
            iDeltaTimer->Remove(iPendingRequests[i].DeltaTimerEntry());
            }
        }
    iPendingRequests.Close();
    delete iDeltaTimer;
    iSocketUserQueue.Close();
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::ResolvePublicAddrL
// From CSIPNATTraversalController
// -----------------------------------------------------------------------------
//
TUint32 CNATTraversalController::ResolvePublicAddrL(
    TUint32 aIapId,
    RConnection& aConnection,
    const TInetAddr& aLocalAddr,
    const TDesC8& aDomain,
    RSocket& aUdpSocket,
    const TInetAddr& aNextHopAddr,
    MSIPNATBindingObserver* aBindingObserver,
    MSIPNATTraversalRequestObserver& aRequestObserver)
    {
    __ASSERT_ALWAYS(NULL != aBindingObserver,User::Leave(KErrArgument));
    TUint32 requestId = NextRequestId();
    TBool asyncCallInitiated = EFalse;

    if (!BindingExists(aBindingObserver))
        {
        CNATTraversalConfig* config = CNATTraversalConfig::NewLC(aIapId,aDomain);

        CreateBindingL(requestId,config,aConnection,aLocalAddr,aUdpSocket,
                       aNextHopAddr,*aBindingObserver,aRequestObserver,
                       asyncCallInitiated);

        CleanupStack::Pop(config); // ownership transferred
        }

    if (!asyncCallInitiated)
        {
        // Start a timer with a minimum value to make the call asynchronous
        TNATTraversalPendingRequest request(requestId,&aRequestObserver,*this);
        iPendingRequests.InsertInOrderL(request,iRequestOrder);
        TInt index = iPendingRequests.FindInOrder(request,iRequestOrder);
        if (0 <= index && iPendingRequests.Count() > index)
            {
            iDeltaTimer->Queue(1,iPendingRequests[index].DeltaTimerEntry());
            }
        }

    return requestId;
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::Cancel
// From CSIPNATTraversalController
// -----------------------------------------------------------------------------
//
void CNATTraversalController::Cancel(TUint32 aRequestId)
    {
    NATTRAVERSAL_LOG("CNATTraversalController::Cancel")
    TNATTraversalPendingRequest tmp(aRequestId,0,*this);
    TInt index = iPendingRequests.FindInOrder(tmp,iRequestOrder);
    if (0 <= index && iPendingRequests.Count() > index)
        {
        iDeltaTimer->Remove(iPendingRequests[index].DeltaTimerEntry());
        iPendingRequests.Remove(index);
        }
    else
        {
        TBool found = EFalse;
        for (TInt i=0; i < iSessions.Count() && !found; i++)
            {
            found = iSessions[i]->RemoveBinding(aRequestId);
            }
        }
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::RefreshNATBindingL
// From CSIPNATTraversalController
// -----------------------------------------------------------------------------
//
TBool CNATTraversalController::RefreshNATBindingL(
    RSocket& aSocket,
    const MSIPNATBindingObserver* aBindingObserver)
    {
    NATTRAVERSAL_LOG("CNATTraversalController::RefreshNATBindingL - TCP/UDP")
    TBool maintainPersistentConnection = EFalse;
    TBool handled = EFalse;
    for (TInt i=0; i < iSessions.Count() && !handled; i++)
        {
        iSessions[i]->RefreshNATBindingL(
            aSocket,aBindingObserver,
            maintainPersistentConnection,handled);
        }
    return maintainPersistentConnection;
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::RefreshNATBindingL
// From CSIPNATTraversalController
// -----------------------------------------------------------------------------
//
TBool CNATTraversalController::RefreshNATBindingL(
    CSecureSocket& aSecureSocket,
    const MSIPNATBindingObserver* aBindingObserver)
    {
    NATTRAVERSAL_LOG("CNATTraversalController::RefreshNATBindingL - TLS")
    TBool maintainPersistentConnection = EFalse;
    TBool handled = EFalse;
    for (TInt i=0; i < iSessions.Count() && !handled; i++)
        {
        iSessions[i]->RefreshNATBindingL(
            aSecureSocket,aBindingObserver,
            maintainPersistentConnection,handled);
        }
    return maintainPersistentConnection;
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::UpdateNextHop
// -----------------------------------------------------------------------------
//
void CNATTraversalController::UpdateNextHop(
    const TInetAddr& aNextHopAddr,
    const MSIPNATBindingObserver* aBindingObserver)
    {
    NATTRAVERSAL_ADDRLOG("CNATTraversalController::UpdateNextHop ->",
        aNextHopAddr)
    for (TInt i=0; i < iSessions.Count(); i++)
        {
        CNATTraversalSession* session = iSessions[i];
        if (session->HasBinding(aBindingObserver))
            {
            session->UpdateNextHop(aNextHopAddr);
            return;
            }
        }
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::SocketIdle
// From CSIPNATTraversalController
// -----------------------------------------------------------------------------
//
void CNATTraversalController::SocketIdle(
    TBool aIdle,
    RSocket& aSocket)
    {
    NATTRAVERSAL_INTLOG("CNATTraversalController::SocketIdle - TCP/UDP", aIdle)
    for (TInt i=0; i < iSessions.Count(); i++)
        {
        iSessions[i]->SocketIdle(aIdle,aSocket);
        }
    if (!aIdle)
        {
        TBool firstRemoved = EFalse;
        for (TInt i=iSocketUserQueue.Count()-1; i >= 0; i--)
            {
            const RSocket& socket = iSocketUserQueue[i]->Socket();
            if (socket.SubSessionHandle() == aSocket.SubSessionHandle())
                {
                iSocketUserQueue[i]->CancelSending();
                iSocketUserQueue.Remove(i);
                firstRemoved = (i == 0);
                }
            }
        if (firstRemoved)
            {
            HandleNextFromSocketUserQueue();
            }
        }
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::SocketIdle
// From CSIPNATTraversalController
// -----------------------------------------------------------------------------
//
void CNATTraversalController::SocketIdle(
    TBool aIdle,
    CSecureSocket& aSecureSocket)
    {
    NATTRAVERSAL_INTLOG("CNATTraversalController::SocketIdle - TLS", aIdle)
    for (TInt i=0; i < iSessions.Count(); i++)
        {
        iSessions[i]->SocketIdle(aIdle,aSecureSocket);
        }
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::DataReceived
// From CSIPNATTraversalController
// -----------------------------------------------------------------------------
//
void CNATTraversalController::DataReceivedL(
    const TDesC8& aData,
    const RSocket& aSocket,
    TBool& aHandled)
    {
    NATTRAVERSAL_LOG("CNATTraversalController::DataReceivedL");
    aHandled = EFalse;
    for (TInt i=0; i < iSessions.Count() && !aHandled; i++)
        {
        iSessions[i]->DataReceivedL(aData,aSocket,aHandled);
        }
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::FreeResources
// From CSIPNATTraversalController
// -----------------------------------------------------------------------------
//
void CNATTraversalController::FreeResources(TUint32 aIapId)
    {
    NATTRAVERSAL_LOG("CNATTraversalController::FreeResources for IAP")
    for (TInt i = iSessions.Count()-1; i >= 0; i--)
        {
        CNATTraversalSession* session = iSessions[i];
        if (session->IapId() == aIapId)
            {
            iSessions.Remove(i);
            delete session;
            }
        }
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::FreeResources
// From CSIPNATTraversalController
// -----------------------------------------------------------------------------
//
void CNATTraversalController::FreeResources(
    MSIPNATBindingObserver& aBindingObserver)
    {
    NATTRAVERSAL_LOG("CNATTraversalController::FreeResources for observer")
    for (TInt i=iSessions.Count()-1; i>=0; i--)
        {
        CNATTraversalSession* session = iSessions[i];
        session->RemoveBinding(aBindingObserver);
        if (!session->HasBindings())
            {
            iSessions.Remove(i);
            delete session;
            }
        }
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::RequestCompleted
// From MNATTraversalPendingRequestContainer
// -----------------------------------------------------------------------------
//
void CNATTraversalController::RequestCompleted(
    TUint32 aRequestId)
    {
    NATTRAVERSAL_LOG("CNATTraversalController::RequestCompleted")
    TNATTraversalPendingRequest tmp(aRequestId,0,*this);
    TInt index = iPendingRequests.FindInOrder(tmp,iRequestOrder);
    if (0 <= index && iPendingRequests.Count() > index)
        {
        TNATTraversalPendingRequest request = iPendingRequests[index]; // copy
        iDeltaTimer->Remove(iPendingRequests[index].DeltaTimerEntry());
        iPendingRequests.Remove(index); // remove from array
        request.CompleteRequest(); // use copy for informing the observer
        }
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::STUNClientInitCompleted
// From MSTUNClientObserver
// -----------------------------------------------------------------------------
//
void CNATTraversalController::STUNClientInitCompleted(
    const CSTUNClient& aClient,
    TInt aCompletionCode)
    {
    TBool found = EFalse;
    for (TInt i=0; i<iSessions.Count() && !found; i++)
        {
        CNATTraversalSession* session = iSessions[i];
        if (session->HasSTUNClient(aClient))
            {
            found = ETrue;
            if (aCompletionCode != KErrNone)
                {
                if (KErrNATFWDnsFailure == aCompletionCode)
                    {
                    aCompletionCode = KErrSIPResolvingFailure;
                    }
                session->InitFailed(aCompletionCode);
                }
            else
                {
                TRAPD(err,session->InitCompletedL());
                if (err)
                    {
                    session->InitFailed(err);
                    }
                }
            }
        }
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::STUNBindingEventOccurredL
// From MSTUNClientObserver
// -----------------------------------------------------------------------------
//
void CNATTraversalController::STUNBindingEventOccurredL(
    TSTUNBindingEvent aEvent,
    const CBinding& aBinding)
    {
    if (aEvent == MSTUNClientObserver::EPublicAddressResolved)
        {
        TBool found = EFalse;
        for (TInt i=0; i<iSessions.Count() && !found; i++)
            {
            CNATTraversalSession* session = iSessions[i];
            found = session->AddressResolvedL(aBinding);
            }
        }
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::STUNBindingErrorOccurred
// From MSTUNClientObserver
// -----------------------------------------------------------------------------
//
void CNATTraversalController::STUNBindingErrorOccurred(
    const CBinding& aBinding,
    TInt aError)
    {
    TBool found = EFalse;
    for (TInt i=0; i<iSessions.Count() && !found; i++)
        {
        CNATTraversalSession* session = iSessions[i];
        found = session->BindingFailed(aBinding,aError);
        }
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::AddToSendingQueueL
// From MNATTraversalSocketManager
// -----------------------------------------------------------------------------
//
TBool CNATTraversalController::AddToSendingQueueL(
    MNATTraversalSocketUser& aUser)
    {
    TBool requestPendingForSocket = EFalse;
    for (TInt i=0; i < iSocketUserQueue.Count(); i++)
        {
        const RSocket& socket = iSocketUserQueue[i]->Socket();
        if (socket.SubSessionHandle() == aUser.Socket().SubSessionHandle())
            {
            requestPendingForSocket = ETrue;
            }
        }
    iSocketUserQueue.AppendL(&aUser);
    return requestPendingForSocket;
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::SendingCompleted
// From MNATTraversalSocketManager
// -----------------------------------------------------------------------------
//
void CNATTraversalController::SendingCompleted(MNATTraversalSocketUser& aUser)
    {
    TInt index = iSocketUserQueue.Find(&aUser);
    if (index >= 0)
        {
        iSocketUserQueue.Remove(index);
        if (index == 0)
            {
            HandleNextFromSocketUserQueue();
            }
        }
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::NextRequestId
// -----------------------------------------------------------------------------
//
TUint32 CNATTraversalController::NextRequestId()
    {
    if (iRequestIdCounter == KMaxTUint32)
        {
        iRequestIdCounter = 1;
        }
    else
        {
        iRequestIdCounter++;
        }
    return iRequestIdCounter;
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::CreateBindingL
// -----------------------------------------------------------------------------
//
void CNATTraversalController::CreateBindingL(
    TUint32 aRequestId,
    CNATTraversalConfig* aConfig,
    RConnection& aConnection,
    const TInetAddr& aLocalAddr,
    RSocket& aUdpSocket,
    const TInetAddr& aNextHopAddr,
    MSIPNATBindingObserver& aBindingObserver,
    MSIPNATTraversalRequestObserver& aRequestObserver,
    TBool& aAsyncInitiated)
    {
    __ASSERT_ALWAYS(aConfig, User::Leave(KErrArgument));

    __ASSERT_ALWAYS(!BindingExists(&aBindingObserver),
                    User::Leave(KErrAlreadyExists));

    TBool useSTUN = (aConfig->STUNServer().Compare(KZeroIPAddress) != 0);
    if (!useSTUN)
        {
        aAsyncInitiated = EFalse;
        if ( aConfig->CRLFRefreshEnabledToProxyValue() !=
        	 CNATTraversalConfig::EEnabled )
            {
            // No need to create binding nor a dummy CRLF refresh
            delete aConfig;
            return;
            }
        }

    TInt index = FindSessionIndex(aConfig->IapId(),aConfig->Domain());
    if (index >= 0)
        {
        // Session exists
        CNATTraversalSession* session = iSessions[index];
        session->CreateNATBindingL(aRequestId,aUdpSocket,
                                   aBindingObserver,aRequestObserver,
                                   aAsyncInitiated);
        delete aConfig; // ownership transferred after leaving functions
        }
    else
        {
        // Create new session and add a binding
        CNATTraversalSession* session =
            CNATTraversalSession::NewLC(*iDeltaTimer,iSocketServ,aConnection,
                                        aLocalAddr,aNextHopAddr,aConfig,
                                        *this,*this);
        TCleanupItem cleanupItem(DetachConfigFromSession,session);
        CleanupStack::PushL(cleanupItem);
        session->CreateNATBindingL(aRequestId,aUdpSocket,
                                   aBindingObserver,aRequestObserver,
                                   aAsyncInitiated);
        iSessions.AppendL(session);
        CleanupStack::Pop(); // cleanupItem
        CleanupStack::Pop(session);
        }
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::FindSessionIndex
// -----------------------------------------------------------------------------
//
TInt CNATTraversalController::FindSessionIndex(
    TUint32 aIapId,
    const TDesC8& aDomain) const
    {
    for (TInt i=0; i < iSessions.Count(); i++)
        {
        CNATTraversalSession* session = iSessions[i];
        if (session->IapId() == aIapId &&
            session->Domain().CompareF(aDomain) == 0)
            {
            return i;
            }
        }
    return KErrNotFound;
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::BindingExists
// -----------------------------------------------------------------------------
//
TBool CNATTraversalController::BindingExists(
    const MSIPNATBindingObserver* aObserver) const
    {
    for (TInt i=0; i < iSessions.Count(); i++)
        {
        if (iSessions[i]->HasBinding(aObserver))
            {
            return ETrue;
            }
        }
    return EFalse;
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::DetachConfigFromSession
// -----------------------------------------------------------------------------
//
void CNATTraversalController::DetachConfigFromSession(TAny* aSession)
    {
    CNATTraversalSession* session =
        reinterpret_cast<CNATTraversalSession*>(aSession);
    if (session)
        {
        session->DetachConfig();
        }
    }

// -----------------------------------------------------------------------------
// CNATTraversalController::HandleNextFromSocketUserQueue
// -----------------------------------------------------------------------------
//
void CNATTraversalController::HandleNextFromSocketUserQueue()
    {
    if (iSocketUserQueue.Count() > 0 && !iSocketUserQueue[0]->IsSending())
        {
        iSocketUserQueue[0]->SendingAllowed();
        }
    }