/// <summary>
        /// Creates new SIP server transaction for specified request.
        /// </summary>
        /// <param name="flow">SIP data flow which is used to receive request.</param>
        /// <param name="request">SIP request.</param>
        /// <returns>Returns added server transaction.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        public SIP_ServerTransaction CreateServerTransaction(SIP_Flow flow, SIP_Request request)
        {
            if (m_IsDisposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            lock (m_pServerTransactions)
            {
                SIP_ServerTransaction transaction = new SIP_ServerTransaction(m_pStack, flow, request);
                m_pServerTransactions.Add(transaction.Key, transaction);
                transaction.StateChanged += delegate
                {
                    if (transaction.State == SIP_TransactionState.Terminated)
                    {
                        lock (m_pClientTransactions)
                        {
                            m_pServerTransactions.Remove(transaction.Key);
                        }
                    }
                };

                return(transaction);
            }
        }
        /// <summary>
        /// Sends specified request to the specified data flow.
        /// </summary>
        /// <param name="flow">SIP data flow.</param>
        /// <param name="request">SIP request to send.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        private void SendToFlow(SIP_Flow flow, SIP_Request request)
        {
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            #region Contact (RFC 3261 8.1.1.8)

            /*
             *  The Contact header field provides a SIP or SIPS URI that can be used
             *  to contact that specific instance of the UA for subsequent requests.
             *  The Contact header field MUST be present and contain exactly one SIP
             *  or SIPS URI in any request that can result in the establishment of a
             *  dialog.  For the methods defined in this specification, that includes
             *  only the INVITE request.  For these requests, the scope of the
             *  Contact is global.  That is, the Contact header field value contains
             *  the URI at which the UA would like to receive requests, and this URI
             *  MUST be valid even if used in subsequent requests outside of any
             *  dialogs.
             *
             *  If the Request-URI or top Route header field value contains a SIPS
             *  URI, the Contact header field MUST contain a SIPS URI as well.
             */

            SIP_t_ContactParam contact = request.Contact.GetTopMostValue();

            // Add contact header If request-Method can establish dialog and contact header not present.
            if (SIP_Utils.MethodCanEstablishDialog(request.RequestLine.Method) && contact == null)
            {
                SIP_Uri from = (SIP_Uri)request.From.Address.Uri;

                request.Contact.Add((flow.IsSecure ? "sips:" : "sip:") + from.User + "@" +
                                    m_pStack.TransportLayer.GetContactHost(flow));
            }
            // If contact SIP URI and host = auto-allocate, allocate it as needed.
            else if (contact != null && contact.Address.Uri is SIP_Uri &&
                     ((SIP_Uri)contact.Address.Uri).Host == "auto-allocate")
            {
                ((SIP_Uri)contact.Address.Uri).Host =
                    m_pStack.TransportLayer.GetContactHost(flow).ToString();
            }

            #endregion

            m_pTransaction = m_pStack.TransactionLayer.CreateClientTransaction(flow, request, true);
            m_pTransaction.ResponseReceived += ClientTransaction_ResponseReceived;
            m_pTransaction.TimedOut         += ClientTransaction_TimedOut;
            m_pTransaction.TransportError   += ClientTransaction_TransportError;

            // Start transaction processing.
            m_pTransaction.Start();
        }
예제 #3
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner SIP stack.</param>
        /// <param name="flow">Transaction data flow.</param>
        /// <param name="request">SIP request that transaction will handle.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>flow</b> or <b>request</b> is null reference.</exception>
        public SIP_Transaction(SIP_Stack stack, SIP_Flow flow, SIP_Request request)
        {
            if (stack == null)
            {
                throw new ArgumentNullException("stack");
            }
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            m_pStack     = stack;
            m_pFlow      = flow;
            m_pRequest   = request;
            m_Method     = request.RequestLine.Method;
            m_CreateTime = DateTime.Now;
            m_pResponses = new List <SIP_Response>();

            // Validate Via:
            SIP_t_ViaParm via = request.Via.GetTopMostValue();

            if (via == null)
            {
                throw new ArgumentException("Via: header is missing !");
            }
            if (via.Branch == null)
            {
                throw new ArgumentException("Via: header 'branch' parameter is missing !");
            }

            m_ID = via.Branch;

            if (this is SIP_ServerTransaction)
            {
                /*
                 *  We use branch and sent-by as indexing key for transaction, the only special what we need to
                 *  do is to handle CANCEL, because it has same branch as transaction to be canceled.
                 *  For avoiding key collision, we add branch + '-' + 'sent-by' + CANCEL for cancel index key.
                 *  ACK has also same branch, but we won't do transaction for ACK, so it isn't problem.
                 */
                string key = request.Via.GetTopMostValue().Branch + '-' + request.Via.GetTopMostValue().SentBy;
                if (request.RequestLine.Method == SIP_Methods.CANCEL)
                {
                    key += "-CANCEL";
                }
                m_Key = key;
            }
            else
            {
                m_Key = m_ID + "-" + request.RequestLine.Method;
            }
        }
예제 #4
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner SIP stack.</param>
        /// <param name="flow">Transaction data flow.</param>
        /// <param name="request">SIP request that transaction will handle.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>flow</b> or <b>request</b> is null reference.</exception>
        public SIP_Transaction(SIP_Stack stack, SIP_Flow flow, SIP_Request request)
        {
            if (stack == null)
            {
                throw new ArgumentNullException("stack");
            }
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            m_pStack = stack;
            m_pFlow = flow;
            m_pRequest = request;
            m_Method = request.RequestLine.Method;
            m_CreateTime = DateTime.Now;
            m_pResponses = new List<SIP_Response>();

            // Validate Via:
            SIP_t_ViaParm via = request.Via.GetTopMostValue();
            if (via == null)
            {
                throw new ArgumentException("Via: header is missing !");
            }
            if (via.Branch == null)
            {
                throw new ArgumentException("Via: header 'branch' parameter is missing !");
            }

            m_ID = via.Branch;

            if (this is SIP_ServerTransaction)
            {
                /*
                    We use branch and sent-by as indexing key for transaction, the only special what we need to 
                    do is to handle CANCEL, because it has same branch as transaction to be canceled.
                    For avoiding key collision, we add branch + '-' + 'sent-by' + CANCEL for cancel index key.
                    ACK has also same branch, but we won't do transaction for ACK, so it isn't problem.
                */
                string key = request.Via.GetTopMostValue().Branch + '-' + request.Via.GetTopMostValue().SentBy;
                if (request.RequestLine.Method == SIP_Methods.CANCEL)
                {
                    key += "-CANCEL";
                }
                m_Key = key;
            }
            else
            {
                m_Key = m_ID + "-" + request.RequestLine.Method;
            }
        }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="stack">Reference to SIP stack.</param>
 /// <param name="flow">SIP data flow.</param>
 /// <param name="request">Recieved request.</param>
 /// <param name="dialog">SIP dialog which received request.</param>
 /// <param name="transaction">SIP server transaction which must be used to send response back to request maker.</param>
 internal SIP_RequestReceivedEventArgs(SIP_Stack stack,
                                       SIP_Flow flow,
                                       SIP_Request request,
                                       SIP_Dialog dialog,
                                       SIP_ServerTransaction transaction)
 {
     m_pStack = stack;
     m_pFlow = flow;
     m_pRequest = request;
     m_pDialog = dialog;
     m_pTransaction = transaction;
 }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="stack">Reference to SIP stack.</param>
 /// <param name="flow">SIP data flow.</param>
 /// <param name="request">Recieved request.</param>
 /// <param name="dialog">SIP dialog which received request.</param>
 /// <param name="transaction">SIP server transaction which must be used to send response back to request maker.</param>
 internal SIP_RequestReceivedEventArgs(SIP_Stack stack,
                                       SIP_Flow flow,
                                       SIP_Request request,
                                       SIP_Dialog dialog,
                                       SIP_ServerTransaction transaction)
 {
     m_pStack       = stack;
     m_pFlow        = flow;
     m_pRequest     = request;
     m_pDialog      = dialog;
     m_pTransaction = transaction;
 }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner SIP stack.</param>
        /// <param name="flow">SIP data flow which received request.</param>
        /// <param name="request">SIP request that transaction will handle.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>flow</b> or <b>request</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        public SIP_ServerTransaction(SIP_Stack stack, SIP_Flow flow, SIP_Request request)
            : base(stack, flow, request)
        {
            // Log
            if (Stack.Logger != null)
            {
                Stack.Logger.AddText(ID,
                                     "Transaction [branch='" + ID + "';method='" + Method +
                                     "';IsServer=true] created.");
            }

            Start();
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner SIP stack.</param>
        /// <param name="flow">SIP data flow which received request.</param>
        /// <param name="request">SIP request that transaction will handle.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>flow</b> or <b>request</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        public SIP_ServerTransaction(SIP_Stack stack, SIP_Flow flow, SIP_Request request)
            : base(stack, flow, request)
        {
            // Log
            if (Stack.Logger != null)
            {
                Stack.Logger.AddText(ID,
                                     "Transaction [branch='" + ID + "';method='" + Method +
                                     "';IsServer=true] created.");
            }

            Start();
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner SIP stack.</param>
        /// <param name="flow">SIP data flow which is used to send request.</param>
        /// <param name="request">SIP request that transaction will handle.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>flow</b> or <b>request</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        internal SIP_ClientTransaction(SIP_Stack stack, SIP_Flow flow, SIP_Request request)
            : base(stack, flow, request)
        {
            // Log
            if (Stack.Logger != null)
            {
                Stack.Logger.AddText(ID,
                                     "Transaction [branch='" + ID + "';method='" + Method +
                                     "';IsServer=false] created.");
            }

            SetState(SIP_TransactionState.WaitingToStart);
        }
예제 #10
0
        /// <summary>
        /// Cleans up any resources being used.
        /// </summary>
        public virtual void Dispose()
        {
            SetState(SIP_TransactionState.Disposed);
            OnDisposed();

            m_pStack   = null;
            m_pFlow    = null;
            m_pRequest = null;

            StateChanged   = null;
            Disposed       = null;
            TimedOut       = null;
            TransportError = null;
        }
        /// <summary>
        /// Creates new client transaction.
        /// </summary>
        /// <param name="flow">SIP data flow which is used to send request.</param>
        /// <param name="request">SIP request that transaction will handle.</param>
        /// <param name="addVia">If true, transaction will add <b>Via:</b> header, otherwise it's user responsibility.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        /// <returns>Returns created transaction.</returns>
        public SIP_ClientTransaction CreateClientTransaction(SIP_Flow flow, SIP_Request request, bool addVia)
        {
            if (m_IsDisposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            // Add Via:
            if (addVia)
            {
                SIP_t_ViaParm via = new SIP_t_ViaParm();
                via.ProtocolName      = "SIP";
                via.ProtocolVersion   = "2.0";
                via.ProtocolTransport = flow.Transport;
                via.SentBy            = new HostEndPoint("transport_layer_will_replace_it", -1);
                via.Branch            = SIP_t_ViaParm.CreateBranch();
                via.RPort             = 0;
                request.Via.AddToTop(via.ToStringValue());
            }

            lock (m_pClientTransactions)
            {
                SIP_ClientTransaction transaction = new SIP_ClientTransaction(m_pStack, flow, request);
                m_pClientTransactions.Add(transaction.Key, transaction);
                transaction.StateChanged += delegate
                {
                    if (transaction.State == SIP_TransactionState.Terminated)
                    {
                        lock (m_pClientTransactions)
                        {
                            m_pClientTransactions.Remove(transaction.Key);
                        }
                    }
                };

                return(transaction);
            }
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner stack.</param>
        /// <param name="request">SIP request.</param>
        /// <param name="flow">Active data flow what to try before RFC 3261 [4](RFC 3263) methods to use to send request.
        /// This value can be null.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b> or <b>request</b> is null.</exception>
        internal SIP_RequestSender(SIP_Stack stack, SIP_Request request, SIP_Flow flow)
        {
            if (stack == null)
            {
                throw new ArgumentNullException("stack");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            m_pStack   = stack;
            m_pRequest = request;
            m_pFlow    = flow;

            m_pCredentials = new List <NetworkCredential>();
            m_pHops        = new Queue <SIP_Hop>();
        }
예제 #13
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner stack.</param>
        /// <param name="request">SIP request.</param>
        /// <param name="flow">Active data flow what to try before RFC 3261 [4](RFC 3263) methods to use to send request.
        /// This value can be null.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b> or <b>request</b> is null.</exception>
        internal SIP_RequestSender(SIP_Stack stack, SIP_Request request, SIP_Flow flow)
        {
            if (stack == null)
            {
                throw new ArgumentNullException("stack");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            m_pStack = stack;
            m_pRequest = request;
            m_pFlow = flow;

            m_pCredentials = new List<NetworkCredential>();
            m_pHops = new Queue<SIP_Hop>();
        }
        /// <summary>
        /// Ensures that specified request has matching server transaction. If server transaction doesn't exist,
        /// it will be created, otherwise existing transaction will be returned.
        /// </summary>
        /// <param name="flow">SIP data flow which is used to receive request.</param>
        /// <param name="request">SIP request.</param>
        /// <returns>Returns matching transaction.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Is raised when request.Method is ACK request.</exception>
        public SIP_ServerTransaction EnsureServerTransaction(SIP_Flow flow, SIP_Request request)
        {
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }
            if (request.RequestLine.Method == SIP_Methods.ACK)
            {
                throw new InvalidOperationException(
                          "ACK request is transaction less request, can't create transaction for it.");
            }

            /*
             *  We use branch and sent-by as indexing key for transaction, the only special what we need to
             *  do is to handle CANCEL, because it has same branch as transaction to be canceled.
             *  For avoiding key collision, we add branch + '-' + 'sent-by' + CANCEL for cancel index key.
             *  ACK has also same branch, but we won't do transaction for ACK, so it isn't problem.
             */
            string key = request.Via.GetTopMostValue().Branch + '-' + request.Via.GetTopMostValue().SentBy;

            if (request.RequestLine.Method == SIP_Methods.CANCEL)
            {
                key += "-CANCEL";
            }

            lock (m_pServerTransactions)
            {
                SIP_ServerTransaction retVal = null;
                m_pServerTransactions.TryGetValue(key, out retVal);
                // We don't have transaction, create it.
                if (retVal == null)
                {
                    retVal = CreateServerTransaction(flow, request);
                }

                return(retVal);
            }
        }
예제 #15
0
        /// <summary>
        /// Cleans up any resources being used.
        /// </summary>
        public virtual void Dispose()
        {
            lock (m_pLock)
            {
                if (State == SIP_DialogState.Disposed)
                {
                    return;
                }

                SetState(SIP_DialogState.Disposed, true);

                m_pStack        = null;
                m_CallID        = null;
                m_LocalTag      = null;
                m_RemoteTag     = null;
                m_pLocalUri     = null;
                m_pRemoteUri    = null;
                m_pLocalContact = null;
                m_pRemoteTarget = null;
                m_pRouteSet     = null;
                m_pFlow         = null;
                m_pLock         = null;
            }
        }
        /// <summary>
        /// Is called when client transactions receives response.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event data.</param>
        private void ClientTransaction_ResponseReceived(object sender, SIP_ResponseReceivedEventArgs e)
        {
            lock (m_pLock)
            {
                m_pFlow = e.ClientTransaction.Request.Flow;

                if (e.Response.StatusCode == 401 || e.Response.StatusCode == 407)
                {
                    // Check if authentication failed(We sent authorization data and it's challenged again,
                    // probably user name or password inccorect)
                    bool hasFailedAuthorization = false;
                    foreach (SIP_t_Challenge challange in e.Response.WWWAuthenticate.GetAllValues())
                    {
                        foreach (SIP_t_Credentials credentials in
                                 m_pTransaction.Request.Authorization.GetAllValues())
                        {
                            if (new Auth_HttpDigest(challange.AuthData, "").Realm ==
                                new Auth_HttpDigest(credentials.AuthData, "").Realm)
                            {
                                hasFailedAuthorization = true;
                                break;
                            }
                        }
                    }
                    foreach (SIP_t_Challenge challange in e.Response.ProxyAuthenticate.GetAllValues())
                    {
                        foreach (SIP_t_Credentials credentials in
                                 m_pTransaction.Request.ProxyAuthorization.GetAllValues())
                        {
                            if (new Auth_HttpDigest(challange.AuthData, "").Realm ==
                                new Auth_HttpDigest(credentials.AuthData, "").Realm)
                            {
                                hasFailedAuthorization = true;
                                break;
                            }
                        }
                    }

                    // Authorization failed, pass response to UA.
                    if (hasFailedAuthorization)
                    {
                        OnResponseReceived(e.Response);
                    }
                    // Try to authorize challanges.
                    else
                    {
                        SIP_Request request = m_pRequest.Copy();

                        /* RFC 3261 22.2.
                         *  When a UAC resubmits a request with its credentials after receiving a
                         *  401 (Unauthorized) or 407 (Proxy Authentication Required) response,
                         *  it MUST increment the CSeq header field value as it would normally
                         *  when sending an updated request.
                         */
                        request.CSeq = new SIP_t_CSeq(m_pStack.ConsumeCSeq(), request.CSeq.RequestMethod);

                        // All challanges authorized, resend request.
                        if (Authorize(request, e.Response, Credentials.ToArray()))
                        {
                            SIP_Flow flow = m_pTransaction.Flow;
                            CleanUpActiveTransaction();
                            SendToFlow(flow, request);
                        }
                        // We don't have credentials for one or more challenges.
                        else
                        {
                            OnResponseReceived(e.Response);
                        }
                    }
                }
                else
                {
                    OnResponseReceived(e.Response);
                    if (e.Response.StatusCodeType != SIP_StatusCodeType.Provisional)
                    {
                        OnCompleted();
                    }
                }
            }
        }
        /// <summary>
        /// Processes specified request through this transaction.
        /// </summary>
        /// <param name="flow">SIP data flow.</param>
        /// <param name="request">SIP request.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        internal void ProcessRequest(SIP_Flow flow, SIP_Request request)
        {
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            lock (SyncRoot)
            {
                if (State == SIP_TransactionState.Disposed)
                {
                    return;
                }

                try
                {
                    // Log
                    if (Stack.Logger != null)
                    {
                        byte[] requestData = request.ToByteData();

                        Stack.Logger.AddRead(Guid.NewGuid().ToString(),
                                             null,
                                             0,
                                             "Request [transactionID='" + ID + "'; method='" +
                                             request.RequestLine.Method + "'; cseq='" +
                                             request.CSeq.SequenceNumber + "'; " + "transport='" +
                                             flow.Transport + "'; size='" + requestData.Length +
                                             "'; received '" + flow.LocalEP + "' <- '" + flow.RemoteEP + "'.",
                                             flow.LocalEP,
                                             flow.RemoteEP,
                                             requestData);
                    }

                    #region INVITE

                    if (Method == SIP_Methods.INVITE)
                    {
                        #region INVITE

                        if (request.RequestLine.Method == SIP_Methods.INVITE)
                        {
                            if (State == SIP_TransactionState.Proceeding)
                            {
                                /* RFC 3261 17.2.1.
                                    If a request retransmission is received while in the "Proceeding" state, the most recent provisional 
                                    response that was received from the TU MUST be passed to the transport layer for retransmission.
                                */
                                SIP_Response response = LastProvisionalResponse;
                                if (response != null)
                                {
                                    Stack.TransportLayer.SendResponse(this, response);
                                }
                            }
                            else if (State == SIP_TransactionState.Completed)
                            {
                                /* RFC 3261 17.2.1.
                                    While in the "Completed" state, if a request retransmission is received, the server SHOULD 
                                    pass the response to the transport for retransmission.
                                */
                                Stack.TransportLayer.SendResponse(this, FinalResponse);
                            }
                        }

                            #endregion

                            #region ACK

                        else if (request.RequestLine.Method == SIP_Methods.ACK)
                        {
                            /* RFC 3261 17.2.1
                                If an ACK is received while the server transaction is in the "Completed" state, the server transaction 
                                MUST transition to the "Confirmed" state.  As Timer G is ignored in this state, any retransmissions of the 
                                response will cease.
                         
                                When this state is entered, timer I is set to fire in T4 seconds for unreliable transports, 
                                and zero seconds for reliable transports.
                            */

                            if (State == SIP_TransactionState.Completed)
                            {
                                SetState(SIP_TransactionState.Confirmed);

                                // Stop timers G,H
                                if (m_pTimerG != null)
                                {
                                    m_pTimerG.Dispose();
                                    m_pTimerG = null;

                                    // Log
                                    if (Stack.Logger != null)
                                    {
                                        Stack.Logger.AddText(ID,
                                                             "Transaction [branch='" + ID + "';method='" +
                                                             Method +
                                                             "';IsServer=true] timer G(INVITE response(3xx - 6xx) retransmission) stoped.");
                                    }
                                }
                                if (m_pTimerH != null)
                                {
                                    m_pTimerH.Dispose();
                                    m_pTimerH = null;

                                    // Log
                                    if (Stack.Logger != null)
                                    {
                                        Stack.Logger.AddText(ID,
                                                             "Transaction [branch='" + ID + "';method='" +
                                                             Method +
                                                             "';IsServer=true] timer H(INVITE ACK wait) stoped.");
                                    }
                                }

                                // Start timer I.
                                m_pTimerI = new TimerEx((flow.IsReliable ? 0 : SIP_TimerConstants.T4), false);
                                m_pTimerI.Elapsed += m_pTimerI_Elapsed;
                                // Log
                                if (Stack.Logger != null)
                                {
                                    Stack.Logger.AddText(ID,
                                                         "Transaction [branch='" + ID + "';method='" + Method +
                                                         "';IsServer=true] timer I(INVITE ACK retransission wait) started, will triger after " +
                                                         m_pTimerI.Interval + ".");
                                }
                                m_pTimerI.Enabled = true;
                            }
                        }

                        #endregion
                    }

                        #endregion

                        #region Non-INVITE

                    else
                    {
                        // Non-INVITE transaction may have only request retransmission requests.
                        if (Method == request.RequestLine.Method)
                        {
                            if (State == SIP_TransactionState.Proceeding)
                            {
                                /* RFC 3261 17.2.2.
                                    If a retransmission of the request is received while in the "Proceeding" state, the most
                                    recently sent provisional response MUST be passed to the transport layer for retransmission.
                                */
                                Stack.TransportLayer.SendResponse(this, LastProvisionalResponse);
                            }
                            else if (State == SIP_TransactionState.Completed)
                            {
                                /* RFC 3261 17.2.2.
                                    While in the "Completed" state, the server transaction MUST pass the final response to the transport
                                    layer for retransmission whenever a retransmission of the request is received.
                                */
                                Stack.TransportLayer.SendResponse(this, FinalResponse);
                            }
                        }
                    }

                    #endregion
                }
                catch (SIP_TransportException x)
                {
                    // Log
                    if (Stack.Logger != null)
                    {
                        Stack.Logger.AddText(ID,
                                             "Transaction [branch='" + ID + "';method='" + Method +
                                             "';IsServer=true] transport exception: " + x.Message);
                    }

                    OnTransportError(x);
                    SetState(SIP_TransactionState.Terminated);
                }
            }
        }
예제 #18
0
            /// <summary>
            /// Creates new flow from TCP server session.
            /// </summary>
            /// <param name="session">TCP server session.</param>
            /// <returns>Returns created flow.</returns>
            /// <exception cref="ArgumentNullException">Is raised when <b>session</b> is null reference.</exception>
            internal SIP_Flow CreateFromSession(TCP_ServerSession session)
            {
                if (session == null)
                {
                    throw new ArgumentNullException("session");
                }

                string flowID = session.LocalEndPoint + "-" + session.RemoteEndPoint + "-" +
                                (session.IsSecureConnection ? SIP_Transport.TLS : SIP_Transport.TCP);

                lock (m_pLock)
                {
                    SIP_Flow flow = new SIP_Flow(m_pOwner.Stack, session);
                    m_pFlows.Add(flowID, flow);
                    flow.IsDisposing += delegate
                                            {
                                                lock (m_pLock)
                                                {
                                                    m_pFlows.Remove(flowID);
                                                }
                                            };
                    flow.Start();

                    return flow;
                }
            }
예제 #19
0
 /// <summary>
 /// Sends request to the specified flow.
 /// </summary>
 /// <param name="flow">Data flow.</param>
 /// <param name="request">SIP request.</param>
 /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
 /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
 /// <exception cref="ArgumentException">Is raised when any of the arguments contains invalid value.</exception>
 public void SendRequest(SIP_Flow flow, SIP_Request request)
 {
     SendRequest(flow, request, null);
 }
예제 #20
0
        /// <summary>
        /// Sends request to the specified flow.
        /// </summary>
        /// <param name="flow">Data flow.</param>
        /// <param name="request">SIP request.</param>
        /// <param name="transaction">Owner client transaction or null if stateless sending.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments contains invalid value.</exception>
        internal void SendRequest(SIP_Flow flow, SIP_Request request, SIP_ClientTransaction transaction)
        {
            if (m_IsDisposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }
            if (request.Via.GetTopMostValue() == null)
            {
                throw new ArgumentException("Argument 'request' doesn't contain required Via: header field.");
            }

            // Set sent-by
            SIP_t_ViaParm via = request.Via.GetTopMostValue();
            via.ProtocolTransport = flow.Transport;
            // Via sent-by is used only to send responses when request maker data flow is not active.
            // Normally this never used, so just report first local listening point as sent-by.
            HostEndPoint sentBy = null;
            foreach (IPBindInfo bind in BindInfo)
            {
                if (flow.Transport == SIP_Transport.UDP && bind.Protocol == BindInfoProtocol.UDP)
                {
                    if (!string.IsNullOrEmpty(bind.HostName))
                    {
                        sentBy = new HostEndPoint(bind.HostName, bind.Port);
                    }
                    else
                    {
                        sentBy = new HostEndPoint(flow.LocalEP.Address.ToString(), bind.Port);
                    }
                    break;
                }
                else if (flow.Transport == SIP_Transport.TLS && bind.Protocol == BindInfoProtocol.TCP &&
                         bind.SslMode == SslMode.SSL)
                {
                    if (!string.IsNullOrEmpty(bind.HostName))
                    {
                        sentBy = new HostEndPoint(bind.HostName, bind.Port);
                    }
                    else
                    {
                        sentBy = new HostEndPoint(flow.LocalEP.Address.ToString(), bind.Port);
                    }
                    break;
                }
                else if (flow.Transport == SIP_Transport.TCP && bind.Protocol == BindInfoProtocol.TCP)
                {
                    if (!string.IsNullOrEmpty(bind.HostName))
                    {
                        sentBy = new HostEndPoint(bind.HostName, bind.Port);
                    }
                    else
                    {
                        sentBy = new HostEndPoint(flow.LocalEP.Address.ToString(), bind.Port);
                    }
                    break;
                }
            }
            // No local end point for sent-by, just use flow local end point for it.
            if (sentBy == null)
            {
                via.SentBy = new HostEndPoint(flow.LocalEP);
            }
            else
            {
                via.SentBy = sentBy;
            }

            // Send request.
            flow.Send(request);

            // Log.
            if (m_pStack.Logger != null)
            {
                byte[] requestData = request.ToByteData();

                m_pStack.Logger.AddWrite(Guid.NewGuid().ToString(),
                                         null,
                                         0,
                                         "Request [" +
                                         (transaction == null ? "" : "transactionID='" + transaction.ID + "';") +
                                         "method='" + request.RequestLine.Method + "'; cseq='" +
                                         request.CSeq.SequenceNumber + "'; " + "transport='" + flow.Transport +
                                         "'; size='" + requestData.Length + "'; sent '" + flow.LocalEP +
                                         "' -> '" + flow.RemoteEP + "'.",
                                         flow.LocalEP,
                                         flow.RemoteEP,
                                         requestData);
            }
        }
        /// <summary>
        /// This method is called when REGISTER has finished.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event data.</param>
        private void m_pRegisterSender_ResponseReceived(object sender, SIP_ResponseReceivedEventArgs e)
        {
            m_pFlow = e.ClientTransaction.Flow;

            if (e.Response.StatusCodeType == SIP_StatusCodeType.Success)
            {
                SetState(SIP_UA_RegistrationState.Registered);

                OnRegistered();

                m_pFlow.SendKeepAlives = true;
            }
            else
            {
                SetState(SIP_UA_RegistrationState.Error);

                OnError(e);
            }

            // REMOVE ME:
            if (AutoFixContact && (m_pContact is SIP_Uri))
            {
                // If Via: received or rport paramter won't match to our sent-by, use received and rport to construct new contact value.

                SIP_Uri   cContact   = ((SIP_Uri)m_pContact);
                IPAddress cContactIP = Net_Utils.IsIPAddress(cContact.Host)
                                           ? IPAddress.Parse(cContact.Host)
                                           : null;
                SIP_t_ViaParm via = e.Response.Via.GetTopMostValue();
                if (via != null && cContactIP != null)
                {
                    IPEndPoint ep = new IPEndPoint(via.Received != null ? via.Received : cContactIP,
                                                   via.RPort > 0 ? via.RPort : cContact.Port);
                    if (!cContactIP.Equals(ep.Address) || cContact.Port != via.RPort)
                    {
                        // Unregister old contact.
                        BeginUnregister(false);

                        // Fix contact.
                        cContact.Host = ep.Address.ToString();
                        cContact.Port = ep.Port;

                        m_pRegisterSender.Dispose();
                        m_pRegisterSender = null;

                        BeginRegister(m_AutoRefresh);

                        return;
                    }
                }
            }

            if (m_AutoRefresh)
            {
                // Set registration refresh timer.
                m_pTimer.Enabled = true;
            }

            m_pRegisterSender.Dispose();
            m_pRegisterSender = null;
        }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="stack">Reference to SIP stack.</param>
 /// <param name="flow">SIP data flow.</param>
 /// <param name="request">Recieved request.</param>
 internal SIP_RequestReceivedEventArgs(SIP_Stack stack, SIP_Flow flow, SIP_Request request)
     : this(stack, flow, request, null, null)
 {
 }
        /// <summary>
        /// Processes specified response through this transaction.
        /// </summary>
        /// <param name="flow">SIP data flow what received response.</param>
        /// <param name="response">SIP response to process.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b>,<b>response</b> is null reference.</exception>
        internal void ProcessResponse(SIP_Flow flow, SIP_Response response)
        {
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (response == null)
            {
                throw new ArgumentNullException("response");
            }

            lock (SyncRoot)
            {
                if (State == SIP_TransactionState.Disposed)
                {
                    return;
                }
                    /* RFC 3261 9.1. CANCEL.
                    *) If provisional response, send CANCEL, we should get '478 Request terminated'.
                    *) If final response, skip canceling, nothing to cancel.
                */
                else if (m_IsCanceling && response.StatusCodeType == SIP_StatusCodeType.Provisional)
                {
                    SendCancel();
                    return;
                }

                // Log
                if (Stack.Logger != null)
                {
                    byte[] responseData = response.ToByteData();

                    Stack.Logger.AddRead(Guid.NewGuid().ToString(),
                                         null,
                                         0,
                                         "Response [transactionID='" + ID + "'; method='" +
                                         response.CSeq.RequestMethod + "'; cseq='" +
                                         response.CSeq.SequenceNumber + "'; " + "transport='" + flow.Transport +
                                         "'; size='" + responseData.Length + "'; statusCode='" +
                                         response.StatusCode + "'; " + "reason='" + response.ReasonPhrase +
                                         "'; received '" + flow.LocalEP + "' <- '" + flow.RemoteEP + "'.",
                                         flow.LocalEP,
                                         flow.RemoteEP,
                                         responseData);
                }

                #region INVITE

                /* RFC 3261 17.1.1.2.
                                                   |INVITE from TU
                                 Timer A fires     |INVITE sent
                                 Reset A,          V                      Timer B fires
                                 INVITE sent +-----------+                or Transport Err.
                                   +---------|           |---------------+inform TU
                                   |         |  Calling  |               |
                                   +-------->|           |-------------->|
                                             +-----------+ 2xx           |
                                                |  |       2xx to TU     |
                                                |  |1xx                  |
                        300-699 +---------------+  |1xx to TU            |
                       ACK sent |                  |                     |
                    resp. to TU |  1xx             V                     |
                                |  1xx to TU  -----------+               |
                                |  +---------|           |               |
                                |  |         |Proceeding |-------------->|
                                |  +-------->|           | 2xx           |
                                |            +-----------+ 2xx to TU     |
                                |       300-699    |                     |
                                |       ACK sent,  |                     |
                                |       resp. to TU|                     |
                                |                  |                     |      NOTE:
                                |  300-699         V                     |
                                |  ACK sent  +-----------+Transport Err. |  transitions
                                |  +---------|           |Inform TU      |  labeled with
                                |  |         | Completed |-------------->|  the event
                                |  +-------->|           |               |  over the action
                                |            +-----------+               |  to take
                                |              ^   |                     |
                                |              |   | Timer D fires       |
                                +--------------+   | -                   |
                                                   |                     |
                                                   V                     |
                                             +-----------+               |
                                             |           |               |
                                             | Terminated|<--------------+
                                             |           |
                                             +-----------+

                */

                if (Method == SIP_Methods.INVITE)
                {
                    #region Calling

                    if (State == SIP_TransactionState.Calling)
                    {
                        // Store response.
                        AddResponse(response);

                        // Stop timer A,B
                        if (m_pTimerA != null)
                        {
                            m_pTimerA.Dispose();
                            m_pTimerA = null;

                            // Log
                            if (Stack.Logger != null)
                            {
                                Stack.Logger.AddText(ID,
                                                     "Transaction [branch='" + ID + "';method='" + Method +
                                                     "';IsServer=false] timer A(INVITE request retransmission) stoped.");
                            }
                        }
                        if (m_pTimerB != null)
                        {
                            m_pTimerB.Dispose();
                            m_pTimerB = null;

                            // Log
                            if (Stack.Logger != null)
                            {
                                Stack.Logger.AddText(ID,
                                                     "Transaction [branch='" + ID + "';method='" + Method +
                                                     "';IsServer=false] timer B(INVITE calling state timeout) stoped.");
                            }
                        }

                        // 1xx response.
                        if (response.StatusCodeType == SIP_StatusCodeType.Provisional)
                        {
                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Proceeding);
                        }
                            // 2xx response.
                        else if (response.StatusCodeType == SIP_StatusCodeType.Success)
                        {
                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Terminated);
                        }
                            // 3xx - 6xx response.
                        else
                        {
                            SendAck(response);
                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Completed);

                            /* RFC 3261 17.1.1.2. 
                                The client transaction SHOULD start timer D when it enters the "Completed" state, 
                                with a value of at least 32 seconds for unreliable transports, and a value of zero 
                                seconds for reliable transports.
                            */
                            m_pTimerD = new TimerEx(Flow.IsReliable ? 0 : Workaround.Definitions.MaxStreamLineLength, false);
                            m_pTimerD.Elapsed += m_pTimerD_Elapsed;
                            // Log
                            if (Stack.Logger != null)
                            {
                                Stack.Logger.AddText(ID,
                                                     "Transaction [branch='" + ID + "';method='" + Method +
                                                     "';IsServer=false] timer D(INVITE 3xx - 6xx response retransmission wait) started, will triger after " +
                                                     m_pTimerD.Interval + ".");
                            }
                            m_pTimerD.Enabled = true;
                        }
                    }

                        #endregion

                        #region Proceeding

                    else if (State == SIP_TransactionState.Proceeding)
                    {
                        // Store response.
                        AddResponse(response);

                        // 1xx response.
                        if (response.StatusCodeType == SIP_StatusCodeType.Provisional)
                        {
                            OnResponseReceived(response);
                        }
                            // 2xx response.
                        else if (response.StatusCodeType == SIP_StatusCodeType.Success)
                        {
                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Terminated);
                        }
                            // 3xx - 6xx response.
                        else
                        {
                            SendAck(response);
                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Completed);

                            /* RFC 3261 17.1.1.2. 
                                The client transaction SHOULD start timer D when it enters the "Completed" state, 
                                with a value of at least 32 seconds for unreliable transports, and a value of zero 
                                seconds for reliable transports.
                            */
                            m_pTimerD = new TimerEx(Flow.IsReliable ? 0 : Workaround.Definitions.MaxStreamLineLength, false);
                            m_pTimerD.Elapsed += m_pTimerD_Elapsed;
                            // Log
                            if (Stack.Logger != null)
                            {
                                Stack.Logger.AddText(ID,
                                                     "Transaction [branch='" + ID + "';method='" + Method +
                                                     "';IsServer=false] timer D(INVITE 3xx - 6xx response retransmission wait) started, will triger after " +
                                                     m_pTimerD.Interval + ".");
                            }
                            m_pTimerD.Enabled = true;
                        }
                    }

                        #endregion

                        #region Completed

                    else if (State == SIP_TransactionState.Completed)
                    {
                        // 3xx - 6xx
                        if (response.StatusCode >= 300)
                        {
                            SendAck(response);
                        }
                    }

                        #endregion

                        #region Terminated

                    else if (State == SIP_TransactionState.Terminated)
                    {
                        // We should never reach here, but if so, do nothing.
                    }

                    #endregion
                }

                    #endregion

                    #region Non-INVITE

                    /* RFC 3251 17.1.2.2
                                               |Request from TU
                                               |send request
                           Timer E             V
                           send request  +-----------+
                               +---------|           |-------------------+
                               |         |  Trying   |  Timer F          |
                               +-------->|           |  or Transport Err.|
                                         +-----------+  inform TU        |
                            200-699         |  |                         |
                            resp. to TU     |  |1xx                      |
                            +---------------+  |resp. to TU              |
                            |                  |                         |
                            |   Timer E        V       Timer F           |
                            |   send req +-----------+ or Transport Err. |
                            |  +---------|           | inform TU         |
                            |  |         |Proceeding |------------------>|
                            |  +-------->|           |-----+             |
                            |            +-----------+     |1xx          |
                            |              |      ^        |resp to TU   |
                            | 200-699      |      +--------+             |
                            | resp. to TU  |                             |
                            |              |                             |
                            |              V                             |
                            |            +-----------+                   |
                            |            |           |                   |
                            |            | Completed |                   |
                            |            |           |                   |
                            |            +-----------+                   |
                            |              ^   |                         |
                            |              |   | Timer K                 |
                            +--------------+   | -                       |
                                               |                         |
                                               V                         |
                         NOTE:           +-----------+                   |
                                         |           |                   |
                     transitions         | Terminated|<------------------+
                     labeled with        |           |
                     the event           +-----------+
                     over the action
                     to take
                */

                else
                {
                    #region Trying

                    if (State == SIP_TransactionState.Trying)
                    {
                        // Store response.
                        AddResponse(response);

                        // Stop timer E
                        if (m_pTimerE != null)
                        {
                            m_pTimerE.Dispose();
                            m_pTimerE = null;

                            // Log
                            if (Stack.Logger != null)
                            {
                                Stack.Logger.AddText(ID,
                                                     "Transaction [branch='" + ID + "';method='" + Method +
                                                     "';IsServer=false] timer E(Non-INVITE request retransmission) stoped.");
                            }
                        }

                        // 1xx response.
                        if (response.StatusCodeType == SIP_StatusCodeType.Provisional)
                        {
                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Proceeding);
                        }
                            // 2xx - 6xx response.
                        else
                        {
                            // Stop timer F
                            if (m_pTimerF != null)
                            {
                                m_pTimerF.Dispose();
                                m_pTimerF = null;

                                // Log
                                if (Stack.Logger != null)
                                {
                                    Stack.Logger.AddText(ID,
                                                         "Transaction [branch='" + ID + "';method='" + Method +
                                                         "';IsServer=false] timer F(Non-INVITE trying,proceeding state timeout) stoped.");
                                }
                            }

                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Completed);

                            /* RFC 3261 17.1.2.2. 
                                The client transaction enters the "Completed" state, it MUST set
                                Timer K to fire in T4 seconds for unreliable transports, and zero
                                seconds for reliable transports.
                            */
                            m_pTimerK = new TimerEx(Flow.IsReliable ? 1 : SIP_TimerConstants.T4, false);
                            m_pTimerK.Elapsed += m_pTimerK_Elapsed;
                            // Log
                            if (Stack.Logger != null)
                            {
                                Stack.Logger.AddText(ID,
                                                     "Transaction [branch='" + ID + "';method='" + Method +
                                                     "';IsServer=false] timer K(Non-INVITE 3xx - 6xx response retransmission wait) started, will triger after " +
                                                     m_pTimerK.Interval + ".");
                            }
                            m_pTimerK.Enabled = true;
                        }
                    }

                        #endregion

                        #region Proceeding

                    else if (State == SIP_TransactionState.Proceeding)
                    {
                        // Store response.
                        AddResponse(response);

                        // 1xx response.
                        if (response.StatusCodeType == SIP_StatusCodeType.Provisional)
                        {
                            OnResponseReceived(response);
                        }                        
                            // 2xx - 6xx response.
                        else
                        {
                            // Stop timer F
                            if (m_pTimerF != null)
                            {
                                m_pTimerF.Dispose();
                                m_pTimerF = null;

                                // Log
                                if (Stack.Logger != null)
                                {
                                    Stack.Logger.AddText(ID,
                                                         "Transaction [branch='" + ID + "';method='" + Method +
                                                         "';IsServer=false] timer F(Non-INVITE trying,proceeding state timeout) stoped.");
                                }
                            }

                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Completed);

                            /* RFC 3261 17.1.2.2. 
                                The client transaction enters the "Completed" state, it MUST set
                                Timer K to fire in T4 seconds for unreliable transports, and zero
                                seconds for reliable transports.
                            */
                            m_pTimerK = new TimerEx(Flow.IsReliable ? 0 : SIP_TimerConstants.T4, false);
                            m_pTimerK.Elapsed += m_pTimerK_Elapsed;
                            // Log
                            if (Stack.Logger != null)
                            {
                                Stack.Logger.AddText(ID,
                                                     "Transaction [branch='" + ID + "';method='" + Method +
                                                     "';IsServer=false] timer K(Non-INVITE 3xx - 6xx response retransmission wait) started, will triger after " +
                                                     m_pTimerK.Interval + ".");
                            }
                            m_pTimerK.Enabled = true;
                        }
                    }

                        #endregion

                        #region Completed

                    else if (State == SIP_TransactionState.Completed)
                    {
                        // Eat retransmited response.
                    }

                        #endregion

                        #region Terminated

                    else if (State == SIP_TransactionState.Terminated)
                    {
                        // We should never reach here, but if so, do nothing.
                    }

                    #endregion
                }

                #endregion
            }
        }
