Пример #1
0
        /// <summary>
        /// Processes specified response through this dialog.
        /// </summary>
        /// <param name="response">SIP response to process.</param>
        /// <returns>Returns true if this dialog processed specified response, otherwise false.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null.</exception>
        internal protected virtual bool ProcessResponse(SIP_Response response)
        {
            if (response == null)
            {
                throw new ArgumentNullException("response");
            }

            return(false);
        }
        /// <summary>
        /// Gets existing or creates new dialog.
        /// </summary>
        /// <param name="transaction">Owner transaction what forces to create dialog.</param>
        /// <param name="response">Response what forces to create dialog.</param>
        /// <returns>Returns dialog.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>transaction</b> or <b>response</b> is null.</exception>
        public SIP_Dialog GetOrCreateDialog(SIP_Transaction transaction, SIP_Response response)
        {
            if (transaction == null)
            {
                throw new ArgumentNullException("transaction");
            }
            if (response == null)
            {
                throw new ArgumentNullException("response");
            }

            string dialogID = "";

            if (transaction is SIP_ServerTransaction)
            {
                dialogID = response.CallID + "-" + response.To.Tag + "-" + response.From.Tag;
            }
            else
            {
                dialogID = response.CallID + "-" + response.From.Tag + "-" + response.To.Tag;
            }

            lock (m_pDialogs){
                SIP_Dialog dialog = null;
                m_pDialogs.TryGetValue(dialogID, out dialog);
                // Dialog doesn't exist, create it.
                if (dialog == null)
                {
                    if (response.CSeq.RequestMethod.ToUpper() == SIP_Methods.INVITE)
                    {
                        dialog = new SIP_Dialog_Invite();
                    }
                    else if (response.CSeq.RequestMethod.ToUpper() == SIP_Methods.REFER)
                    {
                        dialog = new SIP_Dialog_Refer();
                    }
                    else
                    {
                        throw new ArgumentException("Method '" + response.CSeq.RequestMethod + "' has no dialog handler.");
                    }

                    dialog.Init(m_pStack, transaction, response);
                    dialog.StateChanged += delegate(object s, EventArgs a){
                        if (dialog.State == SIP_DialogState.Terminated)
                        {
                            m_pDialogs.Remove(dialog.ID);
                        }
                    };
                    m_pDialogs.Add(dialog.ID, dialog);
                }

                return(dialog);
            }
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="transaction">Server transaction.</param>
        /// <param name="response">SIP response.</param>
        /// <exception cref="ArgumentNullException">Is raised when any of the arguments is null.</exception>
        public SIP_ResponseSentEventArgs(SIP_ServerTransaction transaction,SIP_Response response)
        {
            if(transaction == null){
                throw new ArgumentNullException("transaction");
            }
            if(response == null){
                throw new ArgumentNullException("response");
            }

            m_pTransaction = transaction;
            m_pResponse    = response;
        }
Пример #4
0
        /// <summary>
        /// Adds specified response to transaction responses collection.
        /// </summary>
        /// <param name="response">SIP response.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference.</exception>
        protected void AddResponse(SIP_Response response)
        {
            if (response == null)
            {
                throw new ArgumentNullException("response");
            }

            // Don't store more than 15 responses, otherwise hacker may try todo buffer overrun with provisional responses.
            if (m_pResponses.Count < 15 || response.StatusCode >= 200)
            {
                m_pResponses.Add(response);
            }
        }
Пример #5
0
        /// <summary>
        /// Parses SIP_Response from stream.
        /// </summary>
        /// <param name="stream">Stream what contains valid SIP response.</param>
        /// <returns>Returns parsed SIP_Response obeject.</returns>
        /// <exception cref="ArgumentNullException">Raised when <b>stream</b> is null.</exception>
        /// <exception cref="SIP_ParseException">Raised when invalid SIP message.</exception>
        public static SIP_Response Parse(Stream stream)
        {
            /* Syntax:
             *  SIP-Version SP Status-Code SP Reason-Phrase
             *  SIP-Message
             */

            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

            SIP_Response retVal = new SIP_Response();

            // Parse Response-line
            StreamLineReader r = new StreamLineReader(stream);

            r.Encoding = "utf-8";
            string[] version_code_text = r.ReadLineString().Split(new char[] { ' ' }, 3);
            if (version_code_text.Length != 3)
            {
                throw new SIP_ParseException("Invalid SIP Status-Line syntax ! Syntax: {SIP-Version SP Status-Code SP Reason-Phrase}.");
            }
            // SIP-Version
            try
            {
                retVal.SipVersion = Convert.ToDouble(version_code_text[0].Split('/')[1], System.Globalization.NumberFormatInfo.InvariantInfo);
            }
            catch
            {
                throw new SIP_ParseException("Invalid Status-Line SIP-Version value !");
            }

            // Status-Code
            try
            {
                retVal.StatusCode = Convert.ToInt32(version_code_text[1]);
            }
            catch
            {
                throw new SIP_ParseException("Invalid Status-Line Status-Code value !");
            }

            // Reason-Phrase
            retVal.ReasonPhrase = version_code_text[2];

            // Parse SIP-Message
            retVal.InternalParse(stream);

            return(retVal);
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="transaction">Server transaction.</param>
        /// <param name="response">SIP response.</param>
        /// <exception cref="ArgumentNullException">Is raised when any of the arguments is null.</exception>
        public SIP_ResponseSentEventArgs(SIP_ServerTransaction transaction, SIP_Response response)
        {
            if (transaction == null)
            {
                throw new ArgumentNullException("transaction");
            }
            if (response == null)
            {
                throw new ArgumentNullException("response");
            }

            m_pTransaction = transaction;
            m_pResponse    = response;
        }
Пример #7
0
            /// <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 + ".");
                            }
                        }
                    }
                }
            }
        /// <summary>
        /// Creates and sends ACK for final(3xx - 6xx) failure response.
        /// </summary>
        /// <param name="response">SIP response.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null.</exception>
        private void SendAck(SIP_Response response)
        {
            if (response == null)
            {
                throw new ArgumentNullException("resposne");
            }

            /* RFC 3261 17.1.1.3 Construction of the ACK Request.
             *  The ACK request constructed by the client transaction MUST contain
             *  values for the Call-ID, From, and Request-URI that are equal to the
             *  values of those header fields in the request passed to the transport
             *  by the client transaction (call this the "original request").  The To
             *  header field in the ACK MUST equal the To header field in the
             *  response being acknowledged, and therefore will usually differ from
             *  the To header field in the original request by the addition of the
             *  tag parameter.  The ACK MUST contain a single Via header field, and
             *  this MUST be equal to the top Via header field of the original
             *  request.  The CSeq header field in the ACK MUST contain the same
             *  value for the sequence number as was present in the original request,
             *  but the method parameter MUST be equal to "ACK".
             *
             *  If the INVITE request whose response is being acknowledged had Route
             *  header fields, those header fields MUST appear in the ACK.  This is
             *  to ensure that the ACK can be routed properly through any downstream
             *  stateless proxies.
             */

            SIP_Request ackRequest = new SIP_Request(SIP_Methods.ACK);

            ackRequest.RequestLine.Uri = this.Request.RequestLine.Uri;
            ackRequest.Via.AddToTop(this.Request.Via.GetTopMostValue().ToStringValue());
            ackRequest.CallID = this.Request.CallID;
            ackRequest.From   = this.Request.From;
            ackRequest.To     = response.To;
            ackRequest.CSeq   = new SIP_t_CSeq(this.Request.CSeq.SequenceNumber, "ACK");
            foreach (SIP_HeaderField h in response.Header.Get("Route:"))
            {
                ackRequest.Header.Add("Route:", h.Value);
            }
            ackRequest.MaxForwards = 70;

            try{
                // Send request to target.
                this.Stack.TransportLayer.SendRequest(this.Flow, ackRequest, this);
            }
            catch (SIP_TransportException x) {
                OnTransportError(x);
                SetState(SIP_TransactionState.Terminated);
            }
        }
Пример #9
0
        /// <summary>
        /// Sends specified response to flow remote end point.
        /// </summary>
        /// <param name="response">SIP response 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>response</b> is null reference.</exception>
        public void Send(SIP_Response response)
        {
            lock (m_pLock){
                if (m_IsDisposed)
                {
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                if (response == null)
                {
                    throw new ArgumentNullException("response");
                }

                SendInternal(response.ToByteData());
            }
        }
Пример #10
0
            /// <summary>
            /// Checks if specified response matches this 2xx response retransmission wait entry.
            /// </summary>
            /// <param name="response">INVITE 2xx response.</param>
            /// <returns>Returns true if response matches, othwerwise false.</returns>
            /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference value.</exception>
            public bool Match(SIP_Response response)
            {
                if (response == null)
                {
                    throw new ArgumentNullException("response");
                }

                if (m_pInvite.CSeq.RequestMethod == response.CSeq.RequestMethod && m_pInvite.CSeq.SequenceNumber == response.CSeq.SequenceNumber)
                {
                    return(true);
                }
                else
                {
                    return(false);
                }
            }
Пример #11
0
        /// <summary>
        /// Is raised when INVITE 100 (Trying) response must be sent if no response sent by transaction user.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event data.</param>
        private void m_pTimer100_Elapsed(object sender
#if !NETSTANDARD
                                         , System.Timers.ElapsedEventArgs e
#endif
                                         )
        {
            lock (this.SyncRoot){
                // RFC 3261 17.2.1. TU didn't generate response in 200 ms, send '100 Trying' to stop request retransmission.
                if (this.State == SIP_TransactionState.Proceeding && this.Responses.Length == 0)
                {
                    /* RFC 3261 17.2.1.
                     *  The 100 (Trying) response is constructed according to the procedures in Section 8.2.6, except that the
                     *  insertion of tags in the To header field of the response (when none was present in the request)
                     *  is downgraded from MAY to SHOULD NOT.
                     *
                     * RFC 3261 8.2.6.
                     *  When a 100 (Trying) response is generated, any Timestamp header field present in the request MUST
                     *  be copied into this 100 (Trying) response. If there is a delay in generating the response, the UAS
                     *  SHOULD add a delay value into the Timestamp value in the response. This value MUST contain the difference
                     *  between the time of sending of the response and receipt of the request, measured in seconds.
                     */

                    SIP_Response tryingResponse = this.Stack.CreateResponse(SIP_ResponseCodes.x100_Trying, this.Request);
                    if (this.Request.Timestamp != null)
                    {
                        tryingResponse.Timestamp = new SIP_t_Timestamp(this.Request.Timestamp.Time, (DateTime.Now - this.CreateTime).Seconds);
                    }

                    try{
                        this.Stack.TransportLayer.SendResponse(this, tryingResponse);
                    }
                    catch (Exception x) {
                        OnTransportError(x);
                        SetState(SIP_TransactionState.Terminated);
                        return;
                    }
                }

                if (m_pTimer100 != null)
                {
                    m_pTimer100.Dispose();
                    m_pTimer100 = null;
                }
            }
        }
Пример #12
0
            /// <summary>
            /// Cleans up any resources being used.
            /// </summary>
            public void Dispose()
            {
                lock (m_pLock){
                    if (m_IsDisposed)
                    {
                        return;
                    }
                    m_IsDisposed = true;

                    m_pDialog.m_pUasInvite2xxRetransmits.Remove(this);

                    if (m_pTimer != null)
                    {
                        m_pTimer.Dispose();
                        m_pTimer = null;
                    }
                    m_pDialog   = null;
                    m_pResponse = null;
                }
            }
Пример #13
0
        /// <summary>
        /// Processes specified response through this dialog.
        /// </summary>
        /// <param name="response">SIP response to process.</param>
        /// <returns>Returns true if this dialog processed specified response, otherwise false.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null.</exception>
        internal protected override bool ProcessResponse(SIP_Response response)
        {
            if (response == null)
            {
                throw new ArgumentNullException("response");
            }

            if (response.StatusCodeType == SIP_StatusCodeType.Success)
            {
                // Search pending INVITE 2xx response retransmission waite entry.
                foreach (UacInvite2xxRetransmissionWaiter w in m_pUacInvite2xxRetransmitWaits)
                {
                    if (w.Match(response))
                    {
                        w.Process(response);

                        return(true);
                    }
                }
            }

            return(false);
        }
Пример #14
0
            /// <summary>
            /// Default constructor.
            /// </summary>
            /// <param name="dialog">Owner INVITE dialog.</param>
            /// <param name="response">INVITE 2xx response.</param>
            /// <exception cref="ArgumentNullException">Is raised when <b>dialog</b> or <b>response</b> is null reference.</exception>
            public UasInvite2xxRetransmit(SIP_Dialog_Invite dialog, SIP_Response response)
            {
                if (dialog == null)
                {
                    throw new ArgumentNullException("dialog");
                }
                if (response == null)
                {
                    throw new ArgumentNullException("response");
                }

                m_pDialog   = dialog;
                m_pResponse = response;

                /* RFC 3261 13.3.1.4.
                 *  Once the response has been constructed, it is passed to the INVITE
                 *  server transaction.  Note, however, that the INVITE server
                 *  transaction will be destroyed as soon as it receives this final
                 *  response and passes it to the transport.  Therefore, it is necessary
                 *  to periodically pass the response directly to the transport until the
                 *  ACK arrives.  The 2xx response is passed to the transport with an
                 *  interval that starts at T1 seconds and doubles for each
                 *  retransmission until it reaches T2 seconds (T1 and T2 are defined in
                 *  Section 17).  Response retransmissions cease when an ACK request for
                 *  the response is received.  This is independent of whatever transport
                 *  protocols are used to send the response.
                 *
                 *      Since 2xx is retransmitted end-to-end, there may be hops between
                 *      UAS and UAC that are UDP.  To ensure reliable delivery across
                 *      these hops, the response is retransmitted periodically even if the
                 *      transport at the UAS is reliable.
                 */

                m_pTimer          = new TimerEx(SIP_TimerConstants.T1, false);
                m_pTimer.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimer_Elapsed);
                m_pTimer.Enabled  = true;
            }
        /// <summary>
        /// Matches SIP response to client transaction. If not matching transaction found, returns null.
        /// </summary>
        /// <param name="response">SIP response to match.</param>
        internal SIP_ClientTransaction MatchClientTransaction(SIP_Response response)
        {
            /* RFC 3261 17.1.3 Matching Responses to Client Transactions.
             *  1.  If the response has the same value of the branch parameter in
             *      the top Via header field as the branch parameter in the top
             *      Via header field of the request that created the transaction.
             *
             *  2.  If the method parameter in the CSeq header field matches the
             *      method of the request that created the transaction.  The
             *      method is needed since a CANCEL request constitutes a
             *      different transaction, but shares the same value of the branch
             *      parameter.
             */

            SIP_ClientTransaction retVal = null;

            string transactionID = response.Via.GetTopMostValue().Branch + "-" + response.CSeq.RequestMethod;

            lock (m_pClientTransactions){
                m_pClientTransactions.TryGetValue(transactionID, out retVal);
            }

            return(retVal);
        }
