/// <summary>
            /// Processes retransmited INVITE 2xx response.
            /// </summary>
            /// <param name="response">INVITE 2xx response.</param>
            /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference.</exception>
            public void Process(SIP_Response response)
            {
                if (response == null)
                {
                    throw new ArgumentNullException("response");
                }

                lock (m_pLock)
                {
                    SIP_Request ack = CreateAck();

                    try
                    {
                        // Try existing flow.
                        m_pDialog.Flow.Send(ack);

                        // Log
                        if (m_pDialog.Stack.Logger != null)
                        {
                            byte[] ackBytes = ack.ToByteData();

                            m_pDialog.Stack.Logger.AddWrite(m_pDialog.ID,
                                                            null,
                                                            ackBytes.Length,
                                                            "Dialog [id='" + m_pDialog.ID +
                                                            "] ACK sent for 2xx response.",
                                                            m_pDialog.Flow.LocalEP,
                                                            m_pDialog.Flow.RemoteEP,
                                                            ackBytes);
                        }
                    }
                    catch
                    {
                        /* RFC 3261 13.2.2.4.
                         *  Once the ACK has been constructed, the procedures of [4] are used to
                         *  determine the destination address, port and transport.  However, the
                         *  request is passed to the transport layer directly for transmission,
                         *  rather than a client transaction.
                         */
                        try
                        {
                            m_pDialog.Stack.TransportLayer.SendRequest(ack);
                        }
                        catch (Exception x)
                        {
                            // Log
                            if (m_pDialog.Stack.Logger != null)
                            {
                                m_pDialog.Stack.Logger.AddText("Dialog [id='" + m_pDialog.ID +
                                                               "'] ACK send for 2xx response failed: " +
                                                               x.Message + ".");
                            }
                        }
                    }
                }
            }
Esempio n. 2
0
        /// <summary>
        /// Sends specified request to flow remote end point.
        /// </summary>
        /// <param name="request">SIP request to send.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>request</b> is null reference.</exception>
        public void Send(SIP_Request request)
        {
            lock (m_pLock)
            {
                if (m_IsDisposed)
                {
                    throw new ObjectDisposedException(GetType().Name);
                }
                if (request == null)
                {
                    throw new ArgumentNullException("request");
                }

                SendInternal(request.ToByteData());
            }
        }
        /// <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);
                }
            }
        }
        /// <summary>
        /// Starts sending request.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        /// <exception cref="InvalidOperationException">Is raised when <b>Start</b> method has alredy called.</exception>
        /// <exception cref="SIP_TransportException">Is raised when no transport hop(s) for request.</exception>
        public void Start()
        {
            lock (m_pLock)
            {
                if (m_State == SIP_RequestSenderState.Disposed)
                {
                    throw new ObjectDisposedException(GetType().Name);
                }
                if (m_IsStarted)
                {
                    throw new InvalidOperationException("Start method has been already called.");
                }
                m_IsStarted = true;
                m_State     = SIP_RequestSenderState.Starting;

                // Start may take so, process it on thread pool.
                ThreadPool.QueueUserWorkItem(delegate
                {
                    lock (m_pLock)
                    {
                        if (m_State == SIP_RequestSenderState.Disposed)
                        {
                            return;
                        }

                        /* RFC 3261 8.1.2 Sending the Request
                         *  The destination for the request is then computed.  Unless there is
                         *  local policy specifying otherwise, the destination MUST be determined
                         *  by applying the DNS procedures described in [4] as follows.  If the
                         *  first element in the route set indicated a strict router (resulting
                         *  in forming the request as described in Section 12.2.1.1), the
                         *  procedures MUST be applied to the Request-URI of the request.
                         *  Otherwise, the procedures are applied to the first Route header field
                         *  value in the request (if one exists), or to the request's Request-URI
                         *  if there is no Route header field present.  These procedures yield an
                         *  ordered set of address, port, and transports to attempt.  Independent
                         *  of which URI is used as input to the procedures of [4], if the
                         *  Request-URI specifies a SIPS resource, the UAC MUST follow the
                         *  procedures of [4] as if the input URI were a SIPS URI.
                         *
                         *  The UAC SHOULD follow the procedures defined in [4] for stateful
                         *  elements, trying each address until a server is contacted.  Each try
                         *  constitutes a new transaction, and therefore each carries a different
                         *  topmost Via header field value with a new branch parameter.
                         *  Furthermore, the transport value in the Via header field is set to
                         *  whatever transport was determined for the target server.
                         */

                        // We never use strict, only loose route.
                        bool isStrictRoute = false;

                        SIP_Uri uri = null;
                        if (isStrictRoute)
                        {
                            uri = (SIP_Uri)m_pRequest.RequestLine.Uri;
                        }
                        else if (m_pRequest.Route.GetTopMostValue() != null)
                        {
                            uri =
                                (SIP_Uri)
                                m_pRequest.Route.GetTopMostValue().Address.
                                Uri;
                        }
                        else
                        {
                            uri = (SIP_Uri)m_pRequest.RequestLine.Uri;
                        }
                        //uri.Param_Transport = "TCP";

                        // Queue hops.
                        foreach (SIP_Hop hop in
                                 m_pStack.GetHops(uri,
                                                  m_pRequest.ToByteData().Length,
                                                  ((SIP_Uri)
                                                   m_pRequest.RequestLine.Uri).
                                                  IsSecure))
                        {
                            m_pHops.Enqueue(hop);
                        }

                        if (m_pHops.Count == 0)
                        {
                            OnTransportError(
                                new SIP_TransportException("No hops for '" +
                                                           uri + "'."));
                            OnCompleted();
                        }
                        else
                        {
                            m_State = SIP_RequestSenderState.Started;

                            try
                            {
                                if (m_pFlow != null)
                                {
                                    SendToFlow(m_pFlow, m_pRequest.Copy());

                                    return;
                                }
                            }
                            catch
                            {
                                // Sending to specified flow failed, probably disposed, just try send to first hop.
                            }

                            SendToNextHop();
                        }
                    }
                });
            }
        }
Esempio n. 5
0
        /// <summary>
        /// Sends specified request to flow remote end point.
        /// </summary>
        /// <param name="request">SIP request to send.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>request</b> is null reference.</exception>
        public void Send(SIP_Request request)
        {
            lock (m_pLock)
            {
                if (m_IsDisposed)
                {
                    throw new ObjectDisposedException(GetType().Name);
                }
                if (request == null)
                {
                    throw new ArgumentNullException("request");
                }

                SendInternal(request.ToByteData());
            }
        }
        /// <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>
        /// Sends request using methods as described in RFC 3261 [4](RFC 3263).
        /// </summary>
        /// <param name="request">SIP request to send.</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.</exception>
        /// <exception cref="SIP_TransportException">Is raised when transport error happens.</exception>
        public void SendRequest(SIP_Request request)
        {
            if (m_IsDisposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            SIP_Hop[] hops = m_pStack.GetHops((SIP_Uri) request.RequestLine.Uri,
                                              request.ToByteData().Length,
                                              false);
            if (hops.Length == 0)
            {
                throw new SIP_TransportException("No target hops for URI '" + request.RequestLine.Uri + "'.");
            }

            SIP_TransportException lastException = null;
            foreach (SIP_Hop hop in hops)
            {
                try
                {
                    SendRequest(request, null, hop);

                    return;
                }
                catch (SIP_TransportException x)
                {
                    lastException = x;
                }
            }

            // If we reach so far, send failed, return last error.
            throw lastException;
        }
        /// <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);
                }
            }
        }