Example #1
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="localEP">Local end point to use to connect remote destination.</param>
        /// <param name="destination">Remote destination URI.</param>
        public SIP_Target(SIP_EndPointInfo localEP,SIP_Uri destination)
        {
            m_pLocalEndPoint = localEP;

            if(destination.Param_Transport != null){
                m_Transport = destination.Param_Transport;
            }
            else{
                // If SIPS, use always TLS.
                if(destination.IsSecure){
                    m_Transport = SIP_Transport.TLS;
                }
                else{
                    m_Transport = SIP_Transport.UDP;
                }
            }
            m_Host = destination.Host;
            if(destination.Port != -1){
                m_Port = destination.Port;
            }
            else{
                if(m_Transport == SIP_Transport.TLS){
                    m_Port = 5061;
                }
                else{
                    m_Port = 5060;
                }
            }
        }
Example #2
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner SIP stack.</param>
        /// <param name="server">Registrar server URI. For example: sip:domain.com.</param>
        /// <param name="aor">Address of record. For example: [email protected].</param>
        /// <param name="contact">Contact URI.</param>
        /// <param name="expires">Gets after how many seconds reigisration expires.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>ua</b>,<b>server</b>,<b>transport</b>,<b>aor</b> or <b>contact</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments contains invalid value.</exception>
        internal SIP_UA_Registration(SIP_Stack stack,SIP_Uri server,string aor,AbsoluteUri contact,int expires)
        {
            if(stack == null){
                throw new ArgumentNullException("stack");
            }
            if(server == null){
                throw new ArgumentNullException("server");
            }
            if(aor == null){
                throw new ArgumentNullException("aor");
            }
            if(aor == string.Empty){
                throw new ArgumentException("Argument 'aor' value must be specified.");
            }
            if(contact == null){
                throw new ArgumentNullException("contact");
            }

            m_pStack          = stack;
            m_pServer         = server;
            m_AOR             = aor;
            m_pContact        = contact;
            m_RefreshInterval = expires;

            m_pContacts = new List<AbsoluteUri>();

            m_pTimer = new TimerEx((m_RefreshInterval - 15) * 1000);
            m_pTimer.AutoReset = false;
            m_pTimer.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimer_Elapsed);
            m_pTimer.Enabled = false;
        }
        /// <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;
        }
            /// <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>();  
            }
        /// <summary>
        /// Re-invites remote party.
        /// </summary>
        /// <param name="contact">New contact value. Value null means current contact value used.</param>
        /// <param name="sdp">SDP media offer.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="InvalidOperationException">Is raised when there is pending invite and this method is called or dialog is in invalid state.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>sdp</b> is null reference.</exception>
        public void ReInvite(SIP_Uri contact, SDP_Message sdp)
        {
            if (State == SIP_DialogState.Disposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }
            if (HasPendingInvite)
            {
                throw new InvalidOperationException("There is pending INVITE.");
            }
            if (State != SIP_DialogState.Confirmed)
            {
                throw new InvalidOperationException("ReInvite is only available in Confirmed state.");
            }
            if (sdp == null)
            {
                throw new ArgumentNullException("sdp");
            }

            lock (SyncRoot)
            {
                // TODO:

                SIP_Request reinvite = CreateRequest(SIP_Methods.INVITE);
                if (contact != null)
                {
                    reinvite.Contact.RemoveAll();
                    reinvite.Contact.Add(contact.ToString());
                }
                reinvite.ContentType = "application/sdp";
                // reinvite.Data = sdp.ToStringData();

                // TODO: Create request sender
                // TODO: Try to reuse existing data flow
                //SIP_RequestSender sender = this.Stack.CreateRequestSender(reinvite);
                //sender.Start();
            }
        }
Example #6
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner SIP stack.</param>
        /// <param name="server">Registrar server URI. For example: sip:domain.com.</param>
        /// <param name="aor">Address of record. For example: [email protected].</param>
        /// <param name="contact">Contact URI.</param>
        /// <param name="expires">Gets after how many seconds reigisration expires.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>ua</b>,<b>server</b>,<b>transport</b>,<b>aor</b> or <b>contact</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments contains invalid value.</exception>
        internal SIP_UA_Registration(SIP_Stack stack,
                                     SIP_Uri server,
                                     string aor,
                                     AbsoluteUri contact,
                                     int expires)
        {
            if (stack == null)
            {
                throw new ArgumentNullException("stack");
            }
            if (server == null)
            {
                throw new ArgumentNullException("server");
            }
            if (aor == null)
            {
                throw new ArgumentNullException("aor");
            }
            if (aor == string.Empty)
            {
                throw new ArgumentException("Argument 'aor' value must be specified.");
            }
            if (contact == null)
            {
                throw new ArgumentNullException("contact");
            }

            m_pStack          = stack;
            m_pServer         = server;
            m_AOR             = aor;
            m_pContact        = contact;
            m_RefreshInterval = expires;

            m_pTimer           = new TimerEx((m_RefreshInterval - 15) * 1000);
            m_pTimer.AutoReset = false;
            m_pTimer.Elapsed  += m_pTimer_Elapsed;
            m_pTimer.Enabled   = false;
        }
        /// <summary>
        /// Cleans up any resources being used.
        /// </summary>
        public virtual void Dispose()
        {
            lock (m_pLock){
                if (this.State == SIP_DialogState.Disposed)
                {
                    return;
                }

                SetState(SIP_DialogState.Disposed, true);

                m_pStack        = null;
                m_CallID        = null;
                m_LocalTag      = null;
                m_RemoteTag     = null;
                m_pLocalUri     = null;
                m_pRemoteUri    = null;
                m_pLocalContact = null;
                m_pRemoteTarget = null;
                m_pRouteSet     = null;
                m_pFlow         = null;
                m_pLock         = null;
            }
        }
Example #8
0
        /// <summary>
        /// Gets if this proxy server is responsible for specified route.
        /// </summary>
        /// <param name="uri">Route value to check.</param>
        /// <returns>Returns trues if server route, otherwise false.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>uri</b> is null reference.</exception>
        internal bool IsLocalRoute(SIP_Uri uri)
        {
            if (uri == null)
            {
                throw new ArgumentNullException("uri");
            }

            // Not a route.
            if (uri.User != null)
            {
                return(false);
            }

            foreach (IPBindInfo bind in m_pStack.BindInfo)
            {
                if (uri.Host.ToLower() == bind.HostName.ToLower())
                {
                    return(true);
                }
            }

            return(false);
        }
Example #9
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="localEP">Local end point to use to connect remote destination.</param>
        /// <param name="destination">Remote destination URI.</param>
        public SIP_Target(SIP_EndPointInfo localEP, SIP_Uri destination)
        {
            m_pLocalEndPoint = localEP;

            if (destination.Param_Transport != null)
            {
                m_Transport = destination.Param_Transport;
            }
            else
            {
                // If SIPS, use always TLS.
                if (destination.IsSecure)
                {
                    m_Transport = SIP_Transport.TLS;
                }
                else
                {
                    m_Transport = SIP_Transport.UDP;
                }
            }
            m_Host = destination.Host;
            if (destination.Port != -1)
            {
                m_Port = destination.Port;
            }
            else
            {
                if (m_Transport == SIP_Transport.TLS)
                {
                    m_Port = 5061;
                }
                else
                {
                    m_Port = 5060;
                }
            }
        }
