Пример #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>
        /// Default constructor.
        /// </summary>
        /// <param name="method">SIP method.</param>
        /// <param name="uri">Request URI.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>method</b> or <b>uri</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        public SIP_RequestLine(string method, AbsoluteUri uri)
        {
            if (method == null)
            {
                throw new ArgumentNullException("method");
            }
            if (!SIP_Utils.IsToken(method))
            {
                throw new ArgumentException("Argument 'method' value must be token.");
            }
            if (uri == null)
            {
                throw new ArgumentNullException("uri");
            }

            m_Method  = method.ToUpper();
            m_pUri    = uri;
            m_Version = "SIP/2.0";
        }
Пример #3
0
        /// <summary>
        /// Authenticates SIP request. This method also sends all needed replys to request sender.
        /// </summary>
        /// <param name="e">Request event arguments.</param>
        /// <param name="userName">If authentication sucessful, then authenticated user name is stored to this variable.</param>
        /// <returns>Returns true if request was authenticated.</returns>
        internal bool AuthenticateRequest(SIP_RequestReceivedEventArgs e, out string userName)
        {
            userName = null;

            SIP_t_Credentials credentials = SIP_Utils.GetCredentials(e.Request, m_pStack.Realm);

            // No credentials for our realm.
            if (credentials == null)
            {
                SIP_Response notAuthenticatedResponse = m_pStack.CreateResponse(SIP_ResponseCodes.x407_Proxy_Authentication_Required, e.Request);
                notAuthenticatedResponse.ProxyAuthenticate.Add(new Auth_HttpDigest(m_pStack.Realm, m_pStack.DigestNonceManager.CreateNonce(), m_Opaque).ToChallenge());

                e.ServerTransaction.SendResponse(notAuthenticatedResponse);
                return(false);
            }

            Auth_HttpDigest auth = new Auth_HttpDigest(credentials.AuthData, e.Request.RequestLine.Method);

            // Check opaque validity.
            if (auth.Opaque != m_Opaque)
            {
                SIP_Response notAuthenticatedResponse = m_pStack.CreateResponse(SIP_ResponseCodes.x407_Proxy_Authentication_Required + ": Opaque value won't match !", e.Request);
                notAuthenticatedResponse.ProxyAuthenticate.Add(new Auth_HttpDigest(m_pStack.Realm, m_pStack.DigestNonceManager.CreateNonce(), m_Opaque).ToChallenge());

                // Send response
                e.ServerTransaction.SendResponse(notAuthenticatedResponse);
                return(false);
            }
            // Check nonce validity.
            if (!m_pStack.DigestNonceManager.NonceExists(auth.Nonce))
            {
                SIP_Response notAuthenticatedResponse = m_pStack.CreateResponse(SIP_ResponseCodes.x407_Proxy_Authentication_Required + ": Invalid nonce value !", e.Request);
                notAuthenticatedResponse.ProxyAuthenticate.Add(new Auth_HttpDigest(m_pStack.Realm, m_pStack.DigestNonceManager.CreateNonce(), m_Opaque).ToChallenge());

                // Send response
                e.ServerTransaction.SendResponse(notAuthenticatedResponse);
                return(false);
            }
            // Valid nonce, consume it so that nonce can't be used any more.
            else
            {
                m_pStack.DigestNonceManager.RemoveNonce(auth.Nonce);
            }

            SIP_AuthenticateEventArgs eArgs = this.OnAuthenticate(auth);

            // Authenticate failed.
            if (!eArgs.Authenticated)
            {
                SIP_Response notAuthenticatedResponse = m_pStack.CreateResponse(SIP_ResponseCodes.x407_Proxy_Authentication_Required + ": Authentication failed.", e.Request);
                notAuthenticatedResponse.ProxyAuthenticate.Add(new Auth_HttpDigest(m_pStack.Realm, m_pStack.DigestNonceManager.CreateNonce(), m_Opaque).ToChallenge());

                // Send response
                e.ServerTransaction.SendResponse(notAuthenticatedResponse);
                return(false);
            }

            userName = auth.UserName;

            return(true);
        }