Пример #16
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>
        internal protected override 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");
            }

            base.Init(stack, transaction, response);

            if (transaction is SIP_ServerTransaction)
            {
                if (response.StatusCodeType == SIP_StatusCodeType.Success)
                {
                    SetState(SIP_DialogState.Early, false);

                    // We need to retransmit 2xx response while we get ACK or timeout. (RFC 3261 13.3.1.4.)
                    m_pUasInvite2xxRetransmits.Add(new UasInvite2xxRetransmit(this, response));
                }
                else if (response.StatusCodeType == SIP_StatusCodeType.Provisional)
                {
                    SetState(SIP_DialogState.Early, false);

                    m_pActiveInvite = transaction;
                    m_pActiveInvite.StateChanged += delegate(object s, EventArgs a){
                        if (m_pActiveInvite != null && m_pActiveInvite.State == SIP_TransactionState.Terminated)
                        {
                            m_pActiveInvite = null;
                        }
                    };
                    // Once we send 2xx response, we need to retransmit it while get ACK or timeout. (RFC 3261 13.3.1.4.)
                    ((SIP_ServerTransaction)m_pActiveInvite).ResponseSent += delegate(object s, SIP_ResponseSentEventArgs a){
                        if (a.Response.StatusCodeType == SIP_StatusCodeType.Success)
                        {
                            m_pUasInvite2xxRetransmits.Add(new UasInvite2xxRetransmit(this, a.Response));
                        }
                    };
                }
                else
                {
                    throw new ArgumentException("Argument 'response' has invalid status code, 1xx - 2xx is only allowed.");
                }
            }
            else
            {
                if (response.StatusCodeType == SIP_StatusCodeType.Success)
                {
                    SetState(SIP_DialogState.Confirmed, false);

                    //  Wait for retransmited 2xx responses. (RFC 3261 13.2.2.4.)
                    m_pUacInvite2xxRetransmitWaits.Add(new UacInvite2xxRetransmissionWaiter(this, transaction.Request));
                }
                else if (response.StatusCodeType == SIP_StatusCodeType.Provisional)
                {
                    SetState(SIP_DialogState.Early, false);

                    m_pActiveInvite = transaction;
                    m_pActiveInvite.StateChanged += delegate(object s, EventArgs a){
                        if (m_pActiveInvite != null && m_pActiveInvite.State == SIP_TransactionState.Terminated)
                        {
                            m_pActiveInvite = null;
                        }
                    };
                    // Once we receive 2xx response, we need to wait for retransmitted 2xx responses. (RFC 3261 13.2.2.4)
                    ((SIP_ClientTransaction)m_pActiveInvite).ResponseReceived += delegate(object s, SIP_ResponseReceivedEventArgs a){
                        if (a.Response.StatusCodeType == SIP_StatusCodeType.Success)
                        {
                            UacInvite2xxRetransmissionWaiter waiter = new UacInvite2xxRetransmissionWaiter(this, m_pActiveInvite.Request);
                            m_pUacInvite2xxRetransmitWaits.Add(waiter);
                            // Force to send initial ACK to 2xx response.
                            waiter.Process(a.Response);

                            SetState(SIP_DialogState.Confirmed, true);
                        }
                    };
                }
                else
                {
                    throw new ArgumentException("Argument 'response' has invalid status code, 1xx - 2xx is only allowed.");
                }
            }
        }
Пример #17
0
        /// <summary>
        /// Sends specified response to remote party.
        /// </summary>
        /// <param name="response">SIP response to send.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference.</exception>
        public void SendResponse(SIP_Response response)
        {
            lock(this.SyncRoot){
                if(this.State == SIP_TransactionState.Disposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                if(response == null){
                    throw new ArgumentNullException("response");
                }

                try{
                    #region INVITE

                    /* RFC 6026 7.1. INVITE server transaction. (Udpates RFC 3261)   
                     
                                                             |INVITE
                                                             |pass INV to TU
                                          INVITE             V send 100 if TU won't in 200 ms
                                          send response+------------+
                                              +--------|            |--------+ 101-199 from TU
                                              |        |            |        | send response
                                              +------->|            |<-------+
                                                       | Proceeding |
                                                       |            |--------+ Transport Err.
                                                       |            |        | Inform TU
                                                       |            |<-------+
                                                       +------------+
                                          300-699 from TU |    |2xx from TU
                                          send response   |    |send response
                                           +--------------+    +------------+
                                           |                                |
                          INVITE           V          Timer G fires         |
                          send response +-----------+ send response         |
                               +--------|           |--------+              |
                               |        |           |        |              |
                               +------->| Completed |<-------+      INVITE  |  Transport Err.
                                        |           |               -       |  Inform TU
                               +--------|           |----+          +-----+ |  +---+
                               |        +-----------+    | ACK      |     | v  |   v
                               |          ^   |          | -        |  +------------+
                               |          |   |          |          |  |            |---+ ACK
                               +----------+   |          |          +->|  Accepted  |   | to TU
                               Transport Err. |          |             |            |<--+
                               Inform TU      |          V             +------------+
                                              |      +-----------+        |  ^     |
                                              |      |           |        |  |     |
                                              |      | Confirmed |        |  +-----+
                                              |      |           |        |  2xx from TU
                                Timer H fires |      +-----------+        |  send response
                                -             |          |                |
                                              |          | Timer I fires  |
                                              |          | -              | Timer L fires
                                              |          V                | -
                                              |        +------------+     |
                                              |        |            |<----+
                                              +------->| Terminated |
                                                       |            |
                                                       +------------+

                    */

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

                        if(this.State == SIP_TransactionState.Proceeding){
                            AddResponse(response);

                            // 1xx
                            if(response.StatusCodeType == SIP_StatusCodeType.Provisional){
                                this.Stack.TransportLayer.SendResponse(this,response);
                                OnResponseSent(response);
                            }
                            // 2xx
                            else if(response.StatusCodeType == SIP_StatusCodeType.Success){
                                this.Stack.TransportLayer.SendResponse(this,response);
                                OnResponseSent(response);
                                SetState(SIP_TransactionState.Accpeted);

                                /* RFC 6025 7.1.
                                    When the "Accepted" state is entered, timer L MUST be set to fire in 64*T1.
                                */
                                m_pTimerL = new TimerEx(64 * SIP_TimerConstants.T1);
                                m_pTimerL.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerL_Elapsed);
                                m_pTimerL.Enabled = true;

                                // Log
                                if(this.Stack.Logger != null){
                                    this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer L(ACK wait) started, will trigger after " + m_pTimerL.Interval + ".");
                                }
                            }
                            // 3xx - 6xx
                            else{
                                this.Stack.TransportLayer.SendResponse(this,response);
                                OnResponseSent(response);
                                SetState(SIP_TransactionState.Completed);

                                /* RFC 3261 17.2.1.
                                    For unreliable transports, timer G is set to fire in T1 seconds, and is not set to fire for reliable transports.
                                */
                                if(!this.Flow.IsReliable){
                                    m_pTimerG = new TimerEx(SIP_TimerConstants.T1,false);
                                    m_pTimerG.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerG_Elapsed);
                                    m_pTimerG.Enabled = true;

                                    // Log
                                    if(this.Stack.Logger != null){
                                        this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer G(INVITE response(3xx - 6xx) retransmission) started, will trigger after " + m_pTimerG.Interval + ".");
                                    }
                                }

                                /* RFC 3261 17.2.1.
                                    When the "Completed" state is entered, timer H MUST be set to fire in 64*T1 seconds for all transports.
                                */
                                m_pTimerH = new TimerEx(64 * SIP_TimerConstants.T1);
                                m_pTimerH.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerH_Elapsed);
                                m_pTimerH.Enabled = true;

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

                        #endregion

                        #region Accepted

                        else if(this.State == SIP_TransactionState.Accpeted){
                            this.Stack.TransportLayer.SendResponse(this,response);
                            OnResponseSent(response);
                        }

                        #endregion

                        #region Completed

                        else if(this.State == SIP_TransactionState.Completed){
                            // We do nothing here, we just wait ACK to arrive.
                        }

                        #endregion

                        #region Confirmed

                        else if(this.State == SIP_TransactionState.Confirmed){
                            // We do nothing, just wait ACK retransmissions.
                        }

                        #endregion

                        #region Terminated

                        else if(this.State == SIP_TransactionState.Terminated){
                            // We should never rreach here, but if so, skip it.
                        }

                        #endregion
                    }

                    #endregion

                    #region Non-INVITE

                    /* RFC 3261 17.2.2.
                                              |Request received
                                              |pass to TU
                                              V
                                        +-----------+
                                        |           |
                                        | Trying    |-------------+
                                        |           |             |
                                        +-----------+             |200-699 from TU
                                              |                   |send response
                                              |1xx from TU        |
                                              |send response      |
                                              |                   |
                           Request            V      1xx from TU  |
                           send response+-----------+send response|
                               +--------|           |--------+    |
                               |        | Proceeding|        |    |
                               +------->|           |<-------+    |
                        +<--------------|           |             |
                        |Trnsprt Err    +-----------+             |
                        |Inform TU            |                   |
                        |                     |                   |
                        |                     |200-699 from TU    |
                        |                     |send response      |
                        |  Request            V                   |
                        |  send response+-----------+             |
                        |      +--------|           |             |
                        |      |        | Completed |<------------+
                        |      +------->|           |
                        +<--------------|           |
                        |Trnsprt Err    +-----------+
                        |Inform TU            |
                        |                     |Timer J fires
                        |                     |-
                        |                     |
                        |                     V
                        |               +-----------+
                        |               |           |
                        +-------------->| Terminated|
                                        |           |
                                        +-----------+
                    */

                    else{
                        #region Trying

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

                            // 1xx
                            if(response.StatusCodeType == SIP_StatusCodeType.Provisional){
                                this.Stack.TransportLayer.SendResponse(this,response);
                                OnResponseSent(response);
                                SetState(SIP_TransactionState.Proceeding);
                            }
                            // 2xx - 6xx
                            else{
                                this.Stack.TransportLayer.SendResponse(this,response);
                                OnResponseSent(response);
                                SetState(SIP_TransactionState.Completed);

                                /* RFC 3261 17.2.2.
                                    When the server transaction enters the "Completed" state, it MUST set
                                    Timer J to fire in 64*T1 seconds for unreliable transports, and zero
                                    seconds for reliable transports.
                                */
                                m_pTimerJ = new TimerEx(64 * SIP_TimerConstants.T1,false);
                                m_pTimerJ.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerJ_Elapsed);
                                m_pTimerJ.Enabled = true;

                                // Log
                                if(this.Stack.Logger != null){
                                    this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer J(Non-INVITE request retransmission wait) started, will trigger after " + m_pTimerJ.Interval + ".");
                                }
                            }
                        }

                        #endregion

                        #region Proceeding

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

                            // 1xx
                            if(response.StatusCodeType == SIP_StatusCodeType.Provisional){
                                this.Stack.TransportLayer.SendResponse(this,response);
                                OnResponseSent(response);
                            }
                            // 2xx - 6xx
                            else{
                                this.Stack.TransportLayer.SendResponse(this,response);
                                OnResponseSent(response);
                                SetState(SIP_TransactionState.Completed);

                                /* RFC 3261 17.2.2.
                                    When the server transaction enters the "Completed" state, it MUST set
                                    Timer J to fire in 64*T1 seconds for unreliable transports, and zero
                                    seconds for reliable transports.
                                */
                                m_pTimerJ = new TimerEx(64 * SIP_TimerConstants.T1,false);
                                m_pTimerJ.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerJ_Elapsed);
                                m_pTimerJ.Enabled = true;

                                // Log
                                if(this.Stack.Logger != null){
                                    this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer J(Non-INVITE request retransmission wait) started, will trigger after " + m_pTimerJ.Interval + ".");
                                }
                            }
                        }

                        #endregion

                        #region Completed

                        else if(this.State == SIP_TransactionState.Completed){
                            // Do nothing.
                        }

                        #endregion

                        #region Terminated

                        else if(this.State == SIP_TransactionState.Terminated){
                            // Do nothing.
                        }

                        #endregion
                    }

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

                    OnTransportError(x);
                }
            }
        }
Пример #18
0
        /// <summary>
        /// Parses SIP_Response from stream.
        /// </summary>
        /// <param name="stream">Stream what contains valid SIP response.</param>
        /// <returns>Returns parsed SIP_Response obeject.</returns>
        /// <exception cref="ArgumentNullException">Raised when <b>stream</b> is null.</exception>
        /// <exception cref="SIP_ParseException">Raised when invalid SIP message.</exception>
        public static SIP_Response Parse(Stream stream)
        {
            /* Syntax:
                SIP-Version SP Status-Code SP Reason-Phrase
                SIP-Message                          
            */

            if(stream == null){
                throw new ArgumentNullException("stream");
            }

            SIP_Response retVal = new SIP_Response();

            // Parse Response-line
            StreamLineReader r = new StreamLineReader(stream);
            r.Encoding = "utf-8";
            string[] version_code_text = r.ReadLineString().Split(new char[]{' '},3);
            if(version_code_text.Length != 3){
                throw new SIP_ParseException("Invalid SIP Status-Line syntax ! Syntax: {SIP-Version SP Status-Code SP Reason-Phrase}.");
            }
            // SIP-Version
            try{
                retVal.SipVersion = Convert.ToDouble(version_code_text[0].Split('/')[1],System.Globalization.NumberFormatInfo.InvariantInfo);
            }
            catch{
                throw new SIP_ParseException("Invalid Status-Line SIP-Version value !");
            }

            // Status-Code
            try{
                retVal.StatusCode = Convert.ToInt32(version_code_text[1]);
            }
            catch{
                throw new SIP_ParseException("Invalid Status-Line Status-Code value !");
            }

            // Reason-Phrase
            retVal.ReasonPhrase = version_code_text[2];

            // Parse SIP-Message
            retVal.InternalParse(stream);

            return retVal;
        }
Пример #19
0
 /// <summary>
 /// Raises <b>ResponseSent</b> event.
 /// </summary>
 /// <param name="response">SIP response.</param>
 private void OnResponseSent(SIP_Response response)
 {
     if(this.ResponseSent != null){
         this.ResponseSent(this,new SIP_ResponseSentEventArgs(this,response));
     }
 }