Example #10
0
        /// <summary>
        /// Converts URI to Request-URI by removing all not allowed Request-URI parameters from URI.
        /// </summary>
        /// <param name="uri">URI value.</param>
        /// <returns>Returns valid Request-URI value.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>uri</b> is null reference.</exception>
        public static AbsoluteUri UriToRequestUri(AbsoluteUri uri)
        {
            if (uri == null)
            {
                throw new ArgumentNullException("uri");
            }

            if (uri is SIP_Uri)
            {
                // RFC 3261 19.1.2.(Table)
                // We need to strip off "method-param" and "header" URI parameters".
                // Currently we do it for sip or sips uri, do we need todo it for others too ?

                SIP_Uri sUri = (SIP_Uri)uri;
                sUri.Parameters.Remove("method");
                sUri.Header = null;

                return(sUri);
            }
            else
            {
                return(uri);
            }
        }
        /// <summary>
        /// Starts registering.
        /// </summary>
        /// <param name="autoRefresh">If true, registration takes care of refreshing itself to registrar server.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        public void BeginRegister(bool autoRefresh)
        {
            if (m_IsDisposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }

            // Fix ME: Stack not running, try register on next step.
            // In ideal solution we need to start registering when stack starts.
            if (!m_pStack.IsRunning)
            {
                m_pTimer.Enabled = true;
                return;
            }

            m_AutoRefresh = autoRefresh;
            SetState(SIP_UA_RegistrationState.Registering);

            /* RFC 3261 10.1 Constructing the REGISTER Request.
             *  Request-URI: The Request-URI names the domain of the location service for which the registration is meant (for example,
             *               "sip:chicago.com").  The "userinfo" and "@" components of the SIP URI MUST NOT be present.
             */

            SIP_Request register = m_pStack.CreateRequest(SIP_Methods.REGISTER,
                                                          new SIP_t_NameAddress(m_pServer.Scheme + ":" + m_AOR),
                                                          new SIP_t_NameAddress(m_pServer.Scheme + ":" + m_AOR));

            register.RequestLine.Uri =
                SIP_Uri.Parse(m_pServer.Scheme + ":" + m_AOR.Substring(m_AOR.IndexOf('@') + 1));
            register.Route.Add(m_pServer.ToString());
            register.Contact.Add("<" + Contact + ">;expires=" + m_RefreshInterval);

            m_pRegisterSender = m_pStack.CreateRequestSender(register, m_pFlow);
            m_pRegisterSender.ResponseReceived += m_pRegisterSender_ResponseReceived;
            m_pRegisterSender.Start();
        }
Example #12
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);
            }
        }
        /// <summary>
        /// This method is called when REGISTER has finished.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event data.</param>
        private void m_pRegisterSender_ResponseReceived(object sender, SIP_ResponseReceivedEventArgs e)
        {
            m_pFlow = e.ClientTransaction.Flow;

            if (e.Response.StatusCodeType == SIP_StatusCodeType.Success)
            {
                SetState(SIP_UA_RegistrationState.Registered);

                OnRegistered();

                m_pFlow.SendKeepAlives = true;
            }
            else
            {
                SetState(SIP_UA_RegistrationState.Error);

                OnError(e);
            }

            // REMOVE ME:
            if (AutoFixContact && (m_pContact is SIP_Uri))
            {
                // If Via: received or rport paramter won't match to our sent-by, use received and rport to construct new contact value.

                SIP_Uri   cContact   = ((SIP_Uri)m_pContact);
                IPAddress cContactIP = Net_Utils.IsIPAddress(cContact.Host)
                                           ? IPAddress.Parse(cContact.Host)
                                           : null;
                SIP_t_ViaParm via = e.Response.Via.GetTopMostValue();
                if (via != null && cContactIP != null)
                {
                    IPEndPoint ep = new IPEndPoint(via.Received != null ? via.Received : cContactIP,
                                                   via.RPort > 0 ? via.RPort : cContact.Port);
                    if (!cContactIP.Equals(ep.Address) || cContact.Port != via.RPort)
                    {
                        // Unregister old contact.
                        BeginUnregister(false);

                        // Fix contact.
                        cContact.Host = ep.Address.ToString();
                        cContact.Port = ep.Port;

                        m_pRegisterSender.Dispose();
                        m_pRegisterSender = null;

                        BeginRegister(m_AutoRefresh);

                        return;
                    }
                }
            }

            if (m_AutoRefresh)
            {
                // Set registration refresh timer.
                m_pTimer.Enabled = true;
            }

            m_pRegisterSender.Dispose();
            m_pRegisterSender = null;
        }
