Exemple #1
0
        /// <summary>
        /// Is called by SIP proxy when SIP proxy needs to get gateways for non-SIP URI.
        /// </summary>
        /// <param name="uriScheme">Non-SIP URI scheme which gateways to get.</param>
        /// <param name="userName">Authenticated user name.</param>
        /// <returns>Returns event data.</returns>
        protected SIP_GatewayEventArgs OnGetGateways(string uriScheme, string userName)
        {
            SIP_GatewayEventArgs e = new SIP_GatewayEventArgs(uriScheme, userName);

            if (GetGateways != null)
            {
                GetGateways(e);
            }
            return(e);
        }
Exemple #2
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="request">SIP request to forward.</param>
        /// <param name="addRecordRoute">Specifies if Record-Route header filed is added.</param>
        internal void ForwardRequest(bool statefull,
                                     SIP_RequestReceivedEventArgs e,
                                     SIP_Request request,
                                     bool addRecordRoute)
        {
            List <SIP_ProxyTarget>   targetSet   = new List <SIP_ProxyTarget>();
            List <NetworkCredential> credentials = new List <NetworkCredential>();
            SIP_Uri route = null;

            /* RFC 3261 16.
             *  1. Validate the request (Section 16.3)
             *      1. Reasonable Syntax
             *      2. URI scheme
             *      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)
             *  4. Forward the request (Section 16.6)
             */

            #region 1. Validate the request (Section 16.3)

            // 1.1 Reasonable Syntax.
            //      SIP_Message will do it.

            // 1.2 URI scheme check.
            if (!SIP_Utils.IsSipOrSipsUri(request.RequestLine.Uri.ToString()))
            {
                // TODO:
                SIP_GatewayEventArgs eArgs = OnGetGateways("uriScheme", "userName");
                // No suitable gateway or authenticated user has no access.
                if (eArgs.Gateways.Count == 0)
                {
                    e.ServerTransaction.SendResponse(
                        m_pStack.CreateResponse(SIP_ResponseCodes.x416_Unsupported_URI_Scheme, e.Request));
                    return;
                }
            }

            // 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.
            // We need to auth all foreign calls.
            if (!SIP_Utils.IsSipOrSipsUri(request.RequestLine.Uri.ToString()) ||
                !OnIsLocalUri(((SIP_Uri)request.RequestLine.Uri).Host))
            {
                // We need to pass-through ACK.
                if (request.RequestLine.Method == SIP_Methods.ACK)
                {
                }
                else if (!AuthenticateRequest(e))
                {
                    return;
                }
            }

            #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.
             *
             *  If the first value in the Route header field indicates this proxy,
             *  the proxy MUST remove that value from the request.
             */

            // Strict route.
            if (SIP_Utils.IsSipOrSipsUri(request.RequestLine.Uri.ToString()) &&
                IsLocalRoute(((SIP_Uri)request.RequestLine.Uri)))
            {
                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();
            }
            // Loose route.
            else if (request.Route.GetAllValues().Length > 0 &&
                     IsLocalRoute(SIP_Uri.Parse(request.Route.GetTopMostValue().Address.Uri.ToString())))
            {
                route = (SIP_Uri)request.Route.GetTopMostValue().Address.Uri;
                request.Route.RemoveTopMostValue();
            }

            #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

            // FIX ME: we may have tel: here
            SIP_Uri requestUri = (SIP_Uri)e.Request.RequestLine.Uri;

            // Proxy is not responsible for the domain in the Request-URI.
            if (!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);
                    }
                }

                targetSet.Add(new SIP_ProxyTarget(requestUri, targetFlow));
            }
            // Proxy is responsible for the domain in the Request-URI.
            else
            {
                // TODO: tel:
                //SIP_Uri requestUri = SIP_Uri.Parse(e.Request.Uri);

                // 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)
                        {
                            targetSet.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 (!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 (targetSet.Count == 0)
            {
                e.ServerTransaction.SendResponse(
                    m_pStack.CreateResponse(SIP_ResponseCodes.x480_Temporarily_Unavailable, e.Request));
                return;
            }

            #endregion

            #region 4. Forward the request (Section 16.6)

            #region Statefull

            if (statefull)
            {
                // Create proxy context that will be responsible for forwarding request.
                SIP_ProxyContext proxyContext = new SIP_ProxyContext(this,
                                                                     e.ServerTransaction,
                                                                     request,
                                                                     addRecordRoute,
                                                                     m_ForkingMode,
                                                                     (ProxyMode & SIP_ProxyMode.B2BUA) != 0,
                                                                     false,
                                                                     false,
                                                                     targetSet.ToArray(),
                                                                     credentials.ToArray());
                m_pProxyContexts.Add(proxyContext);
                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 = targetSet[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-" +
                    Core.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 (targetSet[0].Flow != null)
                        {
                            m_pStack.TransportLayer.SendRequest(targetSet[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
        }
 /// <summary>
 /// Is called by SIP proxy when SIP proxy needs to get gateways for non-SIP URI.
 /// </summary>
 /// <param name="uriScheme">Non-SIP URI scheme which gateways to get.</param>
 /// <param name="userName">Authenticated user name.</param>
 /// <returns>Returns event data.</returns>
 protected SIP_GatewayEventArgs OnGetGateways(string uriScheme, string userName)
 {
     SIP_GatewayEventArgs e = new SIP_GatewayEventArgs(uriScheme, userName);
     if (GetGateways != null)
     {
         GetGateways(e);
     }
     return e;
 }