realtimenetprots/sipfw/SIP/ConnectionMgr/src/COutgoingRequestQueue.cpp
changeset 0 307788aac0a8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/realtimenetprots/sipfw/SIP/ConnectionMgr/src/COutgoingRequestQueue.cpp	Tue Feb 02 01:03:15 2010 +0200
@@ -0,0 +1,609 @@
+// Copyright (c) 2006-2009 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:
+// Name        : COutgoingRequestQueue.cpp
+// Part of     : ConnectionMgr
+// Version     : SIP/5.0
+//
+
+
+
+#include "COutgoingRequestQueue.h"
+#include "MOutgoingRequestQueueContext.h"
+#include "CTransport.h"
+#include "siprequest.h"
+#include "sipstrings.h"
+#include "sipstrconsts.h"
+#include "sipcontactheader.h"
+#include "sipviaheader.h"
+#include "siptoheader.h"
+#include "sipfromheader.h"
+#include "uricontainer.h"
+#include "sipuri.h"
+#include "sipaddress.h"
+#include "siphostport.h"
+#include "TSIPTransportParams.h"
+#include "sipstrings.h"
+#include "sipstrconsts.h"
+#include "SIPHeaderLookup.h"
+#include "siperr.h"
+#include <sipnattraversalcontroller.h> 
+
+const TInt KRequestQueueGranularity = 4;
+    
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::NewLC
+// -----------------------------------------------------------------------------
+//
+COutgoingRequestQueue* COutgoingRequestQueue::NewL( 
+    MOutgoingRequestQueueContext& aContext,
+    CSIPNATTraversalController& aNATTraversal,
+    RConnection& aConnection,
+    TUint32 aIapId )
+    {
+    COutgoingRequestQueue* self = 
+        new ( ELeave ) COutgoingRequestQueue( aContext, 
+                                              aNATTraversal, 
+                                              aConnection, 
+                                              aIapId );
+    
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop( self );
+    return self;
+    }
+    
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::~COutgoingRequestQueue
+// -----------------------------------------------------------------------------
+//
+COutgoingRequestQueue::~COutgoingRequestQueue()
+    {
+    iNATTraversal.FreeResources( iIapId );
+    iRequestQueue.Close();
+    iStrNATTraversalRequired.Close();
+    }
+
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::RequestComplete
+// From MSIPNATTraversalRequestObserver
+// -----------------------------------------------------------------------------
+//
+void COutgoingRequestQueue::RequestComplete( 
+    TUint32 aRequestId, 
+    TInt aCompletionCode )
+    {
+    TInt index = FindRequestQueueItemIndex( aRequestId );
+    if ( index != KErrNotFound )
+        {
+        Complete( iRequestQueue[ index ], aCompletionCode, EFalse );
+        iRequestQueue.Remove( index );
+        iRequestQueue.Compress();
+        }  
+    ActivatePendingRequests();
+    }
+
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::PublicAddrResolved
+// From MSIPNATTraversalRequestObserver
+// -----------------------------------------------------------------------------
+//		    
+void COutgoingRequestQueue::PublicAddrResolved( 
+    TUint32 aRequestId, 
+    const TInetAddr& aPublicAddr )
+    {
+    TInt index = FindRequestQueueItemIndex( aRequestId );
+    if ( index != KErrNotFound )
+        {
+        Update( iRequestQueue[ index ], aPublicAddr );
+        iRequestQueue.Remove( index );
+        iRequestQueue.Compress();
+        }
+    ActivatePendingRequests();
+    }
+
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::UsagePermissionChanged
+// From MSocketUsagePermissionObserver
+// -----------------------------------------------------------------------------
+//    
+void COutgoingRequestQueue::UsagePermissionChanged( 
+    RSocket& aSocket, 
+    TBool aPermissionToUse )
+    {
+    TInt lastIndex( iRequestQueue.Count() - 1 );
+    for ( TInt i = lastIndex; i >= 0; i-- )
+        {
+        TOutgoingRequestQueueItem& item = iRequestQueue[ i ];
+        if ( item.Match( aSocket ) )
+            {
+            // May lead to removal of the item
+            HandleUsagePermissionChange( item, aPermissionToUse );
+            }
+        }
+    ActivatePendingRequests();
+    }
+
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::NATTraverseL
+// -----------------------------------------------------------------------------
+//	
+void COutgoingRequestQueue::NATTraverseL( 
+    const TSIPTransportParams& aParams,
+    CSIPRequest& aRequest,
+    TBool aForceUDP,
+	const TInetAddr& aAddr,
+	const CUri8* aOutboundProxy,
+	TUint aOrigTransport,
+	TRequestStatus &aStatus,
+	const TInetAddr& aLocalAddr )
+    {
+    HBufC8* localAddrBuf = CTransport::ConvertInetAddrToTextL( aLocalAddr );
+	CleanupStack::PushL( localAddrBuf );
+    TUint localPort = ContactPortL( aRequest, *localAddrBuf );
+    CleanupStack::PopAndDestroy( localAddrBuf );    
+    
+    if ( aRequest.Method() == SIPStrings::StringF( SipStrConsts::ERegister ) &&
+         aRequest.To()->HasParam( iStrNATTraversalRequired ) &&
+         !aLocalAddr.IsUnspecified() ) //Nat Traversal for both IPv6 and IPv4 addresses.
+        {
+        RemoveNATTraversalParameter( aRequest );
+        TBool permissionToUse( EFalse );
+        iLocalAddr = aLocalAddr;
+        RSocket& udpSocket = iContext.GetUdpSocketL( aParams, 
+                                                     aAddr, 
+                                                     localPort, 
+                                                     this, 
+                                                     permissionToUse ); 
+    
+        TOutgoingRequestQueueItem queueItem( aParams, 
+                                             aRequest, 
+                                             aForceUDP,
+                                             aAddr, 
+                                             localPort,
+                                             aOutboundProxy,
+                                             aOrigTransport,
+                                             aStatus,
+                                             udpSocket,
+                                             permissionToUse );
+    
+        // If there's already active request for this particular UDP socket,
+        // request cannot be activated before existing request has completed.   
+        if ( permissionToUse && !HasRequestForSocket( udpSocket, EFalse ) )
+            {
+            IssueRequestL( queueItem );
+            }                                                  
+                           
+        iRequestQueue.AppendL( queueItem );
+        
+        // Request status has to be set to pending before going to async.
+        // Set it when this synchronous execution path cannot leave anymore. 
+        aStatus = KRequestPending;        
+        }
+    else
+        {
+        iNATTraversal.UpdateNextHop( 
+            aAddr, 
+            const_cast<TSIPTransportParams&>( aParams ).NATBindingObserver() );
+
+        iContext.ContinueSendToTransportL( aParams,
+                                           aRequest, 
+                                           aForceUDP,
+                                           aAddr,
+                                           localPort,
+                                           aOrigTransport,
+                                           aStatus,
+                                           EFalse );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::FreeResources
+// -----------------------------------------------------------------------------
+//    
+void COutgoingRequestQueue::FreeResources( 
+    MSIPNATBindingObserver& aSIPNATBindingObserver )
+    {
+    iNATTraversal.FreeResources( aSIPNATBindingObserver );
+    }
+
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::CancelSend
+// -----------------------------------------------------------------------------
+//     
+TBool COutgoingRequestQueue::CancelSend( TRequestStatus& aStatus )
+    {
+    for ( TInt i = 0; i < iRequestQueue.Count(); i++ )
+        {
+        TOutgoingRequestQueueItem& item = iRequestQueue[ i ];
+        if ( item.Match( aStatus ) )
+            {
+            iNATTraversal.Cancel( item.RequestId() );
+            Complete( item, KErrSIPTransportFailure, EFalse );
+            iRequestQueue.Remove( i );
+            iRequestQueue.Compress();
+            return ETrue;
+            }
+        }
+    return EFalse;
+    }
+
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::Update
+// -----------------------------------------------------------------------------
+//
+void COutgoingRequestQueue::Update( 
+    TOutgoingRequestQueueItem& aItem, 
+    const TInetAddr& aPublicAddr )
+    {
+    TRAPD( err, UpdateHeadersL( aItem, iLocalAddr, aPublicAddr ) );
+    Complete( aItem, err, ETrue );
+    }
+    
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::Complete
+// -----------------------------------------------------------------------------
+//
+void COutgoingRequestQueue::Complete( 
+    TOutgoingRequestQueueItem& aItem, 
+    TInt aCompletionCode,
+    TBool aPublicAddrResolved )
+    {     
+    if ( !HasRequestForSocket( aItem.Socket(), ETrue ) )
+        {
+        // Notify if no pending requests exists for the socket
+        iContext.UdpSocketFree( aItem.Params(), 
+                                aItem.Addr(), 
+                                aItem.LocalPort(), 
+                                this,
+                                aItem.Socket() );
+        }
+    
+    if ( !aCompletionCode )
+        {
+        TRAP( aCompletionCode,
+              iContext.ContinueSendToTransportL( aItem.Params(),
+                                                 aItem.Request(), 
+                                                 aItem.ForceUDP(),
+                                                 aItem.Addr(),
+                                                 aItem.LocalPort(),
+                                                 aItem.OrigTransport(),
+                                                 aItem.Status(),
+                                                 aPublicAddrResolved ) );
+        }
+        
+    if ( aCompletionCode )
+        {
+        iContext.RequestFailed( aItem.Status(), aCompletionCode );
+        }
+    }
+    
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::UpdateHeadersL
+// -----------------------------------------------------------------------------
+//
+void COutgoingRequestQueue::UpdateHeadersL(
+    TOutgoingRequestQueueItem& aItem,
+    const TInetAddr& aLocalAddr, 
+    const TInetAddr& aPublicAddr )
+    {
+    // Some NAT-aware proxies/registrars detect from the private IP address
+    // that the UA is behind a NAT and maintain a persistent TCP-connection.
+    // If the public address is used with TCP, 
+    // these proxies/registrars will not maintain persistent TCP-connection. 
+    if ( aItem.OrigTransport() == KProtocolInetUdp )
+        {
+        HBufC8* localAddrBuf = 
+            CTransport::ConvertInetAddrToTextL( aLocalAddr );
+    	CleanupStack::PushL( localAddrBuf );
+    	
+    	HBufC8* publicAddrBuf = 
+    	    CTransport::ConvertInetAddrToTextL( aPublicAddr );
+    	CleanupStack::PushL( publicAddrBuf );
+    	
+    	TUint publicPort( aPublicAddr.Port() );
+    	
+    	UpdateContactHeaderL( aItem.Request(), *localAddrBuf, 
+    	                      *publicAddrBuf, publicPort );
+    	                      
+    	UpdateViaHeaderL( aItem.Request(), *publicAddrBuf, publicPort );                    
+    	
+    	CleanupStack::PopAndDestroy( publicAddrBuf );
+    	CleanupStack::PopAndDestroy( localAddrBuf );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::UpdateContactHeaderL
+// -----------------------------------------------------------------------------
+//
+void COutgoingRequestQueue::UpdateContactHeaderL(
+    CSIPMessage& aMessage,
+    const TDesC8& aLocalAddr, 
+    const TDesC8& aPublicAddr,
+    TUint aPublicPort )  
+    {
+    if ( aMessage.HasHeader( 
+                        SIPStrings::StringF(SipStrConsts::EContactHeader) ) )
+		{
+        TSglQueIter<CSIPHeaderBase> iter = 
+            aMessage.Headers( 
+                SIPStrings::StringF(SipStrConsts::EContactHeader) );
+		CSIPHeaderBase* header = 0;
+		while ( ( header = iter++ ) != 0 )
+			{
+			CSIPContactHeader* contact = 
+			        static_cast<CSIPContactHeader*>( header );
+			CSIPAddress* sipAddr = contact->SIPAddress();
+
+			if ( sipAddr )
+			    {
+			    CURIContainer& uriContainer = sipAddr->URI();
+			    if ( uriContainer.IsSIPURI() && 
+			         uriContainer.SIPURI()->HostPort().Host().Compare( 
+			            aLocalAddr ) == 0 )
+			        {
+			        uriContainer.SIPURI()->HostPort().SetHostL( aPublicAddr );
+			        uriContainer.SIPURI()->HostPort().SetPort( aPublicPort );
+			        }
+			    }
+			}
+		}
+    }
+    
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::UpdateViaHeaderL
+// -----------------------------------------------------------------------------
+//
+void COutgoingRequestQueue::UpdateViaHeaderL(
+    CSIPMessage& aMessage,
+    const TDesC8& aPublicAddr,
+    TUint aPublicPort )
+    {
+    CSIPViaHeader* via = CTransport::TopViaHeader( &aMessage );
+    if ( via )
+        {
+        via->SentByHostPort().SetHostL( aPublicAddr );
+    	via->SentByHostPort().SetPort( aPublicPort );
+        }
+    }    
+    
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::HandleUsagePermissionChange
+// -----------------------------------------------------------------------------
+//   
+void COutgoingRequestQueue::HandleUsagePermissionChange( 
+    TOutgoingRequestQueueItem& aItem, 
+    TBool aPermissionToUse )
+    {     
+    aItem.SetPermissionToUse( aPermissionToUse );
+    
+    if ( !aPermissionToUse && aItem.IsActiveRequest() )
+        {
+        CancelSend( aItem.Status() );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::HasRequestForSocket
+// -----------------------------------------------------------------------------
+//
+TBool COutgoingRequestQueue::HasRequestForSocket( 
+    RSocket& aSocket, 
+    TBool aFindPendingRequest )
+    {
+    for ( TInt i = 0; i < iRequestQueue.Count(); i++ )
+        {
+        TOutgoingRequestQueueItem& item = iRequestQueue[ i ];
+        if ( item.Match( aSocket ) && 
+             !item.IsFailed() &&
+             ( item.IsActiveRequest() ^ aFindPendingRequest ) )
+            {
+            return ETrue;
+            }
+        }
+    return EFalse;
+    }
+
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::CleanupFailedRequests
+// -----------------------------------------------------------------------------
+//
+void COutgoingRequestQueue::CleanupFailedRequests()
+    {
+    TInt lastIndex( iRequestQueue.Count() - 1 );
+    for ( TInt i = lastIndex; i >= 0; i-- )
+        {
+        TOutgoingRequestQueueItem& item = iRequestQueue[ i ];
+        if ( item.IsFailed() )
+            {
+            iContext.RequestFailed( item.Status(), item.Error() );
+            iRequestQueue.Remove( i );
+            iRequestQueue.Compress();
+            }
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::ActivatePendingRequests
+// -----------------------------------------------------------------------------
+//    
+void COutgoingRequestQueue::ActivatePendingRequests()
+    {
+    for ( TInt i = 0; i < iRequestQueue.Count(); i++ )
+        {
+        TOutgoingRequestQueueItem& item = iRequestQueue[ i ];
+        
+        // Activate if there's no other active request for the socket
+        // this pending request uses.
+        if ( !item.IsActiveRequest() && 
+             !HasRequestForSocket( item.Socket(), EFalse ) &&
+             item.PermissionToUse() )
+            {
+            TRAPD( err, IssueRequestL( item ) );
+            item.SetFailed( err );
+            if ( err && !HasRequestForSocket( item.Socket(), ETrue ) )
+                {
+                // If no more pending requests, inform that socket is free
+                iContext.UdpSocketFree( item.Params(), 
+                                        item.Addr(), 
+                                        item.LocalPort(), 
+                                        this,
+                                        item.Socket() );
+                }            
+            }
+        }
+    CleanupFailedRequests();
+    }
+
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::IssueRequestL
+// -----------------------------------------------------------------------------
+// 
+void COutgoingRequestQueue::IssueRequestL( TOutgoingRequestQueueItem& aItem )
+    {
+    __ASSERT_ALWAYS( !HasRequestForSocket( aItem.Socket(), EFalse ), 
+                     User::Leave( KErrInUse ) );      
+                     
+    TPtrC8 domain( KNullDesC8 );
+    CSIPURI* toSIPURI = aItem.Request().To()->SIPAddress().URI().SIPURI();
+    if ( toSIPURI )
+        {
+        domain.Set( toSIPURI->HostPort().Host() );
+        }
+                     
+    // Have to do const cast for transport params to get binding observer
+    TUint32 requestId = iNATTraversal.ResolvePublicAddrL( 
+        iIapId, 
+        iConnection, 
+        iLocalAddr,
+        domain,
+        aItem.Socket(),
+        aItem.Addr(),
+        const_cast<TSIPTransportParams&>( aItem.Params() ).NATBindingObserver(),
+        *this );
+    
+    aItem.SetRequestId( requestId );
+    aItem.SetFailed( KErrNone );
+    }
+    
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::FindRequestQueueItemIndex
+// -----------------------------------------------------------------------------
+//  
+TInt COutgoingRequestQueue::FindRequestQueueItemIndex( TUint32 aRequestId ) const
+    {
+    for ( TInt i = 0; i < iRequestQueue.Count(); i++ )
+        {
+        if ( iRequestQueue[ i ].Match( aRequestId ) )
+            {
+            return i;
+            }
+        }
+    return KErrNotFound;
+    }
+    
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::RemoveNATTraversalParameter
+// -----------------------------------------------------------------------------
+//
+void COutgoingRequestQueue::RemoveNATTraversalParameter( CSIPRequest& aRequest )
+    {
+    CSIPToHeader* to = aRequest.To();
+    if ( to )
+        {
+        to->DeleteParam( iStrNATTraversalRequired );
+        }
+    CSIPFromHeader* from = aRequest.From();
+    if ( from )
+        {
+        from->DeleteParam( iStrNATTraversalRequired );
+        }   
+    }
+
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::ContactPortL
+// -----------------------------------------------------------------------------
+//   
+TUint COutgoingRequestQueue::ContactPortL( 
+    CSIPRequest& aRequest,
+    const TDesC8& aLocalAddr )
+    {
+    if ( aRequest.HasHeader( 
+            SIPStrings::StringF(SipStrConsts::EContactHeader) ) )
+		{
+        TSglQueIter<CSIPHeaderBase> iter = 
+            aRequest.Headers( 
+                SIPStrings::StringF(SipStrConsts::EContactHeader) );
+		CSIPHeaderBase* header = 0;
+		while ( ( header = iter++ ) != 0 )
+			{
+			CSIPContactHeader* contact = 
+			        static_cast<CSIPContactHeader*>( header );
+			CSIPAddress* sipAddr = contact->SIPAddress();
+			
+			if ( sipAddr )
+			    {
+			    CURIContainer& uriContainer = sipAddr->URI();
+			    if ( uriContainer.IsSIPURI() && 
+			         uriContainer.SIPURI()->HostPort().Host().Compare( 
+			            aLocalAddr ) == 0 &&
+			         uriContainer.SIPURI()->HostPort().HasPort() )
+			        {
+			        return uriContainer.SIPURI()->HostPort().Port();
+			        }
+			    }
+			}
+		}
+    return 0;
+    }
+
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::ConvertToSIPURIRollBack
+// -----------------------------------------------------------------------------
+//
+void COutgoingRequestQueue::ConvertToSIPURIRollBack(TAny* /*aDummy*/)
+    {
+    SIPHeaderLookup::SetConvertToSIPURI(ETrue);
+    }
+
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::ConstructL
+// -----------------------------------------------------------------------------
+// 
+void COutgoingRequestQueue::ConstructL()
+    {   
+    _LIT8( KNATTraversalRequired, "nat_traversal_required" );    
+    
+    iStrNATTraversalRequired = 
+        SIPStrings::Pool().OpenFStringL( KNATTraversalRequired ); 
+    }
+    
+// -----------------------------------------------------------------------------
+// COutgoingRequestQueue::COutgoingRequestQueue
+// -----------------------------------------------------------------------------
+//    
+COutgoingRequestQueue::COutgoingRequestQueue(
+    MOutgoingRequestQueueContext& aContext,
+    CSIPNATTraversalController& aNATTraversal,
+    RConnection& aConnection,
+    TUint32 aIapId ) :
+    iContext( aContext ),
+    iNATTraversal( aNATTraversal ),
+    iConnection( aConnection ),
+    iIapId( aIapId ),
+    iRequestQueue( KRequestQueueGranularity )
+    {
+    }
+    
+// End of file