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

            #region Contact (RFC 3261 8.1.1.8)

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

            SIP_t_ContactParam contact = request.Contact.GetTopMostValue();

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

                request.Contact.Add((flow.IsSecure ? "sips:" : "sip:") + from.User + "@" + flow.LocalPublicEP.ToString());

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

                // REMOVE ME: 22.10.2010
                //((SIP_Uri)contact.Address.Uri).Host =  m_pStack.TransportLayer.GetContactHost(flow).ToString();
            }

            #endregion

            m_pTransaction = m_pStack.TransactionLayer.CreateClientTransaction(flow, request, true);
            m_pTransaction.ResponseReceived += new EventHandler <SIP_ResponseReceivedEventArgs>(ClientTransaction_ResponseReceived);
            m_pTransaction.TimedOut         += new EventHandler(ClientTransaction_TimedOut);
            m_pTransaction.TransportError   += new EventHandler <ExceptionEventArgs>(ClientTransaction_TransportError);

            // Start transaction processing.
            m_pTransaction.Start();
        }
        /// <summary>
        /// Creates and send CANCEL request to remote target.
        /// </summary>
        private void SendCancel()
        {
            /* RFC 3261 9.1.
             *  The following procedures are used to construct a CANCEL request.  The
             *  Request-URI, Call-ID, To, the numeric part of CSeq, and From header
             *  fields in the CANCEL request MUST be identical to those in the
             *  request being cancelled, including tags.  A CANCEL constructed by a
             *  client MUST have only a single Via header field value matching the
             *  top Via value in the request being cancelled.  Using the same values
             *  for these header fields allows the CANCEL to be matched with the
             *  request it cancels (Section 9.2 indicates how such matching occurs).
             *  However, the method part of the CSeq header field MUST have a value
             *  of CANCEL.  This allows it to be identified and processed as a
             *  transaction in its own right (See Section 17).
             *
             *  If the request being cancelled contains a Route header field, the
             *  CANCEL request MUST include that Route header field's values.
             *
             *      This is needed so that stateless proxies are able to route CANCEL
             *      requests properly.
             */

            SIP_Request cancelRequest = new SIP_Request(SIP_Methods.CANCEL);

            cancelRequest.RequestLine.Uri = this.Request.RequestLine.Uri;
            cancelRequest.Via.Add(this.Request.Via.GetTopMostValue().ToStringValue());
            cancelRequest.CallID = this.Request.CallID;
            cancelRequest.From   = this.Request.From;
            cancelRequest.To     = this.Request.To;
            cancelRequest.CSeq   = new SIP_t_CSeq(this.Request.CSeq.SequenceNumber, SIP_Methods.CANCEL);
            foreach (SIP_t_AddressParam route in this.Request.Route.GetAllValues())
            {
                cancelRequest.Route.Add(route.ToStringValue());
            }
            cancelRequest.MaxForwards = 70;

            // We must use same data flow to send CANCEL what sent initial request.
            SIP_ClientTransaction transaction = this.Stack.TransactionLayer.CreateClientTransaction(this.Flow, cancelRequest, false);

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

                /* NAT traversal.
                    When we do record routing, store request sender flow info and request target flow info.
                    Now the tricky part, how proxy later which flow is target (because both sides can send requests).
                      Sender-flow will store from-tag to flow and target-flow will store flowID only (Because we don't know to-tag).
                      Later if request to-tag matches(incoming request), use that flow, otherwise(outgoing request) other flow.

                    flowInfo: sender-flow "/" target-flow
                              sender-flow = from-tag ":" flowID
                              target-flow = flowID
                */
                if(m_AddRecordRoute && request.From.Tag != null){
                    string flowInfo = request.From.Tag + ":" + m_pOwner.ServerTransaction.Flow.ID + "/" + flow.ID;
                    ((SIP_Uri)request.RecordRoute.GetTopMostValue().Address.Uri).Parameters.Add("flowInfo",flowInfo);
                }

                /* RFC 3261 16.6 Request Forwarding.
                        Common Steps 1 - 7 are done in target Init().

                        8.  Add a Via header field value
                        9.  Add a Content-Length header field if necessary
                        10. Forward the new request
                        11. Set timer C
                */

                #region 8.  Add a Via header field value

                // Skip, Client transaction will add it.

                #endregion

                #region 9.  Add a Content-Length header field if necessary

                // Skip, our SIP_Message class is smart and do it when ever it's needed.

                #endregion

                #region 10. Forward the new request

                m_pTransaction = m_pOwner.Proxy.Stack.TransactionLayer.CreateClientTransaction(flow,request,true);
                m_pTransaction.ResponseReceived += new EventHandler<SIP_ResponseReceivedEventArgs>(ClientTransaction_ResponseReceived);
                m_pTransaction.TimedOut += new EventHandler(ClientTransaction_TimedOut);
                m_pTransaction.TransportError += new EventHandler<ExceptionEventArgs>(ClientTransaction_TransportError);
                m_pTransaction.Disposed += new EventHandler(m_pTransaction_Disposed);

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

                #endregion

                #region 11. Set timer C

                /* 11. Set timer C
                    In order to handle the case where an INVITE request never
                    generates a final response, the TU uses a timer which is called
                    timer C.  Timer C MUST be set for each client transaction when
                    an INVITE request is proxied.  The timer MUST be larger than 3
                    minutes.  Section 16.7 bullet 2 discusses how this timer is
                    updated with provisional responses, and Section 16.8 discusses
                    processing when it fires.
                */
                if(request.RequestLine.Method == SIP_Methods.INVITE){
                    m_pTimerC = new TimerEx();
                    m_pTimerC.AutoReset = false;
                    m_pTimerC.Interval = 3 * 60 * 1000;
                    m_pTimerC.Elapsed += new ElapsedEventHandler(m_pTimerC_Elapsed);
                }

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

            #region Contact (RFC 3261 8.1.1.8)

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

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

            SIP_t_ContactParam contact = request.Contact.GetTopMostValue();

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

                request.Contact.Add((flow.IsSecure ? "sips:" : "sip:" ) + from.User + "@" + flow.LocalPublicEP.ToString());

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

                // REMOVE ME: 22.10.2010
                //((SIP_Uri)contact.Address.Uri).Host =  m_pStack.TransportLayer.GetContactHost(flow).ToString();
            }

            #endregion
         
            m_pTransaction = m_pStack.TransactionLayer.CreateClientTransaction(flow,request,true);  
            m_pTransaction.ResponseReceived += new EventHandler<SIP_ResponseReceivedEventArgs>(ClientTransaction_ResponseReceived);
            m_pTransaction.TimedOut += new EventHandler(ClientTransaction_TimedOut);
            m_pTransaction.TransportError += new EventHandler<ExceptionEventArgs>(ClientTransaction_TransportError);
 
            // Start transaction processing.
            m_pTransaction.Start();
        }