Пример #20
0
 /// <summary>
 /// Sends specified response back to request maker using RFC 3261 18. rules.
 /// </summary>
 /// <param name="response">SIP response.</param>
 /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
 /// <exception cref="InvalidOperationException">Is raised when stack ahs not been started and this method is accessed.</exception>
 /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference.</exception>
 /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
 /// <exception cref="SIP_TransportException">Is raised when <b>response</b> sending has failed.</exception>
 /// <remarks>Use this method to send SIP responses from stateless SIP elements, like stateless proxy. 
 /// Otherwise SIP_ServerTransaction.SendResponse method should be used.</remarks>
 public void SendResponse(SIP_Response response)
 {
     SendResponse(response,null);
 }
Пример #21
0
        /// <summary>
        /// Sends specified response back to request maker using RFC 3261 18. rules.
        /// </summary>
        /// <param name="transaction">SIP server transaction which response to send.</param>
        /// <param name="response">SIP response.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        /// <exception cref="InvalidOperationException">Is raised when stack ahs not been started and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>transaction</b> or <b>response</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        /// <exception cref="SIP_TransportException">Is raised when <b>response</b> sending has failed.</exception>
        internal void SendResponse(SIP_ServerTransaction transaction,SIP_Response response)
        {
            if(transaction == null){
                throw new ArgumentNullException("transaction");
            }
            // NOTE: all other paramter / state validations are done in SendResponseInternal.

            SendResponseInternal(transaction,response,null);
        }
Пример #22
0
        /// <summary>
        /// Gets existing or creates new dialog.
        /// </summary>
        /// <param name="transaction">Owner transaction what forces to create dialog.</param>
        /// <param name="response">Response what forces to create dialog.</param>
        /// <returns>Returns dialog.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>transaction</b> or <b>response</b> is null.</exception>
        public SIP_Dialog GetOrCreateDialog(SIP_Transaction transaction,SIP_Response response)
        {
            if(transaction == null){
                throw new ArgumentNullException("transaction");
            }
            if(response == null){
                throw new ArgumentNullException("response");
            }

            string dialogID = "";
            if(transaction is SIP_ServerTransaction){
                dialogID = response.CallID + "-" + response.To.Tag + "-" + response.From.Tag;
            }
            else{
                dialogID = response.CallID + "-" + response.From.Tag + "-" + response.To.Tag;
            }

            lock(m_pDialogs){
                SIP_Dialog dialog = null;
                m_pDialogs.TryGetValue(dialogID,out dialog);
                // Dialog doesn't exist, create it.
                if(dialog == null){
                    if(response.CSeq.RequestMethod.ToUpper() == SIP_Methods.INVITE){
                        dialog = new SIP_Dialog_Invite();
                        dialog.Init(m_pStack,transaction,response);
                        dialog.StateChanged += delegate(object s,EventArgs a){
                            if(dialog.State == SIP_DialogState.Terminated){
                                m_pDialogs.Remove(dialog.ID);
                            }
                        };
                        m_pDialogs.Add(dialog.ID,dialog);
                    }
                    else{
                        throw new ArgumentException("Method '" + response.CSeq.RequestMethod + "' has no dialog handler.");
                    }
                }

                return dialog;
            }
        }
Пример #23
0
        /// <summary>
        /// Sends response to the specified host.
        /// </summary>
        /// <param name="logID">Log ID.</param>
        /// <param name="transactionID">Transaction ID. If null, then stateless response sending.</param>
        /// <param name="localEP">UDP local end point to use for sending. If null, system will use default.</param>
        /// <param name="host">Host name or IP address where to send response.</param>
        /// <param name="port">Target host port.</param>
        /// <param name="transport">SIP transport to use.</param>
        /// <param name="response">SIP response to send.</param>
        private void SendResponseToHost(string logID,string transactionID,IPEndPoint localEP,string host,int port,string transport,SIP_Response response)
        {
            try{
                IPAddress[] targets = null;
                if(Net_Utils.IsIPAddress(host)){
                    targets = new IPAddress[]{IPAddress.Parse(host)};
                }
                else{
                    targets = m_pStack.Dns.GetHostAddresses(host);
                    if(targets.Length == 0){
                        throw new SIP_TransportException("Invalid Via: Sent-By host name '" + host + "' could not be resolved.");
                    }
                }

                byte[] responseData = response.ToByteData();

                for(int i=0;i<targets.Length;i++){
                    IPEndPoint remoteEP = new IPEndPoint(targets[i],port);
                    try{
                        SIP_Flow flow = GetOrCreateFlow(transport,localEP,remoteEP);
                        flow.Send(response);
                        // localEP = flow.LocalEP;

                        if(m_pStack.Logger != null){
                            m_pStack.Logger.AddWrite(
                                logID,
                                null,
                                0,
                                "Response [transactionID='" + transactionID + "'; method='" + response.CSeq.RequestMethod + "'; cseq='" + response.CSeq.SequenceNumber + "'; " +
                                    "transport='" + transport + "'; size='" + responseData.Length + "'; statusCode='" + response.StatusCode + "'; " +
                                    "reason='" + response.ReasonPhrase + "'; sent '" + localEP + "' -> '" + remoteEP + "'.",
                                localEP,
                                remoteEP,
                                responseData
                            );
                        }

                        // If we reach so far, send succeeded.
                        return;
                    }
                    catch{
                        // Generate error, all IP addresses has failed.
                        if(i == (targets.Length - 1)){
                            if(m_pStack.Logger != null){
                                m_pStack.Logger.AddText(logID,"Failed to send response to host '" + host + "' IP end point '" + remoteEP + "'.");
                            }

                            throw new SIP_TransportException("Host '" + host + ":" + port + "' is not accessible.");
                        }
                        // For loop will try next IP address.
                        else{
                            if(m_pStack.Logger != null){
                                m_pStack.Logger.AddText(logID,"Failed to send response to host '" + host + "' IP end point '" + remoteEP + "', will try next A record.");
                            }
                        }
                    }
                }
            }
            catch(DNS_ClientException dnsX){
                throw new SIP_TransportException("Dns error: " + dnsX.ErrorCode.ToString());
            }
        }
        /// <summary>
        /// Creates authorization for each challange in <b>response</b>.
        /// </summary>
        /// <param name="request">SIP request where to add authorization values.</param>
        /// <param name="response">SIP response which challanges to authorize.</param>
        /// <param name="credentials">Credentials for authorization.</param>
        /// <returns>Returns true if all challanges were authorized. If any of the challanges was not authorized, returns false.</returns>
        private bool Authorize(SIP_Request request, SIP_Response response, NetworkCredential[] credentials)
        {
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }
            if (response == null)
            {
                throw new ArgumentNullException("response");
            }
            if (credentials == null)
            {
                throw new ArgumentNullException("credentials");
            }

            bool allAuthorized = true;


            foreach (SIP_t_Challenge challange in response.WWWAuthenticate.GetAllValues())
            {
                Auth_HttpDigest authDigest = new Auth_HttpDigest(challange.AuthData, request.RequestLine.Method);

                // Serach credential for the specified challange.
                NetworkCredential credential = null;
                foreach (NetworkCredential c in credentials)
                {
                    if (c.Domain.ToLower() == authDigest.Realm.ToLower())
                    {
                        credential = c;
                        break;
                    }
                }
                // We don't have credential for this challange.
                if (credential == null)
                {
                    allAuthorized = false;
                }
                // Authorize challange.
                else
                {
                    authDigest.UserName = credential.UserName;
                    authDigest.Password = credential.Password;
                    authDigest.CNonce   = Auth_HttpDigest.CreateNonce();
                    authDigest.Uri      = request.RequestLine.Uri.ToString();

                    request.Authorization.Add(authDigest.ToAuthorization());
                }
            }



            foreach (SIP_t_Challenge challange in response.ProxyAuthenticate.GetAllValues())
            {
                Auth_HttpDigest authDigest = new Auth_HttpDigest(challange.AuthData, request.RequestLine.Method);

                // Serach credential for the specified challange.
                NetworkCredential credential = null;
                foreach (NetworkCredential c in credentials)
                {
                    if (c.Domain.ToLower() == authDigest.Realm.ToLower())
                    {
                        credential = c;
                        break;
                    }
                }
                // We don't have credential for this challange.
                if (credential == null)
                {
                    allAuthorized = false;
                }
                // Authorize challange.
                else
                {
                    authDigest.UserName = credential.UserName;
                    authDigest.Password = credential.Password;
                    authDigest.CNonce   = Auth_HttpDigest.CreateNonce();
                    authDigest.Uri      = request.RequestLine.Uri.ToString();

                    request.ProxyAuthorization.Add(authDigest.ToAuthorization());
                }
            }



            return(allAuthorized);
        }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="stack">Reference to SIP stack.</param>
 /// <param name="transaction">Client transaction what response it is. This value can be null if no matching client response.</param>
 /// <param name="response">Received response.</param>
 internal SIP_ResponseReceivedEventArgs(SIP_Stack stack,SIP_ClientTransaction transaction,SIP_Response response)
 {
     m_pStack       = stack;
     m_pResponse    = response;
     m_pTransaction = transaction;
 }
Пример #26
0
 /// <summary>
 /// Raises ResponseReceived event.
 /// </summary>
 /// <param name="response">SIP response received.</param>
 private void OnResponseReceived(SIP_Response response)
 {
     if(this.ResponseReceived != null){
         this.ResponseReceived(this,new SIP_ResponseReceivedEventArgs(m_pStack,m_pTransaction,response));
     }
 }
Пример #27
0
        /// <summary>
        /// Clones this request.
        /// </summary>
        /// <returns>Returns new cloned request.</returns>
        public SIP_Response Copy()
        {
            SIP_Response retVal = SIP_Response.Parse(this.ToByteData());

            return(retVal);
        }
Пример #28
0
        /// <summary>
        /// Adds specified response to transaction responses collection.
        /// </summary>
        /// <param name="response">SIP response.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference.</exception>
        protected void AddResponse(SIP_Response response)
        {
            if(response == null){
                throw new ArgumentNullException("response");
            }

            // Don't store more than 15 responses, otherwise hacker may try todo buffer overrun with provisional responses.
            if(m_pResponses.Count < 15 || response.StatusCode >= 200){
                m_pResponses.Add(response);
            }
        }
Пример #29
0
 /// <summary>
 /// Raises ResponseReceived event.
 /// </summary>
 /// <param name="response">SIP response received.</param>
 private void OnResponseReceived(SIP_Response response)
 {
     if(this.ResponseReceived != null){
         this.ResponseReceived(this,new SIP_ResponseReceivedEventArgs(this.Stack,this,response));
     }
 }
Пример #30
0
        /// <summary>
        /// Matches SIP response to client transaction. If not matching transaction found, returns null.
        /// </summary>
        /// <param name="response">SIP response to match.</param>
        internal SIP_ClientTransaction MatchClientTransaction(SIP_Response response)
        {
            /* RFC 3261 17.1.3 Matching Responses to Client Transactions.
                1.  If the response has the same value of the branch parameter in
                    the top Via header field as the branch parameter in the top
                    Via header field of the request that created the transaction.

                2.  If the method parameter in the CSeq header field matches the
                    method of the request that created the transaction.  The
                    method is needed since a CANCEL request constitutes a
                    different transaction, but shares the same value of the branch
                    parameter.
            */

            SIP_ClientTransaction retVal = null;

            string transactionID = response.Via.GetTopMostValue().Branch + "-" + response.CSeq.RequestMethod;
            lock(m_pClientTransactions){
                m_pClientTransactions.TryGetValue(transactionID,out retVal);
            }

            return retVal;
        }
Пример #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>
        internal protected override 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");
            }

            base.Init(stack,transaction,response);

            if(transaction is SIP_ServerTransaction){
                if(response.StatusCodeType == SIP_StatusCodeType.Success){
                    SetState(SIP_DialogState.Early,false);                    
                }
                else if(response.StatusCodeType == SIP_StatusCodeType.Provisional){
                    SetState(SIP_DialogState.Early,false);
                                        
                    m_pActiveInvite = transaction;                    
                    m_pActiveInvite.StateChanged += delegate(object s,EventArgs a){
                        if(m_pActiveInvite != null && m_pActiveInvite.State == SIP_TransactionState.Terminated){
                            m_pActiveInvite = null;

                            /* RFC 3261 13.3.1.4.
                                If the server retransmits the 2xx response for 64*T1 seconds without
                                receiving an ACK, the dialog is confirmed, but the session SHOULD be
                                terminated. 
                            */
                            if(this.State == SIP_DialogState.Early){
                                this.SetState(SIP_DialogState.Confirmed,true);
                                Terminate("ACK was not received for initial INVITE 2xx response.",true);
                            }
                            else if(this.State == SIP_DialogState.Terminating){
                                this.SetState(SIP_DialogState.Confirmed,false);
                                Terminate(m_TerminateReason,true);
                            }
                        }
                    };
                }
                else{
                    throw new ArgumentException("Argument 'response' has invalid status code, 1xx - 2xx is only allowed.");
                }
            }
            else{
                if(response.StatusCodeType == SIP_StatusCodeType.Success){
                    SetState(SIP_DialogState.Confirmed,false);
                
                }
                else if(response.StatusCodeType == SIP_StatusCodeType.Provisional){
                    SetState(SIP_DialogState.Early,false);

                    m_pActiveInvite = transaction;                                      
                    m_pActiveInvite.StateChanged += delegate(object s,EventArgs a){
                        if(m_pActiveInvite != null && m_pActiveInvite.State == SIP_TransactionState.Terminated){
                            m_pActiveInvite = null;
                        }
                    };

                    // Once we receive 2xx response, dialog will switch to confirmed state.
                    ((SIP_ClientTransaction)transaction).ResponseReceived += delegate(object s,SIP_ResponseReceivedEventArgs a){
                        if(a.Response.StatusCodeType == SIP_StatusCodeType.Success){
                            
                            SetState(SIP_DialogState.Confirmed,true);
                        }
                    };
                }
                else{
                    throw new ArgumentException("Argument 'response' has invalid status code, 1xx - 2xx is only allowed.");
                }
            }
        }
Пример #32
0
        /// <summary>
        /// Matches specified SIP response to SIP dialog. If no matching dialog found, returns null.
        /// </summary>
        /// <param name="response">SIP response.</param>
        /// <returns>Returns matched SIP dialog or null in no match found.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null.</exception>
        internal SIP_Dialog MatchDialog(SIP_Response response)
        {
            if(response == null){
                throw new ArgumentNullException("response");
            }

            SIP_Dialog dialog = null;

            try{
                string callID  = response.CallID;
                string fromTag = response.From.Tag;
                string toTag   = response.To.Tag;
                if(callID != null && fromTag != null && toTag != null){
                    string dialogID = callID + "-" + fromTag + "-" + toTag;
                    lock(m_pDialogs){
                        m_pDialogs.TryGetValue(dialogID,out dialog);
                    }
                }
            }
            catch{
            }

            return dialog;
        }