Example #14
0
        /// <summary>
        /// Gets target hops(address,port,transport) of the specified URI.
        /// </summary>
        /// <param name="uri">Target URI.</param>
        /// <param name="messageSize">SIP message size.</param>
        /// <param name="forceTLS">If true only TLS hops are returned.</param>
        /// <returns>Returns target hops(address,port,transport) of the specified URI.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>uri</b> is null reference.</exception>
        public SIP_Hop[] GetHops(SIP_Uri uri, int messageSize, bool forceTLS)
        {
            if (uri == null)
            {
                throw new ArgumentNullException("uri");
            }

            List<SIP_Hop> retVal = new List<SIP_Hop>();
            string transport = "";
            bool transportSetExplicitly = false;
            List<DNS_rr_SRV> targetSRV = new List<DNS_rr_SRV>();

            #region RFC 3263 4.1 Selecting a Transport Protocol

            /* 4.1 Selecting a Transport Protocol

                If the URI specifies a transport protocol in the transport parameter,
                that transport protocol SHOULD be used.

                Otherwise, if no transport protocol is specified, but the TARGET is a
                numeric IP address, the client SHOULD use UDP for a SIP URI, and TCP
                for a SIPS URI.  Similarly, if no transport protocol is specified,
                and the TARGET is not numeric, but an explicit port is provided, the
                client SHOULD use UDP for a SIP URI, and TCP for a SIPS URI.  This is
                because UDP is the only mandatory transport in RFC 2543 [6], and thus
                the only one guaranteed to be interoperable for a SIP URI.  It was
                also specified as the default transport in RFC 2543 when no transport
                was present in the SIP URI.  However, another transport, such as TCP,
                MAY be used if the guidelines of SIP mandate it for this particular
                request.  That is the case, for example, for requests that exceed the
                path MTU.

                Otherwise, if no transport protocol or port is specified, and the
                target is not a numeric IP address, the client SHOULD perform a NAPTR
                query for the domain in the URI.  The services relevant for the task
                of transport protocol selection are those with NAPTR service fields
                with values "SIP+D2X" and "SIPS+D2X", where X is a letter that
                corresponds to a transport protocol supported by the domain.  This
                specification defines D2U for UDP, D2T for TCP, and D2S for SCTP.  We
                also establish an IANA registry for NAPTR service name to transport
                protocol mappings.

                These NAPTR records provide a mapping from a domain to the SRV record
                for contacting a server with the specific transport protocol in the
                NAPTR services field.  The resource record will contain an empty
                regular expression and a replacement value, which is the SRV record
                for that particular transport protocol.  If the server supports
                multiple transport protocols, there will be multiple NAPTR records,
                each with a different service value.  As per RFC 2915 [3], the client
                discards any records whose services fields are not applicable.  For
                the purposes of this specification, several rules are defined.

                First, a client resolving a SIPS URI MUST discard any services that
                do not contain "SIPS" as the protocol in the service field.  The
                converse is not true, however.  A client resolving a SIP URI SHOULD
                retain records with "SIPS" as the protocol, if the client supports
                TLS.  Second, a client MUST discard any service fields that identify
                a resolution service whose value is not "D2X", for values of X that
                indicate transport protocols supported by the client.  The NAPTR
                processing as described in RFC 2915 will result in the discovery of
                the most preferred transport protocol of the server that is supported
                by the client, as well as an SRV record for the server.  It will also
                allow the client to discover if TLS is available and its preference
                for its usage.

                As an example, consider a client that wishes to resolve
                sip:[email protected].  The client performs a NAPTR query for that
                domain, and the following NAPTR records are returned:

                ;          order pref flags service      regexp  replacement
                    IN NAPTR 50   50  "s"  "SIPS+D2T"     ""  _sips._tcp.example.com.
                    IN NAPTR 90   50  "s"  "SIP+D2T"      ""  _sip._tcp.example.com
                    IN NAPTR 100  50  "s"  "SIP+D2U"      ""  _sip._udp.example.com.

                This indicates that the server supports TLS over TCP, TCP, and UDP,
                in that order of preference.  Since the client supports TCP and UDP,
                TCP will be used, targeted to a host determined by an SRV lookup of
                _sip._tcp.example.com.  That lookup would return:

                ;;          Priority Weight Port   Target
                    IN SRV  0        1      5060   server1.example.com
                    IN SRV  0        2      5060   server2.example.com

                If a SIP proxy, redirect server, or registrar is to be contacted
                through the lookup of NAPTR records, there MUST be at least three
                records - one with a "SIP+D2T" service field, one with a "SIP+D2U"
                service field, and one with a "SIPS+D2T" service field.  The records
                with SIPS as the protocol in the service field SHOULD be preferred
                (i.e., have a lower value of the order field) above records with SIP
                as the protocol in the service field.  A record with a "SIPS+D2U"
                service field SHOULD NOT be placed into the DNS, since it is not
                possible to use TLS over UDP.

                It is not necessary for the domain suffixes in the NAPTR replacement
                field to match the domain of the original query (i.e., example.com
                above).  However, for backwards compatibility with RFC 2543, a domain
                MUST maintain SRV records for the domain of the original query, even
                if the NAPTR record is in a different domain.  As an example, even
                though the SRV record for TCP is _sip._tcp.school.edu, there MUST
                also be an SRV record at _sip._tcp.example.com.

                RFC 2543 will look up the SRV records for the domain directly.  If
                these do not exist because the NAPTR replacement points to a
                different domain, the client will fail.

                For NAPTR records with SIPS protocol fields, (if the server is using
                a site certificate), the domain name in the query and the domain name
                in the replacement field MUST both be valid based on the site
                certificate handed out by the server in the TLS exchange.  Similarly,
                the domain name in the SRV query and the domain name in the target in
                the SRV record MUST both be valid based on the same site certificate.
                Otherwise, an attacker could modify the DNS records to contain
                replacement values in a different domain, and the client could not
                validate that this was the desired behavior or the result of an
                attack.

                If no NAPTR records are found, the client constructs SRV queries for
                those transport protocols it supports, and does a query for each.
                Queries are done using the service identifier "_sip" for SIP URIs and
                "_sips" for SIPS URIs.  A particular transport is supported if the
                query is successful.  The client MAY use any transport protocol it
                desires which is supported by the server.

                This is a change from RFC 2543.  It specified that a client would
                lookup SRV records for all transports it supported, and merge the
                priority values across those records.  Then, it would choose the
                most preferred record.

                If no SRV records are found, the client SHOULD use TCP for a SIPS
                URI, and UDP for a SIP URI.  However, another transport protocol,
                such as TCP, MAY be used if the guidelines of SIP mandate it for this
                particular request.  That is the case, for example, for requests that
                exceed the path MTU.
             */

            // TLS usage demanded explicitly.
            if (forceTLS)
            {
                transportSetExplicitly = true;
                transport = SIP_Transport.TLS;
            }
                // If the URI specifies a transport protocol in the transport parameter, that transport protocol SHOULD be used.
            else if (uri.Param_Transport != null)
            {
                transportSetExplicitly = true;
                transport = uri.Param_Transport;
            }
                /*  If no transport protocol is specified, but the TARGET is a numeric IP address, 
                the client SHOULD use UDP for a SIP URI, and TCP for a SIPS URI. Similarly, 
                if no transport protocol is specified, and the TARGET is not numeric, but 
                an explicit port is provided, the client SHOULD use UDP for a SIP URI, and 
                TCP for a SIPS URI. However, another transport, such as TCP, MAY be used if 
                the guidelines of SIP mandate it for this particular request. That is the case, 
                for example, for requests that exceed the path MTU.
            */
            else if (Net_Utils.IsIPAddress(uri.Host) || uri.Port != -1)
            {
                if (uri.IsSecure)
                {
                    transport = SIP_Transport.TLS;
                }
                else if (messageSize > MTU)
                {
                    transport = SIP_Transport.TCP;
                }
                else
                {
                    transport = SIP_Transport.UDP;
                }
            }
            else
            {
                DnsServerResponse response = null;
                /*
                DnsServerResponse response = m_pDnsClient.Query(uri.Host,QTYPE.NAPTR);
                // NAPTR records available.
                if(response.GetNAPTRRecords().Length > 0){
                    // TODO: Choose suitable here
                    // 1) If SIPS get SIPS if possible.
                    // 2) If message size > MTU, try to use TCP.
                    // 3) Otherwise use UDP.
                    if(uri.IsSecure){
                        // Get SIPS+D2T records.
                    }
                    else if(messageSize > MTU){
                        // Get SIP+D2T records.
                    }
                    else{
                        // Get SIP+D2U records.
                    }
                }
                else{*/
                Dictionary<string, DNS_rr_SRV[]> supportedTransports = new Dictionary<string, DNS_rr_SRV[]>();
                bool srvRecordsAvailable = false;

                // Query SRV to see what protocols are supported.
                response = m_pDnsClient.Query("_sips._tcp." + uri.Host, QTYPE.SRV);
                if (response.GetSRVRecords().Length > 0)
                {
                    srvRecordsAvailable = true;
                    supportedTransports.Add(SIP_Transport.TLS, response.GetSRVRecords());
                }
                response = m_pDnsClient.Query("_sip._tcp." + uri.Host, QTYPE.SRV);
                if (response.GetSRVRecords().Length > 0)
                {
                    srvRecordsAvailable = true;
                    supportedTransports.Add(SIP_Transport.TCP, response.GetSRVRecords());
                }
                response = m_pDnsClient.Query("_sip._udp." + uri.Host, QTYPE.SRV);
                if (response.GetSRVRecords().Length > 0)
                {
                    srvRecordsAvailable = true;
                    supportedTransports.Add(SIP_Transport.UDP, response.GetSRVRecords());
                }

                // SRV records available.
                if (srvRecordsAvailable)
                {
                    if (uri.IsSecure)
                    {
                        if (supportedTransports.ContainsKey(SIP_Transport.TLS))
                        {
                            transport = SIP_Transport.TLS;
                            targetSRV.AddRange(supportedTransports[SIP_Transport.TLS]);
                        }
                            // Target won't support SIPS.
                        else
                        {
                            // TODO: What to do ?
                        }
                    }
                    else if (messageSize > MTU)
                    {
                        if (supportedTransports.ContainsKey(SIP_Transport.TCP))
                        {
                            transport = SIP_Transport.TCP;
                            targetSRV.AddRange(supportedTransports[SIP_Transport.TCP]);
                        }
                        else if (supportedTransports.ContainsKey(SIP_Transport.TLS))
                        {
                            transport = SIP_Transport.TLS;
                            targetSRV.AddRange(supportedTransports[SIP_Transport.TLS]);
                        }
                            // Target support UDP only, but TCP is required.
                        else
                        {
                            // TODO: What to do ?
                        }
                    }
                    else
                    {
                        if (supportedTransports.ContainsKey(SIP_Transport.UDP))
                        {
                            transport = SIP_Transport.UDP;
                            targetSRV.AddRange(supportedTransports[SIP_Transport.UDP]);
                        }
                        else if (supportedTransports.ContainsKey(SIP_Transport.TCP))
                        {
                            transport = SIP_Transport.TCP;
                            targetSRV.AddRange(supportedTransports[SIP_Transport.TCP]);
                        }
                        else
                        {
                            transport = SIP_Transport.TLS;
                            targetSRV.AddRange(supportedTransports[SIP_Transport.TLS]);
                        }
                    }
                }
                    /*  If no SRV records are found, the client SHOULD use TCP for a SIPS
                        URI, and UDP for a SIP URI.  However, another transport protocol,
                        such as TCP, MAY be used if the guidelines of SIP mandate it for this
                        particular request.  That is the case, for example, for requests that
                        exceed the path MTU.
                    */
                else
                {
                    if (uri.IsSecure)
                    {
                        transport = SIP_Transport.TLS;
                    }
                    else if (messageSize > MTU)
                    {
                        transport = SIP_Transport.TCP;
                    }
                    else
                    {
                        transport = SIP_Transport.UDP;
                    }
                }
                //}
            }

            #endregion

            #region RFC 3263 4.2 Determining Port and IP Address

            /* 4.2 Determining Port and IP Address

                If TARGET is a numeric IP address, the client uses that address.  If
                the URI also contains a port, it uses that port.  If no port is
                specified, it uses the default port for the particular transport
                protocol.

                If the TARGET was not a numeric IP address, but a port is present in
                the URI, the client performs an A or AAAA record lookup of the domain
                name.  The result will be a list of IP addresses, each of which can
                be contacted at the specific port from the URI and transport protocol
                determined previously.  The client SHOULD try the first record.  If
                an attempt should fail, based on the definition of failure in Section
                4.3, the next SHOULD be tried, and if that should fail, the next
                SHOULD be tried, and so on.

                This is a change from RFC 2543.  Previously, if the port was
                explicit, but with a value of 5060, SRV records were used.  Now, A
                or AAAA records will be used.

                If the TARGET was not a numeric IP address, and no port was present
                in the URI, the client performs an SRV query on the record returned
                from the NAPTR processing of Section 4.1, if such processing was
                performed.  If it was not, because a transport was specified
                explicitly, the client performs an SRV query for that specific
                transport, using the service identifier "_sips" for SIPS URIs.  For a
                SIP URI, if the client wishes to use TLS, it also uses the service
                identifier "_sips" for that specific transport, otherwise, it uses
                "_sip".  If the NAPTR processing was not done because no NAPTR
                records were found, but an SRV query for a supported transport
                protocol was successful, those SRV records are selected. Irregardless
                of how the SRV records were determined, the procedures of RFC 2782,
                as described in the section titled "Usage rules" are followed,
                augmented by the additional procedures of Section 4.3 of this
                document.

                If no SRV records were found, the client performs an A or AAAA record
                lookup of the domain name.  The result will be a list of IP
                addresses, each of which can be contacted using the transport
                protocol determined previously, at the default port for that
                transport.  Processing then proceeds as described above for an
                explicit port once the A or AAAA records have been looked up.
            */

            if (Net_Utils.IsIPAddress(uri.Host))
            {
                if (uri.Port != -1)
                {
                    retVal.Add(new SIP_Hop(IPAddress.Parse(uri.Host), uri.Port, transport));
                }
                else if (forceTLS || uri.IsSecure)
                {
                    retVal.Add(new SIP_Hop(IPAddress.Parse(uri.Host), 5061, transport));
                }
                else
                {
                    retVal.Add(new SIP_Hop(IPAddress.Parse(uri.Host), 5060, transport));
                }
            }
            else if (uri.Port != -1)
            {
                foreach (IPAddress ip in m_pDnsClient.GetHostAddresses(uri.Host))
                {
                    retVal.Add(new SIP_Hop(ip, uri.Port, transport));
                }
            }
            else
            {
                //if(naptrRecords){
                // We need to get (IP:Port)'s foreach SRV record.
                //DnsServerResponse response = m_pDnsClient.Query("??? need NAPTR value here",QTYPE.SRV);
                //}    
                if (transportSetExplicitly)
                {
                    DnsServerResponse response = null;
                    if (transport == SIP_Transport.TLS)
                    {
                        response = m_pDnsClient.Query("_sips._tcp." + uri.Host, QTYPE.SRV);
                    }
                    else if (transport == SIP_Transport.TCP)
                    {
                        response = m_pDnsClient.Query("_sip._tcp." + uri.Host, QTYPE.SRV);
                    }
                    else
                    {
                        response = m_pDnsClient.Query("_sip._udp." + uri.Host, QTYPE.SRV);
                    }
                    targetSRV.AddRange(response.GetSRVRecords());
                }

                // We have SRV records, resovle them to (IP:Port)'s.
                if (targetSRV.Count > 0)
                {
                    foreach (DNS_rr_SRV record in targetSRV)
                    {
                        if (Net_Utils.IsIPAddress(record.Target))
                        {
                            retVal.Add(new SIP_Hop(IPAddress.Parse(record.Target), record.Port, transport));
                        }
                        else
                        {
                            foreach (IPAddress ip in m_pDnsClient.GetHostAddresses(record.Target))
                            {
                                retVal.Add(new SIP_Hop(ip, record.Port, transport));
                            }
                        }
                    }
                }
                    // No SRV recors, just use A and AAAA records.
                else
                {
                    int port = 5060;
                    if (transport == SIP_Transport.TLS)
                    {
                        port = 5061;
                    }

                    foreach (IPAddress ip in m_pDnsClient.GetHostAddresses(uri.Host))
                    {
                        retVal.Add(new SIP_Hop(ip, port, transport));
                    }
                }
            }

            #endregion

            return retVal.ToArray();
        }
