Пример #1
0
        /// <summary>
        /// Incoming call constructor.
        /// </summary>
        /// <param name="stack">Reference to SIP stack.</param>
        /// <param name="dialog">Reference SIP dialog.</param>
        /// <param name="session">Call RTP multimedia session.</param>
        /// <param name="localSDP">Local SDP.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>dialog</b>,<b>session</b> or <b>localSDP</b> is null reference.</exception>
        internal SIP_Call(SIP_Stack stack, SIP_Dialog dialog, RTP_MultimediaSession session, SDP_Message localSDP)
        {
            if (stack == null)
            {
                throw new ArgumentNullException("stack");
            }
            if (dialog == null)
            {
                throw new ArgumentNullException("dialog");
            }
            if (session == null)
            {
                throw new ArgumentNullException("session");
            }
            if (localSDP == null)
            {
                throw new ArgumentNullException("localSDP");
            }

            m_pStack  = stack;
            m_pDialog = (SIP_Dialog_Invite)dialog;
            m_pRtpMultimediaSession = session;
            m_pLocalSDP             = localSDP;

            m_StartTime          = DateTime.Now;
            m_pFlow              = dialog.Flow;
            dialog.StateChanged += new EventHandler(m_pDialog_StateChanged);

            SetState(SIP_CallState.Active);

            // Start ping timer.
            m_pKeepAliveTimer          = new TimerEx(40000);
            m_pKeepAliveTimer.Elapsed += new System.Timers.ElapsedEventHandler(m_pKeepAliveTimer_Elapsed);
            m_pKeepAliveTimer.Enabled  = true;
        }
        /// <summary>
        /// Updates specified binding.
        /// </summary>
        /// <param name="flow">SIP data flow what updates this binding. This value is null if binding was not added through network or
        /// flow has disposed.</param>
        /// <param name="expires">Time in seconds when binding will expire.</param>
        /// <param name="qvalue">Binding priority. Higher value means greater priority.</param>
        /// <param name="callID">Call-ID header field value which added/updated this binding.</param>
        /// <param name="cseqNo">CSeq header field sequence number value which added/updated this binding.</param>
        public void Update(SIP_Flow flow, int expires, double qvalue, string callID, int cseqNo)
        {
            if (expires < 0)
            {
                throw new ArgumentException("Argument 'expires' value must be >= 0.");
            }
            if (qvalue < 0 || qvalue > 1)
            {
                throw new ArgumentException("Argument 'qvalue' value must be >= 0.000 and <= 1.000");
            }
            if (callID == null)
            {
                throw new ArgumentNullException("callID");
            }
            if (cseqNo < 0)
            {
                throw new ArgumentException("Argument 'cseqNo' value must be >= 0.");
            }

            m_pFlow   = flow;
            m_Expires = expires;
            m_QValue  = qvalue;
            m_CallID  = callID;
            m_CSeqNo  = cseqNo;

            m_LastUpdate = DateTime.Now;
        }
Пример #3
0
        /// <summary>
        /// Cleans up any resource being used.
        /// </summary>
        public void Dispose()
        {
            lock (m_pLock){
                if (this.State == SIP_CallState.Disposed)
                {
                    return;
                }
                SetState(SIP_CallState.Disposed);

                // TODO: Clean up
                m_pStack    = null;
                m_pLocalSDP = null;
                if (m_pDialog != null)
                {
                    m_pDialog.Dispose();
                    m_pDialog = null;
                }
                m_pFlow = null;
                if (m_pKeepAliveTimer != null)
                {
                    m_pKeepAliveTimer.Dispose();
                    m_pKeepAliveTimer = null;
                }

                this.StateChanged = null;
            }
        }
Пример #4
0
        /// <summary>
        /// Initializes call from Calling state to active..
        /// </summary>
        /// <param name="dialog">SIP dialog.</param>
        /// <param name="localSDP">Local SDP.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>dialog</b> or <b>localSDP</b> is null reference.</exception>
        internal void InitCalling(SIP_Dialog dialog, SDP_Message localSDP)
        {
            if (dialog == null)
            {
                throw new ArgumentNullException("dialog");
            }
            if (localSDP == null)
            {
                throw new ArgumentNullException("localSDP");
            }

            m_pDialog   = (SIP_Dialog_Invite)dialog;
            m_pFlow     = dialog.Flow;
            m_pLocalSDP = localSDP;

            m_StartTime          = DateTime.Now;
            dialog.StateChanged += new EventHandler(m_pDialog_StateChanged);

            SetState(SIP_CallState.Active);

            // Start ping timer.
            m_pKeepAliveTimer          = new TimerEx(40000);
            m_pKeepAliveTimer.Elapsed += new System.Timers.ElapsedEventHandler(m_pKeepAliveTimer_Elapsed);
            m_pKeepAliveTimer.Enabled  = true;
        }
