/*
* 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 "stunassert.h"
#include "natfwunsaflog.h"
#include "natfwunsafmessagefactory.h"
#include "natfwunsafbindingrequest.h"
#include "natfwunsafallocaterequest.h"
#include "natfwunsafconnectrequest.h"
#include "natfwunsafsetactivedestinationrequest.h"
#include "natfwunsafsendindication.h"
#include "cstunrelaybindingimplementation.h"
#include "natfwstunbinding.h"
#include "cstunbindinginit.h"
#include "cstunbindinggetsharedsecret.h"
#include "cstunbindinggetaddress.h"
#include "cstunbindingactive.h"
#include "cstunbindingwaittoretry.h"
#include "mstunbindingobserver.h"
#include "cstuntransaction.h"
#include "stunutils.h"
#include "natfwstunclientdefs.h"
#include "cstunindicationtransmitter.h"
#include "natfwunsaftcprelaypacketfactory.h"

// Attributes
#include "natfwunsafusernameattribute.h"
#include "natfwunsafxoronlyattribute.h"
#include "natfwunsafunknownattributesattribute.h"
#include "natfwunsafmagiccookieattribute.h"
#include "natfwunsafdataattribute.h"
#include "natfwunsafremoteaddressattribute.h"
#include "natfwunsafusecandidateattribute.h"
#include "natfwunsafpriorityattribute.h"
#include "natfwunsaficecontrollingattribute.h"
#include "natfwunsaficecontrolledattribute.h"
#include "natfwunsafalternateserverattribute.h"
#include "natfwunsafrealmattribute.h"
#include "natfwunsafnonceattribute.h"



// ======== MEMBER FUNCTIONS ========

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::NewL
// ---------------------------------------------------------------------------
//
CSTUNRelayBindingImplementation*
    CSTUNRelayBindingImplementation::NewL( CBinding& aBinding,  
                                           MSTUNBindingObserver& aClient,
                                           RSocket& aSocket )
    {

    CSTUNRelayBindingImplementation* self =
    new ( ELeave ) CSTUNRelayBindingImplementation( aBinding, aClient, aSocket );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::NewL - overloaded
// ---------------------------------------------------------------------------
//
CSTUNRelayBindingImplementation*
    CSTUNRelayBindingImplementation::NewL( CBinding& aBinding,  
                                           MSTUNBindingObserver& aClient,
                                           TUint aStreamId,
                                           TUint aConnectionId,
                                           MNcmConnectionMultiplexer* aMux )
    {

    CSTUNRelayBindingImplementation* self =
    new ( ELeave ) CSTUNRelayBindingImplementation( aBinding, aClient, aStreamId, 
                                               aConnectionId, aMux );
    CleanupStack::PushL( self );
    self->ConstructL( );
    CleanupStack::Pop( self );
    return self;
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::CSTUNRelayBindingImplementation
// ---------------------------------------------------------------------------
//
CSTUNRelayBindingImplementation::CSTUNRelayBindingImplementation(
    CBinding& aBinding,
    MSTUNBindingObserver& aClient,
    RSocket& aSocket ) :
    CBindingImplementation( aClient, aBinding, aSocket )
    {
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::CSTUNRelayBindingImplementation
// ---------------------------------------------------------------------------
//
CSTUNRelayBindingImplementation::CSTUNRelayBindingImplementation( 
    CBinding& aBinding,
    MSTUNBindingObserver& aClient, 
    TUint aStreamId,
    TUint aConnectionId,
    MNcmConnectionMultiplexer* aMux ) :
    CBindingImplementation( aClient, aBinding, aStreamId, aConnectionId, aMux )
    {
    }
    
// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::ConstructL
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::ConstructL()
    {
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::ConstructL" )
    ClientL().AttachBindingL( iBinding );

    iGetAddress = new ( ELeave ) CSTUNBindingGetAddress();
    iGetSharedSecret =
    new ( ELeave ) CSTUNBindingGetSharedSecret( *iGetAddress );
    iInit = new ( ELeave ) CSTUNBindingInit( *iGetSharedSecret );
    iActive = new ( ELeave ) CSTUNBindingActive( *iGetSharedSecret );
    iWaitToRetry = new ( ELeave ) CSTUNBindingWaitToRetry( *iGetSharedSecret );
    iGetAddress->SetNeighbourStates( *iGetSharedSecret,
                                     *iWaitToRetry,
                                     *iActive );
    
    iIndicationTx = CStunIndicationTransmitter::NewL( *iMux, 
                                                      iStreamId, 
                                                      iConnectionId );
    ChangeState( *iInit );
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::~CSTUNRelayBindingImplementation
// ---------------------------------------------------------------------------
//
CSTUNRelayBindingImplementation::~CSTUNRelayBindingImplementation()
    {
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::~CSTUNRelayBindingImplementation" )
    if ( iClient )
        {
        iClient->DetachBinding( iBinding );
        }

    this->FreeRequestData();

    delete iInit;
    delete iGetSharedSecret;
    delete iGetAddress;
    delete iWaitToRetry;
    delete iActive;
    delete iIndicationTx;
    
    iSocket = NULL;
    iMux = NULL;
    
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::~CSTUNRelayBindingImplementation exit" )
    }

// -----------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::TimerExpiredL
// -----------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::TimerExpiredL()
    {
    iState->TimerExpiredL( *this );
    }

// -----------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::LeaveFromTimerExpired
// -----------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::LeaveFromTimerExpired( TInt aError )
    {
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::LeaveFromTimerExpired" )
    __STUN_ASSERT_RETURN( aError != KErrNone, KErrArgument );

    Terminate( aError );
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::PublicAddressObtainedL
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::PublicAddressObtainedL( 
    const TInetAddr& aAddress )
    {
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::PublicAddressObtainedL 1" )
    iState->PublicAddressReceivedL( *this, aAddress );
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::PublicAddressObtainedL
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::PublicAddressObtainedL( 
    const TInetAddr& aReflexiveAddr, const TInetAddr& aRelayAddr )
    {
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::PublicAddressObtainedL 2" )
    iState->PublicAddressReceivedL( *this, aReflexiveAddr, aRelayAddr );
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::TransactionError
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::TransactionError( TInt aError,
    CNATFWUNSAFUnknownAttributesAttribute* aUnknownAttr )
    {
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::TransactionError" )
    delete iUnknownAttr;
    iUnknownAttr = aUnknownAttr;

    __STUN_ASSERT_RETURN( aError != KErrNone, KErrArgument );
    iState->TransactionError( *this, aError );
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::TransactionEventOccurredL
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::TransactionEventOccurredL( 
    TSTUNCallbackInfo::TFunction aEvent )
    {
    __STUNTURNCLIENT_INT1( 
        "CSTUNRelayBindingImplementation::TransactionEventOccurredL event:",
             aEvent )
    if ( ESetActiveDestinationRequest == iRequestType )
        {
        *iTimerValue = iTransaction->TimerValue();
        }
    
    FreeRequestData();
    ChangeState( *iActive );
    ClientL().BindingEventOccurred( iBinding, aEvent );
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::HandleTransactionError
// ---------------------------------------------------------------------------
//  
TBool CSTUNRelayBindingImplementation::HandleTransactionError( TInt aError )
    {
    __STUNTURNCLIENT_INT1( "HandleTransactionError, reason", aError )
       
    const TInt KMaxErrorResponseCount = 3;

    __STUN_ASSERT_RETURN_VALUE( iRequest && aError != KErrNone,
    EFalse );
    
    delete iTransaction;
    iTransaction = NULL;
    iTransactionError = aError;

    if ( ( aError > 0 ) && ( ++iErrorResponseCount >= KMaxErrorResponseCount ) )
        {
        iErrorResponseCount = 0;
        return EFalse;
        }
        
    CNATFWUNSAFAlternateServerAttribute* alternateServer = NULL;
    HBufC8* realmValue = NULL;
    
    switch ( aError )
        {
        case KErrTimedOut:
            __STUNTURNCLIENT( 
                "CSTUNRelayBindingImplementation - KErrTimedOut" )
            //Clear iAddXorOnly as another address is tried
            iAddXorOnly = EFalse;
            return iClient && iClient->ObtainServerAddress( iServerAddress );
        
        case E300TryAlternate:
            // The client SHOULD attempt a new transaction to the server
            // indicated in the ALTERNATE-SERVER attribute.
            alternateServer = static_cast<CNATFWUNSAFAlternateServerAttribute*>
            ( iRequest->Attribute( CNATFWUNSAFAttribute::EAlternateServer ) );
            if ( alternateServer )
                {
                iServerAddress = alternateServer->Address();
                return ETrue;
                }
            return EFalse;
        
        case E4XX:
            return EFalse;
                        
        case E401Unauthorized:        
            if ( iSharedSecret )
                {                                              
                CNATFWUNSAFRealmAttribute* realm = 
                    static_cast<CNATFWUNSAFRealmAttribute*>
                    ( iRequest->Attribute( CNATFWUNSAFAttribute::ERealm ) );
                if ( realm || !iRealmFromResponse )
                    {
                    // indicates that this was retry and it did not work.
                    // notify client -> unrecoverable error
                    return EFalse;                   
                    }
                }
            return ETrue;    

        case E420UnknownAttributes:
            //Remove the unknown attributes and retry
            return iUnknownAttr != NULL;
                            
        case E430StaleCredentials:
            // Client used a short term credential that has expired so
            // generate a new Shared Secret request            
            return iSharedSecret != NULL;

        case E431IntegrityCheckFailure:
            {
            TBool retry = EFalse;
            TRAPD( err, retry =
                ClientL().SharedSecretRejectedL( iBinding,
                                                 Username(),
                                                 *iSharedSecret ) );
            return err == KErrNone && retry;
            }
                    
        case E432MissingUsername:
            //If missing USERNAME or MESSAGE-INTEGRITY, add them and retry
            return !iRequest->Attribute( CNATFWUNSAFAttribute::EUsername ) ||
               !iSharedSecret;
               
        case E433UseTLS:
            // If request was a Shared Secret request and wasn't sent over
            // TLS, the client SHOULD retry the request with TLS.
            return iSharedSecret != NULL;

        case E434MissingRealm:
            if ( !iRequest->Attribute( CNATFWUNSAFAttribute::ERealm ) &&
                 iSharedSecret )
                {
                // use a long term credential and retry the request using the username
                // and password associated with the REALM
                return ETrue;
                }
                
            TRAPD( err, realmValue = ( static_cast<CNATFWUNSAFRealmAttribute*>
                ( iRequest->Attribute( CNATFWUNSAFAttribute::ERealm ) ) )->
                Value().AllocL() );
            if ( err || !realmValue )
                {
                delete realmValue;
                return EFalse;
                }
                
            if (  KErrNone != realmValue->Compare( *iRealmFromResponse ) )
                {
                // the client SHOULD retry using the username and 
                // password associated with the REALM in the response
                iUseRealmFromResponse = ETrue;
                delete realmValue;
                return ETrue;
                }
            delete realmValue;           
            return EFalse;                                    
              
 
        case E435MissingNonce:
            if ( iRequest->Attribute( CNATFWUNSAFAttribute::ENonce ) )
                {
                return EFalse; 
                }
            // retry using a nonce from error response                
            return ETrue;
                      
        case E436UnknownUsername:
            if ( iSharedSecret )
                {
                return ETrue;                
                }
            // If the username was collected from the user, alert the user.
            return EFalse;

        case E437NoBindind:
            // There is none yet in place
            return EFalse;

        case E438StaleNonce:                                  
            if ( iRequest->Attribute( CNATFWUNSAFAttribute::ENonce ) )
                {
                // retry using a nonce from error response
                return ETrue;
                }
            return EFalse;
            
        case E439Transitioning:
            // The client should reset the active destination, wait for 
            // 5 seconds and set the active destination to the new value.
            return EFalse;
            
        case E442UnsupportedTransportProtocol:

            return EFalse;
                    
        case E443InvalidIPAddress:
            
            return EFalse;           
            
        case E444InvalidPort:
        
            return EFalse;

        case E445OperationForTCPOnly:

            return EFalse;

        case E446ConnectionAlreadyExists:
         
            return EFalse;
            
        case E486AllocationQuotaReached:
            // The user or client is not authorized to request additional
            // allocations.          
            return EFalse;

        case E500ServerError:               
            return ETrue;  
                                      
        case E507InsufficientCapacity:
            // The server cannot allocate a new port for this client as it has
            // exhausted its relay capacity.        
            return EFalse;

        case E600GlobalFailure:
            return EFalse; 

        case ERetryAfterAddingXorOnly:
            iAddXorOnly = ETrue;
            return ETrue;

        default:
            return STUNUtils::Is5xxResponse( aError );
        }

    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::IcmpError
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::IcmpError( const TInetAddr& aAddress )
    {
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::IcmpError" )
    if ( iTransaction && iServerAddress.CmpAddr( aAddress ) )
        {
        //Try next address, or terminate with KErrTimedOut.
        iTransaction->Terminate( KErrTimedOut );
        }
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::AllocateRequestL
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::AllocateRequestL( TUint /*aRtoValue*/ )
    {
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::AllocateRequestL" )
    iRequestType = EAllocateRequest;
    iState->SendRequestL( *this );
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::SetActiveDestinationRequestL
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::SetActiveDestinationRequestL( 
    const TInetAddr& aRemoteAddr, TUint32& aTimerValue )
    {
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::SetActiveDestinationRequestL" )
    
    iRequestType = ESetActiveDestinationRequest;
    iRemoteAddr = aRemoteAddr;
    iTimerValue = NULL;
    iTimerValue = &aTimerValue;
    iState->SendRequestL( *this );
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::SendIndicationL
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::SendIndicationL( 
    const TInetAddr& aRemoteAddr, const TDesC8& aData, 
    TBool /*aAddFingerprint*/ )
    {
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::SendIndicationL" )
    
    TNATFWUNSAFTransactionID transactionID;
    ClientL().ObtainTransactionIDL( transactionID );
    
    CNATFWUNSAFMessage* 
        indication = CNATFWUNSAFSendIndication::NewL( transactionID );
    CleanupStack::PushL( indication );
    
    indication->AddAttributeL( CNATFWUNSAFDataAttribute::NewLC( aData ) );
    CleanupStack::Pop(); // CNATFWUNSAFDataAttribute
    
    indication->AddAttributeL( CNATFWUNSAFRemoteAddressAttribute::NewLC( 
        aRemoteAddr ) );
    CleanupStack::Pop(); // CNATFWUNSAFRemoteAddressAttribute
    

    if ( iUnknownAttr )
        {
        STUNUtils::RemoveUnknownAttributes( *indication, *iUnknownAttr );
        delete iUnknownAttr;
        iUnknownAttr = NULL;
        }
        
    if ( ETcpProtocol == ClientL().TransportProtocol() )
        {
        __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::SendIndicationL TCP" )
        
        CBufBase* message = indication->EncodeL();
        CleanupStack::PushL( message );
        CNATFWUNSAFTcpRelayPacket* packet = 
            CNATFWUNSAFTcpRelayPacket::NewLC( message->Ptr( 0 ),
            CNATFWUNSAFTcpRelayPacket::EFrameTypeStun );

        iIndicationTx->TransmitL( *packet );
        CleanupStack::PopAndDestroy( packet );
        CleanupStack::PopAndDestroy( message );
        }
    else
        {
        iIndicationTx->TransmitL( *indication );
        }

    CleanupStack::PopAndDestroy( indication );
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::SendIndicationL exit" )
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::ConnectRequestL
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::ConnectRequestL( 
    const TInetAddr& aRemoteAddr )
    {
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::ConnectRequestL" )
    iRemoteAddr = aRemoteAddr;
    iRequestType = ETCPConnectRequest;
    iState->SendRequestL( *this );
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::ConnectRequestL end" )
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::StreamId
// ---------------------------------------------------------------------------
//
TUint CSTUNRelayBindingImplementation::StreamId() const
    {
    return iStreamId;
    }
    
// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::ConnectionId
// ---------------------------------------------------------------------------
//
TUint CSTUNRelayBindingImplementation::ConnectionId() const
    {
    return iConnectionId;
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::CancelRequest
// If transaction exists, delete it to stop using the socket.
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::CancelRequest()
    {
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::CancelRequest" )
    if ( AddressResolved() )
        {
        ChangeState( *iActive );
        }
    else
        {
        //The initial request is canceled
        ChangeState( *iInit );
        }

    delete iTransaction;
    iTransaction = NULL;
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::CancelRequest end" )
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::Socket
// ---------------------------------------------------------------------------
//
const RSocket& CSTUNRelayBindingImplementation::Socket() const
    {
    return *iSocket;
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::AddressResolved
// The most recent public address is kept also during a refresh.
// ---------------------------------------------------------------------------
//
TBool CSTUNRelayBindingImplementation::AddressResolved() const
    {
    return !iPublicAddr.IsUnspecified();
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::PublicAddr
// ---------------------------------------------------------------------------
//
const TInetAddr& CSTUNRelayBindingImplementation::PublicAddr() const
    {
    return iPublicAddr;
    }
    
// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::AlternateServerAddr
// ---------------------------------------------------------------------------
//
const TInetAddr& CSTUNRelayBindingImplementation::AlternateServerAddr() const
    {
    return iServerAddress;
    }
    
// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::RealmFromResponse
// ---------------------------------------------------------------------------
//
const HBufC8* CSTUNRelayBindingImplementation::RealmFromResponse() const
    {
    return iRealmFromResponse;
    }          

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::HandleDataL
// Must NOT leave, if aData is not a STUN message!
// ---------------------------------------------------------------------------
//
HBufC8* CSTUNRelayBindingImplementation::HandleDataL(
    const TDesC8& aData, TBool& aConsumed, TInetAddr& aRemoteAddr )
    {
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::HandleDataL" )
    aConsumed = EFalse;
    TInt err( KErrNone );
    HBufC8* dataPointer = NULL;
    CNATFWUNSAFMessage* msg = NULL;    
    CNATFWUNSAFTcpRelayPacket* relayPacket = NULL;
    
    if ( ETcpProtocol == ClientL().TransportProtocol() )
        {
        __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::HandleDataL - TCP" )
        
        TRAP( err, relayPacket = DecodeTcpRelayMessageL( aData ) );
        CleanupStack::PushL( relayPacket );
        
        if ( KErrNone == err && relayPacket && 
             CNATFWUNSAFTcpRelayPacket::EFrameTypeStun == relayPacket->Type() )
            {
            TRAP( err, msg = DecodeMessageL( relayPacket->Data() ) );
            if ( KErrNoMemory == err )
                {
                User::Leave( err );
                }
            CleanupStack::PopAndDestroy( relayPacket );
            }
        else if ( relayPacket && 
                  CNATFWUNSAFTcpRelayPacket::EFrameTypeData == relayPacket->Type() )
            {
            HBufC8* ptr = relayPacket->Data().AllocL();
            CleanupStack::PopAndDestroy( relayPacket ); 
            return ptr;
            }
        else if ( KErrNoMemory == err )
            {
            User::Leave( err );
            }
        else
            {            
            __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::HandleDataL -\
                Tcp framing invalid!" )
                
            CleanupStack::PopAndDestroy( relayPacket );
            return NULL;
            }
        }
    else
        {
        TRAP( err, msg = DecodeMessageL( aData ) );
        if ( KErrNoMemory == err )
            {
            User::Leave( err );
            }
        }
    if ( msg )
        {
        CleanupStack::PushL( msg );
        CNATFWUNSAFRealmAttribute* realm = static_cast<CNATFWUNSAFRealmAttribute*>(
            msg->Attribute( CNATFWUNSAFAttribute::ERealm ) );          
        if ( realm )
            {
            delete iRealmFromResponse;
            iRealmFromResponse = NULL;
            const TDesC8& realmValue = ( static_cast<CNATFWUNSAFRealmAttribute*>(
                msg->Attribute( CNATFWUNSAFAttribute::ERealm ) )->Value() );
            iRealmFromResponse = realmValue.AllocL();
            __STUNTURNCLIENT_STR8( "realm: ", *iRealmFromResponse )
            }
            
        CNATFWUNSAFNonceAttribute* nonce = static_cast<CNATFWUNSAFNonceAttribute*>(
            msg->Attribute( CNATFWUNSAFAttribute::ENonce ) );        
        if ( nonce )
            {
            delete iNonce;
            iNonce = NULL;
            const TDesC8& nonceValue = ( static_cast<CNATFWUNSAFNonceAttribute*>(
            msg->Attribute( CNATFWUNSAFAttribute::ENonce ) )->Value() );
            iNonce = nonceValue.AllocL();
            }

        // if aData contains data indication, decode it
        if ( msg->Type() == CNATFWUNSAFMessage::EDataIndication &&
             KErrNone == err )
            {
            const TDesC8& data = ( static_cast<CNATFWUNSAFDataAttribute*>(
                msg->Attribute( CNATFWUNSAFAttribute::EData ) )->Value() );

            // Remote address is returned through reference
            CNATFWUNSAFRemoteAddressAttribute* remoteAddress =
                static_cast<CNATFWUNSAFRemoteAddressAttribute*>
                ( msg->Attribute( CNATFWUNSAFAttribute::ERemoteAddress ) );

            if( remoteAddress )
                {
                aRemoteAddr = remoteAddress->Address();
                }
            else
                {
                remoteAddress = NULL;
                }

            CNATFWUNSAFMessage* msg_unsaf = NULL;

            dataPointer = data.AllocLC(); // to cleanupstack

            TRAP( err, msg_unsaf = DecodeMessageL( *dataPointer ) );
            CleanupStack::PushL( msg_unsaf );
            if ( KErrNoMemory == err )
            	{
            	User::Leave( err );
            	}
            // if we have outstanding transaction, offer data to it
            if ( iTransaction && iRequest && !err && msg_unsaf )
                {
                if ( ValidateMsgType( msg_unsaf ) &&
                     iRequest->TransactionID() == msg_unsaf->TransactionID() )
                    {
                    // Data was encapsulated in data indication.
                    // Data is consumed. dataPointer is not needed.
                    aConsumed = ETrue;
                    iTransaction->ReceiveL( *msg_unsaf, *dataPointer );
                    CleanupStack::PopAndDestroy( msg_unsaf );
                    CleanupStack::PopAndDestroy( dataPointer );
                    dataPointer = NULL;
                    }
                }

            if ( dataPointer )
                {
                // dataPointer is still valid. This means that message was not
                // consumed, and data indication is ripped off from it.
                // Message can also contain media data.
                // Data indication is decapsulated out of that message.
                CleanupStack::PopAndDestroy( msg_unsaf );
                CleanupStack::Pop( dataPointer );
                }
            }

        // Data was NOT in encapsulated in data indication, so offer
        // message to this client.
        else if ( KErrNone == err && iTransaction && iRequest )
            {
            if ( ValidateMsgType( msg ) &&
                 iRequest->TransactionID() == msg->TransactionID() )
                {
                aConsumed = ETrue;
                iTransaction->ReceiveL( *msg, aData );
                }
            }
        else
            {
            // for PCLint approval
            }
        CleanupStack::PopAndDestroy( msg );
        }

    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::HandleDataL end" )

    // if there is not any outstanding transaction, data does not belong
    // to this client. Pointer to decoded data IS NOT returned.
    return dataPointer;
    }


// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::STUNClient
// ---------------------------------------------------------------------------
//
const CSTUNClient* CSTUNRelayBindingImplementation::STUNClient() const
    {
    return iClient ? &iClient->STUNClient() : NULL;
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::GetServerAddressL
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::GetServerAddressL()
    {
    __ASSERT_ALWAYS( iServerAddress.IsUnspecified(),
     User::Leave( KErrAlreadyExists ) );
    __ASSERT_ALWAYS( ClientL().ObtainServerAddress( iServerAddress ),
     User::Leave( KErrNotFound ) );
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::GetSharedSecretL
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::GetSharedSecretL()
    {
    ClientL().ObtainSharedSecretL( iBinding );
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::Username
// ---------------------------------------------------------------------------
//
const TDesC8& CSTUNRelayBindingImplementation::Username() const
    {
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::Username" )
    if ( iRequest && iRequest->HasAttribute( CNATFWUNSAFAttribute::EUsername ) )
        {
        return static_cast<CNATFWUNSAFUsernameAttribute*>(
        iRequest->Attribute( CNATFWUNSAFAttribute::EUsername ) )->Value();
        }

    return KNullDesC8;
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::DetachClient
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::DetachClient()
    {
    iClient = NULL;
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::StoreAddressL
// Transaction no longer needed.
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::StoreAddressL( 
    const TInetAddr& aReflexiveAddr,
    const TInetAddr& aRelayAddr )
    {
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::StoreAddressL" )
    
    iErrorResponseCount = 0;
    iPublicAddr = aReflexiveAddr;
    iRelayAddr = aRelayAddr;
    ClientL().AddressResolvedL( iBinding );

    FreeRequestData();
    
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::StoreAddressL end" )
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::StoreAddressL
// Transaction no longer needed.
// This is kind of wrong functionality if this method get called.
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::StoreAddressL( 
    const TInetAddr& aPublicAddress )
    {
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::StoreAddressL" )
    
    iErrorResponseCount = 0;
    iPublicAddr = aPublicAddress;
    ClientL().AddressResolvedL( iBinding );

    FreeRequestData();
    
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::StoreAddressL end" )
    }

// -----------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::ChangeState
// -----------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::ChangeState( 
    CSTUNBindingState& aNewState )
    {
    iState = &aNewState;
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::Terminate
// Binding can be re-started with SendRequestL.
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::Terminate( TInt aError )
    {
    __STUNTURNCLIENT_INT1( "CSTUNRelayBindingImplementation::Terminate, error:", aError )
    __STUN_ASSERT_RETURN( aError != KErrNone, KErrArgument );

    NATFWUNSAF_INTLOG( "STUNRelayBinding terminated, reason", aError )

    TInetAddr emptyAddr;
    iPublicAddr = emptyAddr;
    iServerAddress = emptyAddr;
    iAddXorOnly = EFalse;
    iErrorResponseCount = 0;
    FreeRequestData();

    if ( iState != iInit )
        {
        __STUNTURNCLIENT( "Binding terminated" )
        
        ChangeState( *iInit );
        TRAP_IGNORE( ClientL().BindingErrorL( Binding(), aError, ETrue ) )
        }
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::FreeRequestData
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::FreeRequestData()
    {
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::FreeRequestData" )
    delete iTransaction;
    iTransaction = NULL;
    delete iUnknownAttr;
    iUnknownAttr = NULL;
    delete iRequest;
    iRequest = NULL;
    delete iSharedSecret;
    iSharedSecret = NULL;
    delete iRealmFromResponse;
    iRealmFromResponse = NULL;
    delete iNonce;
    iNonce = NULL;    
    iRequestType = EUnknown;
    iRemoteAddr.SetAddress( KAFUnspec );
    iICEAttributes = TICEAttributes();
    
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::FreeRequestData end" )
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::IsWaitingSharedSecret
// ---------------------------------------------------------------------------
//
TBool CSTUNRelayBindingImplementation::IsWaitingSharedSecret() const
    {
    return iState == iGetSharedSecret;
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::SharedSecretObtainedL
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::SharedSecretObtainedL( 
    const TDesC8& aUsername, const TDesC8& aPassword )
    {
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::SharedSecretObtainedL" )
    __STUN_ASSERT_L( ( aUsername.Length() > 0 && aPassword.Length() > 0 ) ||
     ( aUsername.Length() == 0 && aPassword.Length() == 0 ), KErrArgument );
    
    
    iState->SharedSecretObtainedL( *this, aUsername, aPassword );
    
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::SharedSecretObtainedL end" )
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::CreateBindingRequestL
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::CreateBindingRequestL( 
    const TDesC8& aUsername, const TDesC8& aPassword )
    {
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::CreateBindingRequestL" )
    
    delete iRequest;
    iRequest = NULL;

    if ( IsSharedSecretRequired() &&
         ( aUsername.Length() == 0 || aPassword.Length() == 0 ) )
         {
         Terminate( KErrAccessDenied );
         User::Leave( KErrAccessDenied );
         }
    iTransactionError = KErrNone;

    HBufC8* newSharedSecret = aPassword.AllocL();
    delete iSharedSecret;
    iSharedSecret = newSharedSecret;

    TNATFWUNSAFTransactionID transactionID;
    ClientL().ObtainTransactionIDL( transactionID );
    
    // Check the request type
    switch( iRequestType )
        {
        case ESendRequest:
            __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::\
            CreateBindingRequestL - BindingRequest" )
            iRequest = CNATFWUNSAFBindingRequest::NewL( transactionID );
            break;
            
        case EAllocateRequest:
            __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::\
            CreateBindingRequestL - AllocateRequest" )
            iRequest = CNATFWUNSAFAllocateRequest::NewL( transactionID );
            break;
                        
        case ETCPConnectRequest:
            __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::\
            CreateBindingRequestL - ConnectRequest" )
            iRequest = CNATFWUNSAFConnectRequest::NewL( transactionID );
            break;
            
        case ESetActiveDestinationRequest:
            __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::\
            CreateBindingRequestL - SetActiveDestinationRequest" )
            iRequest = CNATFWUNSAFSetActiveDestinationRequest::NewL( 
                transactionID );
            break;
            
        // request not recognised
        default:
            User::Leave( KErrArgument );
        }


    if ( aUsername.Length() > 0 )
        {
        __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::\
        CreateBindingRequestL - Add USERNAME attribute" )
        iRequest->AddAttributeL( CNATFWUNSAFUsernameAttribute::NewLC( 
            aUsername ) );
        CleanupStack::Pop(); // CNATFWUNSAFUsernameAttribute
        }

    if ( iICEAttributes.iPriority > 0 )
        {
        __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::\
        CreateBindingRequestL - Add PRIORITY attribute" )
        iRequest->AddAttributeL( CNATFWUNSAFPriorityAttribute::NewLC( 
            iICEAttributes.iPriority ) );
        CleanupStack::Pop(); // CNATFWUNSAFPriorityAttribute
        }
    
    if ( iICEAttributes.iUseCandidate )
        {
        __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::\
        CreateBindingRequestL - Add USE_CANDIDATE attribute" )
        iRequest->AddAttributeL( CNATFWUNSAFUseCandidateAttribute::NewLC() );
        CleanupStack::Pop(); // CNATFWUNSAFUseCandidateAttribute
        }
    
    if ( iICEAttributes.iControlled > 0 )
        {
        __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::\
        CreateBindingRequestL - Add CONTROLLED attribute" )
        iRequest->AddAttributeL( CNATFWUNSAFIceControlledAttribute::NewLC( 
            iICEAttributes.iControlled ) );
        CleanupStack::Pop(); // CNATFWUNSAFIceControlledAttribute
        }
        
    if ( iICEAttributes.iControlling > 0 )
        {
        __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::\
        CreateBindingRequestL - Add CONTROLLING attribute" )
        iRequest->AddAttributeL( CNATFWUNSAFIceControllingAttribute::NewLC( 
            iICEAttributes.iControlling ) );
        CleanupStack::Pop(); // CNATFWUNSAFIceControllingAttribute
        }
    
    if ( !iRemoteAddr.IsUnspecified() )
        {
        __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::\
        CreateBindingRequestL - Add REMOTEADDRESS attribute" )
        iRequest->AddAttributeL( CNATFWUNSAFRemoteAddressAttribute::NewLC( 
            iRemoteAddr ) );
        CleanupStack::Pop(); // CNATFWUNSAFRemoteAddressAttribute
        }
    
    if ( iUseRealmFromResponse )
        {
        iRequest->AddAttributeL( CNATFWUNSAFRealmAttribute::NewLC(
            *iRealmFromResponse ) );
       
        CleanupStack::Pop(); // CNATFWUNSAFRealmAttribute
        }
        
    if ( iUnknownAttr )
        {
        STUNUtils::RemoveUnknownAttributes( *iRequest, *iUnknownAttr );
        delete iUnknownAttr;
        iUnknownAttr = NULL;
        }
        
    if ( iNonce )
        {
        __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::\
        CreateBindingRequestL - Add NONCE attribute" )
        
        iRequest->AddAttributeL( CNATFWUNSAFNonceAttribute::NewLC(
            *iNonce ) );
        CleanupStack::Pop(); // CNATFWUNSAFNonceAttribute        
        }                     

    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::\
    CreateBindingRequestL, exit" )
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::SendBindingRequestL
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::SendBindingRequestL()
    {
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::SendBindingRequestL" )
    
    __STUN_ASSERT_L( !iTransaction, KErrAlreadyExists );
    __STUN_ASSERT_L( iRequest && iSharedSecret, KErrNotFound );
    // Happens if resolving returned unspecified address
    
    if ( iSocket )
        {
        // old way to do it
        iTransaction = CSTUNTransaction::NewL( *iRequest,
                                               iServerAddress,
                                               *iSharedSecret,
                                               *iSocket,
                                               ClientL().TimerProvider(),
                                               ClientL().RetransmitInterval(),
                                               *this,
                                               KStunRelay,
                                               ClientL().TransportProtocol() );
        }

    else
        {
        // New way, to be used
        iTransaction = CSTUNTransaction::NewL( *iRequest,
                                               iServerAddress,
                                               *iSharedSecret,
                                               iStreamId,
                                               iConnectionId,
                                               ClientL().TimerProvider(),
                                               ClientL().RetransmitInterval(),
                                               *this,
                                               KStunRelay,
                                               *iMux,
                                               ClientL().TransportProtocol() );
        }

    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::SendBindingRequestL end" )
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::WaitBeforeRetrying
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::WaitBeforeRetrying()
    {
    StartTimer( STUNUtils::EWaitBeforeRetryDuration );
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::SetICESpecificAttributes
// ---------------------------------------------------------------------------
//
void CSTUNRelayBindingImplementation::SetICESpecificAttributes( 
    const TICEAttributes& aAttributes )
    {
    iICEAttributes = aAttributes;
    }


// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::Binding
// ---------------------------------------------------------------------------
//
const CBinding& CSTUNRelayBindingImplementation::Binding()
    {
    return iBinding;
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::RelayAddr
// ---------------------------------------------------------------------------
//
const TInetAddr& CSTUNRelayBindingImplementation::RelayAddr() const 
    {
    __STUNTURNCLIENT( "CSTUNRelayBindingImplementation::RelayAddr" )
    
    return iRelayAddr;
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::IsSharedSecretRequired
// ---------------------------------------------------------------------------
//
TBool CSTUNRelayBindingImplementation::IsSharedSecretRequired() const
    {
    return iTransactionError == E401Unauthorized || 
       iTransactionError == E430StaleCredentials || 
       iTransactionError == E431IntegrityCheckFailure || 
       iTransactionError == E432MissingUsername ||
       iTransactionError == E433UseTLS ||
       iTransactionError == E434MissingRealm;
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::ClientL
// ---------------------------------------------------------------------------
//
MSTUNBindingObserver& CSTUNRelayBindingImplementation::ClientL() const
    {
    __ASSERT_ALWAYS( iClient, User::Leave( KErrNotFound ) );

    return *iClient;
    }

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::DecodeMessageL
// ---------------------------------------------------------------------------
//
CNATFWUNSAFMessage*
    CSTUNRelayBindingImplementation::DecodeMessageL( const TDesC8& aData ) const
    {
    CNATFWUNSAFMessageFactory* decoder = CNATFWUNSAFMessageFactory::NewLC();
    CNATFWUNSAFMessage* msg = decoder->DecodeL( aData );
    CleanupStack::PopAndDestroy( decoder );
    return msg;
    }
    
// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::DecodeTcpRelayMessageL
// ---------------------------------------------------------------------------
//
CNATFWUNSAFTcpRelayPacket*
    CSTUNRelayBindingImplementation::DecodeTcpRelayMessageL( const TDesC8& aData ) const
    {
    CNATFWUNSAFTcpRelayPacketFactory* decoder = 
                                    CNATFWUNSAFTcpRelayPacketFactory::NewLC();
    CNATFWUNSAFTcpRelayPacket* msg = decoder->DecodeL( aData );
    CleanupStack::PopAndDestroy( decoder );
    return msg;
    }    

// ---------------------------------------------------------------------------
// CSTUNRelayBindingImplementation::ValidateMsgType
// ---------------------------------------------------------------------------
//
TBool CSTUNRelayBindingImplementation::ValidateMsgType( 
    CNATFWUNSAFMessage* aMsg ) const
    {
    if ( aMsg )
        {
        return ( aMsg->Type() == CNATFWUNSAFMessage::EBindingResponse ||
             aMsg->Type() == CNATFWUNSAFMessage::EAllocateResponse ||
             aMsg->Type() == CNATFWUNSAFMessage::EBindingErrorResponse ||
             aMsg->Type() == CNATFWUNSAFMessage::EAllocateErrorResponse ||
             aMsg->Type() == CNATFWUNSAFMessage::EConnectResponse ||
             aMsg->Type() == CNATFWUNSAFMessage::EConnectErrorResponse ||
             aMsg->Type() == 
                CNATFWUNSAFMessage::ESetActiveDestinationResponse ||
             aMsg->Type() == 
                CNATFWUNSAFMessage::ESetActiveDestinationErrorResponse );
        }
    return EFalse;
    }