Example #15
0
        /// <summary>
        /// This method is called when new request is received.
        /// </summary>
        /// <param name="e">Request event arguments.</param>
        internal void OnRequestReceived(SIP_RequestReceivedEventArgs e)
        {
            SIP_Request request = e.Request;

            if (request.RequestLine.Method == SIP_Methods.CANCEL)
            {
                /* RFC 3261 9.2.
                 *  If the UAS did not find a matching transaction for the CANCEL
                 *  according to the procedure above, it SHOULD respond to the CANCEL
                 *  with a 481 (Call Leg/Transaction Does Not Exist).
                 *
                 *  Regardless of the method of the original request, as long as the
                 *  CANCEL matched an existing transaction, the UAS answers the CANCEL
                 *  request itself with a 200 (OK) response.
                 */

                SIP_ServerTransaction trToCancel = m_pProxy.Stack.TransactionLayer.MatchCancelToTransaction(e.Request);
                if (trToCancel != null)
                {
                    trToCancel.Cancel();
                    //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x200_Ok));
                }
                else
                {
                    //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist));
                }
            }
            // We never should ge BYE here, because transport layer must match it to dialog.
            else if (request.RequestLine.Method == SIP_Methods.BYE)
            {
                /* RFC 3261 15.1.2.
                 *  If the BYE does not match an existing dialog, the UAS core SHOULD generate a 481
                 *  (Call/Transaction Does Not Exist) response and pass that to the server transaction.
                 */
                //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist));
            }
            // We never should ge ACK here, because transport layer must match it to dialog.
            else if (request.RequestLine.Method == SIP_Methods.ACK)
            {
                // ACK is response less request, so we may not return error to it.
            }
            // B2BUA must respond to OPTIONS request, not to forward it.
            else if (request.RequestLine.Method == SIP_Methods.OPTIONS)      /*
                                                                              * SIP_Response response = e.Request.CreateResponse(SIP_ResponseCodes.x200_Ok);
                                                                              * // Add Allow to non ACK response.
                                                                              * if(e.Request.RequestLine.Method != SIP_Methods.ACK){
                                                                              * response.Allow.Add("INVITE,ACK,OPTIONS,CANCEL,BYE,PRACK,MESSAGE,UPDATE");
                                                                              * }
                                                                              * // Add Supported to 2xx non ACK response.
                                                                              * if(response.StatusCodeType == SIP_StatusCodeType.Success && e.Request.RequestLine.Method != SIP_Methods.ACK){
                                                                              * response.Supported.Add("100rel,timer");
                                                                              * }
                                                                              * e.ServerTransaction.SendResponse(response);*/
            {
            }
            // We never should get PRACK here, because transport layer must match it to dialog.
            else if (request.RequestLine.Method == SIP_Methods.PRACK)
            {
                //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist));
            }
            // We never should get UPDATE here, because transport layer must match it to dialog.
            else if (request.RequestLine.Method == SIP_Methods.UPDATE)
            {
                //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist));
            }
            else
            {
                /* draft-marjou-sipping-b2bua-00 4.1.3.
                 *  When the UAS of the B2BUA receives an upstream SIP request, its
                 *  associated UAC generates a new downstream SIP request with its new
                 *  Via, Max-Forwards, Call-Id, CSeq, and Contact header fields. Route
                 *  header fields of the upstream request are copied in the downstream
                 *  request, except the first Route header if it is under the
                 *  responsibility of the B2BUA.  Record-Route header fields of the
                 *  upstream request are not copied in the new downstream request, as
                 *  Record-Route is only meaningful for the upstream dialog.  The UAC
                 *  SHOULD copy other header fields and body from the upstream request
                 *  into this downstream request before sending it.
                 */

                SIP_Request b2buaRequest = e.Request.Copy();
                b2buaRequest.Via.RemoveAll();
                b2buaRequest.MaxForwards         = 70;
                b2buaRequest.CallID              = SIP_t_CallID.CreateCallID().CallID;
                b2buaRequest.CSeq.SequenceNumber = 1;
                b2buaRequest.Contact.RemoveAll();
                // b2buaRequest.Contact.Add(m_pProxy.CreateContact(b2buaRequest.To.Address).ToStringValue());
                if (b2buaRequest.Route.Count > 0 && m_pProxy.IsLocalRoute(SIP_Uri.Parse(b2buaRequest.Route.GetTopMostValue().Address.Uri.ToString())))
                {
                    b2buaRequest.Route.RemoveTopMostValue();
                }
                b2buaRequest.RecordRoute.RemoveAll();

                // Remove our Authorization header if it's there.
                foreach (SIP_SingleValueHF <SIP_t_Credentials> header in b2buaRequest.ProxyAuthorization.HeaderFields)
                {
                    try{
                        Auth_HttpDigest digest = new Auth_HttpDigest(header.ValueX.AuthData, b2buaRequest.RequestLine.Method);
                        if (m_pProxy.Stack.Realm == digest.Realm)
                        {
                            b2buaRequest.ProxyAuthorization.Remove(header);
                        }
                    }
                    catch {
                        // We don't care errors here. This can happen if remote server xxx auth method here and
                        // we don't know how to parse it, so we leave it as is.
                    }
                }

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

                // RFC 4028 7.4. For re-INVITE and UPDATE we need to add Session-Expires and Min-SE: headers.
                if (request.RequestLine.Method == SIP_Methods.INVITE || request.RequestLine.Method == SIP_Methods.UPDATE)
                {
                    b2buaRequest.SessionExpires = new SIP_t_SessionExpires(m_pProxy.Stack.SessionExpries, "uac");
                    b2buaRequest.MinSE          = new SIP_t_MinSE(m_pProxy.Stack.MinimumSessionExpries);
                }

                // Forward request.
                //m_pProxy.ForwardRequest(true,e,b2buaRequest,false);
            }
        }