예제 #24
0
        /// <summary>
        /// Is called when client transactions receives response.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event data.</param>
        private void ClientTransaction_ResponseReceived(object sender, SIP_ResponseReceivedEventArgs e)
        {
            lock (m_pLock)
            {
                m_pFlow = e.ClientTransaction.Request.Flow;

                if (e.Response.StatusCode == 401 || e.Response.StatusCode == 407)
                {
                    // Check if authentication failed(We sent authorization data and it's challenged again, 
                    // probably user name or password inccorect)
                    bool hasFailedAuthorization = false;
                    foreach (SIP_t_Challenge challange in e.Response.WWWAuthenticate.GetAllValues())
                    {
                        foreach (SIP_t_Credentials credentials in
                            m_pTransaction.Request.Authorization.GetAllValues())
                        {
                            if (new Auth_HttpDigest(challange.AuthData, "").Realm ==
                                new Auth_HttpDigest(credentials.AuthData, "").Realm)
                            {
                                hasFailedAuthorization = true;
                                break;
                            }
                        }
                    }
                    foreach (SIP_t_Challenge challange in e.Response.ProxyAuthenticate.GetAllValues())
                    {
                        foreach (SIP_t_Credentials credentials in
                            m_pTransaction.Request.ProxyAuthorization.GetAllValues())
                        {
                            if (new Auth_HttpDigest(challange.AuthData, "").Realm ==
                                new Auth_HttpDigest(credentials.AuthData, "").Realm)
                            {
                                hasFailedAuthorization = true;
                                break;
                            }
                        }
                    }

                    // Authorization failed, pass response to UA.
                    if (hasFailedAuthorization)
                    {
                        OnResponseReceived(e.Response);
                    }
                        // Try to authorize challanges.
                    else
                    {
                        SIP_Request request = m_pRequest.Copy();

                        /* RFC 3261 22.2.
                            When a UAC resubmits a request with its credentials after receiving a
                            401 (Unauthorized) or 407 (Proxy Authentication Required) response,
                            it MUST increment the CSeq header field value as it would normally
                            when sending an updated request.
                        */
                        request.CSeq = new SIP_t_CSeq(m_pStack.ConsumeCSeq(), request.CSeq.RequestMethod);

                        // All challanges authorized, resend request.
                        if (Authorize(request, e.Response, Credentials.ToArray()))
                        {
                            SIP_Flow flow = m_pTransaction.Flow;
                            CleanUpActiveTransaction();
                            SendToFlow(flow, request);
                        }
                            // We don't have credentials for one or more challenges.
                        else
                        {
                            OnResponseReceived(e.Response);
                        }
                    }
                }
                else
                {
                    OnResponseReceived(e.Response);
                    if (e.Response.StatusCodeType != SIP_StatusCodeType.Provisional)
                    {
                        OnCompleted();
                    }
                }
            }
        }