Пример #4
0
        /// <summary>
        /// Forwards specified request to target recipient.
        /// </summary>
        /// <param name="statefull">Specifies if request is sent statefully or statelessly.</param>
        /// <param name="e">Request event arguments.</param>
        /// <param name="addRecordRoute">If true Record-Route header field is added.</param>
        internal void ForwardRequest(bool statefull, SIP_RequestReceivedEventArgs e, bool addRecordRoute)
        {
            SIP_RequestContext requestContext = new SIP_RequestContext(this, e.Request, e.Flow);

            SIP_Request request = e.Request;
            SIP_Uri     route   = null;

            /* RFC 3261 16.
             *  1. Validate the request (Section 16.3)
             *      1. Reasonable Syntax
             *      2. URI scheme (NOTE: We do it later)
             *      3. Max-Forwards
             *      4. (Optional) Loop Detection
             *      5. Proxy-Require
             *      6. Proxy-Authorization
             *  2. Preprocess routing information (Section 16.4)
             *  3. Determine target(s) for the request (Section 16.5)
             *  x. Custom handling (non-RFC)
             *      1. Process custom request handlers.
             *      2. URI scheme
             *  4. Forward the request (Section 16.6)
             */

            #region 1. Validate the request (Section 16.3)

            // 1.1 Reasonable Syntax.
            //      SIP_Message parsing have done it.

            // 1.2 URI scheme check.
            //      We do it later.

            // 1.3 Max-Forwards.
            if (request.MaxForwards <= 0)
            {
                e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x483_Too_Many_Hops, request));
                return;
            }

            // 1.4 (Optional) Loop Detection.
            //      Skip.

            // 1.5 Proxy-Require.
            //      TODO:

            // 1.6 Proxy-Authorization.

            /*  If an element requires credentials before forwarding a request,
             *  the request MUST be inspected as described in Section 22.3.  That
             *  section also defines what the element must do if the inspection
             *  fails.
             */

            // We need to auth all foreign calls.
            if (!SIP_Utils.IsSipOrSipsUri(request.RequestLine.Uri.ToString()) || !this.OnIsLocalUri(((SIP_Uri)request.RequestLine.Uri).Host))
            {
                // If To: field is registrar AOR and request-URI is local registration contact, skip authentication.
                bool skipAuth = false;
                if (request.To.Address.IsSipOrSipsUri)
                {
                    SIP_Registration registration = m_pRegistrar.GetRegistration(((SIP_Uri)request.To.Address.Uri).Address);
                    if (registration != null)
                    {
                        if (registration.GetBinding(request.RequestLine.Uri) != null)
                        {
                            skipAuth = true;
                        }
                    }
                }

                if (!skipAuth)
                {
                    string userName = null;

                    // We need to pass-through ACK.
                    if (request.RequestLine.Method == SIP_Methods.ACK)
                    {
                    }
                    else if (!AuthenticateRequest(e, out userName))
                    {
                        return;
                    }

                    requestContext.SetUser(userName);
                }
            }

            #endregion

            #region 2. Preprocess routing information (Section 16.4).

            /*
             *  The proxy MUST inspect the Request-URI of the request.  If the
             *  Request-URI of the request contains a value this proxy previously
             *  placed into a Record-Route header field (see Section 16.6 item 4),
             *  the proxy MUST replace the Request-URI in the request with the last
             *  value from the Route header field, and remove that value from the
             *  Route header field.  The proxy MUST then proceed as if it received
             *  this modified request.
             *
             *      This will only happen when the element sending the request to the
             *      proxy (which may have been an endpoint) is a strict router.  This
             *      rewrite on receive is necessary to enable backwards compatibility
             *      with those elements.  It also allows elements following this
             *      specification to preserve the Request-URI through strict-routing
             *      proxies (see Section 12.2.1.1).
             *
             *      This requirement does not obligate a proxy to keep state in order
             *      to detect URIs it previously placed in Record-Route header fields.
             *      Instead, a proxy need only place enough information in those URIs
             *      to recognize them as values it provided when they later appear.
             *
             *  If the Request-URI contains a maddr parameter, the proxy MUST check
             *  to see if its value is in the set of addresses or domains the proxy
             *  is configured to be responsible for.  If the Request-URI has a maddr
             *  parameter with a value the proxy is responsible for, and the request
             *  was received using the port and transport indicated (explicitly or by
             *  default) in the Request-URI, the proxy MUST strip the maddr and any
             *  non-default port or transport parameter and continue processing as if
             *  those values had not been present in the request.
             *
             *      A request may arrive with a maddr matching the proxy, but on a
             *      port or transport different from that indicated in the URI.  Such
             *      a request needs to be forwarded to the proxy using the indicated
             *      port and transport.
             *
             *  If the first value in the Route header field indicates this proxy,
             *  the proxy MUST remove that value from the request.
             */

            // Strict route - handle it.
            if ((request.RequestLine.Uri is SIP_Uri) && IsRecordRoute(((SIP_Uri)request.RequestLine.Uri)) && request.Route.GetAllValues().Length > 0)
            {
                request.RequestLine.Uri = request.Route.GetAllValues()[request.Route.GetAllValues().Length - 1].Address.Uri;
                SIP_t_AddressParam[] routes = request.Route.GetAllValues();
                route = (SIP_Uri)routes[routes.Length - 1].Address.Uri;
                request.Route.RemoveLastValue();
            }

            // Check if Route header field indicates this proxy.
            if (request.Route.GetAllValues().Length > 0)
            {
                route = (SIP_Uri)request.Route.GetTopMostValue().Address.Uri;

                // We consider loose-route always ours, because otherwise this message never reach here.
                if (route.Param_Lr)
                {
                    request.Route.RemoveTopMostValue();
                }
                // It's our route, remove it.
                else if (IsLocalRoute(route))
                {
                    request.Route.RemoveTopMostValue();
                }
            }

            #endregion

            #region REGISTER request processing

            if (e.Request.RequestLine.Method == SIP_Methods.REGISTER)
            {
                SIP_Uri requestUri = (SIP_Uri)e.Request.RequestLine.Uri;

                // REGISTER is meant for us.
                if (this.OnIsLocalUri(requestUri.Host))
                {
                    if ((m_ProxyMode & SIP_ProxyMode.Registrar) != 0)
                    {
                        m_pRegistrar.Register(e);

                        return;
                    }
                    else
                    {
                        e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x405_Method_Not_Allowed, e.Request));

                        return;
                    }
                }
                // Forward REGISTER.
                // else{
            }

            #endregion

            #region 3. Determine target(s) for the request (Section 16.5)

            /* 3. Determine target(s) for the request (Section 16.5)
             *      Next, the proxy calculates the target(s) of the request.  The set of
             *      targets will either be predetermined by the contents of the request
             *      or will be obtained from an abstract location service.  Each target
             *      in the set is represented as a URI.
             *
             *      If the domain of the Request-URI indicates a domain this element is
             *      not responsible for, the Request-URI MUST be placed into the target
             *      set as the only target, and the element MUST proceed to the task of
             *      Request Forwarding (Section 16.6).
             *
             *      If the target set for the request has not been predetermined as
             *      described above, this implies that the element is responsible for the
             *      domain in the Request-URI, and the element MAY use whatever mechanism
             *      it desires to determine where to send the request.  Any of these
             *      mechanisms can be modeled as accessing an abstract Location Service.
             *      This may consist of obtaining information from a location service
             *      created by a SIP Registrar, reading a database, consulting a presence
             *      server, utilizing other protocols, or simply performing an
             *      algorithmic substitution on the Request-URI.  When accessing the
             *      location service constructed by a registrar, the Request-URI MUST
             *      first be canonicalized as described in Section 10.3 before being used
             *      as an index.  The output of these mechanisms is used to construct the
             *      target set.
             */

            // Non-SIP
            // Foreign SIP
            // Local SIP

            if (e.Request.RequestLine.Uri is SIP_Uri)
            {
                SIP_Uri requestUri = (SIP_Uri)e.Request.RequestLine.Uri;

                // Proxy is not responsible for the domain in the Request-URI.
                if (!this.OnIsLocalUri(requestUri.Host))
                {
                    /* 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
                     */

                    SIP_Flow targetFlow = null;
                    string   flowInfo   = (route != null && route.Parameters["flowInfo"] != null) ? route.Parameters["flowInfo"].Value : null;
                    if (flowInfo != null && request.To.Tag != null)
                    {
                        string flow1Tag = flowInfo.Substring(0, flowInfo.IndexOf(':'));
                        string flow1ID  = flowInfo.Substring(flowInfo.IndexOf(':') + 1, flowInfo.IndexOf('/') - flowInfo.IndexOf(':') - 1);
                        string flow2ID  = flowInfo.Substring(flowInfo.IndexOf('/') + 1);

                        if (flow1Tag == request.To.Tag)
                        {
                            targetFlow = m_pStack.TransportLayer.GetFlow(flow1ID);
                        }
                        else
                        {
                            ;
                            targetFlow = m_pStack.TransportLayer.GetFlow(flow2ID);
                        }
                    }

                    requestContext.Targets.Add(new SIP_ProxyTarget(requestUri, targetFlow));
                }
                // Proxy is responsible for the domain in the Request-URI.
                else
                {
                    // Try to get AOR from registrar.
                    SIP_Registration registration = m_pRegistrar.GetRegistration(requestUri.Address);

                    // We have AOR specified in request-URI in registrar server.
                    if (registration != null)
                    {
                        // Add all AOR SIP contacts to target set.
                        foreach (SIP_RegistrationBinding binding in registration.Bindings)
                        {
                            if (binding.ContactURI is SIP_Uri && binding.TTL > 0)
                            {
                                requestContext.Targets.Add(new SIP_ProxyTarget((SIP_Uri)binding.ContactURI, binding.Flow));
                            }
                        }
                    }
                    // We don't have AOR specified in request-URI in registrar server.
                    else
                    {
                        // If the Request-URI indicates a resource at this proxy that does not
                        // exist, the proxy MUST return a 404 (Not Found) response.
                        if (!this.OnAddressExists(requestUri.Address))
                        {
                            e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x404_Not_Found, e.Request));
                            return;
                        }
                    }

                    // If the target set remains empty after applying all of the above, the proxy MUST return an error response,
                    // which SHOULD be the 480 (Temporarily Unavailable) response.
                    if (requestContext.Targets.Count == 0)
                    {
                        e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x480_Temporarily_Unavailable, e.Request));
                        return;
                    }
                }
            }

            #endregion

            #region x. Custom handling

            // x.1 Process custom request handlers, if any.
            foreach (SIP_ProxyHandler handler in this.Handlers)
            {
                try{
                    SIP_ProxyHandler h = handler;

                    // Reusing existing handler not allowed, create new instance of handler.
                    if (!handler.IsReusable)
                    {
                        h = (SIP_ProxyHandler)System.Activator.CreateInstance(handler.GetType());
                    }

                    if (h.ProcessRequest(requestContext))
                    {
                        // Handler processed request, we are done.
                        return;
                    }
                }
                catch (Exception x) {
                    m_pStack.OnError(x);
                }
            }

            // x.2 URI scheme.
            //  If no targets and request-URI non-SIP, reject request because custom handlers should have been handled it.
            if (requestContext.Targets.Count == 0 && !SIP_Utils.IsSipOrSipsUri(request.RequestLine.Uri.ToString()))
            {
                e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x416_Unsupported_URI_Scheme, e.Request));
                return;
            }

            #endregion

            #region 4. Forward the request (Section 16.6)

            #region Statefull

            if (statefull)
            {
                SIP_ProxyContext proxyContext = this.CreateProxyContext(requestContext, e.ServerTransaction, request, addRecordRoute);
                proxyContext.Start();
            }

            #endregion

            #region Stateless

            else
            {
                /* RFC 3261 16.6 Request Forwarding.
                 * For each target, the proxy forwards the request following these steps:
                 *  1.  Make a copy of the received request
                 *  2.  Update the Request-URI
                 *  3.  Update the Max-Forwards header field
                 *  4.  Optionally add a Record-route header field value
                 *  5.  Optionally add additional header fields
                 *  6.  Postprocess routing information
                 *  7.  Determine the next-hop address, port, and transport
                 *  8.  Add a Via header field value
                 *  9.  Add a Content-Length header field if necessary
                 *  10. Forward the new request
                 */

                /* RFC 3261 16.11 Stateless Proxy.
                 *  o  A stateless proxy MUST choose one and only one target from the target set. This choice
                 *     MUST only rely on fields in the message and time-invariant properties of the server. In
                 *     particular, a retransmitted request MUST be forwarded to the same destination each time
                 *     it is processed. Furthermore, CANCEL and non-Routed ACK requests MUST generate the same
                 *     choice as their associated INVITE.
                 *
                 *  However, a stateless proxy cannot simply use a random number generator to compute
                 *  the first component of the branch ID, as described in Section 16.6 bullet 8.
                 *  This is because retransmissions of a request need to have the same value, and
                 *  a stateless proxy cannot tell a retransmission from the original request.
                 *
                 *  We just use: "z9hG4bK-" + md5(topmost branch)
                 */

                bool      isStrictRoute = false;
                SIP_Hop[] hops          = null;

                #region 1.  Make a copy of the received request

                SIP_Request forwardRequest = request.Copy();

                #endregion

                #region 2.  Update the Request-URI

                forwardRequest.RequestLine.Uri = requestContext.Targets[0].TargetUri;

                #endregion

                #region 3.  Update the Max-Forwards header field

                forwardRequest.MaxForwards--;

                #endregion

                #region 4.  Optionally add a Record-route header field value

                #endregion

                #region 5.  Optionally add additional header fields

                #endregion

                #region 6.  Postprocess routing information

                /* 6. Postprocess routing information.
                 *
                 *  If the copy contains a Route header field, the proxy MUST inspect the URI in its first value.
                 *  If that URI does not contain an lr parameter, the proxy MUST modify the copy as follows:
                 *      - The proxy MUST place the Request-URI into the Route header
                 *        field as the last value.
                 *
                 *      - The proxy MUST then place the first Route header field value
                 *        into the Request-URI and remove that value from the Route header field.
                 */
                if (forwardRequest.Route.GetAllValues().Length > 0 && !forwardRequest.Route.GetTopMostValue().Parameters.Contains("lr"))
                {
                    forwardRequest.Route.Add(forwardRequest.RequestLine.Uri.ToString());

                    forwardRequest.RequestLine.Uri = SIP_Utils.UriToRequestUri(forwardRequest.Route.GetTopMostValue().Address.Uri);
                    forwardRequest.Route.RemoveTopMostValue();

                    isStrictRoute = true;
                }

                #endregion

                #region 7.  Determine the next-hop address, port, and transport

                /* 7. Determine the next-hop address, port, and transport.
                 *    The proxy MAY have a local policy to send the request to a
                 *    specific IP address, port, and transport, independent of the
                 *    values of the Route and Request-URI.  Such a policy MUST NOT be
                 *    used if the proxy is not certain that the IP address, port, and
                 *    transport correspond to a server that is a loose router.
                 *    However, this mechanism for sending the request through a
                 *    specific next hop is NOT RECOMMENDED; instead a Route header
                 *    field should be used for that purpose as described above.
                 *
                 *    In the absence of such an overriding mechanism, the proxy
                 *    applies the procedures listed in [4] as follows to determine
                 *    where to send the request.  If the proxy has reformatted the
                 *    request to send to a strict-routing element as described in
                 *    step 6 above, the proxy MUST apply those procedures to the
                 *    Request-URI of the request.  Otherwise, the proxy MUST apply
                 *    the procedures to the first value in the Route header field, if
                 *    present, else the Request-URI.  The procedures will produce an
                 *    ordered set of (address, port, transport) tuples.
                 *    Independently of which URI is being used as input to the
                 *    procedures of [4], if the Request-URI specifies a SIPS
                 *    resource, the proxy MUST follow the procedures of [4] as if the
                 *    input URI were a SIPS URI.
                 *
                 *    As described in [4], the proxy MUST attempt to deliver the
                 *    message to the first tuple in that set, and proceed through the
                 *    set in order until the delivery attempt succeeds.
                 *
                 *    For each tuple attempted, the proxy MUST format the message as
                 *    appropriate for the tuple and send the request using a new
                 *    client transaction as detailed in steps 8 through 10.
                 *
                 *    Since each attempt uses a new client transaction, it represents
                 *    a new branch.  Thus, the branch parameter provided with the Via
                 *    header field inserted in step 8 MUST be different for each
                 *    attempt.
                 *
                 *    If the client transaction reports failure to send the request
                 *    or a timeout from its state machine, the proxy continues to the
                 *    next address in that ordered set.  If the ordered set is
                 *    exhausted, the request cannot be forwarded to this element in
                 *    the target set.  The proxy does not need to place anything in
                 *    the response context, but otherwise acts as if this element of
                 *    the target set returned a 408 (Request Timeout) final response.
                 */
                SIP_Uri uri = null;
                if (isStrictRoute)
                {
                    uri = (SIP_Uri)forwardRequest.RequestLine.Uri;
                }
                else if (forwardRequest.Route.GetTopMostValue() != null)
                {
                    uri = (SIP_Uri)forwardRequest.Route.GetTopMostValue().Address.Uri;
                }
                else
                {
                    uri = (SIP_Uri)forwardRequest.RequestLine.Uri;
                }

                hops = m_pStack.GetHops(uri, forwardRequest.ToByteData().Length, ((SIP_Uri)forwardRequest.RequestLine.Uri).IsSecure);

                if (hops.Length == 0)
                {
                    if (forwardRequest.RequestLine.Method != SIP_Methods.ACK)
                    {
                        e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x503_Service_Unavailable + ": No hop(s) for target.", forwardRequest));
                    }

                    return;
                }

                #endregion

                #region 8.  Add a Via header field value

                forwardRequest.Via.AddToTop("SIP/2.0/transport-tl-addign sentBy-tl-assign-it;branch=z9hG4bK-" + Net_Utils.ComputeMd5(request.Via.GetTopMostValue().Branch, true));

                // Add 'flowID' what received request, you should use the same flow to send response back.
                // For more info see RFC 3261 18.2.2.
                forwardRequest.Via.GetTopMostValue().Parameters.Add("flowID", request.Flow.ID);

                #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

                try{
                    try{
                        if (requestContext.Targets[0].Flow != null)
                        {
                            m_pStack.TransportLayer.SendRequest(requestContext.Targets[0].Flow, request);

                            return;
                        }
                    }
                    catch {
                        m_pStack.TransportLayer.SendRequest(request, null, hops[0]);
                    }
                }
                catch (SIP_TransportException x) {
                    string dummy = x.Message;

                    if (forwardRequest.RequestLine.Method != SIP_Methods.ACK)
                    {
                        /* RFC 3261 16.9 Handling Transport Errors
                         *  If the transport layer notifies a proxy of an error when it tries to
                         *  forward a request (see Section 18.4), the proxy MUST behave as if the
                         *  forwarded request received a 503 (Service Unavailable) response.
                         */
                        e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x503_Service_Unavailable + ": Transport error.", forwardRequest));
                    }
                }

                #endregion
            }

            #endregion

            #endregion
        }