Example #16
0
 public SIPUri()
 {
     _uri = new SIP_Uri();
 }
            /// <summary>
            /// Cleans up any resources being used.
            /// </summary>
            public void Dispose()
            {
                lock(m_pLock){
                    if(m_IsDisposed){
                        return;
                    }
                    m_IsDisposed = true;

                    m_pOwner.TargetHandler_Disposed(this);

                    m_pOwner = null;
                    m_pRequest = null;
                    m_pTargetUri = null;
                    m_pHops = null;
                    if(m_pTransaction != null){
                        m_pTransaction.Dispose();
                        m_pTransaction = null;
                    }
                    if(m_pTimerC != null){
                        m_pTimerC.Dispose();
                        m_pTimerC = null;
                    }
                }
            }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="targetUri">Target request-URI.</param>
 /// <exception cref="ArgumentNullException">Is raised when <b>targetUri</b> is null reference.</exception>
 public SIP_ProxyTarget(SIP_Uri targetUri) : this(targetUri, null)
 {
 }
        /// <summary>
        /// Starts sending request.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        /// <exception cref="InvalidOperationException">Is raised when <b>Start</b> method has alredy called.</exception>
        /// <exception cref="SIP_TransportException">Is raised when no transport hop(s) for request.</exception>
        public void Start()
        {
            lock (m_pLock)
            {
                if (m_State == SIP_RequestSenderState.Disposed)
                {
                    throw new ObjectDisposedException(GetType().Name);
                }
                if (m_IsStarted)
                {
                    throw new InvalidOperationException("Start method has been already called.");
                }
                m_IsStarted = true;
                m_State     = SIP_RequestSenderState.Starting;

                // Start may take so, process it on thread pool.
                ThreadPool.QueueUserWorkItem(delegate
                {
                    lock (m_pLock)
                    {
                        if (m_State == SIP_RequestSenderState.Disposed)
                        {
                            return;
                        }

                        /* RFC 3261 8.1.2 Sending the Request
                         *  The destination for the request is then computed.  Unless there is
                         *  local policy specifying otherwise, the destination MUST be determined
                         *  by applying the DNS procedures described in [4] as follows.  If the
                         *  first element in the route set indicated a strict router (resulting
                         *  in forming the request as described in Section 12.2.1.1), the
                         *  procedures MUST be applied to the Request-URI of the request.
                         *  Otherwise, the procedures are applied to the first Route header field
                         *  value in the request (if one exists), or to the request's Request-URI
                         *  if there is no Route header field present.  These procedures yield an
                         *  ordered set of address, port, and transports to attempt.  Independent
                         *  of which URI is used as input to the procedures of [4], if the
                         *  Request-URI specifies a SIPS resource, the UAC MUST follow the
                         *  procedures of [4] as if the input URI were a SIPS URI.
                         *
                         *  The UAC SHOULD follow the procedures defined in [4] for stateful
                         *  elements, trying each address until a server is contacted.  Each try
                         *  constitutes a new transaction, and therefore each carries a different
                         *  topmost Via header field value with a new branch parameter.
                         *  Furthermore, the transport value in the Via header field is set to
                         *  whatever transport was determined for the target server.
                         */

                        // We never use strict, only loose route.
                        bool isStrictRoute = false;

                        SIP_Uri uri = null;
                        if (isStrictRoute)
                        {
                            uri = (SIP_Uri)m_pRequest.RequestLine.Uri;
                        }
                        else if (m_pRequest.Route.GetTopMostValue() != null)
                        {
                            uri =
                                (SIP_Uri)
                                m_pRequest.Route.GetTopMostValue().Address.
                                Uri;
                        }
                        else
                        {
                            uri = (SIP_Uri)m_pRequest.RequestLine.Uri;
                        }
                        //uri.Param_Transport = "TCP";

                        // Queue hops.
                        foreach (SIP_Hop hop in
                                 m_pStack.GetHops(uri,
                                                  m_pRequest.ToByteData().Length,
                                                  ((SIP_Uri)
                                                   m_pRequest.RequestLine.Uri).
                                                  IsSecure))
                        {
                            m_pHops.Enqueue(hop);
                        }

                        if (m_pHops.Count == 0)
                        {
                            OnTransportError(
                                new SIP_TransportException("No hops for '" +
                                                           uri + "'."));
                            OnCompleted();
                        }
                        else
                        {
                            m_State = SIP_RequestSenderState.Started;

                            try
                            {
                                if (m_pFlow != null)
                                {
                                    SendToFlow(m_pFlow, m_pRequest.Copy());

                                    return;
                                }
                            }
                            catch
                            {
                                // Sending to specified flow failed, probably disposed, just try send to first hop.
                            }

                            SendToNextHop();
                        }
                    }
                });
            }
        }
