Exemplo n.º 1
0
        /// <summary>
        /// Default incoming call constructor.
        /// </summary>
        /// <param name="ua">Owner UA.</param>
        /// <param name="invite">INVITE server transaction.</param>
        /// <exception cref="ArgumentNullException">Is riased when <b>ua</b> or <b>invite</b> is null reference.</exception>
        internal SIP_UA_Call(SIP_UA ua, SIP_ServerTransaction invite)
        {
            if (ua == null)
            {
                throw new ArgumentNullException("ua");
            }
            if (invite == null)
            {
                throw new ArgumentNullException("invite");
            }

            m_pUA = ua;
            m_pInitialInviteTransaction = invite;
            m_pLocalUri  = invite.Request.To.Address.Uri;
            m_pRemoteUri = invite.Request.From.Address.Uri;
            m_pInitialInviteTransaction.Canceled += new EventHandler(delegate(object sender, EventArgs e){
                // If transaction canceled, terminate call.
                SetState(SIP_UA_CallState.Terminated);
            });

            // Parse SDP if INVITE contains SDP.
            // RFC 3261 13.2.1. INVITE may be offerless, we must thne send offer and remote party sends sdp in ACK.
            if (invite.Request.ContentType != null && invite.Request.ContentType.ToLower().IndexOf("application/sdp") > -1)
            {
                m_pRemoteSDP = SDP_Message.Parse(Encoding.UTF8.GetString(invite.Request.Data));
            }

            m_pTags = new Dictionary <string, object>();

            m_State = SIP_UA_CallState.WaitingToAccept;
        }
Exemplo n.º 2
0
 /// <summary>
 /// This method is called when caller dialog client transaction receives response.
 /// </summary>
 /// <param name="sender">Sender.</param>
 /// <param name="e">Event data.</param>
 private void m_pCaller_ResponseReceived(object sender, SIP_ResponseReceivedEventArgs e)
 {
     SIP_ServerTransaction serverTransaction = (SIP_ServerTransaction)e.ClientTransaction.Tag;
     //SIP_Response response = serverTransaction.Request.CreateResponse(e.Response.StatusCode_ReasonPhrase);
     //CopyMessage(e.Response,response,new string[]{"Via:","Call-Id:","To:","From:","CSeq:","Contact:","Route:","Record-Route:","Allow:","Supported:"});
     //serverTransaction.SendResponse(response);
 }
Exemplo n.º 3
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="invite">SIP INVITE server transaction.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>invite</b> is null reference.</exception>
        public wfrm_IncomingCall(SIP_ServerTransaction invite)
        {
            if (invite == null)
            {
                throw new ArgumentNullException("invite");
            }

            InitUI();

            m_pTransaction           = invite;
            m_pTransaction.Canceled += new EventHandler(m_pTransaction_Canceled);

            m_pFrom.Text = invite.Request.To.Address.ToStringValue();
        }
Exemplo n.º 4
0
        /// <summary>
        /// Default incoming call constructor.
        /// </summary>
        /// <param name="ua">Owner UA.</param>
        /// <param name="invite">INVITE server transaction.</param>
        /// <exception cref="ArgumentNullException">Is riased when <b>ua</b> or <b>invite</b> is null reference.</exception>
        internal SIP_UA_Call(SIP_UA ua, SIP_ServerTransaction invite)
        {
            if (ua == null)
            {
                throw new ArgumentNullException("ua");
            }
            if (invite == null)
            {
                throw new ArgumentNullException("invite");
            }

            m_pUA = ua;
            m_pInitialInviteTransaction = invite;
            m_pLocalUri  = invite.Request.To.Address.Uri;
            m_pRemoteUri = invite.Request.From.Address.Uri;
            m_pInitialInviteTransaction.Canceled += new EventHandler(delegate(object sender, EventArgs e){
                // If transaction canceled, terminate call.
                SetState(SIP_UA_CallState.Terminated);
            });

            m_State = SIP_UA_CallState.WaitingToAccept;
        }