예제 #25
0
        /// <summary>
        /// Creates response for the specified request.
        /// </summary>
        /// <param name="statusCode_reasonText">Status-code reasontext.</param>
        /// <param name="request">SIP request.</param>
        /// <param name="flow">Data flow what sends response. This value is used to construct Contact: header value. 
        /// This value can be null, but then adding Contact: header is response sender responsibility.</param>
        /// <returns>Returns created response.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>statusCode_reasonText</b> or <b>request</b> is null reference.</exception>
        /// <exception cref="InvalidOperationException">Is raised when request is ACK-request. ACK request is response less.</exception>
        public SIP_Response CreateResponse(string statusCode_reasonText, SIP_Request request, SIP_Flow flow)
        {
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }
            if (request.RequestLine.Method == SIP_Methods.ACK)
            {
                throw new InvalidOperationException("ACK is responseless request !");
            }

            /* RFC 3261 8.2.6.1.
                When a 100 (Trying) response is generated, any Timestamp header field
                present in the request MUST be copied into this 100 (Trying)
                response.
              
               RFC 3261 8.2.6.2.
                The From field of the response MUST equal the From header field of
                the request.  The Call-ID header field of the response MUST equal the
                Call-ID header field of the request.  The CSeq header field of the
                response MUST equal the CSeq field of the request.  The Via header
                field values in the response MUST equal the Via header field values
                in the request and MUST maintain the same ordering.

                If a request contained a To tag in the request, the To header field
                in the response MUST equal that of the request.  However, if the To
                header field in the request did not contain a tag, the URI in the To
                header field in the response MUST equal the URI in the To header
                field; additionally, the UAS MUST add a tag to the To header field in
                the response (with the exception of the 100 (Trying) response, in
                which a tag MAY be present).  This serves to identify the UAS that is
                responding, possibly resulting in a component of a dialog ID.  The
                same tag MUST be used for all responses to that request, both final
                and provisional (again excepting the 100 (Trying)).  Procedures for
                the generation of tags are defined in Section 19.3.
            
               RFC 3261 12.1.1.
                When a UAS responds to a request with a response that establishes a
                dialog (such as a 2xx to INVITE), the UAS MUST copy all Record-Route
                header field values from the request into the response (including the
                URIs, URI parameters, and any Record-Route header field parameters,
                whether they are known or unknown to the UAS) and MUST maintain the
                order of those values.            
            */

            SIP_Response response = new SIP_Response(request);
            response.StatusCode_ReasonPhrase = statusCode_reasonText;
            foreach (SIP_t_ViaParm via in request.Via.GetAllValues())
            {
                response.Via.Add(via.ToStringValue());
            }
            response.From = request.From;
            response.To = request.To;
            if (request.To.Tag == null)
            {
                response.To.Tag = SIP_Utils.CreateTag();
            }
            response.CallID = request.CallID;
            response.CSeq = request.CSeq;

            // TODO: Allow: / Supported:

            if (SIP_Utils.MethodCanEstablishDialog(request.RequestLine.Method))
            {
                foreach (SIP_t_AddressParam route in request.RecordRoute.GetAllValues())
                {
                    response.RecordRoute.Add(route.ToStringValue());
                }

                if (response.StatusCodeType == SIP_StatusCodeType.Success &&
                    response.Contact.GetTopMostValue() == null && flow != null)
                {
                    try
                    {
                        string user = ((SIP_Uri) response.To.Address.Uri).User;
                        response.Contact.Add((flow.IsSecure ? "sips:" : "sip:") + user + "@" +
                                             TransportLayer.Resolve(flow));
                    }
                    catch
                    {
                        // TODO: Log
                    }
                }
            }

            return response;
        }