Example #20
0
        /// <summary>
        /// Initializes dialog.
        /// </summary>
        /// <param name="stack">Owner stack.</param>
        /// <param name="transaction">Owner transaction.</param>
        /// <param name="response">SIP response what caused dialog creation.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>transaction</b> or <b>response</b>.</exception>
        protected internal virtual void Init(SIP_Stack stack,
                                             SIP_Transaction transaction,
                                             SIP_Response response)
        {
            if (stack == null)
            {
                throw new ArgumentNullException("stack");
            }
            if (transaction == null)
            {
                throw new ArgumentNullException("transaction");
            }
            if (response == null)
            {
                throw new ArgumentNullException("response");
            }

            m_pStack = stack;

            #region UAS

            /* RFC 3261 12.1.1.
                The UAS then constructs the state of the dialog.  This state MUST be
                maintained for the duration of the dialog.

                If the request arrived over TLS, and the Request-URI contained a SIPS
                URI, the "secure" flag is set to TRUE.

                The route set MUST be set to the list of URIs in the Record-Route
                header field from the request, taken in order and preserving all URI
                parameters.  If no Record-Route header field is present in the
                request, the route set MUST be set to the empty set.  This route set,
                even if empty, overrides any pre-existing route set for future
                requests in this dialog.  The remote target MUST be set to the URI
                from the Contact header field of the request.

                The remote sequence number MUST be set to the value of the sequence
                number in the CSeq header field of the request.  The local sequence
                number MUST be empty.  The call identifier component of the dialog ID
                MUST be set to the value of the Call-ID in the request.  The local
                tag component of the dialog ID MUST be set to the tag in the To field
                in the response to the request (which always includes a tag), and the
                remote tag component of the dialog ID MUST be set to the tag from the
                From field in the request.  A UAS MUST be prepared to receive a
                request without a tag in the From field, in which case the tag is
                considered to have a value of null.

                    This is to maintain backwards compatibility with RFC 2543, which
                    did not mandate From tags.

                The remote URI MUST be set to the URI in the From field, and the
                local URI MUST be set to the URI in the To field.
            */

            if (transaction is SIP_ServerTransaction)
            {
                // TODO: Validate request or client transaction must do it ?

                m_IsSecure = ((SIP_Uri) transaction.Request.RequestLine.Uri).IsSecure;
                m_pRouteSet =
                    (SIP_t_AddressParam[]) Core.ReverseArray(transaction.Request.RecordRoute.GetAllValues());
                m_pRemoteTarget = (SIP_Uri) transaction.Request.Contact.GetTopMostValue().Address.Uri;
                m_RemoteSeqNo = transaction.Request.CSeq.SequenceNumber;
                m_LocalSeqNo = 0;
                m_CallID = transaction.Request.CallID;
                m_LocalTag = response.To.Tag;
                m_RemoteTag = transaction.Request.From.Tag;
                m_pRemoteUri = transaction.Request.From.Address.Uri;
                m_pLocalUri = transaction.Request.To.Address.Uri;
                m_pLocalContact = (SIP_Uri) response.Contact.GetTopMostValue().Address.Uri;
            }

                #endregion

                #region UAC

                /* RFC 3261 12.1.2.
                When a UAC receives a response that establishes a dialog, it
                constructs the state of the dialog.  This state MUST be maintained
                for the duration of the dialog.

                If the request was sent over TLS, and the Request-URI contained a
                SIPS URI, the "secure" flag is set to TRUE.

                The route set MUST be set to the list of URIs in the Record-Route
                header field from the response, taken in reverse order and preserving
                all URI parameters.  If no Record-Route header field is present in
                the response, the route set MUST be set to the empty set.  This route
                set, even if empty, overrides any pre-existing route set for future
                requests in this dialog.  The remote target MUST be set to the URI
                from the Contact header field of the response.

                The local sequence number MUST be set to the value of the sequence
                number in the CSeq header field of the request.  The remote sequence
                number MUST be empty (it is established when the remote UA sends a
                request within the dialog).  The call identifier component of the
                dialog ID MUST be set to the value of the Call-ID in the request.
                The local tag component of the dialog ID MUST be set to the tag in
                the From field in the request, and the remote tag component of the
                dialog ID MUST be set to the tag in the To field of the response.  A
                UAC MUST be prepared to receive a response without a tag in the To
                field, in which case the tag is considered to have a value of null.

                    This is to maintain backwards compatibility with RFC 2543, which
                    did not mandate To tags.

                The remote URI MUST be set to the URI in the To field, and the local
                URI MUST be set to the URI in the From field.
            */

            else
            {
                // TODO: Validate request or client transaction must do it ?

                m_IsSecure = ((SIP_Uri) transaction.Request.RequestLine.Uri).IsSecure;
                m_pRouteSet = (SIP_t_AddressParam[]) Core.ReverseArray(response.RecordRoute.GetAllValues());
                m_pRemoteTarget = (SIP_Uri) response.Contact.GetTopMostValue().Address.Uri;
                m_LocalSeqNo = transaction.Request.CSeq.SequenceNumber;
                m_RemoteSeqNo = 0;
                m_CallID = transaction.Request.CallID;
                m_LocalTag = transaction.Request.From.Tag;
                m_RemoteTag = response.To.Tag;
                m_pRemoteUri = transaction.Request.To.Address.Uri;
                m_pLocalUri = transaction.Request.From.Address.Uri;
                m_pLocalContact = (SIP_Uri) transaction.Request.Contact.GetTopMostValue().Address.Uri;
            }

            #endregion

            m_pFlow = transaction.Flow;
        }