Exemplo n.º 5
0
        /// <summary>
        /// This method is called when new request is received.
        /// </summary>
        /// <param name="e">Request event arguments.</param>
        private void OnRequestReceived(SIP_RequestReceivedEventArgs e)
        {
            SIP_Request request = e.Request;

            try{
                #region Statefull

                // Statefull
                if ((m_ProxyMode & SIP_ProxyMode.Statefull) != 0)
                {
                    // Statefull proxy is transaction statefull proxy only,
                    // what don't create dialogs and keep dialog state.

                    /* RFC 3261 16.10.
                     *  StateFull proxy:
                     *          If a matching response context is found, the element MUST
                     *          immediately return a 200 (OK) response to the CANCEL request.
                     *
                     *          If a response context is not found, the element does not have any
                     *          knowledge of the request to apply the CANCEL to.  It MUST statelessly
                     *          forward the CANCEL request (it may have statelessly forwarded the
                     *          associated request previously).
                     */
                    if (e.Request.RequestLine.Method == SIP_Methods.CANCEL)
                    {
                        // Don't do server transaction before we get CANCEL matching transaction.
                        SIP_ServerTransaction trToCancel = m_pStack.TransactionLayer.MatchCancelToTransaction(e.Request);
                        if (trToCancel != null)
                        {
                            trToCancel.Cancel();
                            e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, request));
                        }
                        else
                        {
                            ForwardRequest(false, e, true);
                        }
                    }
                    // ACK never creates transaction, it's always passed directly to transport layer.
                    else if (e.Request.RequestLine.Method == SIP_Methods.ACK)
                    {
                        ForwardRequest(false, e, true);
                    }
                    else
                    {
                        ForwardRequest(true, e, true);
                    }
                }

                #endregion

                #region B2BUA

                // B2BUA
                else if ((m_ProxyMode & SIP_ProxyMode.B2BUA) != 0)
                {
                    m_pB2BUA.OnRequestReceived(e);
                }

                #endregion

                #region Stateless

                // Stateless
                else if ((m_ProxyMode & SIP_ProxyMode.Stateless) != 0)
                {
                    // Stateless proxy don't do transaction, just forwards all.
                    ForwardRequest(false, e, true);
                }

                #endregion

                #region Proxy won't accept command

                else
                {
                    e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x501_Not_Implemented, request));
                }

                #endregion
            }
            catch (Exception x) {
                try{
                    m_pStack.TransportLayer.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error + ": " + x.Message, e.Request));
                }
                catch {
                    // Skip transport layer exception if send fails.
                }

                // Don't raise OnError for transport errors.
                if (!(x is SIP_TransportException))
                {
                    m_pStack.OnError(x);
                }
            }
        }
Exemplo n.º 6
0
        internal SIP_ProxyContext CreateProxyContext(SIP_RequestContext requestContext, SIP_ServerTransaction transaction, SIP_Request request, bool addRecordRoute)
        {
            // Create proxy context that will be responsible for forwarding request.
            SIP_ProxyContext proxyContext = new SIP_ProxyContext(
                this,
                transaction,
                request,
                addRecordRoute,
                m_ForkingMode,
                (this.ProxyMode & SIP_ProxyMode.B2BUA) != 0,
                false,
                false,
                requestContext.Targets.ToArray()
                );

            m_pProxyContexts.Add(proxyContext);

            return(proxyContext);
        }
Exemplo n.º 7
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);
            }
        }
Exemplo n.º 8
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
        }
Exemplo n.º 9
0
        /// <summary>
        /// This method is called when SIP stack received new message.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event data.</param>
        private void m_pStack_RequestReceived(object sender, SIP_RequestReceivedEventArgs e)
        {
            // TODO: Performance: rise events on thread pool or see if this method called on pool aready, then we may not keep lock for events ?

            if (e.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_pStack.TransactionLayer.MatchCancelToTransaction(e.Request);
                if (trToCancel != null)
                {
                    trToCancel.Cancel();
                    e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, e.Request));
                }
                else
                {
                    e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist, e.Request));
                }
            }
            else if (e.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.
                 */
                // TODO:

                SIP_Dialog dialog = m_pStack.TransactionLayer.MatchDialog(e.Request);
                if (dialog != null)
                {
                    e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, e.Request));
                    dialog.Terminate();
                }
                else
                {
                    e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist, e.Request));
                }
            }
            else if (e.Request.RequestLine.Method == SIP_Methods.INVITE)
            {
                // Supress INVITE retransmissions.
                e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x100_Trying, e.Request));

                // Create call.
                SIP_UA_Call call = new SIP_UA_Call(this, e.ServerTransaction);
                call.StateChanged += new EventHandler(Call_StateChanged);
                m_pCalls.Add(call);

                OnIncomingCall(call);
            }
            else
            {
                OnRequestReceived(e);
            }
        }