예제 #26
0
        /// <summary>
        /// Creates SIP request sender for the specified request.
        /// </summary>
        /// <param name="request">SIP request.</param>
        /// <param name="flow">Data flow.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>request</b> is null reference.</exception>
        internal SIP_RequestSender CreateRequestSender(SIP_Request request, SIP_Flow flow)
        {
            if (m_IsDisposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            SIP_RequestSender sender = new SIP_RequestSender(this, request, flow);
            sender.Credentials.AddRange(m_pCredentials);

            return sender;
        }
예제 #27
0
        /// <summary>
        /// Gets contact URI <b>host</b> parameter suitable to the specified flow.
        /// </summary>
        /// <param name="flow">Data flow.</param>
        /// <returns>Returns contact URI <b>host</b> parameter suitable to the specified flow.</returns>
        internal HostEndPoint GetContactHost(SIP_Flow flow)
        {
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }

            HostEndPoint retVal = null;

            // Find suitable listening point for flow.
            foreach (IPBindInfo bind in BindInfo)
            {
                if (bind.Protocol == BindInfoProtocol.UDP && flow.Transport == SIP_Transport.UDP)
                {
                    // For UDP flow localEP is also listeining EP, so use it.
                    if (bind.IP.AddressFamily == flow.LocalEP.AddressFamily && bind.Port == flow.LocalEP.Port)
                    {
                        retVal =
                            new HostEndPoint(
                                (string.IsNullOrEmpty(bind.HostName)
                                     ? flow.LocalEP.Address.ToString()
                                     : bind.HostName),
                                bind.Port);
                        break;
                    }
                }
                else if (bind.Protocol == BindInfoProtocol.TCP && bind.SslMode == SslMode.SSL &&
                         flow.Transport == SIP_Transport.TLS)
                {
                    // Just use first matching listening point.
                    //   TODO: Probably we should imporve it with load-balanched local end point.
                    if (bind.IP.AddressFamily == flow.LocalEP.AddressFamily)
                    {
                        if (bind.IP == IPAddress.Any || bind.IP == IPAddress.IPv6Any)
                        {
                            retVal =
                                new HostEndPoint(
                                    (string.IsNullOrEmpty(bind.HostName)
                                         ? flow.LocalEP.Address.ToString()
                                         : bind.HostName),
                                    bind.Port);
                        }
                        else
                        {
                            retVal =
                                new HostEndPoint(
                                    (string.IsNullOrEmpty(bind.HostName)
                                         ? bind.IP.ToString()
                                         : bind.HostName),
                                    bind.Port);
                        }
                        break;
                    }
                }
                else if (bind.Protocol == BindInfoProtocol.TCP && flow.Transport == SIP_Transport.TCP)
                {
                    // Just use first matching listening point.
                    //   TODO: Probably we should imporve it with load-balanched local end point.
                    if (bind.IP.AddressFamily == flow.LocalEP.AddressFamily)
                    {
                        if (bind.IP.Equals(IPAddress.Any) || bind.IP.Equals(IPAddress.IPv6Any))
                        {
                            retVal =
                                new HostEndPoint(
                                    (string.IsNullOrEmpty(bind.HostName)
                                         ? flow.LocalEP.Address.ToString()
                                         : bind.HostName),
                                    bind.Port);
                        }
                        else
                        {
                            retVal =
                                new HostEndPoint(
                                    (string.IsNullOrEmpty(bind.HostName)
                                         ? bind.IP.ToString()
                                         : bind.HostName),
                                    bind.Port);
                        }
                        break;
                    }
                }
            }

            // We don't have suitable listening point for active flow.
            // RFC 3261 forces to have, but for TCP based protocls + NAT, server can't connect to use anyway, 
            // so just ignore it and report flow local EP.
            if (retVal == null)
            {
                retVal = new HostEndPoint(flow.LocalEP);
            }

            // If flow remoteEP is public IP and our localEP is private IP, resolve localEP to public.
            if (retVal.IsIPAddress && Core.IsPrivateIP(IPAddress.Parse(retVal.Host)) &&
                !Core.IsPrivateIP(flow.RemoteEP.Address))
            {
                retVal = new HostEndPoint(Resolve(flow));
            }

            return retVal;
        }