Пример #33
0
        /// <summary>
        /// Sends specified response back to request maker using RFC 3263 5. rules.
        /// </summary>
        /// <param name="logID">Log ID.</param>
        /// <param name="transactionID">Transaction ID. If null, then stateless response sending.</param>
        /// <param name="localEP">UDP local end point to use for sending. If null, system will use default.</param>
        /// <param name="response">SIP response.</param>
        /// <exception cref="SIP_TransportException">Is raised when <b>response</b> sending has failed.</exception>
        private void SendResponse_RFC_3263_5(string logID,string transactionID,IPEndPoint localEP,SIP_Response response)
        {
            /* RFC 3263 5.
                    RFC 3261 [1] defines procedures for sending responses from a server
                    back to the client.  Typically, for unicast UDP requests, the
                    response is sent back to the source IP address where the request came
                    from, using the port contained in the Via header.  For reliable
                    transport protocols, the response is sent over the connection the
                    request arrived on.  However, it is important to provide failover
                    support when the client element fails between sending the request and
                    receiving the response.

                    A server, according to RFC 3261 [1], will send a response on the
                    connection it arrived on (in the case of reliable transport
                    protocols), and for unreliable transport protocols, to the source
                    address of the request, and the port in the Via header field.  The
                    procedures here are invoked when a server attempts to send to that
                    location and that response fails (the specific conditions are
                    detailed in RFC 3261). "Fails" is defined as any closure of the
                    transport connection the request came in on before the response can
                    be sent, or communication of a fatal error from the transport layer.

                    In these cases, the server examines the value of the sent-by
                    construction in the topmost Via header.  If it contains a numeric IP
                    address, the server attempts to send the response to that address,
                    using the transport protocol from the Via header, and the port from
                    sent-by, if present, else the default for that transport protocol.
                    The transport protocol in the Via header can indicate "TLS", which
                    refers to TLS over TCP.  When this value is present, the server MUST
                    use TLS over TCP to send the response.

                    If, however, the sent-by field contained a domain name and a port
                    number, the server queries for A or AAAA records with that name.  It
                    tries to send the response to each element on the resulting list of
                    IP addresses, using the port from the Via, and the transport protocol
                    from the Via (again, a value of TLS refers to TLS over TCP).  As in
                    the client processing, the next entry in the list is tried if the one
                    before it results in a failure.

                    If, however, the sent-by field contained a domain name and no port,
                    the server queries for SRV records at that domain name using the
                    service identifier "_sips" if the Via transport is "TLS", "_sip"
                    otherwise, and the transport from the topmost Via header ("TLS"
                    implies that the transport protocol in the SRV query is TCP).  The
                    resulting list is sorted as described in [2], and the response is
                    sent to the topmost element on the new list described there.  If that
                    results in a failure, the next entry on the list is tried.
                */

            SIP_t_ViaParm via = response.Via.GetTopMostValue();

            #region Sent-By is IP address

            if(via.SentBy.IsIPAddress){
                SendResponseToHost(logID,transactionID,localEP,via.SentBy.Host,via.SentByPortWithDefault,via.ProtocolTransport,response);
            }

            #endregion

            #region Sent-By is host name with port number

            else if(via.SentBy.Port != -1){
                SendResponseToHost(logID,transactionID,localEP,via.SentBy.Host,via.SentByPortWithDefault,via.ProtocolTransport,response);
            }

            #endregion

            #region Sent-By is just host name

            else{
                try{
                    // Query SRV records.
                    string srvQuery = "";
                    if(via.ProtocolTransport == SIP_Transport.UDP){
                        srvQuery = "_sip._udp." + via.SentBy.Host;
                    }
                    else if(via.ProtocolTransport == SIP_Transport.TCP){
                        srvQuery = "_sip._tcp." + via.SentBy.Host;
                    }
                    else if(via.ProtocolTransport == SIP_Transport.UDP){
                        srvQuery = "_sips._tcp." + via.SentBy.Host;
                    }
                    DnsServerResponse dnsResponse = m_pStack.Dns.Query(srvQuery,QTYPE.SRV);
                    if(dnsResponse.ResponseCode != RCODE.NO_ERROR){
                        throw new SIP_TransportException("Dns error: " + dnsResponse.ResponseCode.ToString());
                    }
                    DNS_rr_SRV[] srvRecords = dnsResponse.GetSRVRecords();

                    // Use SRV records.
                    if(srvRecords.Length > 0){
                        for(int i=0;i<srvRecords.Length;i++){
                            DNS_rr_SRV srv = srvRecords[i];
                            try{
                                if(m_pStack.Logger != null){
                                    m_pStack.Logger.AddText(logID,"Starts sending response to DNS SRV record '" + srv.Target + "'.");
                                }

                                SendResponseToHost(logID,transactionID,localEP,srv.Target,srv.Port,via.ProtocolTransport,response);
                            }
                            catch{
                                // Generate error, all SRV records has failed.
                                if(i == (srvRecords.Length - 1)){
                                    if(m_pStack.Logger != null){
                                        m_pStack.Logger.AddText(logID,"Failed to send response to DNS SRV record '" + srv.Target + "'.");
                                    }

                                    throw new SIP_TransportException("Host '" + via.SentBy.Host + "' is not accessible.");
                                }
                                // For loop will try next SRV record.
                                else{
                                    if(m_pStack.Logger != null){
                                        m_pStack.Logger.AddText(logID,"Failed to send response to DNS SRV record '" + srv.Target + "', will try next.");
                                    }
                                }
                            }
                        }
                    }
                    // If no SRV, use A and AAAA records. (Thats not in 3263 5., but we need to todo it so.)
                    else{
                        if(m_pStack.Logger != null){
                            m_pStack.Logger.AddText(logID,"No DNS SRV records found, starts sending to Via: sent-by host '" + via.SentBy.Host + "'.");
                        }

                        SendResponseToHost(logID,transactionID,localEP,via.SentBy.Host,via.SentByPortWithDefault,via.ProtocolTransport,response);
                    }
                }
                catch(DNS_ClientException dnsX){
                    throw new SIP_TransportException("Dns error: " + dnsX.ErrorCode.ToString());
                }
            }

            #endregion
        }
        /// <summary>
        /// Sends trying response to request maker.
        /// </summary>
        private void SendTrying()
        {
            /* RFC 3261 8.2.6.1 Sending a Provisional Response.
                When a 100 (Trying) response is generated, any Timestamp header field
                present in the request MUST be copied into this 100 (Trying)
                response.  If there is a delay in generating the response, the UAS
                SHOULD add a delay value into the Timestamp value in the response.
            */

            SIP_Response sipTryingResponse = new SIP_Response();
            sipTryingResponse.StatusCode_ReasonPhrase = SIP_ResponseCodes.x100_Trying;
            sipTryingResponse.Via.AddToTop(m_pRequest.Via.GetTopMostValue().ToStringValue());
            sipTryingResponse.To = m_pRequest.To;
            sipTryingResponse.From = m_pRequest.From;
            sipTryingResponse.CallID = m_pRequest.CallID;
            sipTryingResponse.CSeq = m_pRequest.CSeq;

            // Add time stamp, because we send Trying at once, we don't have delay.
            if(m_pRequest.Timestamp != null){
                sipTryingResponse.Timestamp = new SIP_t_Timestamp(m_pRequest.Timestamp.Time,0);
            }

            // Send response
            m_pSipStack.TransportLayer.SendResponse(m_pRequest.Socket,m_pRequest.RemoteEndPoint,sipTryingResponse);
        }
Пример #35
0
        /// <summary>
        /// Sends specified response back to request maker using RFC 3261 18. rules.
        /// </summary>
        /// <param name="response">SIP response.</param>
        /// <param name="localEP">Local IP end point to use for sending resposne. Value null means system will allocate it.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        /// <exception cref="InvalidOperationException">Is raised when stack ahs not been started and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        /// <exception cref="SIP_TransportException">Is raised when <b>response</b> sending has failed.</exception>
        /// <remarks>Use this method to send SIP responses from stateless SIP elements, like stateless proxy. 
        /// Otherwise SIP_ServerTransaction.SendResponse method should be used.</remarks>
        public void SendResponse(SIP_Response response,IPEndPoint localEP)
        {
            // NOTE: all  paramter / state validations are done in SendResponseInternal.

            SendResponseInternal(null,response,localEP);
        }