Example #21
0
        /// <summary>
        /// Gets if this proxy server is responsible for specified route.
        /// </summary>
        /// <param name="uri">Route value to check.</param>
        /// <returns>Returns trues if server route, otherwise false.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>uri</b> is null reference.</exception>
        internal bool IsLocalRoute(SIP_Uri uri)
        {
            if (uri == null)
            {
                throw new ArgumentNullException("uri");
            }

            // Not a route.
            if (uri.User != null)
            {
                return false;
            }

            // Consider any IP address as local route, because if server behind NAT we can't do IP check.
            if (Net_Utils.IsIPAddress(uri.Host))
            {
                return true;
            }
            else
            {
                foreach (IPBindInfo bind in m_pStack.BindInfo)
                {
                    if (uri.Host.ToLower() == bind.HostName.ToLower())
                    {
                        return true;
                    }
                }
            }

            return false;
        }
Example #22
0
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="targetUri">Target request-URI.</param>
 /// <exception cref="ArgumentNullException">Is raised when <b>targetUri</b> is null reference.</exception>
 public SIP_ProxyTarget(SIP_Uri targetUri)
     : this(targetUri,null)
 {
 }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="destination">Remote destination URI.</param>
 public SIP_Destination(SIP_Uri destination)
     : this(null,destination)
 {
 }
Example #24
0
        /// <summary>
        /// Initializes dialog.
        /// </summary>
        /// <param name="stack">Owner stack.</param>
        /// <param name="transaction">Owner transaction.</param>
        /// <param name="response">SIP response what caused dialog creation.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>transaction</b> or <b>response</b>.</exception>
        protected internal virtual void Init(SIP_Stack stack,
                                             SIP_Transaction transaction,
                                             SIP_Response response)
        {
            if (stack == null)
            {
                throw new ArgumentNullException("stack");
            }
            if (transaction == null)
            {
                throw new ArgumentNullException("transaction");
            }
            if (response == null)
            {
                throw new ArgumentNullException("response");
            }

            m_pStack = stack;

            #region UAS

            /* RFC 3261 12.1.1.
             *  The UAS then constructs the state of the dialog.  This state MUST be
             *  maintained for the duration of the dialog.
             *
             *  If the request arrived over TLS, and the Request-URI contained a SIPS
             *  URI, the "secure" flag is set to TRUE.
             *
             *  The route set MUST be set to the list of URIs in the Record-Route
             *  header field from the request, taken in order and preserving all URI
             *  parameters.  If no Record-Route header field is present in the
             *  request, the route set MUST be set to the empty set.  This route set,
             *  even if empty, overrides any pre-existing route set for future
             *  requests in this dialog.  The remote target MUST be set to the URI
             *  from the Contact header field of the request.
             *
             *  The remote sequence number MUST be set to the value of the sequence
             *  number in the CSeq header field of the request.  The local sequence
             *  number MUST be empty.  The call identifier component of the dialog ID
             *  MUST be set to the value of the Call-ID in the request.  The local
             *  tag component of the dialog ID MUST be set to the tag in the To field
             *  in the response to the request (which always includes a tag), and the
             *  remote tag component of the dialog ID MUST be set to the tag from the
             *  From field in the request.  A UAS MUST be prepared to receive a
             *  request without a tag in the From field, in which case the tag is
             *  considered to have a value of null.
             *
             *      This is to maintain backwards compatibility with RFC 2543, which
             *      did not mandate From tags.
             *
             *  The remote URI MUST be set to the URI in the From field, and the
             *  local URI MUST be set to the URI in the To field.
             */

            if (transaction is SIP_ServerTransaction)
            {
                // TODO: Validate request or client transaction must do it ?

                m_IsSecure  = ((SIP_Uri)transaction.Request.RequestLine.Uri).IsSecure;
                m_pRouteSet =
                    (SIP_t_AddressParam[])Core.ReverseArray(transaction.Request.RecordRoute.GetAllValues());
                m_pRemoteTarget = (SIP_Uri)transaction.Request.Contact.GetTopMostValue().Address.Uri;
                m_RemoteSeqNo   = transaction.Request.CSeq.SequenceNumber;
                m_LocalSeqNo    = 0;
                m_CallID        = transaction.Request.CallID;
                m_LocalTag      = response.To.Tag;
                m_RemoteTag     = transaction.Request.From.Tag;
                m_pRemoteUri    = transaction.Request.From.Address.Uri;
                m_pLocalUri     = transaction.Request.To.Address.Uri;
                m_pLocalContact = (SIP_Uri)response.Contact.GetTopMostValue().Address.Uri;
            }

            #endregion

            #region UAC

            /* RFC 3261 12.1.2.
             * When a UAC receives a response that establishes a dialog, it
             * constructs the state of the dialog.  This state MUST be maintained
             * for the duration of the dialog.
             *
             * If the request was sent over TLS, and the Request-URI contained a
             * SIPS URI, the "secure" flag is set to TRUE.
             *
             * The route set MUST be set to the list of URIs in the Record-Route
             * header field from the response, taken in reverse order and preserving
             * all URI parameters.  If no Record-Route header field is present in
             * the response, the route set MUST be set to the empty set.  This route
             * set, even if empty, overrides any pre-existing route set for future
             * requests in this dialog.  The remote target MUST be set to the URI
             * from the Contact header field of the response.
             *
             * The local sequence number MUST be set to the value of the sequence
             * number in the CSeq header field of the request.  The remote sequence
             * number MUST be empty (it is established when the remote UA sends a
             * request within the dialog).  The call identifier component of the
             * dialog ID MUST be set to the value of the Call-ID in the request.
             * The local tag component of the dialog ID MUST be set to the tag in
             * the From field in the request, and the remote tag component of the
             * dialog ID MUST be set to the tag in the To field of the response.  A
             * UAC MUST be prepared to receive a response without a tag in the To
             * field, in which case the tag is considered to have a value of null.
             *
             *  This is to maintain backwards compatibility with RFC 2543, which
             *  did not mandate To tags.
             *
             * The remote URI MUST be set to the URI in the To field, and the local
             * URI MUST be set to the URI in the From field.
             */

            else
            {
                // TODO: Validate request or client transaction must do it ?

                m_IsSecure      = ((SIP_Uri)transaction.Request.RequestLine.Uri).IsSecure;
                m_pRouteSet     = (SIP_t_AddressParam[])Core.ReverseArray(response.RecordRoute.GetAllValues());
                m_pRemoteTarget = (SIP_Uri)response.Contact.GetTopMostValue().Address.Uri;
                m_LocalSeqNo    = transaction.Request.CSeq.SequenceNumber;
                m_RemoteSeqNo   = 0;
                m_CallID        = transaction.Request.CallID;
                m_LocalTag      = transaction.Request.From.Tag;
                m_RemoteTag     = response.To.Tag;
                m_pRemoteUri    = transaction.Request.To.Address.Uri;
                m_pLocalUri     = transaction.Request.From.Address.Uri;
                m_pLocalContact = (SIP_Uri)transaction.Request.Contact.GetTopMostValue().Address.Uri;
            }

            #endregion

            m_pFlow = transaction.Flow;
        }