예제 #28
0
        /// <summary>
        /// Resolves data flow local NATed IP end point to public IP end point.
        /// </summary>
        /// <param name="flow">Data flow.</param>
        /// <returns>Returns public IP end point of local NATed IP end point.</returns>
        /// <exception cref="ArgumentNullException">Is raised <b>flow</b> is null reference.</exception>
        internal IPEndPoint Resolve(SIP_Flow flow)
        {
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }

            IPEndPoint resolvedEP = null;
            AutoResetEvent completionWaiter = new AutoResetEvent(false);
            // Create OPTIONS request
            SIP_Request optionsRequest = m_pStack.CreateRequest(SIP_Methods.OPTIONS,
                                                                new SIP_t_NameAddress("sip:[email protected]"),
                                                                new SIP_t_NameAddress("sip:[email protected]"));
            SIP_ClientTransaction optionsTransaction = m_pStack.TransactionLayer.CreateClientTransaction(
                flow, optionsRequest, true);
            optionsTransaction.ResponseReceived += delegate(object s, SIP_ResponseReceivedEventArgs e)
                                                       {
                                                           SIP_t_ViaParm via =
                                                               e.Response.Via.GetTopMostValue();

                                                           resolvedEP =
                                                               new IPEndPoint(
                                                                   via.Received == null
                                                                       ? flow.LocalEP.Address
                                                                       : via.Received,
                                                                   via.RPort > 0
                                                                       ? via.RPort
                                                                       : flow.LocalEP.Port);

                                                           completionWaiter.Set();
                                                       };
            optionsTransaction.StateChanged += delegate
                                                   {
                                                       if (optionsTransaction.State ==
                                                           SIP_TransactionState.Terminated)
                                                       {
                                                           completionWaiter.Set();
                                                       }
                                                   };
            optionsTransaction.Start();

            // Wait OPTIONS request to complete.
            completionWaiter.WaitOne();

            if (resolvedEP != null)
            {
                return resolvedEP;
            }
            else
            {
                return flow.LocalEP;
            }
        }