Пример #36
0
        /// <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 (this.SyncRoot){
                if (this.State == SIP_TransactionState.Disposed)
                {
                    return;
                }

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

                        this.Stack.Logger.AddRead(
                            Guid.NewGuid().ToString(),
                            null,
                            0,
                            "Request [transactionID='" + this.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 (this.Method == SIP_Methods.INVITE)
                    {
                        #region INVITE

                        if (request.RequestLine.Method == SIP_Methods.INVITE)
                        {
                            if (this.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 = this.LastProvisionalResponse;
                                if (response != null)
                                {
                                    this.Stack.TransportLayer.SendResponse(this, response);
                                }
                            }
                            else if (this.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.
                                 */
                                this.Stack.TransportLayer.SendResponse(this, 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 (this.State == SIP_TransactionState.Completed)
                            {
                                SetState(SIP_TransactionState.Confirmed);

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

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

                                    // Log
                                    if (this.Stack.Logger != null)
                                    {
                                        this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.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 += new System.Timers.ElapsedEventHandler(m_pTimerI_Elapsed);
                                // Log
                                if (this.Stack.Logger != null)
                                {
                                    this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.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 (this.Method == request.RequestLine.Method)
                        {
                            if (this.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.
                                 */
                                this.Stack.TransportLayer.SendResponse(this, this.LastProvisionalResponse);
                            }
                            else if (this.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.
                                 */
                                this.Stack.TransportLayer.SendResponse(this, this.FinalResponse);
                            }
                        }
                    }

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

                    OnTransportError(x);
                    SetState(SIP_TransactionState.Terminated);
                }
            }
        }
Пример #37
0
        /// <summary>
        /// Sends response to request maker using RFC 3261 18. rules.
        /// </summary>
        /// <param name="transaction">Owner server transaction. Can be null if stateless response sending.</param>
        /// <param name="response">SIP response to send.</param>
        /// <param name="localEP">Local IP end point to use for sending resposne. Value null means system will allocate it.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        /// <exception cref="InvalidOperationException">Is raised when stack ahs not been started and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        /// <exception cref="SIP_TransportException">Is raised when <b>response</b> sending has failed.</exception>
        private void SendResponseInternal(SIP_ServerTransaction transaction,SIP_Response response,IPEndPoint localEP)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(!m_IsRunning){
                throw new InvalidOperationException("Stack has not been started.");
            }
            if(response == null){
                throw new ArgumentNullException("response");
            }

            /* RFC 3261 18.2.2.
                The server transport uses the value of the top Via header field in
                order to determine where to send a response.  It MUST follow the
                following process:

                    o  If the "sent-protocol" is a reliable transport protocol such as
                       TCP or SCTP, or TLS over those, the response MUST be sent using
                       the existing connection to the source of the original request
                       that created the transaction, if that connection is still open.
                       This requires the server transport to maintain an association
                       between server transactions and transport connections.  If that
                       connection is no longer open, the server SHOULD open a
                       connection to the IP address in the "received" parameter, if
                       present, using the port in the "sent-by" value, or the default
                       port for that transport, if no port is specified.  If that
                       connection attempt fails, the server SHOULD use the procedures
                       in [4] for servers in order to determine the IP address and
                       port to open the connection and send the response to.

                    o  Otherwise, if the Via header field value contains a "maddr"
                       parameter, the response MUST be forwarded to the address listed
                       there, using the port indicated in "sent-by", or port 5060 if
                       none is present.  If the address is a multicast address, the
                       response SHOULD be sent using the TTL indicated in the "ttl"
                       parameter, or with a TTL of 1 if that parameter is not present.

                    o  Otherwise (for unreliable unicast transports), if the top Via
                       has a "received" parameter, the response MUST be sent to the
                       address in the "received" parameter, using the port indicated
                       in the "sent-by" value, or using port 5060 if none is specified
                       explicitly.  If this fails, for example, elicits an ICMP "port
                       unreachable" response, the procedures of Section 5 of [4]
                       SHOULD be used to determine where to send the response.

                    o  Otherwise, if it is not receiver-tagged, the response MUST be
                       sent to the address indicated by the "sent-by" value, using the
                       procedures in Section 5 of [4].
            */

            /* RFC 3581 4. (Adds new processing between RFC 3261 18.2.2. bullet 2 and 3)
                When a server attempts to send a response, it examines the topmost
                Via header field value of that response.  If the "sent-protocol"
                component indicates an unreliable unicast transport protocol, such as
                UDP, and there is no "maddr" parameter, but there is both a
                "received" parameter and an "rport" parameter, the response MUST be
                sent to the IP address listed in the "received" parameter, and the
                port in the "rport" parameter.  The response MUST be sent from the
                same address and port that the corresponding request was received on.
                This effectively adds a new processing step between bullets two and
                three in Section 18.2.2 of SIP [1].

                The response must be sent from the same address and port that the
                request was received on in order to traverse symmetric NATs.  When a
                server is listening for requests on multiple ports or interfaces, it
                will need to remember the one on which the request was received.  For
                a stateful proxy, storing this information for the duration of the
                transaction is not an issue.  However, a stateless proxy does not
                store state between a request and its response, and therefore cannot
                remember the address and port on which a request was received.  To
                properly implement this specification, a stateless proxy can encode
                the destination address and port of a request into the Via header
                field value that it inserts.  When the response arrives, it can
                extract this information and use it to forward the response.
            */

            SIP_t_ViaParm via = response.Via.GetTopMostValue();
            if(via == null){
                throw new ArgumentException("Argument 'response' does not contain required Via: header field.");
            }

            // TODO: If transport is not supported.
            //throw new SIP_TransportException("Not supported transport '" + via.ProtocolTransport + "'.");

            string logID         = Guid.NewGuid().ToString();
            string transactionID = transaction == null ? "" : transaction.ID;

            // Try to get local IP end point which we should use to send response back.
            if(transaction != null && transaction.Request.LocalEndPoint != null){
                localEP = transaction.Request.LocalEndPoint;
            }

            // TODO: no "localEP" at moment

            // TODO: Stateless should use flowID instead.

            // Our stateless proxy add 'localEP' parameter to Via: if so normally we can get it from there.
            else if(via.Parameters["localEP"] != null){
                localEP = Net_Utils.ParseIPEndPoint(via.Parameters["localEP"].Value);
            }

            byte[] responseData = response.ToByteData();

            #region Try existing flow first

            /* First try active flow to send response, thats not 100% as RFC says, but works better in any case.
               RFC says that for TCP and TLS only, we do it for any transport.
            */

            if(transaction != null){
                try{
                    SIP_Flow flow = transaction.Flow;
                    flow.Send(response);

                    if(m_pStack.Logger != null){
                            m_pStack.Logger.AddWrite(
                                logID,
                                null,
                                0,
                                "Response [flowReuse=true; transactionID='" + transactionID + "'; method='" + response.CSeq.RequestMethod + "'; cseq='" + response.CSeq.SequenceNumber + "'; " +
                                    "transport='" + flow.Transport + "'; size='" + responseData.Length + "'; statusCode='" + response.StatusCode + "'; " +
                                    "reason='" + response.ReasonPhrase + "'; sent '" + flow.LocalEP + "' -> '" + flow.RemoteEP + "'.",
                                localEP,
                                flow.RemoteEP,
                                responseData
                            );
                        }

                    return;
                }
                catch{
                    // Do nothing, processing will continue.
                }
            }

            #endregion

            #region Reliable TCP,TLS, ...

            if(SIP_Utils.IsReliableTransport(via.ProtocolTransport)){
                // Get original request remote end point.
                IPEndPoint remoteEP = null;
                if(transaction != null && transaction.Request.RemoteEndPoint != null){
                    remoteEP = transaction.Request.RemoteEndPoint;
                }
                else if(via.Received != null){
                    remoteEP = new IPEndPoint(via.Received,via.SentBy.Port == -1 ? 5060 : via.SentBy.Port);
                }

                #region If original request connection alive, use it

                try{
                    SIP_Flow flow = null;

                    // Statefull
                    if(transaction != null){
                        if(transaction.Request.Flow != null && !transaction.Request.Flow.IsDisposed){
                            flow = transaction.Request.Flow;
                        }
                    }
                    // Stateless
                    else{
                        string flowID = via.Parameters["connectionID"].Value;
                        if(flowID != null){
                            flow = m_pFlowManager[flowID];
                        }
                    }

                    if(flow != null){
                        flow.Send(response);

                        if(m_pStack.Logger != null){
                            m_pStack.Logger.AddWrite(
                                logID,
                                null,
                                0,
                                "Response [flowReuse=true; transactionID='" + transactionID + "'; method='" + response.CSeq.RequestMethod + "'; cseq='" + response.CSeq.SequenceNumber + "'; " +
                                    "transport='" + flow.Transport + "'; size='" + responseData.Length + "'; statusCode='" + response.StatusCode + "'; " +
                                    "reason='" + response.ReasonPhrase + "'; sent '" + flow.RemoteEP + "' -> '" + flow.LocalEP + "'.",
                                localEP,
                                remoteEP,
                                responseData
                            );
                        }

                        return;
                    }
                }
                catch{
                    // Do nothing, processing will continue.
                    // Override RFC, if there is any existing connection and it gives error, try always RFC 3261 18.2.2(recieved) and 3265 5.
                }

                #endregion

                #region Send RFC 3261 18.2.2(recieved)

                if(remoteEP != null){
                    try{
                        SendResponseToHost(logID,transactionID,null,remoteEP.Address.ToString(),remoteEP.Port,via.ProtocolTransport,response);
                    }
                    catch{
                        // Do nothing, processing will continue -> "RFC 3265 5.".
                    }
                }

                #endregion

                #region Send RFC 3265 5.

                SendResponse_RFC_3263_5(logID,transactionID,localEP,response);

                #endregion

            }

            #endregion

            #region UDP Via: maddr parameter

            else if(via.Maddr != null){
                throw new SIP_TransportException("Sending responses to multicast address(Via: 'maddr') is not supported.");
            }

            #endregion

            #region RFC 3581 4. UDP Via: received and rport parameters

            else if(via.Maddr == null && via.Received != null && via.RPort > 0){
                SendResponseToHost(logID,transactionID,localEP,via.Received.ToString(),via.RPort,via.ProtocolTransport,response);
            }

            #endregion

            #region UDP Via: received parameter

            else if(via.Received != null){
                SendResponseToHost(logID,transactionID,localEP,via.Received.ToString(),via.SentByPortWithDefault,via.ProtocolTransport,response);
            }

            #endregion

            #region UDP

            else{
                SendResponse_RFC_3263_5(logID,transactionID,localEP,response);
            }

            #endregion
        }
        /// <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 (this.SyncRoot){
                if (this.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 (this.Stack.Logger != null)
                {
                    byte[] responseData = response.ToByteData();

                    this.Stack.Logger.AddRead(
                        Guid.NewGuid().ToString(),
                        null,
                        0,
                        "Response [transactionID='" + this.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 (this.Method == SIP_Methods.INVITE)
                {
                    #region Calling

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

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

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

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

                        // 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(this.Flow.IsReliable ? 0 : 32000, false);
                            m_pTimerD.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerD_Elapsed);
                            // Log
                            if (this.Stack.Logger != null)
                            {
                                this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer D(INVITE 3xx - 6xx response retransmission wait) started, will trigger after " + m_pTimerD.Interval + ".");
                            }
                            m_pTimerD.Enabled = true;
                        }
                    }

                    #endregion

                    #region Proceeding

                    else if (this.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(this.Flow.IsReliable ? 0 : 32000, false);
                            m_pTimerD.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerD_Elapsed);
                            // Log
                            if (this.Stack.Logger != null)
                            {
                                this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer D(INVITE 3xx - 6xx response retransmission wait) started, will trigger after " + m_pTimerD.Interval + ".");
                            }
                            m_pTimerD.Enabled = true;
                        }
                    }

                    #endregion

                    #region Completed

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

                    #endregion

                    #region Terminated

                    else if (this.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 (this.State == SIP_TransactionState.Trying)
                    {
                        // Store response.
                        AddResponse(response);

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

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

                        // 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 (this.Stack.Logger != null)
                                {
                                    this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer F(Non-INVITE trying,proceeding state timeout) stopped.");
                                }
                            }

                            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(this.Flow.IsReliable ? 1 : SIP_TimerConstants.T4, false);
                            m_pTimerK.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerK_Elapsed);
                            // Log
                            if (this.Stack.Logger != null)
                            {
                                this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer K(Non-INVITE 3xx - 6xx response retransmission wait) started, will trigger after " + m_pTimerK.Interval + ".");
                            }
                            m_pTimerK.Enabled = true;
                        }
                    }

                    #endregion

                    #region Proceeding

                    else if (this.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 (this.Stack.Logger != null)
                                {
                                    this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer F(Non-INVITE trying,proceeding state timeout) stopped.");
                                }
                            }

                            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(this.Flow.IsReliable ? 0 : SIP_TimerConstants.T4, false);
                            m_pTimerK.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerK_Elapsed);
                            // Log
                            if (this.Stack.Logger != null)
                            {
                                this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer K(Non-INVITE 3xx - 6xx response retransmission wait) started, will trigger after " + m_pTimerK.Interval + ".");
                            }
                            m_pTimerK.Enabled = true;
                        }
                    }

                    #endregion

                    #region Completed

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

                    #endregion

                    #region Terminated

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

                    #endregion
                }

                #endregion
            }
        }
Пример #39
0
        /// <summary>
        /// Sends specified response to remote party.
        /// </summary>
        /// <param name="response">SIP response to send.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference.</exception>
        public void SendResponse(SIP_Response response)
        {
            lock (this.SyncRoot){
                if (this.State == SIP_TransactionState.Disposed)
                {
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                if (response == null)
                {
                    throw new ArgumentNullException("response");
                }

                try{
                    #region INVITE

                    /* RFC 3261 17.2.1.
                     |INVITE
                     |pass INV to TU
                     *  INVITE             V send 100 if TU won't in 200ms
                     *  send response+-----------+
                     +--------|           |--------+101-199 from TU
                     |        | Proceeding|        |send response
                     +------->|           |<-------+
                     |           |          Transport Err.
                     |           |          Inform TU
                     |           |--------------->+
                     +-----------+                |
                     |  300-699 from TU |     |2xx from TU        |
                     |  send response   |     |send response      |
                     |     +------------------>+
                     |                         |
                     |  INVITE          V          Timer G fires  |
                     |  send response+-----------+ send response  |
                     +--------|           |--------+       |
                     |        | Completed |        |       |
                     +------->|           |<-------+       |
                     +-----------+                |
                     |     |                   |
                     |              ACK |     |                   |
                     |              -   |     +------------------>+
                     |        Timer H fires    |
                     |                  V        or Transport Err.|
                     +-----------+  Inform TU     |
                     |           |                |
                     | Confirmed |                |
                     |           |                |
                     +-----------+                |
                     |                      |
                     |Timer I fires         |
                     |-                     |
                     |                      |
                     |                     V                      |
                     +-----------+                |
                     |           |                |
                     | Terminated|<---------------+
                     |           |
                     +-----------+
                     */

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

                        if (this.State == SIP_TransactionState.Proceeding)
                        {
                            AddResponse(response);

                            // 1xx
                            if (response.StatusCodeType == SIP_StatusCodeType.Provisional)
                            {
                                this.Stack.TransportLayer.SendResponse(this, response);
                                OnResponseSent(response);
                            }
                            // 2xx
                            else if (response.StatusCodeType == SIP_StatusCodeType.Success)
                            {
                                this.Stack.TransportLayer.SendResponse(this, response);
                                OnResponseSent(response);
                                SetState(SIP_TransactionState.Terminated);
                            }
                            // 3xx - 6xx
                            else
                            {
                                this.Stack.TransportLayer.SendResponse(this, response);
                                OnResponseSent(response);
                                SetState(SIP_TransactionState.Completed);

                                /* RFC 3261 17.2.1.
                                 *  For unreliable transports, timer G is set to fire in T1 seconds, and is not set to fire for reliable transports.
                                 */
                                if (!this.Flow.IsReliable)
                                {
                                    m_pTimerG          = new TimerEx(SIP_TimerConstants.T1, false);
                                    m_pTimerG.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerG_Elapsed);
                                    m_pTimerG.Enabled  = true;

                                    // Log
                                    if (this.Stack.Logger != null)
                                    {
                                        this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer G(INVITE response(3xx - 6xx) retransmission) started, will triger after " + m_pTimerG.Interval + ".");
                                    }
                                }

                                /* RFC 3261 17.2.1.
                                 *  When the "Completed" state is entered, timer H MUST be set to fire in 64*T1 seconds for all transports.
                                 */
                                m_pTimerH          = new TimerEx(64 * SIP_TimerConstants.T1);
                                m_pTimerH.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerH_Elapsed);
                                m_pTimerH.Enabled  = true;

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

                        #endregion

                        #region Completed

                        else if (this.State == SIP_TransactionState.Completed)
                        {
                            // We do nothing here, we just wait ACK to arrive.
                        }

                        #endregion

                        #region Confirmed

                        else if (this.State == SIP_TransactionState.Confirmed)
                        {
                            // We do nothing, just wait ACK retransmissions.
                        }

                        #endregion

                        #region Terminated

                        else if (this.State == SIP_TransactionState.Terminated)
                        {
                            // We should never rreach here, but if so, skip it.
                        }

                        #endregion
                    }

                    #endregion

                    #region Non-INVITE

                    /* RFC 3261 17.2.2.
                     |Request received
                     |pass to TU
                     *                        V
                     +-----------+
                     |           |
                     | Trying    |-------------+
                     |           |             |
                     +-----------+             |200-699 from TU
                     |                   |send response
                     |1xx from TU        |
                     |send response      |
                     |                   |
                     |     Request            V      1xx from TU  |
                     |     send response+-----------+send response|
                     +--------|           |--------+    |
                     |        | Proceeding|        |    |
                     +------->|           |<-------+    |
                     +<--------------|           |             |
                     |Trnsprt Err    +-----------+             |
                     |Inform TU            |                   |
                     |                     |                   |
                     |                     |200-699 from TU    |
                     |                     |send response      |
                     |  Request            V                   |
                     |  send response+-----------+             |
                     |      +--------|           |             |
                     |      |        | Completed |<------------+
                     |      +------->|           |
                     +<--------------|           |
                     |Trnsprt Err    +-----------+
                     |Inform TU            |
                     |                     |Timer J fires
                     |                     |-
                     |                     |
                     |                     V
                     |               +-----------+
                     |               |           |
                     +-------------->| Terminated|
                     |           |
                     +-----------+
                     */

                    else
                    {
                        #region Trying

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

                            // 1xx
                            if (response.StatusCodeType == SIP_StatusCodeType.Provisional)
                            {
                                this.Stack.TransportLayer.SendResponse(this, response);
                                OnResponseSent(response);
                                SetState(SIP_TransactionState.Proceeding);
                            }
                            // 2xx - 6xx
                            else
                            {
                                this.Stack.TransportLayer.SendResponse(this, response);
                                OnResponseSent(response);
                                SetState(SIP_TransactionState.Completed);

                                /* RFC 3261 17.2.2.
                                 *  When the server transaction enters the "Completed" state, it MUST set
                                 *  Timer J to fire in 64*T1 seconds for unreliable transports, and zero
                                 *  seconds for reliable transports.
                                 */
                                m_pTimerJ          = new TimerEx(64 * SIP_TimerConstants.T1, false);
                                m_pTimerJ.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerJ_Elapsed);
                                m_pTimerJ.Enabled  = true;

                                // Log
                                if (this.Stack.Logger != null)
                                {
                                    this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer J(Non-INVITE request retransmission wait) started, will triger after " + m_pTimerJ.Interval + ".");
                                }
                            }
                        }

                        #endregion

                        #region Proceeding

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

                            // 1xx
                            if (response.StatusCodeType == SIP_StatusCodeType.Provisional)
                            {
                                this.Stack.TransportLayer.SendResponse(this, response);
                                OnResponseSent(response);
                            }
                            // 2xx - 6xx
                            else
                            {
                                this.Stack.TransportLayer.SendResponse(this, response);
                                OnResponseSent(response);
                                SetState(SIP_TransactionState.Completed);

                                /* RFC 3261 17.2.2.
                                 *  When the server transaction enters the "Completed" state, it MUST set
                                 *  Timer J to fire in 64*T1 seconds for unreliable transports, and zero
                                 *  seconds for reliable transports.
                                 */
                                m_pTimerJ          = new TimerEx(64 * SIP_TimerConstants.T1, false);
                                m_pTimerJ.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerJ_Elapsed);
                                m_pTimerJ.Enabled  = true;

                                // Log
                                if (this.Stack.Logger != null)
                                {
                                    this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer J(Non-INVITE request retransmission wait) started, will triger after " + m_pTimerJ.Interval + ".");
                                }
                            }
                        }

                        #endregion

                        #region Completed

                        else if (this.State == SIP_TransactionState.Completed)
                        {
                            // Do nothing.
                        }

                        #endregion

                        #region Terminated

                        else if (this.State == SIP_TransactionState.Terminated)
                        {
                            // Do nothing.
                        }

                        #endregion
                    }

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

                    OnTransportError(x);
                    SetState(SIP_TransactionState.Terminated);
                }
            }
        }