Пример #5
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="targetUri">Target request-URI.</param>
        /// <param name="flow">Data flow to try for forwarding..</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>targetUri</b> is null reference.</exception>
        public SIP_ProxyTarget(SIP_Uri targetUri, SIP_Flow flow)
        {
            if (targetUri == null)
            {
                throw new ArgumentNullException("targetUri");
            }

            m_pTargetUri = targetUri;
            m_pFlow      = flow;
        }
Пример #6
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="targetUri">Target request-URI.</param>
        /// <param name="flow">Data flow to try for forwarding..</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>targetUri</b> is null reference.</exception>
        public SIP_ProxyTarget(SIP_Uri targetUri, SIP_Flow flow)
        {
            if (targetUri == null)
            {
                throw new ArgumentNullException("targetUri");
            }

            m_pTargetUri = targetUri;
            m_pFlow = flow;
        }
Пример #7
0
        /// <summary>
        /// Add or updates specified SIP registration info.
        /// </summary>
        /// <param name="aor">Registration address of record.</param>
        /// <param name="contacts">Registration address of record contacts to update.</param>
        /// <param name="flow">SIP proxy local data flow what accpeted this contact registration.</param>
        public void SetRegistration(string aor, SIP_t_ContactParam[] contacts, SIP_Flow flow)
        {
            lock (m_pRegistrations){
                SIP_Registration registration = m_pRegistrations[aor];
                if (registration == null)
                {
                    registration = new SIP_Registration("system", aor);
                    m_pRegistrations.Add(registration);
                    OnAorRegistered(registration);
                }

                registration.AddOrUpdateBindings(flow, "", 1, contacts);
            }
        }
Пример #8
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="proxy">Owner SIP proxy server.</param>
        /// <param name="request">The request what is represented by this context.</param>
        /// <param name="flow">Data flow what received request.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>proxy</b>, <b>request</b> or <b>flow</b> is null reference.</exception>
        internal SIP_RequestContext(SIP_Proxy proxy, SIP_Request request, SIP_Flow flow)
        {
            if (proxy == null)
            {
                throw new ArgumentNullException("proxy");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }

            m_pProxy   = proxy;
            m_pRequest = request;
            m_pFlow    = flow;

            m_pTargets = new List <SIP_ProxyTarget>();
        }
Пример #9
0
        /// <summary>
        /// Adds or updates matching bindings.
        /// </summary>
        /// <param name="flow">SIP data flow what updates this binding. This value is null if binding was not added through network or
        /// flow has disposed.</param>
        /// <param name="callID">Call-ID header field value.</param>
        /// <param name="cseqNo">CSeq header field sequence number value.</param>
        /// <param name="contacts">Contacts to add or update.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>callID</b> or <b>contacts</b> is null reference.</exception>
        public void AddOrUpdateBindings(SIP_Flow flow,
                                        string callID,
                                        int cseqNo,
                                        SIP_t_ContactParam[] contacts)
        {
            if (callID == null)
            {
                throw new ArgumentNullException("callID");
            }
            if (cseqNo < 0)
            {
                throw new ArgumentException("Argument 'cseqNo' value must be >= 0.");
            }
            if (contacts == null)
            {
                throw new ArgumentNullException("contacts");
            }

            lock (m_pLock)
            {
                foreach (SIP_t_ContactParam contact in contacts)
                {
                    SIP_RegistrationBinding binding = GetBinding(contact.Address.Uri);
                    // Add binding.
                    if (binding == null)
                    {
                        binding = new SIP_RegistrationBinding(this, contact.Address.Uri);
                        m_pBindings.Add(binding);
                    }

                    // Update binding.
                    binding.Update(flow,
                                   contact.Expires == -1 ? 3600 : contact.Expires,
                                   contact.QValue == -1 ? 1.0 : contact.QValue,
                                   callID,
                                   cseqNo);
                }
            }
        }
Пример #10
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
        }
        /// <summary>
        /// Updates specified binding.
        /// </summary>
        /// <param name="flow">SIP data flow what updates this binding. This value is null if binding was not added through network or
        /// flow has disposed.</param>
        /// <param name="expires">Time in seconds when binding will expire.</param>
        /// <param name="qvalue">Binding priority. Higher value means greater priority.</param>
        /// <param name="callID">Call-ID header field value which added/updated this binding.</param>
        /// <param name="cseqNo">CSeq header field sequence number value which added/updated this binding.</param>
        public void Update(SIP_Flow flow, int expires, double qvalue, string callID, int cseqNo)
        {
            if (expires < 0)
            {
                throw new ArgumentException("Argument 'expires' value must be >= 0.");
            }
            if (qvalue < 0 || qvalue > 1)
            {
                throw new ArgumentException("Argument 'qvalue' value must be >= 0.000 and <= 1.000");
            }
            if (callID == null)
            {
                throw new ArgumentNullException("callID");
            }
            if (cseqNo < 0)
            {
                throw new ArgumentException("Argument 'cseqNo' value must be >= 0.");
            }

            m_pFlow = flow;
            m_Expires = expires;
            m_QValue = qvalue;
            m_CallID = callID;
            m_CSeqNo = cseqNo;

            m_LastUpdate = DateTime.Now;
        }
