Esempio n. 1
0
        /// <summary>
        /// Starts the client transaction.
        /// </summary>
        public void Start()
        {
            SipCSeqValue vCSeq;

            // Add a CSeq header to the request if necessary and intialize the
            // transaction's local sequence number.

            vCSeq = request.GetHeader <SipCSeqValue>(SipHeader.CSeq);
            if (vCSeq == null)
            {
                request.AddHeader(SipHeader.CSeq, new SipCSeqValue(SipHelper.GenCSeq(), request.MethodText));
            }

            // Add Max-Forwards if necessary.

            if (request.GetHeaderText(SipHeader.MaxForwards) == null)
            {
                request.AddHeader(SipHeader.MaxForwards, SipHelper.MaxForwards);
            }

            // Start the transaction

            using (TimedLock.Lock(agent))
            {
                SetState(request.Method == SipMethod.Invite ? SipTransactionState.InviteCalling : SipTransactionState.Trying);
                transport.Send(remoteEP, request);
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Creates the proper ACK request based on the original INVITE request sent
        /// to the server and the 2xx response received.
        /// </summary>
        /// <param name="inviteRequest">The INVITE <see cref="SipRequest" /> sent to the server.</param>
        /// <param name="response">The 2xx <see cref="SipResponse" /> received from the server.</param>
        /// <returns>The created ACK <see cref="SipRequest" />.</returns>
        private SipRequest CreateAckRequest(SipRequest inviteRequest, SipResponse response)
        {
            SipRequest   ackRequest;
            SipHeader    callID;
            SipHeader    to;
            SipHeader    from;
            SipHeader    via;
            SipHeader    contact;
            SipHeader    route;
            SipCSeqValue vCSeq;

            if (inviteRequest.Method != SipMethod.Invite)
            {
                throw new ArgumentException("INVITE request expected.", "inviteRequest");
            }

            if (response.IsProvisional)
            {
                throw new ArgumentException("Non-provisional response expected.", "response");
            }

            ackRequest = new SipRequest(SipMethod.Ack, inviteRequest.Uri, null);

            callID  = inviteRequest[SipHeader.CallID];
            to      = response[SipHeader.To];
            from    = inviteRequest[SipHeader.From];
            via     = inviteRequest[SipHeader.Via];
            contact = inviteRequest[SipHeader.Contact];
            route   = inviteRequest[SipHeader.Route];

            if (callID == null)
            {
                throw new SipException("INVITE request is missing header: [Call-ID]");
            }

            if (to == null)
            {
                throw new SipException("INVITE response is missing header: [To]");
            }

            if (from == null)
            {
                throw new SipException("INVITE request is missing header: [From]");
            }

            if (contact == null)
            {
                throw new SipException("INVITE request is missing header: [Contact]");
            }

            if (via == null)
            {
                throw new SipException("INVITE request is missing header: [Via]");
            }

            vCSeq = inviteRequest.GetHeader <SipCSeqValue>(SipHeader.CSeq);
            if (vCSeq == null)
            {
                throw new SipException("INVITE request is missing header: [CSeq]");
            }

            ackRequest.AddHeader(SipHeader.Via, via.Text);
            ackRequest.AddHeader(SipHeader.To, to.Text);
            ackRequest.AddHeader(SipHeader.From, from.Text);
            ackRequest.AddHeader(SipHeader.Contact, contact.Text);
            ackRequest.AddHeader(SipHeader.CallID, callID.Text);
            ackRequest.AddHeader(SipHeader.CSeq, new SipCSeqValue(vCSeq.Number, "ACK"));
            ackRequest.AddHeader(SipHeader.MaxForwards, SipHelper.MaxForwards);
            ackRequest.AddHeader(SipHeader.UserAgent, agent.Core.Settings.UserAgent);

            if (route != null)
            {
                ackRequest.Headers.Add(SipHeader.Route, route.Clone());
            }

            return(ackRequest);
        }
Esempio n. 3
0
        /// <summary>
        /// The managing <see cref="ISipAgent" /> is responsible for calling this
        /// method whenever it receives requests correlated to this transaction.
        /// </summary>
        /// <param name="request">The received <see cref="SipRequest" />.</param>
        public void OnRequest(SipRequest request)
        {
            SipRequest callbackMsg          = null;
            bool       callOnRequest        = false;
            bool       callOnInviteBegin    = false;
            bool       callOnInviteComplete = false;

            try
            {
                request.SourceTransaction = this;

                using (TimedLock.Lock(agent))
                {
                    if (this.request == null)
                    {
                        SipViaValue     viaValue;
                        SipContactValue toValue;
                        NetworkBinding  sentBy;
                        IPAddress       address;

                        // This is the initial transaction request.

                        this.request = request;

                        // Handle the Via "received" and "rport" header parameters (mostly) as described on page
                        // RFC 3261 (page 145) and RFC 3581 (page 4).

                        viaValue = request.GetHeader <SipViaValue>(SipHeader.Via);
                        if (viaValue == null)
                        {
                            // Illegal request

                            SetState(SipTransactionState.Terminated);
                            return;
                        }

                        sentBy = viaValue.SentByBinding;
                        if (sentBy == null || sentBy.IsHost || sentBy.Address != request.RemoteEndpoint.Address)
                        {
                            viaValue.Received = request.RemoteEndpoint.Address.ToString();
                        }

                        if (viaValue.RPort != null)
                        {
                            viaValue.RPort = request.RemoteEndpoint.Port.ToString();
                        }

                        // Determine the destination network endpoint based on the
                        // rules described on RFC 3261 (page 146).

                        if (request.SourceTransport.IsStreaming)
                        {
                            // $todo(jeff.lill):
                            //
                            // This implementation is incomplete.  To be fully
                            // compliant with the RFC, I'd have to check to
                            // see if the connection is still present in the
                            // transport and if not, use the received and
                            // rport values as described.

                            remoteEP = request.RemoteEndpoint;
                        }
                        else
                        {
                            if (viaValue.MAddr != null)
                            {
                                if (!IPAddress.TryParse(viaValue.MAddr, out address))
                                {
                                    SipException e;

                                    // Illegal request

                                    SetState(SipTransactionState.Terminated);

                                    e                = new SipException("Illegal request: Invalid [Via: maddr].");
                                    e.Transport      = transport.Name;
                                    e.SourceEndpoint = request.RemoteEndpoint;
                                    e.BadMessage     = request;

                                    throw e;
                                }

                                remoteEP = new NetworkBinding(address, viaValue.SentByBinding.Port);
                            }
                            else
                            {
                                remoteEP = request.RemoteEndpoint;
                            }
                        }

                        // INVITE and non-INVITE requests have different state machines.

                        if (request.Method == SipMethod.Invite)
                        {
                            // Start an INVITE transaction

                            SetState(SipTransactionState.InviteProceeding);

                            // If the request has a "To" header without a "tag" parameter then
                            // generate a tag.  Note that this code will cause provisional INVITE
                            // responses to include a generated tag which the RFC indicates
                            // SHOULD NOT be done.  But, it's much safer to do this once here
                            // for all transaction types, avoiding special cases, and besides,
                            // I've noticed that Asterisk includes a tag in its provisional
                            // INVITE responses.

                            toValue = request.GetHeader <SipContactValue>(SipHeader.To);
                            if (toValue != null)
                            {
                                if (toValue["tag"] == null)
                                {
                                    toValue["tag"] = SipHelper.GenerateTagID();
                                }

                                request.SetHeader(SipHeader.To, toValue);
                            }

                            // Always send an initial provisional trying response.

                            provisionalResponse = request.CreateResponse(SipStatus.Trying, null);
                            SendResponse(provisionalResponse);

                            // Setup to call the agent's OnInviteBegin() method.

                            callOnInviteBegin = true;
                            callbackMsg       = request;
                        }
                        else if (request.Method == SipMethod.Ack)
                        {
                            // Allow an ACK request to drop through to the state machine.
                        }
                        else
                        {
                            // Start a non-INVITE transaction

                            SetState(SipTransactionState.Trying);

                            // Setup to call the agent's OnRequest() method.

                            callOnRequest = true;
                            callbackMsg   = request;
                        }

                        return;
                    }

                    // Handle state specific processing

                    switch (base.State)
                    {
                    default:
                    case SipTransactionState.Unknown:

                        SysLog.LogError("Unexpected SIP transaction state.");
                        SetState(SipTransactionState.Terminated);
                        return;

                    case SipTransactionState.InviteCalling:

                        break;

                    case SipTransactionState.InviteProceeding:

                        if (provisionalResponse != null)
                        {
                            transport.Send(remoteEP, provisionalResponse);
                        }

                        break;

                    case SipTransactionState.InviteCompleted:

                        if (request.Method == SipMethod.Ack)
                        {
                            SetState(SipTransactionState.InviteConfirmed);

                            // Setup to call OnInviteComplete(ack);

                            callOnInviteComplete = true;
                            callbackMsg          = request;
                            return;
                        }

                        Assertion.Test(finalResponse != null);
                        transport.Send(remoteEP, finalResponse);
                        break;

                    case SipTransactionState.InviteConfirmed:

                        break;

                    case SipTransactionState.Trying:

                        break;

                    case SipTransactionState.Proceeding:

                        Assertion.Test(provisionalResponse != null);
                        transport.Send(remoteEP, provisionalResponse);
                        break;

                    case SipTransactionState.Completed:

                        Assertion.Test(finalResponse != null);
                        transport.Send(remoteEP, finalResponse);
                        break;

                    case SipTransactionState.Terminated:

                        break;
                    }
                }
            }
            finally
            {
                // Handle the agent callbacks outside of the lock to avoid
                // deadlock issues.

                if (callOnRequest)
                {
                    agent.OnRequest(this, callbackMsg);
                }

                if (callOnInviteBegin)
                {
                    agent.OnInviteBegin(this, request);
                }

                if (callOnInviteComplete)
                {
                    agent.OnInviteComplete(this, this.request, finalResponse, callbackMsg);
                }
            }
        }
Esempio n. 4
0
        /// <summary>
        /// Initiates an asynchronous SIP request transaction.
        /// </summary>
        /// <param name="request">The <see cref="SipRequest" /> to be submitted.</param>
        /// <param name="dialog">The <see cref="SipDialog" /> for requests that initiate a dialog (or <c>null</c>).</param>
        /// <param name="callback">The delegate to be called when the operation completes (or <c>null</c>).</param>
        /// <param name="state">Application defined state (or <c>null</c>).</param>
        /// <returns>The <see cref="IAsyncResult" /> to be used to track the operation's progress.</returns>
        /// <remarks>
        /// <para>
        /// All requests to <see cref="BeginRequest(SipRequest,SipDialog,AsyncCallback,object)" /> must be matched with a
        /// call to <see cref="EndRequest" />.
        /// </para>
        /// <note>
        /// This method adds reasonable <b>Call-ID</b> and <b>CSeq</b> headers to the request if these
        /// headers are not already present.
        /// </note>
        /// </remarks>
        public IAsyncResult BeginRequest(SipRequest request, SipDialog dialog, AsyncCallback callback, object state)
        {
            ClientAsyncResult    arClient = new ClientAsyncResult(request, dialog, callback, state);
            SipValue             viaValue;
            SipCSeqValue         vCSeq;
            string               transactionID;
            SipClientTransaction transaction;
            ISipTransport        transport;
            NetworkBinding       remoteEP;

            if (dialog != null && request.Method != SipMethod.Invite)
            {
                throw new InvalidOperationException("Dialogs may be created only for INVITE requests.");
            }

            arClient.Dialog = dialog;

            transport = router.SelectTransport(this, request, out remoteEP);
            if (transport == null)
            {
                throw new SipException("No approriate transport is available.");
            }

            // Initialize the request's Via header and transaction ID as necessary.

            transactionID      = SipHelper.GenerateBranchID();
            viaValue           = new SipValue(string.Format("SIP/2.0/{0} {1}", transport.Name, transport.Settings.ExternalBinding.Address));
            viaValue["branch"] = transactionID;
            viaValue["rport"]  = string.Empty;

            request.PrependHeader(SipHeader.Via, viaValue);

            // Initialize common headers as necessary

            if (!request.ContainsHeader(SipHeader.CallID))
            {
                request.AddHeader(SipHeader.CallID, SipHelper.GenerateCallID());
            }

            vCSeq = request.GetHeader <SipCSeqValue>(SipHeader.CSeq);
            if (vCSeq == null)
            {
                vCSeq = new SipCSeqValue(SipHelper.GenCSeq(), request.MethodText);
                request.AddHeader(SipHeader.CSeq, vCSeq);
            }

            // Initialize the transaction

            transaction            = new SipClientTransaction(this, request, transactionID, transport, remoteEP);
            transaction.AgentState = arClient;

            // Handle initial dialog INVITE specific initialization

            if (dialog != null && request.Method == SipMethod.Invite && dialog.State == SipDialogState.Waiting)
            {
                // Client-side dialogs need to know the transaction so
                // they'll be able to send the confirming ACK.

                dialog.InitiatingTransaction = transaction;

                // Dialogs need to know about the sequence number used in INVITE requests so
                // that the ACK can be generated with the same sequence number.

                dialog.AckCSeq = vCSeq.Number;

                // The dialog has been intialized enough to be added to the core's
                // early dialog table.

                core.AddEarlyDialog(dialog);
            }

            // Start the transaction

            using (TimedLock.Lock(this))
            {
                transactions.Add(transactionID, transaction);
            }

            transaction.Start();
            arClient.Started();

            return(arClient);
        }