Пример #40
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>
        internal protected 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){
                m_IsSecure = ((SIP_Uri)transaction.Request.RequestLine.Uri).IsSecure;
                m_pRouteSet = (SIP_t_AddressParam[])Net_Utils.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;

                List<string> allow = new List<string>();
                foreach(SIP_t_Method m in response.Allow.GetAllValues()){
                    allow.Add(m.Method);
                }
                m_pRemoteAllow = allow.ToArray();

                List<string> supported = new List<string>();
                foreach(SIP_t_OptionTag s in response.Supported.GetAllValues()){
                    supported.Add(s.OptionTag);
                }
                m_pRemoteSupported = supported.ToArray();
            }

            #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[])Net_Utils.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;
                
                List<string> allow = new List<string>();
                foreach(SIP_t_Method m in response.Allow.GetAllValues()){
                    allow.Add(m.Method);
                }
                m_pRemoteAllow = allow.ToArray();

                List<string> supported = new List<string>();
                foreach(SIP_t_OptionTag s in response.Supported.GetAllValues()){
                    supported.Add(s.OptionTag);
                }
                m_pRemoteSupported = supported.ToArray();
            }

            #endregion            

            m_pFlow = transaction.Flow;
            AddTransaction(transaction);
        }
Пример #41
0
        /// <summary>
        /// Creates and sends ACK for final(3xx - 6xx) failure response.
        /// </summary>
        /// <param name="response">SIP response.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null.</exception>
        private void SendAck(SIP_Response response)
        {
            if(response == null){
                throw new ArgumentNullException("resposne");
            }

            /* RFC 3261 17.1.1.3 Construction of the ACK Request.
                The ACK request constructed by the client transaction MUST contain
                values for the Call-ID, From, and Request-URI that are equal to the
                values of those header fields in the request passed to the transport
                by the client transaction (call this the "original request").  The To
                header field in the ACK MUST equal the To header field in the
                response being acknowledged, and therefore will usually differ from
                the To header field in the original request by the addition of the
                tag parameter.  The ACK MUST contain a single Via header field, and
                this MUST be equal to the top Via header field of the original
                request.  The CSeq header field in the ACK MUST contain the same
                value for the sequence number as was present in the original request,
                but the method parameter MUST be equal to "ACK".
              
                If the INVITE request whose response is being acknowledged had Route
                header fields, those header fields MUST appear in the ACK.  This is
                to ensure that the ACK can be routed properly through any downstream
                stateless proxies.
            */

            SIP_Request ackRequest = new SIP_Request(SIP_Methods.ACK);
            ackRequest.RequestLine.Uri = this.Request.RequestLine.Uri;
            ackRequest.Via.AddToTop(this.Request.Via.GetTopMostValue().ToStringValue());
            ackRequest.CallID = this.Request.CallID;
            ackRequest.From = this.Request.From;
            ackRequest.To = response.To;
            ackRequest.CSeq = new SIP_t_CSeq(this.Request.CSeq.SequenceNumber,"ACK");
            foreach(SIP_HeaderField h in response.Header.Get("Route:")){
                ackRequest.Header.Add("Route:",h.Value);
            }
            ackRequest.MaxForwards = 70;

            try{
                // Send request to target.
                this.Stack.TransportLayer.SendRequest(this.Flow,ackRequest,this);
            }
            catch(SIP_TransportException x){
                OnTransportError(x);
                SetState(SIP_TransactionState.Terminated);
            }
        }
Пример #42
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>
        internal protected override 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");
            }

            base.Init(stack, transaction, response);

            if (transaction is SIP_ServerTransaction)
            {
                if (response.StatusCodeType == SIP_StatusCodeType.Success)
                {
                    SetState(SIP_DialogState.Early, false);
                }
                else if (response.StatusCodeType == SIP_StatusCodeType.Provisional)
                {
                    SetState(SIP_DialogState.Early, false);

                    m_pActiveInvite = transaction;
                    m_pActiveInvite.StateChanged += delegate(object s, EventArgs a){
                        if (m_pActiveInvite != null && m_pActiveInvite.State == SIP_TransactionState.Terminated)
                        {
                            m_pActiveInvite = null;

                            /* RFC 3261 13.3.1.4.
                             *  If the server retransmits the 2xx response for 64*T1 seconds without
                             *  receiving an ACK, the dialog is confirmed, but the session SHOULD be
                             *  terminated.
                             */
                            if (this.State == SIP_DialogState.Early)
                            {
                                this.SetState(SIP_DialogState.Confirmed, true);
                                Terminate("ACK was not received for initial INVITE 2xx response.", true);
                            }
                            else if (this.State == SIP_DialogState.Terminating)
                            {
                                this.SetState(SIP_DialogState.Confirmed, false);
                                Terminate(m_TerminateReason, true);
                            }
                        }
                    };
                }
                else
                {
                    throw new ArgumentException("Argument 'response' has invalid status code, 1xx - 2xx is only allowed.");
                }
            }
            else
            {
                if (response.StatusCodeType == SIP_StatusCodeType.Success)
                {
                    SetState(SIP_DialogState.Confirmed, false);
                }
                else if (response.StatusCodeType == SIP_StatusCodeType.Provisional)
                {
                    SetState(SIP_DialogState.Early, false);

                    m_pActiveInvite = transaction;
                    m_pActiveInvite.StateChanged += delegate(object s, EventArgs a){
                        if (m_pActiveInvite != null && m_pActiveInvite.State == SIP_TransactionState.Terminated)
                        {
                            m_pActiveInvite = null;
                        }
                    };

                    // Once we receive 2xx response, dialog will switch to confirmed state.
                    ((SIP_ClientTransaction)transaction).ResponseReceived += delegate(object s, SIP_ResponseReceivedEventArgs a){
                        if (a.Response.StatusCodeType == SIP_StatusCodeType.Success)
                        {
                            SetState(SIP_DialogState.Confirmed, true);
                        }
                    };
                }
                else
                {
                    throw new ArgumentException("Argument 'response' has invalid status code, 1xx - 2xx is only allowed.");
                }
            }
        }
        /// <summary>
        /// Matches SIP response to client transaction. If not matching transaction found, returns null.
        /// </summary>
        /// <param name="response">SIP response to match.</param>
        internal SIP_ClientTransaction MatchClientTransaction(SIP_Response response)
        {
            /* RFC 3261 17.1.3 Matching Responses to Client Transactions.
                1.  If the response has the same value of the branch parameter in
                    the top Via header field as the branch parameter in the top
                    Via header field of the request that created the transaction.

                2.  If the method parameter in the CSeq header field matches the
                    method of the request that created the transaction.  The
                    method is needed since a CANCEL request constitutes a
                    different transaction, but shares the same value of the branch
                    parameter.
            */

            string transactionID = response.Via.GetTopMostValue().Branch + "-" + response.CSeq.RequestMethod;
            lock(m_pClientTransactions){
                foreach(SIP_ClientTransaction transaction in m_pClientTransactions){
                    if(transactionID == transaction.ID + "-" + transaction.Request.CSeq.RequestMethod){
                        return transaction;
                    }
                }
            }
            // m_pSipStack.Logger.AddDebug("No matching transaction for response branch='" + response.Via.GetTopMostValue().Branch + "': " + response.StatusCode_ReasonPhrase);

            return null;
        }
Пример #44
0
        /// <summary>
        /// Processes received response.
        /// </summary>
        /// <param name="handler">Target handler what received response.</param>
        /// <param name="transaction">Client transaction what response it is.</param>
        /// <param name="response">Response received.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>handler</b>,<b>transaction</b> or <b>response</b> is null reference.</exception>
        private void ProcessResponse(TargetHandler handler,SIP_ClientTransaction transaction,SIP_Response response)
        {
            if(handler == null){
                throw new ArgumentNullException("handler");
            }
            if(transaction == null){
                throw new ArgumentNullException("transaction");
            }
            if(response == null){
                throw new ArgumentNullException("response");
            }

            /* RFC 3261 16.7 Response Processing.
                Steps 1 - 2 handled in TargetHandler.

                3.  Remove the topmost Via
                4.  Add the response to the response context
                5.  Check to see if this response should be forwarded immediately
                6.  When necessary, choose the best final response from the response context.
                    If no final response has been forwarded after every client
                    transaction associated with the response context has been terminated,
                    the proxy must choose and forward the "best" response from those it
                    has seen so far.

                The following processing MUST be performed on each response that is
                forwarded.  It is likely that more than one response to each request
                will be forwarded: at least each provisional and one final response.

                7.  Aggregate authorization header field values if necessary
                8.  Optionally rewrite Record-Route header field values
                9.  Forward the response
                10. Generate any necessary CANCEL requests
            */

            bool forwardResponse = false;

            lock(m_pLock){

                #region 3.  Remove the topmost Via

                /*
                    The proxy removes the topmost Via header field value from the
                    response.

                    If no Via header field values remain in the response, the
                    response was meant for this element and MUST NOT be forwarded.
                    The remainder of the processing described in this section is
                    not performed on this message, the UAC processing rules
                    described in Section 8.1.3 are followed instead (transport
                    layer processing has already occurred).

                    This will happen, for instance, when the element generates
                    CANCEL requests as described in Section 10.

                    NOTE: We MAY NOT do it for B2BUA, skip it for B2BUA
                */
                if(!m_IsB2BUA){
                    response.Via.RemoveTopMostValue();
                    if(response.Via.GetAllValues().Length == 0){
                        return;
                    }
                }

                #endregion

                #region 4.  Add the response to the response context

                /*
                    Final responses received are stored in the response context
                    until a final response is generated on the server transaction
                    associated with this context.  The response may be a candidate
                    for the best final response to be returned on that server
                    transaction.  Information from this response may be needed in
                    forming the best response, even if this response is not chosen.

                    If the proxy chooses to recurse on any contacts in a 3xx
                    response by adding them to the target set, it MUST remove them
                    from the response before adding the response to the response
                    context.  However, a proxy SHOULD NOT recurse to a non-SIPS URI
                    if the Request-URI of the original request was a SIPS URI.  If
                    the proxy recurses on all of the contacts in a 3xx response,
                    the proxy SHOULD NOT add the resulting contactless response to
                    the response context.

                    Removing the contact before adding the response to the response
                    context prevents the next element upstream from retrying a
                    location this proxy has already attempted.

                    3xx responses may contain a mixture of SIP, SIPS, and non-SIP
                    URIs.  A proxy may choose to recurse on the SIP and SIPS URIs
                    and place the remainder into the response context to be
                    returned, potentially in the final response.
                */

                if(response.StatusCodeType == SIP_StatusCodeType.Redirection && !m_NoRecurse && !handler.IsRecursed){
                    // Get SIP contacts and remove them from response.
                    SIP_t_ContactParam[] contacts = response.Contact.GetAllValues();
                    // Remove all contacts from response, we add non-SIP URIs back.
                    response.Contact.RemoveAll();
                    foreach(SIP_t_ContactParam contact in contacts){
                        // SIP URI add it to fork list.
                        if(contact.Address.IsSipOrSipsUri){
                            m_pTargets.Enqueue(new TargetHandler(this,null,(SIP_Uri)contact.Address.Uri,m_AddRecordRoute,true));
                        }
                        // Add specified URI back to response.
                        else{
                            response.Contact.Add(contact.ToStringValue());
                        }
                    }

                    // There are remaining non-SIP contacts, so we need to add the response to reponses collection.
                    if(response.Contact.GetAllValues().Length > 0){
                        m_pResponses.Add(response);
                    }

                    // Handle forking
                    if(m_pTargets.Count > 0){
                        if(m_ForkingMode == SIP_ForkingMode.Parallel){
                            while(m_pTargets.Count > 0){
                                TargetHandler h = m_pTargets.Dequeue();
                                m_pTargetsHandlers.Add(handler);
                                h.Start();
                            }
                        }
                        // Just fork next.
                        else{
                            TargetHandler h = m_pTargets.Dequeue();
                            m_pTargetsHandlers.Add(handler);
                            h.Start();
                        }

                        // Because we forked request to new target(s), we don't need to do steps 5 - 10.
                        return;
                    }
                }
                // Not 3xx response or recursing disabled.
                else{
                    m_pResponses.Add(response);
                }

                #endregion

                #region 5.  Check to see if this response should be forwarded immediately

                /*
                    Until a final response has been sent on the server transaction,
                    the following responses MUST be forwarded immediately:

                    -  Any provisional response other than 100 (Trying)

                    -  Any 2xx response

                    If a 6xx response is received, it is not immediately forwarded,
                    but the stateful proxy SHOULD cancel all client pending
                    transactions as described in Section 10, and it MUST NOT create
                    any new branches in this context.

                    After a final response has been sent on the server transaction,
                    the following responses MUST be forwarded immediately:

                    -  Any 2xx response to an INVITE request
                */

                if(!m_IsFinalResponseSent){
                    if(response.StatusCodeType == SIP_StatusCodeType.Provisional && response.StatusCode != 100){
                        forwardResponse = true;
                    }
                    else if(response.StatusCodeType == SIP_StatusCodeType.Success){
                        forwardResponse = true;
                    }
                    else if(response.StatusCodeType == SIP_StatusCodeType.GlobalFailure){
                        CancelAllTargets();
                    }
                }
                else{
                    if(response.StatusCodeType == SIP_StatusCodeType.Success && m_pServerTransaction.Request.RequestLine.Method == SIP_Methods.INVITE){
                        forwardResponse = true;
                    }
                }

                #endregion

                #region x.  Handle sequential forking

                /*
                    Sequential Search: In a sequential search, a proxy server attempts
                    each contact address in sequence, proceeding to the next one
                    only after the previous has generated a final response.  A 2xx
                    or 6xx class final response always terminates a sequential
                    search.
                */
                if(m_ForkingMode == SIP_ForkingMode.Sequential && response.StatusCodeType != SIP_StatusCodeType.Provisional){
                    if(response.StatusCodeType == SIP_StatusCodeType.Success){
                        // Do nothing, 2xx will be always forwarded and step 10. Cancels all targets.
                    }
                    else if(response.StatusCodeType == SIP_StatusCodeType.GlobalFailure){
                        // Do nothing, 6xx is already handled in setp 5.
                    }
                    else if(m_pTargets.Count > 0){
                        TargetHandler h = m_pTargets.Dequeue();
                        m_pTargetsHandlers.Add(handler);
                        h.Start();

                        // Skip all next steps, we will get new responses from new target.
                        return;
                    }
                }

                #endregion

                #region 6.  When necessary, choose the best final response from the response context

                /*
                    A stateful proxy MUST send a final response to a response
                    context's server transaction if no final responses have been
                    immediately forwarded by the above rules and all client
                    transactions in this response context have been terminated.

                    The stateful proxy MUST choose the "best" final response among
                    those received and stored in the response context.

                    If there are no final responses in the context, the proxy MUST
                    send a 408 (Request Timeout) response to the server
                    transaction.

                */

                if(!m_IsFinalResponseSent && !forwardResponse && m_pTargets.Count == 0){
                    bool mustChooseBestFinalResponse = true;
                    // Check if all transactions terminated.
                    foreach(TargetHandler h in m_pTargetsHandlers){
                        if(!h.IsCompleted){
                            mustChooseBestFinalResponse = false;
                            break;
                        }
                    }

                    if(mustChooseBestFinalResponse){
                        response = GetBestFinalResponse();
                        if(response == null){
                            response = this.Proxy.Stack.CreateResponse(SIP_ResponseCodes.x408_Request_Timeout,m_pServerTransaction.Request);
                        }

                        forwardResponse = true;
                    }
                }

                #endregion

                if(forwardResponse){

                    #region 7.  Aggregate authorization header field values if necessary

                    /*
                        If the selected response is a 401 (Unauthorized) or 407 (Proxy Authentication Required),
                        the proxy MUST collect any WWW-Authenticate and Proxy-Authenticate header field values
                        from all other 401 (Unauthorized) and 407 (Proxy Authentication Required) responses
                        received so far in this response context and add them to this response without
                        modification before forwarding. The resulting 401 (Unauthorized) or 407 (Proxy
                        Authentication Required) response could have several WWW-Authenticate AND
                        Proxy-Authenticate header field values.

                        This is necessary because any or all of the destinations the request was forwarded to
                        may have requested credentials.  The client needs to receive all of those challenges and
                        supply credentials for each of them when it retries the request.
                    */
                    if(response.StatusCode == 401 || response.StatusCode == 407){
                        foreach(SIP_Response resp in m_pResponses.ToArray()){
                            if(response != resp && (resp.StatusCode == 401 || resp.StatusCode == 407)){
                                // WWW-Authenticate
                                foreach(SIP_HeaderField hf in resp.WWWAuthenticate.HeaderFields){
                                    resp.WWWAuthenticate.Add(hf.Value);
                                }
                                // Proxy-Authenticate
                                foreach(SIP_HeaderField hf in resp.ProxyAuthenticate.HeaderFields){
                                    resp.ProxyAuthenticate.Add(hf.Value);
                                }
                            }
                        }
                    }

                    #endregion

                    #region 8.  Optionally rewrite Record-Route header field values

                    // This is optional so we currently won't do that.

                    #endregion

                    #region 9.  Forward the response

                    SendResponse(transaction,response);
                    if(response.StatusCodeType != SIP_StatusCodeType.Provisional){
                        m_IsFinalResponseSent = true;
                    }

                    #endregion

                    #region 10. Generate any necessary CANCEL requests

                    /*
                        If the forwarded response was a final response, the proxy MUST
                        generate a CANCEL request for all pending client transactions
                        associated with this response context.
                    */
                    if(response.StatusCodeType != SIP_StatusCodeType.Provisional){
                        CancelAllTargets();
                    }

                    #endregion
                }
            }
        }