Пример #12
0
        /// <summary>
        /// Add or updates specified SIP registration info.
        /// </summary>
        /// <param name="aor">Registration address of record.</param>
        /// <param name="contacts">Registration address of record contacts to update.</param>
        /// <param name="flow">SIP proxy local data flow what accpeted this contact registration.</param>
        public void SetRegistration(string aor, SIP_t_ContactParam[] contacts, SIP_Flow flow)
        {
            lock (m_pRegistrations)
            {
                SIP_Registration registration = m_pRegistrations[aor];
                if (registration == null)
                {
                    registration = new SIP_Registration("system", aor);
                    m_pRegistrations.Add(registration);
                    OnAorRegistered(registration);
                }

                registration.AddOrUpdateBindings(flow, "", 1, contacts);
            }
        }
Пример #13
0
        /// <summary>
        /// Adds or updates matching bindings.
        /// </summary>
        /// <param name="flow">SIP data flow what updates this binding. This value is null if binding was not added through network or
        /// flow has disposed.</param>
        /// <param name="callID">Call-ID header field value.</param>
        /// <param name="cseqNo">CSeq header field sequence number value.</param>
        /// <param name="contacts">Contacts to add or update.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>callID</b> or <b>contacts</b> is null reference.</exception>
        public void AddOrUpdateBindings(SIP_Flow flow,
                                        string callID,
                                        int cseqNo,
                                        SIP_t_ContactParam[] contacts)
        {
            if (callID == null)
            {
                throw new ArgumentNullException("callID");
            }
            if (cseqNo < 0)
            {
                throw new ArgumentException("Argument 'cseqNo' value must be >= 0.");
            }
            if (contacts == null)
            {
                throw new ArgumentNullException("contacts");
            }

            lock (m_pLock)
            {
                foreach (SIP_t_ContactParam contact in contacts)
                {
                    SIP_RegistrationBinding binding = GetBinding(contact.Address.Uri);
                    // Add binding.
                    if (binding == null)
                    {
                        binding = new SIP_RegistrationBinding(this, contact.Address.Uri);
                        m_pBindings.Add(binding);
                    }

                    // Update binding.
                    binding.Update(flow,
                                   contact.Expires == -1 ? 3600 : contact.Expires,
                                   contact.QValue == -1 ? 1.0 : contact.QValue,
                                   callID,
                                   cseqNo);
                }
            }
        }
Пример #14
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");
                }

                /* NAT traversal.
                    When we do record routing, store request sender flow info and request target flow info.
                    Now the tricky part, how proxy later which flow is target (because both sides can send requests).
                      Sender-flow will store from-tag to flow and target-flow will store flowID only (Because we don't know to-tag).
                      Later if request to-tag matches(incoming request), use that flow, otherwise(outgoing request) other flow.
                 
                    flowInfo: sender-flow "/" target-flow
                              sender-flow = from-tag ":" flowID
                              target-flow = flowID                        
                */
                if (m_AddRecordRoute && request.From.Tag != null)
                {
                    string flowInfo = request.From.Tag + ":" + m_pOwner.ServerTransaction.Flow.ID + "/" +
                                      flow.ID;
                    ((SIP_Uri) request.RecordRoute.GetTopMostValue().Address.Uri).Parameters.Add("flowInfo",
                                                                                                 flowInfo);
                }

                /* RFC 3261 16.6 Request Forwarding.
                        Common Steps 1 - 7 are done in target Init().
                                                  
                        8.  Add a Via header field value
                        9.  Add a Content-Length header field if necessary
                        10. Forward the new request
                        11. Set timer C
                */

                #region 8.  Add a Via header field value

                // Skip, Client transaction will add it.

                #endregion

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

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

                #endregion

                #region 10. Forward the new request

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

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

                #endregion

                #region 11. Set timer C

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

                #endregion
            }
Пример #15
0
            /// <summary>
            /// Default constructor.
            /// </summary>
            /// <param name="owner">Owner proxy context.</param>
            /// <param name="flow">Data flow to use for sending. Value null means system will choose it.</param>
            /// <param name="targetUri">Target URI where to send request.</param>
            /// <param name="addRecordRoute">If true, handler will add Record-Route header to forwarded message.</param>
            /// <param name="isRecursed">If true then this target is redirected by proxy context.</param>
            /// <exception cref="ArgumentNullException">Is raised when <b>owner</b> or <b>targetURI</b> is null reference.</exception>
            public TargetHandler(SIP_ProxyContext owner,
                                 SIP_Flow flow,
                                 SIP_Uri targetUri,
                                 bool addRecordRoute,
                                 bool isRecursed)
            {
                if (owner == null)
                {
                    throw new ArgumentNullException("owner");
                }
                if (targetUri == null)
                {
                    throw new ArgumentNullException("targetUri");
                }

                m_pOwner = owner;
                m_pFlow = flow;
                m_pTargetUri = targetUri;
                m_AddRecordRoute = addRecordRoute;
                m_IsRecursed = isRecursed;

                m_pHops = new Queue<SIP_Hop>();
            }