Example #25
0
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="destination">Remote destination URI.</param>
 public SIP_Target(SIP_Uri destination) : this(null, destination)
 {
 }
Example #26
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
        }
Example #27
0
        /// <summary>
        /// Checks if the specified route is Record-Route what we add.
        /// </summary>
        /// <param name="route">Record route.</param>
        /// <returns>Returns true if the specified route is local Record-Route value, otherwise false.</returns>
        private bool IsRecordRoute(SIP_Uri route)
        {
            if(route == null){
                throw new ArgumentNullException("route");
            }

            foreach(IPBindInfo bind in m_pStack.BindInfo){
                if(route.Host.ToLower() == bind.HostName.ToLower()){
                    return true;
                }
            }

            return false;
        }
        /// <summary>
        /// Re-invites remote party.
        /// </summary>
        /// <param name="contact">New contact value. Value null means current contact value used.</param>
        /// <param name="sdp">SDP media offer.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="InvalidOperationException">Is raised when there is pending invite and this method is called or dialog is in invalid state.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>sdp</b> is null reference.</exception>
        public void ReInvite(SIP_Uri contact, SDP_Message sdp)
        {
            if (State == SIP_DialogState.Disposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }
            if (HasPendingInvite)
            {
                throw new InvalidOperationException("There is pending INVITE.");
            }
            if (State != SIP_DialogState.Confirmed)
            {
                throw new InvalidOperationException("ReInvite is only available in Confirmed state.");
            }
            if (sdp == null)
            {
                throw new ArgumentNullException("sdp");
            }

            lock (SyncRoot)
            {
                // TODO:

                SIP_Request reinvite = CreateRequest(SIP_Methods.INVITE);
                if (contact != null)
                {
                    reinvite.Contact.RemoveAll();
                    reinvite.Contact.Add(contact.ToString());
                }
                reinvite.ContentType = "application/sdp";
                // reinvite.Data = sdp.ToStringData();

                // TODO: Create request sender
                // TODO: Try to reuse existing data flow
                //SIP_RequestSender sender = this.Stack.CreateRequestSender(reinvite);
                //sender.Start();
            }
        }
Example #29
0
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="destination">Remote destination URI.</param>
 public SIP_Target(SIP_Uri destination)
     : this(null,destination)
 {
 }
Example #30
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
        }
Example #31
0
        /// <summary>
        /// Gets if this proxy server is responsible for specified route.
        /// </summary>
        /// <param name="uri">Route value to check.</param>
        /// <returns>Returns trues if server route, otherwise false.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>uri</b> is null reference.</exception>
        internal bool IsLocalRoute(SIP_Uri uri)
        {
            if(uri == null){
                throw new ArgumentNullException("uri");
            }

            // Not a route.
            if(uri.User != null){
                return false;
            }

            foreach(IPBindInfo bind in m_pStack.BindInfo){
                if(uri.Host.ToLower() == bind.HostName.ToLower()){
                    return true;
                }
            }
            
            return false;
        }
Example #32
0
        /// <summary>
        /// Cleans up any resources being used.
        /// </summary>
        public virtual void Dispose()
        {
            lock (m_pLock)
            {
                if (State == SIP_DialogState.Disposed)
                {
                    return;
                }

                SetState(SIP_DialogState.Disposed, true);

                m_pStack = null;
                m_CallID = null;
                m_LocalTag = null;
                m_RemoteTag = null;
                m_pLocalUri = null;
                m_pRemoteUri = null;
                m_pLocalContact = null;
                m_pRemoteTarget = null;
                m_pRouteSet = null;
                m_pFlow = null;
                m_pLock = null;
            }
        }
Example #33
0
        /// <summary>
        /// Creates new registration.
        /// </summary>
        /// <param name="server">Registrar server URI. For example: sip:domain.com.</param>
        /// <param name="aor">Registration address of record. For example: [email protected].</param>
        /// <param name="contact">Contact URI.</param>
        /// <param name="expires">Gets after how many seconds reigisration expires.</param>
        /// <returns>Returns created registration.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>server</b>,<b>aor</b> or <b>contact</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        public SIP_UA_Registration CreateRegistration(SIP_Uri server,
                                                      string aor,
                                                      AbsoluteUri contact,
                                                      int expires)
        {
            if (m_IsDisposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }
            if (server == null)
            {
                throw new ArgumentNullException("server");
            }
            if (aor == null)
            {
                throw new ArgumentNullException("aor");
            }
            if (aor == string.Empty)
            {
                throw new ArgumentException("Argument 'aor' value must be specified.");
            }
            if (contact == null)
            {
                throw new ArgumentNullException("contact");
            }

            lock (m_pRegistrations)
            {
                SIP_UA_Registration registration = new SIP_UA_Registration(this, server, aor, contact, expires);
                registration.Disposed += delegate
                                             {
                                                 if (!m_IsDisposed)
                                                 {
                                                     m_pRegistrations.Remove(registration);
                                                 }
                                             };
                m_pRegistrations.Add(registration);

                return registration;
            }
        }