Пример #45
0
        /// <summary>
        /// Processes specified request through this dialog.
        /// </summary>
        /// <param name="e">Method arguments.</param>
        /// <returns>Returns true if this dialog processed specified request, otherwise false.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>e</b> is null reference.</exception>
        internal protected override bool ProcessRequest(SIP_RequestReceivedEventArgs e)
        {
            if (e == null)
            {
                throw new ArgumentNullException("e");
            }

            if (base.ProcessRequest(e))
            {
                return(true);
            }

            // We must support: INVITE(re-invite),UPDATE,ACK,  [BYE will be handled by base class]

            #region INVITE

            if (e.Request.RequestLine.Method == SIP_Methods.INVITE)
            {
                /* RFC 3261 14.2.
                 *  A UAS that receives a second INVITE before it sends the final
                 *  response to a first INVITE with a lower CSeq sequence number on the
                 *  same dialog MUST return a 500 (Server Internal Error) response to the
                 *  second INVITE and MUST include a Retry-After header field with a
                 *  randomly chosen value of between 0 and 10 seconds.
                 *
                 *  A UAS that receives an INVITE on a dialog while an INVITE it had sent
                 *  on that dialog is in progress MUST return a 491 (Request Pending)
                 *  response to the received INVITE.
                 */

                if (m_pActiveInvite != null && m_pActiveInvite is SIP_ServerTransaction && ((SIP_ServerTransaction)m_pActiveInvite).Request.CSeq.SequenceNumber < e.Request.CSeq.SequenceNumber)
                {
                    SIP_Response response = this.Stack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error + ": INVITE with a lower CSeq is pending(RFC 3261 14.2).", e.Request);
                    response.RetryAfter = new SIP_t_RetryAfter("10");
                    e.ServerTransaction.SendResponse(response);

                    return(true);
                }
                if (m_pActiveInvite != null && m_pActiveInvite is SIP_ClientTransaction)
                {
                    e.ServerTransaction.SendResponse(this.Stack.CreateResponse(SIP_ResponseCodes.x491_Request_Pending, e.Request));

                    return(true);
                }

                // Force server transaction creation and set it as active INVITE transaction.
                m_pActiveInvite = e.ServerTransaction;
                m_pActiveInvite.StateChanged += delegate(object s, EventArgs a){
                    if (m_pActiveInvite.State == SIP_TransactionState.Terminated)
                    {
                        m_pActiveInvite = null;
                    }
                };
                // Once we send 2xx response, we need to retransmit it while get ACK or timeout. (RFC 3261 13.3.1.4.)
                ((SIP_ServerTransaction)m_pActiveInvite).ResponseSent += delegate(object s, SIP_ResponseSentEventArgs a){
                    if (a.Response.StatusCodeType == SIP_StatusCodeType.Success)
                    {
                        m_pUasInvite2xxRetransmits.Add(new UasInvite2xxRetransmit(this, a.Response));
                    }
                };

                OnReinvite(((SIP_ServerTransaction)m_pActiveInvite));

                return(true);
            }

            #endregion

            #region ACK

            else if (e.Request.RequestLine.Method == SIP_Methods.ACK)
            {
                // Search corresponding INVITE 2xx retransmit entry and dispose it.
                foreach (UasInvite2xxRetransmit t in m_pUasInvite2xxRetransmits)
                {
                    if (t.MatchAck(e.Request))
                    {
                        t.Dispose();
                        if (this.State == SIP_DialogState.Early)
                        {
                            this.SetState(SIP_DialogState.Confirmed, true);

                            // TODO: If Terminating
                        }

                        return(true);
                    }
                }

                return(false);
            }

            #endregion

            #region UPDATE

            //else if(request.RequestLine.Method == SIP_Methods.UPDATE){
            // TODO:
            //}

            #endregion

            // RFC 5057 5.6. Refusing New Usages. Decline(603 Decline) new dialog usages.
            else if (SIP_Utils.MethodCanEstablishDialog(e.Request.RequestLine.Method))
            {
                e.ServerTransaction.SendResponse(this.Stack.CreateResponse(SIP_ResponseCodes.x603_Decline + " : New dialog usages not allowed (RFC 5057).", e.Request));

                return(true);
            }
            else
            {
                return(false);
            }
        }
        /// <summary>
        /// Processes client transaction received response.
        /// </summary>
        /// <param name="response">Response received.</param>
        private void ProcessResponse(SIP_Response response)
        {
            /* RFC 3261 16.7 Response Processing.
                1.  Find the appropriate response context
                2.  Update timer C for provisional responses
                3.  Remove the topmost Via
                4.  Add the response to the response context
                5.  Check to see if this response should be forwarded immediately
                6.  When necessary, choose the best final response from the response context

                If no final response has been forwarded after every client
                transaction associated with the response context has been terminated,
                the proxy must choose and forward the "best" response from those it
                has seen so far.

                The following processing MUST be performed on each response that is
                forwarded.  It is likely that more than one response to each request
                will be forwarded: at least each provisional and one final response.

                7.  Aggregate authorization header field values if necessary
                8.  Optionally rewrite Record-Route header field values
                9.  Forward the response
                10. Generate any necessary CANCEL requests
            */

            lock(this){
                // 1.  Find the appropriate response context.
                // Done, "this" is it.

                // 2.  Update timer C for provisional responses.
                //     Our client transaction will handle it.

                // 3.  Remove the topmost Via. If no Via header field values remain in the response,
                //     the response was meant for this element and MUST NOT be forwarded.
                response.Via.RemoveTopMostValue();
                if(response.Via.GetAllValues().Length == 0){
                    return;
                }

                // 4.  Add the response to the response context.
                if(!m_NoRecurse && response.StausCodeType == SIP_StatusCodeType.Redirection){
                    /*  If the proxy chooses to recurse on any contacts in a 3xx response by adding them to
                        the target set, it MUST remove them from the response before adding the response to
                        the response context. However, a proxy SHOULD NOT recurse to a non-SIPS URI if the
                        Request-URI of the original request was a SIPS URI. If the proxy recurses on all of
                        the contacts in a 3xx response, the proxy SHOULD NOT add the resulting contactless
                        response to the response context.
                    */

                    // Get SIP contacts and remove them from response.
                    SIP_t_ContactParam[] contacts = response.Contact.GetAllValues();
                    // Remove all contacts from response, we add no-SIP URIs back.
                    response.Contact.RemoveAll();
                    foreach(SIP_t_ContactParam contact in contacts){
                        // SIP URI add it to fork list.
                        if(contact.Address.IsSipOrSipsUri){
                            m_pRemainingDestinations.Enqueue(new SIP_Destination(SIP_Uri.Parse(contact.Address.Uri)));
                        }
                        // Add specified URI back to response.
                        else{
                            response.Contact.Add(contact.ToStringValue());
                        }
                    }

                    // There are remaining non-SIP contacts, so we need to add the response to reponses collection.
                    if(response.Contact.GetAllValues().Length > 0){
                        m_pResponses.Add(response);
                    }

                    // Handle forking
                    if(m_pRemainingDestinations.Count > 0){
                        if(m_ForkingMode == SIP_ForkingMode.Parallel){
                            while(m_pRemainingDestinations.Count > 0){
                                CreateClientTransaction(m_pRemainingDestinations.Dequeue());
                            }
                        }
                        // Just fork next.
                        else{
                            CreateClientTransaction(m_pRemainingDestinations.Dequeue());
                        }

                        // Because we forked request to new target(s), we don't need to do steps 5 - 10.
                        return;
                    }
                }
                // Not 3xx response or recursing disabled.
                else{
                    m_pResponses.Add(response);
                }

                // 5.  Check to see if this response should be forwarded immediately.
                bool forwardResponse = false;
                if(m_FinalResponseSent){
                    // -  Any 2xx response to an INVITE request
                    if(response.StausCodeType == SIP_StatusCodeType.Success && m_pServerTransaction.Request.Method == SIP_Methods.INVITE){
                        forwardResponse = true;
                    }
                }
                else{
                    // -  Any provisional response other than 100 (Trying)
                    if(response.StausCodeType == SIP_StatusCodeType.Provisional && response.StatusCode != 101){
                        forwardResponse = true;
                    }
                    // -  Any 2xx response
                    else if(response.StausCodeType == SIP_StatusCodeType.Success){
                        forwardResponse = true;
                    }
                }

                /* 6. When necessary, choose the best final response from the response context.
                      A stateful proxy MUST send a final response to a response context's server transaction
                      if no final responses have been immediately forwarded by the above rules and all client
                      transactions in this response context have been terminated.
                */
                bool mustChooseBestFinalResponse = false;
                if(!forwardResponse && m_pRemainingDestinations.Count == 0){
                    mustChooseBestFinalResponse = true;
                    foreach(SIP_ClientTransaction transaction in m_pClientTransactions.ToArray()){
                        // Acutally we can't relay on terminated state, thats not accurate, just see
                        // any transaction haven't also got final response (the we can expect final response).
                        if(transaction.GetFinalResponse() == null){
                            mustChooseBestFinalResponse = false;
                            break;
                        }
                    }
                }
                if(mustChooseBestFinalResponse){
                    response = GetBestFinalResponse();
                    if(response == null){
                        /*  If there are no final responses in the context, the proxy MUST send a
                            408 (Request Timeout) response to the server transaction.
                        */
                        response = m_pServerTransaction.Request.CreateResponse(SIP_ResponseCodes.x408_Request_Timeout);
                    }
                    forwardResponse = true;
                }

                if(forwardResponse){
                    /* 7.  Aggregate authorization header field values if necessary.
                           If the selected response is a 401 (Unauthorized) or 407 (Proxy Authentication Required),
                           the proxy MUST collect any WWW-Authenticate and Proxy-Authenticate header field values
                           from all other 401 (Unauthorized) and 407 (Proxy Authentication Required) responses
                           received so far in this response context and add them to this response without
                           modification before forwarding. The resulting 401 (Unauthorized) or 407 (Proxy
                           Authentication Required) response could have several WWW-Authenticate AND
                           Proxy-Authenticate header field values.

                           This is necessary because any or all of the destinations the request was forwarded to
                           may have requested credentials.  The client needs to receive all of those challenges and
                           supply credentials for each of them when it retries the request.
                    */
                    if(response.StatusCode == 401 || response.StatusCode == 407){
                        foreach(SIP_Response resp in m_pResponses.ToArray()){
                            if(response != resp && (resp.StatusCode == 401 || resp.StatusCode == 407)){
                                // WWW-Authenticate
                                foreach(SIP_HeaderField hf in resp.WWWAuthenticate.HeaderFields){
                                    resp.WWWAuthenticate.Add(hf.Value);
                                }
                                // Proxy-Authenticate
                                foreach(SIP_HeaderField hf in resp.ProxyAuthenticate.HeaderFields){
                                    resp.ProxyAuthenticate.Add(hf.Value);
                                }
                            }
                        }
                    }

                    // 8.  Optionally rewrite Record-Route header field values.
                    //     This is optional so we currently won't do that.

                    // 9.  Forward the response.
                    m_pServerTransaction.SendResponse(response);
                    if(response.StausCodeType != SIP_StatusCodeType.Provisional){
                        m_FinalResponseSent = true;
                    }

                    /* 10. Generate any necessary CANCEL requests.
                           If the forwarded response was a final response, the proxy MUST
                           generate a CANCEL request for all pending client transactions
                           associated with this response context.
                    */
                    if(response.StausCodeType != SIP_StatusCodeType.Provisional){
                        if(!m_NoCancel){
                            foreach(SIP_ClientTransaction transaction in m_pClientTransactions.ToArray()){
                                transaction.Cancel();
                            }
                        }
                    }
                }
            }
        }