Пример #5
0
        /// <summary>
        /// Checks if SIP request has all required values as request line,header fields and their values.
        /// Throws Exception if not valid SIP request.
        /// </summary>
        public void Validate()
        {
            // Request SIP version
            // Via: + branch prameter
            // To:
            // From:
            // CallID:
            // CSeq
            // Max-Forwards RFC 3261 8.1.1.

            if (!RequestLine.Version.ToUpper().StartsWith("SIP/2.0"))
            {
                throw new SIP_ParseException("Not supported SIP version '" + RequestLine.Version + "' !");
            }

            if (Via.GetTopMostValue() == null)
            {
                throw new SIP_ParseException("Via: header field is missing !");
            }
            if (Via.GetTopMostValue().Branch == null)
            {
                throw new SIP_ParseException("Via: header field branch parameter is missing !");
            }

            if (To == null)
            {
                throw new SIP_ParseException("To: header field is missing !");
            }

            if (From == null)
            {
                throw new SIP_ParseException("From: header field is missing !");
            }

            if (CallID == null)
            {
                throw new SIP_ParseException("CallID: header field is missing !");
            }

            if (CSeq == null)
            {
                throw new SIP_ParseException("CSeq: header field is missing !");
            }

            if (MaxForwards == -1)
            {
                // We can fix it by setting it to default value 70.
                MaxForwards = 70;
            }

            /* RFC 3261 12.1.2
             *  When a UAC sends a request that can establish a dialog (such as an INVITE) it MUST
             *  provide a SIP or SIPS URI with global scope (i.e., the same SIP URI can be used in
             *  messages outside this dialog) in the Contact header field of the request. If the
             *  request has a Request-URI or a topmost Route header field value with a SIPS URI, the
             *  Contact header field MUST contain a SIPS URI.
             */
            if (SIP_Utils.MethodCanEstablishDialog(RequestLine.Method))
            {
                if (Contact.GetAllValues().Length == 0)
                {
                    throw new SIP_ParseException(
                              "Contact: header field is missing, method that can establish a dialog MUST provide a SIP or SIPS URI !");
                }
                if (Contact.GetAllValues().Length > 1)
                {
                    throw new SIP_ParseException(
                              "There may be only 1 Contact: header for the method that can establish a dialog !");
                }
                if (!Contact.GetTopMostValue().Address.IsSipOrSipsUri)
                {
                    throw new SIP_ParseException(
                              "Method that can establish a dialog MUST have SIP or SIPS uri in Contact: header !");
                }
            }

            // TODO: Invite must have From:/To: tag

            // TODO: Check that request-Method equals CSeq method

            // TODO: PRACK must have RAck and RSeq header fields.

            // TODO: get in transport made request, so check if sips and sip set as needed.
        }