예제 #29
0
        /// <summary>
        /// Initializes dialog.
        /// </summary>
        /// <param name="stack">Owner stack.</param>
        /// <param name="transaction">Owner transaction.</param>
        /// <param name="response">SIP response what caused dialog creation.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>transaction</b> or <b>response</b>.</exception>
        protected internal virtual void Init(SIP_Stack stack,
                                             SIP_Transaction transaction,
                                             SIP_Response response)
        {
            if (stack == null)
            {
                throw new ArgumentNullException("stack");
            }
            if (transaction == null)
            {
                throw new ArgumentNullException("transaction");
            }
            if (response == null)
            {
                throw new ArgumentNullException("response");
            }

            m_pStack = stack;

            #region UAS

            /* RFC 3261 12.1.1.
                The UAS then constructs the state of the dialog.  This state MUST be
                maintained for the duration of the dialog.

                If the request arrived over TLS, and the Request-URI contained a SIPS
                URI, the "secure" flag is set to TRUE.

                The route set MUST be set to the list of URIs in the Record-Route
                header field from the request, taken in order and preserving all URI
                parameters.  If no Record-Route header field is present in the
                request, the route set MUST be set to the empty set.  This route set,
                even if empty, overrides any pre-existing route set for future
                requests in this dialog.  The remote target MUST be set to the URI
                from the Contact header field of the request.

                The remote sequence number MUST be set to the value of the sequence
                number in the CSeq header field of the request.  The local sequence
                number MUST be empty.  The call identifier component of the dialog ID
                MUST be set to the value of the Call-ID in the request.  The local
                tag component of the dialog ID MUST be set to the tag in the To field
                in the response to the request (which always includes a tag), and the
                remote tag component of the dialog ID MUST be set to the tag from the
                From field in the request.  A UAS MUST be prepared to receive a
                request without a tag in the From field, in which case the tag is
                considered to have a value of null.

                    This is to maintain backwards compatibility with RFC 2543, which
                    did not mandate From tags.

                The remote URI MUST be set to the URI in the From field, and the
                local URI MUST be set to the URI in the To field.
            */

            if (transaction is SIP_ServerTransaction)
            {
                // TODO: Validate request or client transaction must do it ?

                m_IsSecure = ((SIP_Uri) transaction.Request.RequestLine.Uri).IsSecure;
                m_pRouteSet =
                    (SIP_t_AddressParam[]) Core.ReverseArray(transaction.Request.RecordRoute.GetAllValues());
                m_pRemoteTarget = (SIP_Uri) transaction.Request.Contact.GetTopMostValue().Address.Uri;
                m_RemoteSeqNo = transaction.Request.CSeq.SequenceNumber;
                m_LocalSeqNo = 0;
                m_CallID = transaction.Request.CallID;
                m_LocalTag = response.To.Tag;
                m_RemoteTag = transaction.Request.From.Tag;
                m_pRemoteUri = transaction.Request.From.Address.Uri;
                m_pLocalUri = transaction.Request.To.Address.Uri;
                m_pLocalContact = (SIP_Uri) response.Contact.GetTopMostValue().Address.Uri;
            }

                #endregion

                #region UAC

                /* RFC 3261 12.1.2.
                When a UAC receives a response that establishes a dialog, it
                constructs the state of the dialog.  This state MUST be maintained
                for the duration of the dialog.

                If the request was sent over TLS, and the Request-URI contained a
                SIPS URI, the "secure" flag is set to TRUE.

                The route set MUST be set to the list of URIs in the Record-Route
                header field from the response, taken in reverse order and preserving
                all URI parameters.  If no Record-Route header field is present in
                the response, the route set MUST be set to the empty set.  This route
                set, even if empty, overrides any pre-existing route set for future
                requests in this dialog.  The remote target MUST be set to the URI
                from the Contact header field of the response.

                The local sequence number MUST be set to the value of the sequence
                number in the CSeq header field of the request.  The remote sequence
                number MUST be empty (it is established when the remote UA sends a
                request within the dialog).  The call identifier component of the
                dialog ID MUST be set to the value of the Call-ID in the request.
                The local tag component of the dialog ID MUST be set to the tag in
                the From field in the request, and the remote tag component of the
                dialog ID MUST be set to the tag in the To field of the response.  A
                UAC MUST be prepared to receive a response without a tag in the To
                field, in which case the tag is considered to have a value of null.

                    This is to maintain backwards compatibility with RFC 2543, which
                    did not mandate To tags.

                The remote URI MUST be set to the URI in the To field, and the local
                URI MUST be set to the URI in the From field.
            */

            else
            {
                // TODO: Validate request or client transaction must do it ?

                m_IsSecure = ((SIP_Uri) transaction.Request.RequestLine.Uri).IsSecure;
                m_pRouteSet = (SIP_t_AddressParam[]) Core.ReverseArray(response.RecordRoute.GetAllValues());
                m_pRemoteTarget = (SIP_Uri) response.Contact.GetTopMostValue().Address.Uri;
                m_LocalSeqNo = transaction.Request.CSeq.SequenceNumber;
                m_RemoteSeqNo = 0;
                m_CallID = transaction.Request.CallID;
                m_LocalTag = transaction.Request.From.Tag;
                m_RemoteTag = response.To.Tag;
                m_pRemoteUri = transaction.Request.To.Address.Uri;
                m_pLocalUri = transaction.Request.From.Address.Uri;
                m_pLocalContact = (SIP_Uri) transaction.Request.Contact.GetTopMostValue().Address.Uri;
            }

            #endregion

            m_pFlow = transaction.Flow;
        }
예제 #30
0
            /// <summary>
            /// Returns existing flow if exists, otherwise new created flow.
            /// </summary>
            /// <param name="isServer">Specifies if created flow is server or client flow. This has effect only if flow is created.</param>
            /// <param name="localEP">Local end point.</param>
            /// <param name="remoteEP">Remote end point.</param>
            /// <param name="transport">SIP transport.</param>
            /// <returns>Returns existing flow if exists, otherwise new created flow.</returns>
            /// <exception cref="ArgumentNullException">Is raised when <b>localEP</b>,<b>remoteEP</b> or <b>transport</b> is null reference.</exception>
            /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
            internal SIP_Flow GetOrCreateFlow(bool isServer,
                                              IPEndPoint localEP,
                                              IPEndPoint remoteEP,
                                              string transport)
            {
                if (localEP == null)
                {
                    throw new ArgumentNullException("localEP");
                }
                if (remoteEP == null)
                {
                    throw new ArgumentNullException("remoteEP");
                }
                if (transport == null)
                {
                    throw new ArgumentNullException("transport");
                }

                string flowID = localEP + "-" + remoteEP + "-" + transport;

                lock (m_pLock)
                {
                    SIP_Flow flow = null;
                    if (m_pFlows.TryGetValue(flowID, out flow))
                    {
                        return flow;
                    }
                    else
                    {
                        flow = new SIP_Flow(m_pOwner.Stack, isServer, localEP, remoteEP, transport);
                        m_pFlows.Add(flow.ID, flow);
                        flow.IsDisposing += delegate
                                                {
                                                    lock (m_pLock)
                                                    {
                                                        m_pFlows.Remove(flowID);
                                                    }
                                                };
                        flow.Start();

                        return flow;
                    }
                }
            }
예제 #31
0
        /// <summary>
        /// Initializes dialog.
        /// </summary>
        /// <param name="stack">Owner stack.</param>
        /// <param name="transaction">Owner transaction.</param>
        /// <param name="response">SIP response what caused dialog creation.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>transaction</b> or <b>response</b>.</exception>
        protected internal virtual void Init(SIP_Stack stack,
                                             SIP_Transaction transaction,
                                             SIP_Response response)
        {
            if (stack == null)
            {
                throw new ArgumentNullException("stack");
            }
            if (transaction == null)
            {
                throw new ArgumentNullException("transaction");
            }
            if (response == null)
            {
                throw new ArgumentNullException("response");
            }

            m_pStack = stack;

            #region UAS

            /* RFC 3261 12.1.1.
             *  The UAS then constructs the state of the dialog.  This state MUST be
             *  maintained for the duration of the dialog.
             *
             *  If the request arrived over TLS, and the Request-URI contained a SIPS
             *  URI, the "secure" flag is set to TRUE.
             *
             *  The route set MUST be set to the list of URIs in the Record-Route
             *  header field from the request, taken in order and preserving all URI
             *  parameters.  If no Record-Route header field is present in the
             *  request, the route set MUST be set to the empty set.  This route set,
             *  even if empty, overrides any pre-existing route set for future
             *  requests in this dialog.  The remote target MUST be set to the URI
             *  from the Contact header field of the request.
             *
             *  The remote sequence number MUST be set to the value of the sequence
             *  number in the CSeq header field of the request.  The local sequence
             *  number MUST be empty.  The call identifier component of the dialog ID
             *  MUST be set to the value of the Call-ID in the request.  The local
             *  tag component of the dialog ID MUST be set to the tag in the To field
             *  in the response to the request (which always includes a tag), and the
             *  remote tag component of the dialog ID MUST be set to the tag from the
             *  From field in the request.  A UAS MUST be prepared to receive a
             *  request without a tag in the From field, in which case the tag is
             *  considered to have a value of null.
             *
             *      This is to maintain backwards compatibility with RFC 2543, which
             *      did not mandate From tags.
             *
             *  The remote URI MUST be set to the URI in the From field, and the
             *  local URI MUST be set to the URI in the To field.
             */

            if (transaction is SIP_ServerTransaction)
            {
                // TODO: Validate request or client transaction must do it ?

                m_IsSecure  = ((SIP_Uri)transaction.Request.RequestLine.Uri).IsSecure;
                m_pRouteSet =
                    (SIP_t_AddressParam[])Core.ReverseArray(transaction.Request.RecordRoute.GetAllValues());
                m_pRemoteTarget = (SIP_Uri)transaction.Request.Contact.GetTopMostValue().Address.Uri;
                m_RemoteSeqNo   = transaction.Request.CSeq.SequenceNumber;
                m_LocalSeqNo    = 0;
                m_CallID        = transaction.Request.CallID;
                m_LocalTag      = response.To.Tag;
                m_RemoteTag     = transaction.Request.From.Tag;
                m_pRemoteUri    = transaction.Request.From.Address.Uri;
                m_pLocalUri     = transaction.Request.To.Address.Uri;
                m_pLocalContact = (SIP_Uri)response.Contact.GetTopMostValue().Address.Uri;
            }

            #endregion

            #region UAC

            /* RFC 3261 12.1.2.
             * When a UAC receives a response that establishes a dialog, it
             * constructs the state of the dialog.  This state MUST be maintained
             * for the duration of the dialog.
             *
             * If the request was sent over TLS, and the Request-URI contained a
             * SIPS URI, the "secure" flag is set to TRUE.
             *
             * The route set MUST be set to the list of URIs in the Record-Route
             * header field from the response, taken in reverse order and preserving
             * all URI parameters.  If no Record-Route header field is present in
             * the response, the route set MUST be set to the empty set.  This route
             * set, even if empty, overrides any pre-existing route set for future
             * requests in this dialog.  The remote target MUST be set to the URI
             * from the Contact header field of the response.
             *
             * The local sequence number MUST be set to the value of the sequence
             * number in the CSeq header field of the request.  The remote sequence
             * number MUST be empty (it is established when the remote UA sends a
             * request within the dialog).  The call identifier component of the
             * dialog ID MUST be set to the value of the Call-ID in the request.
             * The local tag component of the dialog ID MUST be set to the tag in
             * the From field in the request, and the remote tag component of the
             * dialog ID MUST be set to the tag in the To field of the response.  A
             * UAC MUST be prepared to receive a response without a tag in the To
             * field, in which case the tag is considered to have a value of null.
             *
             *  This is to maintain backwards compatibility with RFC 2543, which
             *  did not mandate To tags.
             *
             * The remote URI MUST be set to the URI in the To field, and the local
             * URI MUST be set to the URI in the From field.
             */

            else
            {
                // TODO: Validate request or client transaction must do it ?

                m_IsSecure      = ((SIP_Uri)transaction.Request.RequestLine.Uri).IsSecure;
                m_pRouteSet     = (SIP_t_AddressParam[])Core.ReverseArray(response.RecordRoute.GetAllValues());
                m_pRemoteTarget = (SIP_Uri)response.Contact.GetTopMostValue().Address.Uri;
                m_LocalSeqNo    = transaction.Request.CSeq.SequenceNumber;
                m_RemoteSeqNo   = 0;
                m_CallID        = transaction.Request.CallID;
                m_LocalTag      = transaction.Request.From.Tag;
                m_RemoteTag     = response.To.Tag;
                m_pRemoteUri    = transaction.Request.To.Address.Uri;
                m_pLocalUri     = transaction.Request.From.Address.Uri;
                m_pLocalContact = (SIP_Uri)transaction.Request.Contact.GetTopMostValue().Address.Uri;
            }

            #endregion

            m_pFlow = transaction.Flow;
        }