Exemplo n.º 10
0
        /// <summary>
        /// This method is called when new request is received.
        /// </summary>
        /// <param name="e">Request event arguments.</param>
        private void OnRequestReceived(SIP_RequestReceivedEventArgs e)
        {
            /* RFC 3261 16.12. ????????? Forward does all thse steps.
             *  1. The proxy will inspect the Request-URI.  If it indicates a
             *     resource owned by this proxy, the proxy will replace it with
             *     the results of running a location service.  Otherwise, the
             *     proxy will not change the Request-URI.
             *
             *  2. The proxy will inspect the URI in the topmost Route header
             *     field value.  If it indicates this proxy, the proxy removes it
             *     from the Route header field (this route node has been reached).
             *
             *  3. The proxy will forward the request to the resource indicated
             *     by the URI in the topmost Route header field value or in the
             *     Request-URI if no Route header field is present.  The proxy
             *     determines the address, port and transport to use when
             *     forwarding the request by applying the procedures in [4] to that URI.
             */

            SIP_Request request = e.Request;

            try
            {
                #region Registrar

                // Registrar
                if ((m_ProxyMode & SIP_ProxyMode.Registrar) != 0 &&
                    request.RequestLine.Method == SIP_Methods.REGISTER)
                {
                    m_pRegistrar.Register(e);
                }

                #endregion

                #region Presence

                /*
                 * // Presence
                 * else if((m_ProxyMode & SIP_ProxyMode.Presence) != 0 && (request.Method == "SUBSCRIBE" || request.Method == "NOTIFY")){
                 *
                 * }
                 */
                #endregion

                #region Statefull

                // Statefull
                else if ((m_ProxyMode & SIP_ProxyMode.Statefull) != 0)
                {
                    // Statefull proxy is transaction statefull proxy only,
                    // what don't create dialogs and keep dialog state.

                    /* RFC 3261 16.10.
                     *  StateFull proxy:
                     *          If a matching response context is found, the element MUST
                     *          immediately return a 200 (OK) response to the CANCEL request.
                     *
                     *          If a response context is not found, the element does not have any
                     *          knowledge of the request to apply the CANCEL to.  It MUST statelessly
                     *          forward the CANCEL request (it may have statelessly forwarded the
                     *          associated request previously).
                     */
                    if (e.Request.RequestLine.Method == SIP_Methods.CANCEL)
                    {
                        // Don't do server transaction before we get CANCEL matching transaction.
                        SIP_ServerTransaction trToCancel =
                            m_pStack.TransactionLayer.MatchCancelToTransaction(e.Request);
                        if (trToCancel != null)
                        {
                            trToCancel.Cancel();
                            e.ServerTransaction.SendResponse(m_pStack.CreateResponse(
                                                                 SIP_ResponseCodes.x200_Ok, request));
                        }
                        else
                        {
                            ForwardRequest(false, e);
                        }
                    }
                    // ACK never creates transaction, it's always passed directly to transport layer.
                    else if (e.Request.RequestLine.Method == SIP_Methods.ACK)
                    {
                        ForwardRequest(false, e);
                    }
                    else
                    {
                        ForwardRequest(true, e);
                    }
                }

                #endregion

                #region B2BUA

                // B2BUA
                else if ((m_ProxyMode & SIP_ProxyMode.B2BUA) != 0)
                {
                    m_pB2BUA.OnRequestReceived(e);
                }

                #endregion

                #region Stateless

                // Stateless
                else if ((m_ProxyMode & SIP_ProxyMode.Stateless) != 0)
                {
                    // Stateless proxy don't do transaction, just forwards all.
                    ForwardRequest(false, e);
                }

                #endregion

                #region Proxy won't accept command

                else
                {
                    e.ServerTransaction.SendResponse(
                        m_pStack.CreateResponse(SIP_ResponseCodes.x501_Not_Implemented, request));
                }

                #endregion
            }
            catch (Exception x)
            {
                try
                {
                    m_pStack.TransportLayer.SendResponse(
                        m_pStack.CreateResponse(
                            SIP_ResponseCodes.x500_Server_Internal_Error + ": " + x.Message, e.Request));
                }
                catch
                {
                    // Skip transport layer exception if send fails.
                }

                // Don't raise OnError for transport errors.
                if (!(x is SIP_TransportException))
                {
                    m_pStack.OnError(x);
                }
            }
        }
Exemplo n.º 11
0
        /// <summary>
        /// Default incoming call constructor.
        /// </summary>
        /// <param name="ua">Owner UA.</param>
        /// <param name="invite">INVITE server transaction.</param>
        /// <exception cref="ArgumentNullException">Is riased when <b>ua</b> or <b>invite</b> is null reference.</exception>
        internal SIP_UA_Call(SIP_UA ua, SIP_ServerTransaction invite)
        {
            if (ua == null)
            {
                throw new ArgumentNullException("ua");
            }
            if (invite == null)
            {
                throw new ArgumentNullException("invite");
            }

            m_pUA = ua;
            m_pInitialInviteTransaction = invite;
            m_pLocalUri = invite.Request.To.Address.Uri;
            m_pRemoteUri = invite.Request.From.Address.Uri;
            m_pInitialInviteTransaction.Canceled += delegate
                                                        {
                                                            // If transaction canceled, terminate call.
                                                            SetState(SIP_UA_CallState.Terminated);
                                                        };

            m_State = SIP_UA_CallState.WaitingToAccept;
        }