Пример #6
0
        /// <summary>
        /// Handles REGISTER method.
        /// </summary>
        /// <param name="e">Request event arguments.</param>
        internal void Register(SIP_RequestReceivedEventArgs e)
        {
            /* RFC 3261 10.3 Processing REGISTER Requests.
             *  1. The registrar inspects the Request-URI to determine whether it
             *     has access to bindings for the domain identified in the
             *     Request-URI.  If not, and if the server also acts as a proxy
             *     server, the server SHOULD forward the request to the addressed
             *     domain, following the general behavior for proxying messages
             *     described in Section 16.
             *
             *  2. To guarantee that the registrar supports any necessary extensions,
             *     the registrar MUST process the Require header field.
             *
             *  3. A registrar SHOULD authenticate the UAC.
             *
             *  4. The registrar SHOULD determine if the authenticated user is
             *     authorized to modify registrations for this address-of-record.
             *     For example, a registrar might consult an authorization
             *     database that maps user names to a list of addresses-of-record
             *     for which that user has authorization to modify bindings.  If
             *     the authenticated user is not authorized to modify bindings,
             *     the registrar MUST return a 403 (Forbidden) and skip the
             *     remaining steps.
             *
             *  5. The registrar extracts the address-of-record from the To header
             *     field of the request.  If the address-of-record is not valid
             *     for the domain in the Request-URI, the registrar MUST send a
             *     404 (Not Found) response and skip the remaining steps.  The URI
             *     MUST then be converted to a canonical form.  To do that, all
             *     URI parameters MUST be removed (including the user-param), and
             *     any escaped characters MUST be converted to their unescaped
             *     form.  The result serves as an index into the list of bindings.
             *
             *  6. The registrar checks whether the request contains the Contact
             *     header field.  If not, it skips to the last step.  If the
             *     Contact header field is present, the registrar checks if there
             *     is one Contact field value that contains the special value "*"
             *     and an Expires field.  If the request has additional Contact
             *     fields or an expiration time other than zero, the request is
             *     invalid, and the server MUST return a 400 (Invalid Request) and
             *     skip the remaining steps.  If not, the registrar checks whether
             *     the Call-ID agrees with the value stored for each binding.  If
             *     not, it MUST remove the binding.  If it does agree, it MUST
             *     remove the binding only if the CSeq in the request is higher
             *     than the value stored for that binding.  Otherwise, the update
             *     MUST be aborted and the request fails.
             *
             *  7. The registrar now processes each contact address in the Contact
             *     header field in turn.  For each address, it determines the
             *     expiration interval as follows:
             *
             *       -  If the field value has an "expires" parameter, that value
             *          MUST be taken as the requested expiration.
             *
             *       -  If there is no such parameter, but the request has an
             *          Expires header field, that value MUST be taken as the requested expiration.
             *
             *       -  If there is neither, a locally-configured default value MUST
             *          be taken as the requested expiration.
             *
             *     The registrar MAY choose an expiration less than the requested
             *     expiration interval.  If and only if the requested expiration
             *     interval is greater than zero AND smaller than one hour AND
             *     less than a registrar-configured minimum, the registrar MAY
             *     reject the registration with a response of 423 (Interval Too
             *     Brief).  This response MUST contain a Min-Expires header field
             *     that states the minimum expiration interval the registrar is
             *     willing to honor.  It then skips the remaining steps.
             *
             *     For each address, the registrar then searches the list of
             *     current bindings using the URI comparison rules.  If the
             *     binding does not exist, it is tentatively added.  If the
             *     binding does exist, the registrar checks the Call-ID value.  If
             *     the Call-ID value in the existing binding differs from the
             *     Call-ID value in the request, the binding MUST be removed if
             *     the expiration time is zero and updated otherwise.  If they are
             *     the same, the registrar compares the CSeq value.  If the value
             *     is higher than that of the existing binding, it MUST update or
             *     remove the binding as above.  If not, the update MUST be
             *     aborted and the request fails.
             *
             *     This algorithm ensures that out-of-order requests from the same
             *     UA are ignored.
             *
             *     Each binding record records the Call-ID and CSeq values from
             *     the request.
             *
             *     The binding updates MUST be committed (that is, made visible to
             *     the proxy or redirect server) if and only if all binding
             *     updates and additions succeed.  If any one of them fails (for
             *     example, because the back-end database commit failed), the
             *     request MUST fail with a 500 (Server Error) response and all
             *     tentative binding updates MUST be removed.
             *
             *  8. The registrar returns a 200 (OK) response.  The response MUST
             *     contain Contact header field values enumerating all current
             *     bindings.  Each Contact value MUST feature an "expires"
             *     parameter indicating its expiration interval chosen by the
             *     registrar.  The response SHOULD include a Date header field.
             */

            SIP_ServerTransaction transaction = e.ServerTransaction;
            SIP_Request           request     = e.Request;
            SIP_Uri to       = null;
            string  userName = "";

            // Probably we need to do validate in SIP stack.

            #region Validate request

            if (SIP_Utils.IsSipOrSipsUri(request.To.Address.Uri.ToString()))
            {
                to = (SIP_Uri)request.To.Address.Uri;
            }
            else
            {
                transaction.SendResponse(
                    m_pStack.CreateResponse(
                        SIP_ResponseCodes.x400_Bad_Request + ": To: value must be SIP or SIPS URI.", request));
                return;
            }

            #endregion

            #region 1. Check if we are responsible for Request-URI domain

            // if(m_pProxy.OnIsLocalUri(e.Request.Uri)){
            // }
            // TODO:

            #endregion

            #region 2. Check that all required extentions supported

            #endregion

            #region 3. Authenticate request

            if (!m_pProxy.AuthenticateRequest(e, out userName))
            {
                return;
            }

            #endregion

            #region 4. Check if user user is authorized to modify registrations

            // We do this in next step(5.).

            #endregion

            #region 5. Check if address of record exists

            if (!m_pProxy.OnAddressExists(to.Address))
            {
                transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x404_Not_Found, request));
                return;
            }
            else if (!OnCanRegister(userName, to.Address))
            {
                transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x403_Forbidden, request));
                return;
            }

            #endregion

            #region 6. Process * Contact if exists

            // Check if we have star contact.
            SIP_t_ContactParam starContact = null;
            foreach (SIP_t_ContactParam c in request.Contact.GetAllValues())
            {
                if (c.IsStarContact)
                {
                    starContact = c;
                    break;
                }
            }

            // We have star contact.
            if (starContact != null)
            {
                if (request.Contact.GetAllValues().Length > 1)
                {
                    transaction.SendResponse(
                        m_pStack.CreateResponse(
                            SIP_ResponseCodes.x400_Bad_Request +
                            ": RFC 3261 10.3.6 -> If star(*) present, only 1 contact allowed.",
                            request));
                    return;
                }
                else if (starContact.Expires != 0)
                {
                    transaction.SendResponse(
                        m_pStack.CreateResponse(
                            SIP_ResponseCodes.x400_Bad_Request +
                            ": RFC 3261 10.3.6 -> star(*) contact parameter 'expires' value must be always '0'.",
                            request));
                    return;
                }

                // Remove bindings.
                SIP_Registration reg = m_pRegistrations[to.Address];
                if (reg != null)
                {
                    foreach (SIP_RegistrationBinding b in reg.Bindings)
                    {
                        if (request.CallID != b.CallID || request.CSeq.SequenceNumber > b.CSeqNo)
                        {
                            b.Remove();
                        }
                    }
                }
            }

            #endregion

            #region 7. Process Contact values

            if (starContact == null)
            {
                SIP_Registration reg = m_pRegistrations[to.Address];
                if (reg == null)
                {
                    reg = new SIP_Registration(userName, to.Address);
                    m_pRegistrations.Add(reg);
                }

                // We may do updates in batch only.
                // We just validate all values then do update(this ensures that update doesn't fail).

                // Check expires and CSeq.
                foreach (SIP_t_ContactParam c in request.Contact.GetAllValues())
                {
                    if (c.Expires == -1)
                    {
                        c.Expires = request.Expires;
                    }
                    if (c.Expires == -1)
                    {
                        c.Expires = m_pProxy.Stack.MinimumExpireTime;
                    }
                    // We must accept 0 values - means remove contact.
                    if (c.Expires != 0 && c.Expires < m_pProxy.Stack.MinimumExpireTime)
                    {
                        SIP_Response resp = m_pStack.CreateResponse(
                            SIP_ResponseCodes.x423_Interval_Too_Brief, request);
                        resp.MinExpires = m_pProxy.Stack.MinimumExpireTime;
                        transaction.SendResponse(resp);
                        return;
                    }

                    SIP_RegistrationBinding currentBinding = reg.GetBinding(c.Address.Uri);
                    if (currentBinding != null && currentBinding.CallID == request.CallID &&
                        request.CSeq.SequenceNumber < currentBinding.CSeqNo)
                    {
                        transaction.SendResponse(
                            m_pStack.CreateResponse(
                                SIP_ResponseCodes.x400_Bad_Request + ": CSeq value out of order.", request));
                        return;
                    }
                }

                // Do binding updates.
                reg.AddOrUpdateBindings(e.ServerTransaction.Flow,
                                        request.CallID,
                                        request.CSeq.SequenceNumber,
                                        request.Contact.GetAllValues());
            }

            #endregion

            #region 8. Create 200 OK response and return all current bindings

            SIP_Response response = m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, request);
            response.Date = DateTime.Now;
            SIP_Registration registration = m_pRegistrations[to.Address];
            if (registration != null)
            {
                foreach (SIP_RegistrationBinding b in registration.Bindings)
                {
                    // Don't list expired bindings what wait to be disposed.
                    if (b.TTL > 1)
                    {
                        response.Header.Add("Contact:", b.ToContactValue());
                    }
                }
            }
            // Add Authentication-Info:, then client knows next nonce.
            response.AuthenticationInfo.Add("qop=\"auth\",nextnonce=\"" +
                                            m_pStack.DigestNonceManager.CreateNonce() + "\"");
            transaction.SendResponse(response);

            #endregion
        }
        /// <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>
        protected internal 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 &&
                    (m_pActiveInvite).Request.CSeq.SequenceNumber < e.Request.CSeq.SequenceNumber)
                {
                    SIP_Response response =
                        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(
                        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
                {
                    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 (State == SIP_DialogState.Early)
                        {
                            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(
                    Stack.CreateResponse(
                        SIP_ResponseCodes.x603_Decline + " : New dialog usages not allowed (RFC 5057).",
                        e.Request));

                return(true);
            }
            else
            {
                return(false);
            }
        }
Пример #8
0
        /// <summary>
        /// Creates new SIP request using this dialog info.
        /// </summary>
        /// <param name="method">SIP method.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>method</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        /// <returns>Returns created request.</returns>
        public SIP_Request CreateRequest(string method)
        {
            if (State == SIP_DialogState.Disposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }
            if (method == null)
            {
                throw new ArgumentNullException("method");
            }
            if (method == string.Empty)
            {
                throw new ArgumentException("Argument 'method' value must be specified.");
            }

            /* RFC 3261 12.2.1.1.
             *  A request within a dialog is constructed by using many of the
             *  components of the state stored as part of the dialog.
             *
             *  The URI in the To field of the request MUST be set to the remote URI
             *  from the dialog state.  The tag in the To header field of the request
             *  MUST be set to the remote tag of the dialog ID.  The From URI of the
             *  request MUST be set to the local URI from the dialog state.  The tag
             *  in the From header field of the request MUST be set to the local tag
             *  of the dialog ID.  If the value of the remote or local tags is null,
             *  the tag parameter MUST be omitted from the To or From header fields,
             *  respectively.
             *
             *  The Call-ID of the request MUST be set to the Call-ID of the dialog.
             *  Requests within a dialog MUST contain strictly monotonically
             *  increasing and contiguous CSeq sequence numbers (increasing-by-one)
             *  in each direction (excepting ACK and CANCEL of course, whose numbers
             *  equal the requests being acknowledged or cancelled).  Therefore, if
             *  the local sequence number is not empty, the value of the local
             *  sequence number MUST be incremented by one, and this value MUST be
             *  placed into the CSeq header field.  If the local sequence number is
             *  empty, an initial value MUST be chosen using the guidelines of
             *  Section 8.1.1.5.  The method field in the CSeq header field value
             *  MUST match the method of the request.
             *
             *      With a length of 32 bits, a client could generate, within a single
             *      call, one request a second for about 136 years before needing to
             *      wrap around.  The initial value of the sequence number is chosen
             *      so that subsequent requests within the same call will not wrap
             *      around.  A non-zero initial value allows clients to use a time-
             *      based initial sequence number.  A client could, for example,
             *      choose the 31 most significant bits of a 32-bit second clock as an
             *      initial sequence number.
             *
             *  The UAC uses the remote target and route set to build the Request-URI
             *  and Route header field of the request.
             *
             *  If the route set is empty, the UAC MUST place the remote target URI
             *  into the Request-URI.  The UAC MUST NOT add a Route header field to
             *  the request.
             *
             *  If the route set is not empty, and the first URI in the route set
             *  contains the lr parameter (see Section 19.1.1), the UAC MUST place
             *  the remote target URI into the Request-URI and MUST include a Route
             *  header field containing the route set values in order, including all
             *  parameters.
             *
             *  If the route set is not empty, and its first URI does not contain the
             *  lr parameter, the UAC MUST place the first URI from the route set
             *  into the Request-URI, stripping any parameters that are not allowed
             *  in a Request-URI.  The UAC MUST add a Route header field containing
             *  the remainder of the route set values in order, including all
             *  parameters.  The UAC MUST then place the remote target URI into the
             *  Route header field as the last value.
             *
             *  For example, if the remote target is sip:user@remoteua and the route
             *  set contains:
             *      <sip:proxy1>,<sip:proxy2>,<sip:proxy3;lr>,<sip:proxy4>
             *
             *  The request will be formed with the following Request-URI and Route
             *  header field:
             *      METHOD sip:proxy1
             *      Route: <sip:proxy2>,<sip:proxy3;lr>,<sip:proxy4>,<sip:user@remoteua>
             *
             *  If the first URI of the route set does not contain the lr
             *  parameter, the proxy indicated does not understand the routing
             *  mechanisms described in this document and will act as specified in
             *  RFC 2543, replacing the Request-URI with the first Route header
             *  field value it receives while forwarding the message.  Placing the
             *  Request-URI at the end of the Route header field preserves the
             *  information in that Request-URI across the strict router (it will
             *  be returned to the Request-URI when the request reaches a loose-
             *  router).
             *
             *  A UAC SHOULD include a Contact header field in any target refresh
             *  requests within a dialog, and unless there is a need to change it,
             *  the URI SHOULD be the same as used in previous requests within the
             *  dialog.  If the "secure" flag is true, that URI MUST be a SIPS URI.
             *  As discussed in Section 12.2.2, a Contact header field in a target
             *  refresh request updates the remote target URI.  This allows a UA to
             *  provide a new contact address, should its address change during the
             *  duration of the dialog.
             *
             *  However, requests that are not target refresh requests do not affect
             *  the remote target URI for the dialog.
             *
             *  The rest of the request is formed as described in Section 8.1.1.
             */

            lock (m_pLock)
            {
                SIP_Request request = m_pStack.CreateRequest(method,
                                                             new SIP_t_NameAddress("", m_pRemoteUri),
                                                             new SIP_t_NameAddress("", m_pLocalUri));
                if (m_pRouteSet.Length == 0)
                {
                    request.RequestLine.Uri = m_pRemoteTarget;
                }
                else
                {
                    SIP_Uri topmostRoute = ((SIP_Uri)m_pRouteSet[0].Address.Uri);
                    if (topmostRoute.Param_Lr)
                    {
                        request.RequestLine.Uri = m_pRemoteTarget;
                        for (int i = 0; i < m_pRouteSet.Length; i++)
                        {
                            request.Route.Add(m_pRouteSet[i].ToStringValue());
                        }
                    }
                    else
                    {
                        request.RequestLine.Uri = SIP_Utils.UriToRequestUri(topmostRoute);
                        for (int i = 1; i < m_pRouteSet.Length; i++)
                        {
                            request.Route.Add(m_pRouteSet[i].ToStringValue());
                        }
                    }
                }
                request.To.Tag              = m_RemoteTag;
                request.From.Tag            = m_LocalTag;
                request.CallID              = m_CallID;
                request.CSeq.SequenceNumber = ++m_LocalSeqNo;
                request.Contact.Add(m_pLocalContact.ToString());

                return(request);
            }
        }
Пример #9
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);
            }

            if (e.Request.RequestLine.Method == SIP_Methods.ACK)
            {
                if (this.State == SIP_DialogState.Early)
                {
                    this.SetState(SIP_DialogState.Confirmed, true);
                }
                else if (this.State == SIP_DialogState.Terminating)
                {
                    this.SetState(SIP_DialogState.Confirmed, false);

                    Terminate(m_TerminateReason, true);
                }
            }
            else if (e.Request.RequestLine.Method == SIP_Methods.BYE)
            {
                e.ServerTransaction.SendResponse(this.Stack.CreateResponse(SIP_ResponseCodes.x200_Ok, e.Request));

                m_IsTerminatedByRemoteParty = true;
                OnTerminatedByRemoteParty(e);
                SetState(SIP_DialogState.Terminated, true);

                return(true);
            }
            else 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.
                 */
                // Dialog base class will handle this case.

                /*
                 * foreach(SIP_Transaction tr in this.Transactions){
                 *  if(tr is SIP_ServerTransaction && (tr.State == SIP_TransactionState.Calling || tr.State == SIP_TransactionState.Proceeding)){
                 *      if(e.Request.CSeq.SequenceNumber < tr.Request.CSeq.SequenceNumber){
                 *          SIP_Response response = this.Stack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error + ": INVITE with higher Seq sequence number is progress.",e.Request);
                 *          response.RetryAfter = new SIP_t_RetryAfter((new Random()).Next(1,10).ToString());
                 *          e.ServerTransaction.SendResponse(response);
                 *
                 *          return true;
                 *      }
                 *
                 *      break;
                 *  }
                 * }*/

                /* RFC 3261 14.2.
                 *  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 (this.HasPendingInvite)
                {
                    e.ServerTransaction.SendResponse(this.Stack.CreateResponse(SIP_ResponseCodes.x491_Request_Pending, e.Request));

                    return(true);
                }
            }
            // 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 in dialog not allowed (RFC 5057).", e.Request));

                return(true);
            }

            return(false);
        }