예제 #32
0
        /// <summary>
        /// Cleans up any resources being used.
        /// </summary>
        public virtual void Dispose()
        {
            lock (m_pLock)
            {
                if (State == SIP_DialogState.Disposed)
                {
                    return;
                }

                SetState(SIP_DialogState.Disposed, true);

                m_pStack = null;
                m_CallID = null;
                m_LocalTag = null;
                m_RemoteTag = null;
                m_pLocalUri = null;
                m_pRemoteUri = null;
                m_pLocalContact = null;
                m_pRemoteTarget = null;
                m_pRouteSet = null;
                m_pFlow = null;
                m_pLock = null;
            }
        }
        /// <summary>
        /// Creates new SIP server transaction for specified request.
        /// </summary>
        /// <param name="flow">SIP data flow which is used to receive request.</param>
        /// <param name="request">SIP request.</param>
        /// <returns>Returns added server transaction.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        public SIP_ServerTransaction CreateServerTransaction(SIP_Flow flow, SIP_Request request)
        {
            if (m_IsDisposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            lock (m_pServerTransactions)
            {
                SIP_ServerTransaction transaction = new SIP_ServerTransaction(m_pStack, flow, request);
                m_pServerTransactions.Add(transaction.Key, transaction);
                transaction.StateChanged += delegate
                                                {
                                                    if (transaction.State == SIP_TransactionState.Terminated)
                                                    {
                                                        lock (m_pClientTransactions)
                                                        {
                                                            m_pServerTransactions.Remove(transaction.Key);
                                                        }
                                                    }
                                                };

                return transaction;
            }
        }
        /// <summary>
        /// Processes specified request through this transaction.
        /// </summary>
        /// <param name="flow">SIP data flow.</param>
        /// <param name="request">SIP request.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        internal void ProcessRequest(SIP_Flow flow, SIP_Request request)
        {
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            lock (SyncRoot)
            {
                if (State == SIP_TransactionState.Disposed)
                {
                    return;
                }

                try
                {
                    // Log
                    if (Stack.Logger != null)
                    {
                        byte[] requestData = request.ToByteData();

                        Stack.Logger.AddRead(Guid.NewGuid().ToString(),
                                             null,
                                             0,
                                             "Request [transactionID='" + ID + "'; method='" +
                                             request.RequestLine.Method + "'; cseq='" +
                                             request.CSeq.SequenceNumber + "'; " + "transport='" +
                                             flow.Transport + "'; size='" + requestData.Length +
                                             "'; received '" + flow.LocalEP + "' <- '" + flow.RemoteEP + "'.",
                                             flow.LocalEP,
                                             flow.RemoteEP,
                                             requestData);
                    }

                    #region INVITE

                    if (Method == SIP_Methods.INVITE)
                    {
                        #region INVITE

                        if (request.RequestLine.Method == SIP_Methods.INVITE)
                        {
                            if (State == SIP_TransactionState.Proceeding)
                            {
                                /* RFC 3261 17.2.1.
                                 *  If a request retransmission is received while in the "Proceeding" state, the most recent provisional
                                 *  response that was received from the TU MUST be passed to the transport layer for retransmission.
                                 */
                                SIP_Response response = LastProvisionalResponse;
                                if (response != null)
                                {
                                    Stack.TransportLayer.SendResponse(this, response);
                                }
                            }
                            else if (State == SIP_TransactionState.Completed)
                            {
                                /* RFC 3261 17.2.1.
                                 *  While in the "Completed" state, if a request retransmission is received, the server SHOULD
                                 *  pass the response to the transport for retransmission.
                                 */
                                Stack.TransportLayer.SendResponse(this, FinalResponse);
                            }
                        }

                        #endregion

                        #region ACK

                        else if (request.RequestLine.Method == SIP_Methods.ACK)
                        {
                            /* RFC 3261 17.2.1
                             *  If an ACK is received while the server transaction is in the "Completed" state, the server transaction
                             *  MUST transition to the "Confirmed" state.  As Timer G is ignored in this state, any retransmissions of the
                             *  response will cease.
                             *
                             *  When this state is entered, timer I is set to fire in T4 seconds for unreliable transports,
                             *  and zero seconds for reliable transports.
                             */

                            if (State == SIP_TransactionState.Completed)
                            {
                                SetState(SIP_TransactionState.Confirmed);

                                // Stop timers G,H
                                if (m_pTimerG != null)
                                {
                                    m_pTimerG.Dispose();
                                    m_pTimerG = null;

                                    // Log
                                    if (Stack.Logger != null)
                                    {
                                        Stack.Logger.AddText(ID,
                                                             "Transaction [branch='" + ID + "';method='" +
                                                             Method +
                                                             "';IsServer=true] timer G(INVITE response(3xx - 6xx) retransmission) stoped.");
                                    }
                                }
                                if (m_pTimerH != null)
                                {
                                    m_pTimerH.Dispose();
                                    m_pTimerH = null;

                                    // Log
                                    if (Stack.Logger != null)
                                    {
                                        Stack.Logger.AddText(ID,
                                                             "Transaction [branch='" + ID + "';method='" +
                                                             Method +
                                                             "';IsServer=true] timer H(INVITE ACK wait) stoped.");
                                    }
                                }

                                // Start timer I.
                                m_pTimerI          = new TimerEx((flow.IsReliable ? 0 : SIP_TimerConstants.T4), false);
                                m_pTimerI.Elapsed += m_pTimerI_Elapsed;
                                // Log
                                if (Stack.Logger != null)
                                {
                                    Stack.Logger.AddText(ID,
                                                         "Transaction [branch='" + ID + "';method='" + Method +
                                                         "';IsServer=true] timer I(INVITE ACK retransission wait) started, will triger after " +
                                                         m_pTimerI.Interval + ".");
                                }
                                m_pTimerI.Enabled = true;
                            }
                        }

                        #endregion
                    }

                    #endregion

                    #region Non-INVITE

                    else
                    {
                        // Non-INVITE transaction may have only request retransmission requests.
                        if (Method == request.RequestLine.Method)
                        {
                            if (State == SIP_TransactionState.Proceeding)
                            {
                                /* RFC 3261 17.2.2.
                                 *  If a retransmission of the request is received while in the "Proceeding" state, the most
                                 *  recently sent provisional response MUST be passed to the transport layer for retransmission.
                                 */
                                Stack.TransportLayer.SendResponse(this, LastProvisionalResponse);
                            }
                            else if (State == SIP_TransactionState.Completed)
                            {
                                /* RFC 3261 17.2.2.
                                 *  While in the "Completed" state, the server transaction MUST pass the final response to the transport
                                 *  layer for retransmission whenever a retransmission of the request is received.
                                 */
                                Stack.TransportLayer.SendResponse(this, FinalResponse);
                            }
                        }
                    }

                    #endregion
                }
                catch (SIP_TransportException x)
                {
                    // Log
                    if (Stack.Logger != null)
                    {
                        Stack.Logger.AddText(ID,
                                             "Transaction [branch='" + ID + "';method='" + Method +
                                             "';IsServer=true] transport exception: " + x.Message);
                    }

                    OnTransportError(x);
                    SetState(SIP_TransactionState.Terminated);
                }
            }
        }
예제 #35
0
        /// <summary>
        /// Is called when specified SIP flow has got new SIP message.
        /// </summary>
        /// <param name="flow">SIP flow.</param>
        /// <param name="message">Received message.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>message</b> is null reference.</exception>
        internal void OnMessageReceived(SIP_Flow flow, byte[] message)
        {
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (message == null)
            {
                throw new ArgumentNullException("message");
            }

            // TODO: Log

            try
            {
                #region Ping / pong

                // We have "ping"(CRLFCRLF) request, response with "pong".
                if (message.Length == 4)
                {
                    if (Stack.Logger != null)
                    {
                        Stack.Logger.AddRead("",
                                             null,
                                             2,
                                             "Flow [id='" + flow.ID + "'] received \"ping\"",
                                             flow.LocalEP,
                                             flow.RemoteEP);
                    }

                    // Send "pong".
                    flow.SendInternal(new[] {(byte) '\r', (byte) '\n'});

                    if (Stack.Logger != null)
                    {
                        Stack.Logger.AddWrite("",
                                              null,
                                              2,
                                              "Flow [id='" + flow.ID + "'] sent \"pong\"",
                                              flow.LocalEP,
                                              flow.RemoteEP);
                    }

                    return;
                }
                    // We have pong(CRLF), do nothing.
                else if (message.Length == 2)
                {
                    if (Stack.Logger != null)
                    {
                        Stack.Logger.AddRead("",
                                             null,
                                             2,
                                             "Flow [id='" + flow.ID + "'] received \"pong\"",
                                             flow.LocalEP,
                                             flow.RemoteEP);
                    }

                    return;
                }

                #endregion

                #region Response

                if (Encoding.UTF8.GetString(message, 0, 3).ToUpper().StartsWith("SIP"))
                {
                    #region Parse and validate response

                    SIP_Response response = null;
                    try
                    {
                        response = SIP_Response.Parse(message);
                    }
                    catch (Exception x)
                    {
                        if (m_pStack.Logger != null)
                        {
                            m_pStack.Logger.AddText("Skipping message, parse error: " + x);
                        }

                        return;
                    }

                    try
                    {
                        response.Validate();
                    }
                    catch (Exception x)
                    {
                        if (m_pStack.Logger != null)
                        {
                            m_pStack.Logger.AddText("Response validation failed: " + x);
                        }

                        return;
                    }

                    #endregion

                    /* RFC 3261 18.1.2 Receiving Responses.
                        When a response is received, the client transport examines the top
                        Via header field value.  If the value of the "sent-by" parameter in
                        that header field value does not correspond to a value that the
                        client transport is configured to insert into requests, the response
                        MUST be silently discarded.

                        If there are any client transactions in existence, the client
                        transport uses the matching procedures of Section 17.1.3 to attempt
                        to match the response to an existing transaction.  If there is a
                        match, the response MUST be passed to that transaction.  Otherwise,
                        the response MUST be passed to the core (whether it be stateless
                        proxy, stateful proxy, or UA) for further processing.  Handling of
                        these "stray" responses is dependent on the core (a proxy will
                        forward them, while a UA will discard, for example).
                    */

                    SIP_ClientTransaction transaction =
                        m_pStack.TransactionLayer.MatchClientTransaction(response);
                    // Allow client transaction to process response.
                    if (transaction != null)
                    {
                        transaction.ProcessResponse(flow, response);
                    }
                    else
                    {
                        // Pass response to dialog.
                        SIP_Dialog dialog = m_pStack.TransactionLayer.MatchDialog(response);
                        if (dialog != null)
                        {
                            dialog.ProcessResponse(response);
                        }
                            // Pass response to core.
                        else
                        {
                            m_pStack.OnResponseReceived(new SIP_ResponseReceivedEventArgs(m_pStack,
                                                                                          null,
                                                                                          response));
                        }
                    }
                }

                    #endregion

                    #region Request

                    // SIP request.
                else
                {
                    #region Parse and validate request

                    SIP_Request request = null;
                    try
                    {
                        request = SIP_Request.Parse(message);
                    }
                    catch (Exception x)
                    {
                        // Log
                        if (m_pStack.Logger != null)
                        {
                            m_pStack.Logger.AddText("Skipping message, parse error: " + x.Message);
                        }

                        return;
                    }

                    try
                    {
                        request.Validate();
                    }
                    catch (Exception x)
                    {
                        if (m_pStack.Logger != null)
                        {
                            m_pStack.Logger.AddText("Request validation failed: " + x);
                        }

                        // Bad request, send error to request maker.
                        SendResponse(
                            m_pStack.CreateResponse(SIP_ResponseCodes.x400_Bad_Request + ". " + x.Message,
                                                    request));

                        return;
                    }

                    #endregion

                    // TODO: Is that needed, core can reject message as it would like.
                    SIP_ValidateRequestEventArgs eArgs = m_pStack.OnValidateRequest(request, flow.RemoteEP);
                    // Request rejected, return response.
                    if (eArgs.ResponseCode != null)
                    {
                        SendResponse(m_pStack.CreateResponse(eArgs.ResponseCode, request));

                        return;
                    }

                    request.Flow = flow;
                    request.LocalEndPoint = flow.LocalEP;
                    request.RemoteEndPoint = flow.RemoteEP;

                    /* RFC 3261 18.2.1.
                        When the server transport receives a request over any transport, it
                        MUST examine the value of the "sent-by" parameter in the top Via
                        header field value.  If the host portion of the "sent-by" parameter
                        contains a domain name, or if it contains an IP address that differs
                        from the packet source address, the server MUST add a "received"
                        parameter to that Via header field value.  This parameter MUST
                        contain the source address from which the packet was received.  This
                        is to assist the server transport layer in sending the response,
                        since it must be sent to the source IP address from which the request
                        came.

                        Next, the server transport attempts to match the request to a server
                        transaction.  It does so using the matching rules described in
                        Section 17.2.3.  If a matching server transaction is found, the
                        request is passed to that transaction for processing.  If no match is
                        found, the request is passed to the core, which may decide to
                        construct a new server transaction for that request.  Note that when
                        a UAS core sends a 2xx response to INVITE, the server transaction is
                        destroyed.  This means that when the ACK arrives, there will be no
                        matching server transaction, and based on this rule, the ACK is
                        passed to the UAS core, where it is processed.
                    */

                    /* RFC 3581 4. 
                        When a server compliant to this specification (which can be a proxy
                        or UAS) receives a request, it examines the topmost Via header field
                        value.  If this Via header field value contains an "rport" parameter
                        with no value, it MUST set the value of the parameter to the source
                        port of the request.  This is analogous to the way in which a server
                        will insert the "received" parameter into the topmost Via header
                        field value.  In fact, the server MUST insert a "received" parameter
                        containing the source IP address that the request came from, even if
                        it is identical to the value of the "sent-by" component.  Note that
                        this processing takes place independent of the transport protocol.
                    */

                    SIP_t_ViaParm via = request.Via.GetTopMostValue();
                    via.Received = flow.RemoteEP.Address;
                    if (via.RPort == 0)
                    {
                        via.RPort = flow.RemoteEP.Port;
                    }

                    bool processed = false;
                    SIP_ServerTransaction transaction =
                        m_pStack.TransactionLayer.MatchServerTransaction(request);
                    // Pass request to matched server transaction.
                    if (transaction != null)
                    {
                        transaction.ProcessRequest(flow, request);

                        processed = true;
                    }
                    else
                    {
                        SIP_Dialog dialog = m_pStack.TransactionLayer.MatchDialog(request);
                        // Pass request to dialog.
                        if (dialog != null)
                        {
                            processed =
                                dialog.ProcessRequest(new SIP_RequestReceivedEventArgs(m_pStack, flow, request));
                        }
                    }

                    // Request not proecced by dialog or transaction, pass request to TU.
                    if (!processed)
                    {
                        // Log
                        if (m_pStack.Logger != null)
                        {
                            byte[] requestData = request.ToByteData();

                            m_pStack.Logger.AddRead(Guid.NewGuid().ToString(),
                                                    null,
                                                    0,
                                                    "Request [method='" + request.RequestLine.Method +
                                                    "'; cseq='" + request.CSeq.SequenceNumber + "'; " +
                                                    "transport='" + flow.Transport + "'; size='" +
                                                    requestData.Length + "'; " + "received '" + flow.RemoteEP +
                                                    "' -> '" + flow.LocalEP + "'.",
                                                    flow.LocalEP,
                                                    flow.RemoteEP,
                                                    requestData);
                        }

                        m_pStack.OnRequestReceived(new SIP_RequestReceivedEventArgs(m_pStack, flow, request));
                    }
                }

                #endregion
            }
            catch (SocketException s)
            {
                // Skip all socket errors here
                string dummy = s.Message;
            }
                //catch(ArgumentException x){
                //    m_pStack.OnError(x);
                //}
            catch (Exception x)
            {
                m_pStack.OnError(x);
            }
        }
        /// <summary>
        /// Creates new client transaction.
        /// </summary>
        /// <param name="flow">SIP data flow which is used to send request.</param>
        /// <param name="request">SIP request that transaction will handle.</param>
        /// <param name="addVia">If true, transaction will add <b>Via:</b> header, otherwise it's user responsibility.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        /// <returns>Returns created transaction.</returns>
        public SIP_ClientTransaction CreateClientTransaction(SIP_Flow flow, SIP_Request request, bool addVia)
        {
            if (m_IsDisposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            // Add Via:
            if (addVia)
            {
                SIP_t_ViaParm via = new SIP_t_ViaParm();
                via.ProtocolName = "SIP";
                via.ProtocolVersion = "2.0";
                via.ProtocolTransport = flow.Transport;
                via.SentBy = new HostEndPoint("transport_layer_will_replace_it", -1);
                via.Branch = SIP_t_ViaParm.CreateBranch();
                via.RPort = 0;
                request.Via.AddToTop(via.ToStringValue());
            }

            lock (m_pClientTransactions)
            {
                SIP_ClientTransaction transaction = new SIP_ClientTransaction(m_pStack, flow, request);
                m_pClientTransactions.Add(transaction.Key, transaction);
                transaction.StateChanged += delegate
                                                {
                                                    if (transaction.State == SIP_TransactionState.Terminated)
                                                    {
                                                        lock (m_pClientTransactions)
                                                        {
                                                            m_pClientTransactions.Remove(transaction.Key);
                                                        }
                                                    }
                                                };

                return transaction;
            }
        }
예제 #37
0
        /// <summary>
        /// Cleans up any resources being used.
        /// </summary>
        public virtual void Dispose()
        {
            SetState(SIP_TransactionState.Disposed);
            OnDisposed();

            m_pStack = null;
            m_pFlow = null;
            m_pRequest = null;

            StateChanged = null;
            Disposed = null;
            TimedOut = null;
            TransportError = null;
        }
        /// <summary>
        /// Ensures that specified request has matching server transaction. If server transaction doesn't exist, 
        /// it will be created, otherwise existing transaction will be returned.
        /// </summary>
        /// <param name="flow">SIP data flow which is used to receive request.</param>
        /// <param name="request">SIP request.</param>
        /// <returns>Returns matching transaction.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Is raised when request.Method is ACK request.</exception>
        public SIP_ServerTransaction EnsureServerTransaction(SIP_Flow flow, SIP_Request request)
        {
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }
            if (request.RequestLine.Method == SIP_Methods.ACK)
            {
                throw new InvalidOperationException(
                    "ACK request is transaction less request, can't create transaction for it.");
            }

            /*
                We use branch and sent-by as indexing key for transaction, the only special what we need to 
                do is to handle CANCEL, because it has same branch as transaction to be canceled.
                For avoiding key collision, we add branch + '-' + 'sent-by' + CANCEL for cancel index key.
                ACK has also same branch, but we won't do transaction for ACK, so it isn't problem.
            */
            string key = request.Via.GetTopMostValue().Branch + '-' + request.Via.GetTopMostValue().SentBy;
            if (request.RequestLine.Method == SIP_Methods.CANCEL)
            {
                key += "-CANCEL";
            }

            lock (m_pServerTransactions)
            {
                SIP_ServerTransaction retVal = null;
                m_pServerTransactions.TryGetValue(key, out retVal);
                // We don't have transaction, create it.
                if (retVal == null)
                {
                    retVal = CreateServerTransaction(flow, request);
                }

                return retVal;
            }
        }
        /// <summary>
        /// This method is called when REGISTER has finished.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event data.</param>
        private void m_pRegisterSender_ResponseReceived(object sender, SIP_ResponseReceivedEventArgs e)
        {
            m_pFlow = e.ClientTransaction.Flow;

            if (e.Response.StatusCodeType == SIP_StatusCodeType.Success)
            {
                SetState(SIP_UA_RegistrationState.Registered);

                OnRegistered();

                m_pFlow.SendKeepAlives = true;
            }
            else
            {
                SetState(SIP_UA_RegistrationState.Error);

                OnError(e);
            }

            // REMOVE ME:
            if (AutoFixContact && (m_pContact is SIP_Uri))
            {
                // If Via: received or rport paramter won't match to our sent-by, use received and rport to construct new contact value.

                SIP_Uri cContact = ((SIP_Uri) m_pContact);
                IPAddress cContactIP = Net_Utils.IsIPAddress(cContact.Host)
                                           ? IPAddress.Parse(cContact.Host)
                                           : null;
                SIP_t_ViaParm via = e.Response.Via.GetTopMostValue();
                if (via != null && cContactIP != null)
                {
                    IPEndPoint ep = new IPEndPoint(via.Received != null ? via.Received : cContactIP,
                                                   via.RPort > 0 ? via.RPort : cContact.Port);
                    if (!cContactIP.Equals(ep.Address) || cContact.Port != via.RPort)
                    {
                        // Unregister old contact.
                        BeginUnregister(false);

                        // Fix contact.
                        cContact.Host = ep.Address.ToString();
                        cContact.Port = ep.Port;

                        m_pRegisterSender.Dispose();
                        m_pRegisterSender = null;

                        BeginRegister(m_AutoRefresh);

                        return;
                    }
                }
            }

            if (m_AutoRefresh)
            {
                // Set registration refresh timer.
                m_pTimer.Enabled = true;
            }

            m_pRegisterSender.Dispose();
            m_pRegisterSender = null;
        }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="stack">Reference to SIP stack.</param>
 /// <param name="flow">SIP data flow.</param>
 /// <param name="request">Recieved request.</param>
 internal SIP_RequestReceivedEventArgs(SIP_Stack stack, SIP_Flow flow, SIP_Request request)
     : this(stack, flow, request, null, null) {}
예제 #41
0
        /// <summary>
        /// Sends specified request to the specified data flow.
        /// </summary>
        /// <param name="flow">SIP data flow.</param>
        /// <param name="request">SIP request to send.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        private void SendToFlow(SIP_Flow flow, SIP_Request request)
        {
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            #region Contact (RFC 3261 8.1.1.8)

            /*
                The Contact header field provides a SIP or SIPS URI that can be used
                to contact that specific instance of the UA for subsequent requests.
                The Contact header field MUST be present and contain exactly one SIP
                or SIPS URI in any request that can result in the establishment of a
                dialog.  For the methods defined in this specification, that includes
                only the INVITE request.  For these requests, the scope of the
                Contact is global.  That is, the Contact header field value contains
                the URI at which the UA would like to receive requests, and this URI
                MUST be valid even if used in subsequent requests outside of any
                dialogs.

                If the Request-URI or top Route header field value contains a SIPS
                URI, the Contact header field MUST contain a SIPS URI as well.
            */

            SIP_t_ContactParam contact = request.Contact.GetTopMostValue();

            // Add contact header If request-Method can establish dialog and contact header not present.            
            if (SIP_Utils.MethodCanEstablishDialog(request.RequestLine.Method) && contact == null)
            {
                SIP_Uri from = (SIP_Uri) request.From.Address.Uri;

                request.Contact.Add((flow.IsSecure ? "sips:" : "sip:") + from.User + "@" +
                                    m_pStack.TransportLayer.GetContactHost(flow));
            }
                // If contact SIP URI and host = auto-allocate, allocate it as needed.
            else if (contact != null && contact.Address.Uri is SIP_Uri &&
                     ((SIP_Uri) contact.Address.Uri).Host == "auto-allocate")
            {
                ((SIP_Uri) contact.Address.Uri).Host =
                    m_pStack.TransportLayer.GetContactHost(flow).ToString();
            }

            #endregion

            m_pTransaction = m_pStack.TransactionLayer.CreateClientTransaction(flow, request, true);
            m_pTransaction.ResponseReceived += ClientTransaction_ResponseReceived;
            m_pTransaction.TimedOut += ClientTransaction_TimedOut;
            m_pTransaction.TransportError += ClientTransaction_TransportError;

            // Start transaction processing.
            m_pTransaction.Start();
        }