Пример #47
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>
        internal protected 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;

            m_pUsages = new List <SIP_Dialog_Usage>();

            #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[])Net_Utils.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;

                List <string> allow = new List <string>();
                foreach (SIP_t_Method m in response.Allow.GetAllValues())
                {
                    allow.Add(m.Method);
                }
                m_pRemoteAllow = allow.ToArray();

                List <string> supported = new List <string>();
                foreach (SIP_t_OptionTag s in response.Supported.GetAllValues())
                {
                    supported.Add(s.OptionTag);
                }
                m_pRemoteSupported = supported.ToArray();
            }

            #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[])Net_Utils.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;

                List <string> allow = new List <string>();
                foreach (SIP_t_Method m in response.Allow.GetAllValues())
                {
                    allow.Add(m.Method);
                }
                m_pRemoteAllow = allow.ToArray();

                List <string> supported = new List <string>();
                foreach (SIP_t_OptionTag s in response.Supported.GetAllValues())
                {
                    supported.Add(s.OptionTag);
                }
                m_pRemoteSupported = supported.ToArray();
            }

            #endregion

            m_pFlow = transaction.Flow;
        }
Пример #48
0
        /// <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(this.SyncRoot){
                if(this.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(this.Stack.Logger != null){
                    byte[] responseData = response.ToByteData();

                    this.Stack.Logger.AddRead(
                        Guid.NewGuid().ToString(),
                        null,
                        0,
                        "Response [transactionID='" +  this.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 6026 7.2. INVITE client transaction. (Udpates RFC 3261)
                      +-----------+                        +-----------+
                      |           |                        |           |
                      |  Calling  |                        |  Calling  |
                      |           |----------->+           |           |-----------+
                      +-----------+ 2xx        |           +-----------+ 2xx       |
                                    2xx to TU  |                         2xx to TU |
                                               |                                   |
                                               |                                   |
                                               |                                   |
                                               |                                   |
                      +-----------+            |           +-----------+           |
                      |           |            |           |           |           |
                      |Proceeding |----------->|           |Proceeding |---------->|
                      |           | 2xx        |           |           | 2xx       |
                      +-----------+ 2xx to TU  |           +-----------+ 2xx to TU |
                                               |                                   |
                                               |                                   |
                                               |                                   |
                                               |                                   V
                                               |                            +-----------+
                                               |                            |           |
                                               |                            | Accepted  |
                                               |                        +---|           |
                                               |              2xx       |   +-----------+
                                               |              2xx to TU |     ^    |
                                               |                        |     |    |
                                               |                        +-----+    |
                                               |                                   |
                                               |                 +-----------------+
                                               |                 | Timer M fires
                                               |                 | -
                                               |                 V
                      +-----------+            |           +-----------+
                      |           |            |           |           |
                      | Terminated|<-----------+           | Terminated|
                      |           |                        |           |
                      +-----------+                        +-----------+


                */

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

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

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

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

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

                        // 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.Accpeted);

                            /* RFC 6025 7.1.
                                When the "Accepted" state is entered, timer L MUST be set to fire in 64*T1.
                            */
                            m_pTimerM = new TimerEx(64 * SIP_TimerConstants.T1);
                            m_pTimerM.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerM_Elapsed);
                            m_pTimerM.Enabled = true;

                            // Log
                            if(this.Stack.Logger != null){
                                this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer M(2xx retransmission wait) started, will trigger after " + m_pTimerM.Interval + ".");
                            }
                        }
                        // 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(this.Flow.IsReliable ? 0 : 32000,false);
                            m_pTimerD.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerD_Elapsed);
                            // Log
                            if(this.Stack.Logger != null){
                                this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer D(INVITE 3xx - 6xx response retransmission wait) started, will trigger after " + m_pTimerD.Interval + ".");
                            }
                            m_pTimerD.Enabled = true;
                        }
                    }

                    #endregion

                    #region Proceeding

                    else if(this.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.Accpeted);

                            /* RFC 6025 7.1.
                                When the "Accepted" state is entered, timer L MUST be set to fire in 64*T1.
                            */
                            m_pTimerM = new TimerEx(64 * SIP_TimerConstants.T1);
                            m_pTimerM.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerM_Elapsed);
                            m_pTimerM.Enabled = true;

                            // Log
                            if(this.Stack.Logger != null){
                                this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer M(2xx retransmission wait) started, will trigger after " + m_pTimerM.Interval + ".");
                            }
                        }
                        // 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(this.Flow.IsReliable ? 0 : 32000,false);
                            m_pTimerD.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerD_Elapsed);
                            // Log
                            if(this.Stack.Logger != null){
                                this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer D(INVITE 3xx - 6xx response retransmission wait) started, will trigger after " + m_pTimerD.Interval + ".");
                            }
                            m_pTimerD.Enabled = true;
                        }
                    }

                    #endregion

                    #region Accepted

                    else if(this.State == SIP_TransactionState.Accpeted){
                        if(response.StatusCodeType == SIP_StatusCodeType.Success){
                            OnResponseReceived(response);
                        }
                    }

                    #endregion

                    #region Completed

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

                    #endregion

                    #region Terminated

                    else if(this.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(this.State == SIP_TransactionState.Trying){
                        // Store response.
                        AddResponse(response);

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

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

                        // 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(this.Stack.Logger != null){
                                    this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer F(Non-INVITE trying,proceeding state timeout) stopped.");
                                }
                            }

                            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(this.Flow.IsReliable ? 1 : SIP_TimerConstants.T4,false);
                            m_pTimerK.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerK_Elapsed);
                            // Log
                            if(this.Stack.Logger != null){
                                this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer K(Non-INVITE 3xx - 6xx response retransmission wait) started, will trigger after " + m_pTimerK.Interval + ".");
                            }
                            m_pTimerK.Enabled = true;
                        }
                    }

                    #endregion

                    #region Proceeding

                    else if(this.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(this.Stack.Logger != null){
                                    this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer F(Non-INVITE trying,proceeding state timeout) stopped.");
                                }
                            }

                            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(this.Flow.IsReliable ? 0 : SIP_TimerConstants.T4,false);
                            m_pTimerK.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerK_Elapsed);
                            // Log
                            if(this.Stack.Logger != null){
                                this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer K(Non-INVITE 3xx - 6xx response retransmission wait) started, will trigger after " + m_pTimerK.Interval + ".");
                            }
                            m_pTimerK.Enabled = true;
                        }
                    }

                    #endregion

                    #region Completed

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

                    #endregion

                    #region Terminated

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

                    #endregion
                }

                #endregion
            }
        }
        /// <summary>
        /// Creates new SIP UAS dialog.
        /// </summary>
        /// <param name="transaction">Owner transaction what forces to create dialog.</param>
        /// <param name="request">Server transaction request what response it is.</param>
        /// <param name="response">SIP response what causes dialog creation.</param>
        /// <returns>Returns new SIP dialog.</returns>
        internal SIP_Dialog CreateDialog(SIP_ServerTransaction transaction,SIP_Request request,SIP_Response response)
        {
            // TODO: ren EnsureDialog

            SIP_Dialog dialog = new SIP_Dialog(m_pSipStack,request,response);
            m_pDialogs.Add(dialog);

            return dialog;
        }
Пример #50
0
        /// <summary>
        /// Creates authorization for each challange in <b>response</b>.
        /// </summary>
        /// <param name="request">SIP request where to add authorization values.</param>
        /// <param name="response">SIP response which challanges to authorize.</param>
        /// <param name="credentials">Credentials for authorization.</param>
        /// <returns>Returns true if all challanges were authorized. If any of the challanges was not authorized, returns false.</returns>
        private bool Authorize(SIP_Request request,SIP_Response response,NetworkCredential[] credentials)
        {
            if(request == null){
                throw new ArgumentNullException("request");
            }
            if(response == null){
                throw new ArgumentNullException("response");
            }
            if(credentials == null){
                throw new ArgumentNullException("credentials");
            }

            bool allAuthorized = true;

            #region WWWAuthenticate

            foreach(SIP_t_Challenge challange in response.WWWAuthenticate.GetAllValues()){
                Auth_HttpDigest authDigest = new Auth_HttpDigest(challange.AuthData,request.RequestLine.Method);

                // Serach credential for the specified challange.
                NetworkCredential credential = null;
                foreach(NetworkCredential c in credentials){
                    if(c.Domain.ToLower() == authDigest.Realm.ToLower()){
                        credential = c;
                        break;
                    }
                }
                // We don't have credential for this challange.
                if(credential == null){
                    allAuthorized = false;
                }
                // Authorize challange.
                else{
                    authDigest.UserName = credential.UserName;
                    authDigest.Password = credential.Password;
                    authDigest.CNonce   = Auth_HttpDigest.CreateNonce();
                    authDigest.Uri      = request.RequestLine.Uri.ToString();

                    request.Authorization.Add(authDigest.ToAuthorization());
                }
            }

            #endregion

            #region ProxyAuthenticate

            foreach(SIP_t_Challenge challange in response.ProxyAuthenticate.GetAllValues()){
                Auth_HttpDigest authDigest = new Auth_HttpDigest(challange.AuthData,request.RequestLine.Method);

                // Serach credential for the specified challange.
                NetworkCredential credential = null;
                foreach(NetworkCredential c in credentials){
                    if(c.Domain.ToLower() == authDigest.Realm.ToLower()){
                        credential = c;
                        break;
                    }
                }
                // We don't have credential for this challange.
                if(credential == null){
                    allAuthorized = false;
                }
                // Authorize challange.
                else{
                    authDigest.UserName = credential.UserName;
                    authDigest.Password = credential.Password;
                    authDigest.CNonce   = Auth_HttpDigest.CreateNonce();
                    authDigest.Uri      = request.RequestLine.Uri.ToString();

                    request.ProxyAuthorization.Add(authDigest.ToAuthorization());                    
                }
            }

            #endregion

            return allAuthorized;
        }
Пример #51
0
        /// <summary>
        /// Sends specified response to flow remote end point.
        /// </summary>
        /// <param name="response">SIP response 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>response</b> is null reference.</exception>
        public void Send(SIP_Response response)
        {
            lock(m_pLock){
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                if(response == null){
                    throw new ArgumentNullException("response");
                }

                SendInternal(response.ToByteData());
                m_LastPing = DateTime.Now;
            }
        }
Пример #52
0
        /// <summary>
        /// Sends SIP response to caller. If proxy context is in B2BUA mode, new response is generated 
        /// as needed.
        /// </summary>
        /// <param name="transaction">Client transaction what response it is.</param>
        /// <param name="response">Response to send.</param>
        private void SendResponse(SIP_ClientTransaction transaction,SIP_Response response)
        {
            if(m_IsB2BUA){
                /* draft-marjou-sipping-b2bua-00 4.1.3.
                    When the UAC side of the B2BUA receives the downstream SIP response
                    of a forwarded request, its associated UAS creates an upstream
                    response (except for 100 responses).  The creation of the Via, Max-
                    Forwards, Call-Id, CSeq, Record-Route and Contact header fields
                    follows the rules of [2].  The Record-Route header fields of the
                    downstream response are not copied in the new upstream response, as
                    Record-Route is meaningful for the downstream dialog.  The UAS SHOULD
                    copy other header fields and body from the downstream response into
                    this upstream response before sending it.
                */

                SIP_Request originalRequest = m_pServerTransaction.Request;

                // We need to use caller original request to construct response from proxied response.
                SIP_Response b2buaResponse = response.Copy();
                b2buaResponse.Via.RemoveAll();
                b2buaResponse.Via.AddToTop(originalRequest.Via.GetTopMostValue().ToStringValue());
                b2buaResponse.CallID = originalRequest.CallID;
                b2buaResponse.CSeq = originalRequest.CSeq;
                b2buaResponse.Contact.RemoveAll();
                //b2buaResponse.Contact.Add(m_pProxy.CreateContact(originalRequest.From.Address).ToStringValue());
                b2buaResponse.RecordRoute.RemoveAll();

                b2buaResponse.Allow.RemoveAll();
                b2buaResponse.Supported.RemoveAll();
                // Accept to non ACK,BYE request.
                if(originalRequest.RequestLine.Method != SIP_Methods.ACK && originalRequest.RequestLine.Method != SIP_Methods.BYE){
                    b2buaResponse.Allow.Add("INVITE,ACK,OPTIONS,CANCEL,BYE,PRACK");
                }
                // Supported to non ACK request.
                if(originalRequest.RequestLine.Method != SIP_Methods.ACK){
                    b2buaResponse.Supported.Add("100rel,timer");
                }
                // Remove Require: header.
                b2buaResponse.Require.RemoveAll();

                m_pServerTransaction.SendResponse(b2buaResponse);

                // If INVITE 2xx response do call here.
                if(response.CSeq.RequestMethod.ToUpper() == SIP_Methods.INVITE && response.StatusCodeType == SIP_StatusCodeType.Success){
                    m_pProxy.B2BUA.AddCall(m_pServerTransaction.Dialog,transaction.Dialog);
                }
            }
            else{
                m_pServerTransaction.SendResponse(response);
            }
        }
Пример #53
0
        /// <summary>
        /// Processes specified response through this dialog.
        /// </summary>
        /// <param name="response">SIP response to process.</param>
        /// <returns>Returns true if this dialog processed specified response, otherwise false.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null.</exception>
        internal protected virtual bool ProcessResponse(SIP_Response response)
        {      
            if(response == null){
                throw new ArgumentNullException("response");
            }

            return false;            
        }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="stack">Reference to SIP stack.</param>
 /// <param name="transaction">Client transaction what response it is. This value can be null if no matching client response.</param>
 /// <param name="response">Received response.</param>
 internal SIP_ResponseReceivedEventArgs(SIP_Stack stack, SIP_ClientTransaction transaction, SIP_Response response)
 {
     m_pStack       = stack;
     m_pResponse    = response;
     m_pTransaction = transaction;
 }