Exemplo n.º 12
0
        /// <summary>
        /// Cleans up any resources being used.
        /// </summary>
        public void Dispose()
        {
            lock (m_pLock)
            {
                if (m_IsDisposed)
                {
                    return;
                }
                m_IsDisposed = true;

                m_pProxy.Stack.Logger.AddText("ProxyContext(id='" + m_ID + "') disposed.");

                m_pProxy.m_pProxyContexts.Remove(this);

                m_pProxy = null;
                m_pServerTransaction = null;
                m_pTargetsHandlers = null;
                m_pResponses = null;
                m_pTargets = null;
            }
        }
Exemplo n.º 13
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="proxy">Owner proxy.</param>
        /// <param name="transaction">Server transaction what is used to send SIP responses back to caller.</param>
        /// <param name="request">Request to forward.</param>
        /// <param name="addRecordRoute">If true, Record-Route header field will be added.</param>
        /// <param name="forkingMode">Specifies how proxy context must handle forking.</param>
        /// <param name="isB2BUA">Specifies if proxy context is in B2BUA or just transaction satefull mode.</param>
        /// <param name="noCancel">Specifies if proxy should not send Cancel to forked requests.</param>
        /// <param name="noRecurse">Specifies what proxy server does when it gets 3xx response. If true proxy will forward
        /// request to new specified address if false, proxy will return 3xx response to caller.</param>
        /// <param name="targets">Possible remote targets. NOTE: These values must be in priority order !</param>
        /// <param name="credentials">Target set credentials.</param>
        /// <exception cref="ArgumentNullException">Is raised when any of the reference type prameters is null.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        internal SIP_ProxyContext(SIP_ProxyCore proxy,
                                  SIP_ServerTransaction transaction,
                                  SIP_Request request,
                                  bool addRecordRoute,
                                  SIP_ForkingMode forkingMode,
                                  bool isB2BUA,
                                  bool noCancel,
                                  bool noRecurse,
                                  SIP_ProxyTarget[] targets,
                                  NetworkCredential[] credentials)
        {
            if (proxy == null)
            {
                throw new ArgumentNullException("proxy");
            }
            if (transaction == null)
            {
                throw new ArgumentNullException("transaction");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }
            if (targets == null)
            {
                throw new ArgumentNullException("targets");
            }
            if (targets.Length == 0)
            {
                throw new ArgumentException("Argumnet 'targets' must contain at least 1 value.");
            }

            m_pProxy = proxy;

            m_pServerTransaction = transaction;
            m_pServerTransaction.Canceled += m_pServerTransaction_Canceled;
            m_pServerTransaction.Disposed += m_pServerTransaction_Disposed;

            m_pRequest = request;
            m_AddRecordRoute = addRecordRoute;
            m_ForkingMode = forkingMode;
            m_IsB2BUA = isB2BUA;
            m_NoCancel = noCancel;
            m_NoRecurse = noRecurse;

            m_pTargetsHandlers = new List<TargetHandler>();
            m_pResponses = new List<SIP_Response>();
            m_ID = Guid.NewGuid().ToString();
            m_CreateTime = DateTime.Now;

            // Queue targets up, higest to lowest.
            m_pTargets = new Queue<TargetHandler>();
            foreach (SIP_ProxyTarget target in targets)
            {
                m_pTargets.Enqueue(new TargetHandler(this,
                                                     target.Flow,
                                                     target.TargetUri,
                                                     m_AddRecordRoute,
                                                     false));
            }

            m_pCredentials = new List<NetworkCredential>();
            m_pCredentials.AddRange(credentials);

            /*  RFC 3841 9.1.
                The Request-Disposition header field specifies caller preferences for
                how a server should process a request.
              
                Override SIP proxy default value.
            */
            foreach (SIP_t_Directive directive in request.RequestDisposition.GetAllValues())
            {
                if (directive.Directive == SIP_t_Directive.DirectiveType.NoFork)
                {
                    m_ForkingMode = SIP_ForkingMode.None;
                }
                else if (directive.Directive == SIP_t_Directive.DirectiveType.Parallel)
                {
                    m_ForkingMode = SIP_ForkingMode.Parallel;
                }
                else if (directive.Directive == SIP_t_Directive.DirectiveType.Sequential)
                {
                    m_ForkingMode = SIP_ForkingMode.Sequential;
                }
                else if (directive.Directive == SIP_t_Directive.DirectiveType.NoCancel)
                {
                    m_NoCancel = true;
                }
                else if (directive.Directive == SIP_t_Directive.DirectiveType.NoRecurse)
                {
                    m_NoRecurse = true;
                }
            }

            m_pProxy.Stack.Logger.AddText("ProxyContext(id='" + m_ID + "') created.");
        }