Exemple #1
0
        private string uri;                     // The request URI

        /// <summary>
        /// Initializes a SIP message.
        /// </summary>
        /// <param name="methodText">The SIP method text.</param>
        /// <param name="uri">The SIP request URI.</param>
        /// <param name="sipVersion">The SIP version string (or <c>null</c>).</param>
        public SipRequest(string methodText, string uri, string sipVersion)
            : base(true, sipVersion)
        {
            this.methodText = methodText.ToUpper();
            this.method     = SipHelper.ParseMethod(methodText);
            this.uri        = uri;
        }
Exemple #2
0
        /// <summary>
        /// Prepends a name/value pair to the collection, handling multi-value and
        /// special headers.
        /// </summary>
        /// <param name="name">The header name.</param>
        /// <param name="value">The header value.</param>
        /// <returns>
        /// The <see cref="SipHeader" /> instance that actually is actually present collection.
        /// </returns>
        /// <remarks>
        /// <note>The value added will be inserted <b>before</b> any existing values for this header.</note>
        /// </remarks>
        public SipHeader Prepend(string name, string value)
        {
            SipHeader header;
            string    longForm;

            longForm = SipHelper.GetLongHeader(name);
            if (longForm != null)
            {
                hasCompactHeaders = true;
                name = longForm;
            }

            if (specialHeaders.ContainsKey(name))
            {
                if (this.ContainsKey(name))
                {
                    throw new NotImplementedException("LillTek SIP stack does not currently support multiple instances of special headers.");
                }

                this.Add(name, header = new SipHeader(name, value, true));
                return(header);
            }

            if (this.TryGetValue(name, out header))
            {
                header.Prepend(value);
                return(header);
            }
            else
            {
                this.Add(name, header = new SipHeader(name, value));
                return(header);
            }
        }
Exemple #3
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);
            }
        }
Exemple #4
0
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="requestUri">The request URI.</param>
 /// <param name="to">Populates the request's <b>To</b> header.</param>
 /// <param name="from">Populates the request's <b>From</b> header.</param>
 /// <param name="desiredTTL">The requested lifetime of the registration.</param>
 /// <remarks>
 /// <para>
 /// The RFC 3261 requires that UACs use the same Call-ID for all REGISTER requests
 /// made to a registrar and also that that UA must increment the CSeq value by
 /// one for each request.  The UAC will need to track these values and pass them
 /// to this constructor.
 /// </para>
 /// </remarks>
 public SipRegisterRequest(string requestUri, string to, string from, TimeSpan desiredTTL)
     : base(SipMethod.Register, requestUri, SipHelper.SIP20)
 {
     base.AddHeader(SipHeader.To, to);
     base.AddHeader(SipHeader.From, from);
     base.AddHeader(SipHeader.CallID, SipHelper.GenerateCallID());
     base.AddHeader(SipHeader.Expires, ((int)desiredTTL.TotalSeconds).ToString());
 }
Exemple #5
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="requestUri">The request URI.</param>
        /// <param name="to">Populates the request's <b>To</b> header.</param>
        /// <param name="from">Populates the request's <b>From</b> header.</param>
        /// <param name="sdp">The SDP information describing this side's session media.</param>
        public SipInviteRequest(string requestUri, string to, string from, SdpPayload sdp)
            : base(SipMethod.Invite, requestUri, SipHelper.SIP20)
        {
            base.AddHeader(SipHeader.To, to);
            base.AddHeader(SipHeader.From, from);
            base.AddHeader(SipHeader.CallID, SipHelper.GenerateCallID());
            base.AddHeader("Allow", "ACK, CANCEL, BYE");
            base.AddHeader("Accept", SipHelper.SdpMimeType);
            base.AddHeader("Content-Disposition", "session");

            base.Contents = Helper.ToUTF8(sdp.ToString());
        }
Exemple #6
0
        /// <summary>
        /// Adds a name/header pair to the collection.
        /// </summary>
        /// <param name="name">The header name.</param>
        /// <param name="header">The header.</param>
        /// <remarks>
        /// <note>
        /// This method will convert the header's name property
        /// to its long form if necessary.
        /// </note>
        /// </remarks>
        public new void Add(string name, SipHeader header)
        {
            string longForm;

            longForm = SipHelper.GetLongHeader(name);
            if (longForm != null)
            {
                hasCompactHeaders = true;
                name        = longForm;
                header.Name = longForm;
            }

            base.Add(name, header);
        }
Exemple #7
0
        /// <summary>
        /// Returns the <see cref="ISipTransport" /> that will be used to
        /// deliver a <see cref="SipMessage" /> from a source <see cref="ISipAgent" />.
        /// </summary>
        /// <param name="agent">The source agent.</param>
        /// <param name="request">The <see cref="SipRequest" /> to be delivered.</param>
        /// <param name="remoteEP">Returns as the destination server's <see cref="NetworkBinding" />.</param>
        /// <returns>The <see cref="ISipTransport" /> that will be used for delivery (or <c>null</c>).</returns>
        /// <remarks>
        /// <note>
        /// <c>null</c> is a valid return value.  This indicates that there are
        /// no appropriate transports available to deliver this message.
        /// </note>
        /// </remarks>
        public ISipTransport SelectTransport(ISipAgent agent, SipRequest request, out NetworkBinding remoteEP)
        {
            SipTransportType transportType;
            SipUri           proxyUri;

            proxyUri = base.OutboundProxyUri;
            if (proxyUri != null)
            {
                // Select a transport to route the message to the outbound proxy.

                if (!SipHelper.TryGetRemoteBinding("<" + proxyUri + ">", out remoteEP, out transportType))
                {
                    return(null);
                }
            }
            else if (!SipHelper.TryGetRemoteBinding("<" + request.Uri + ">", out remoteEP, out transportType))
            {
                return(null);
            }

            // Select the first transport that looks decent.  If the desired transport
            // is not specified, then favor UDP since most of the world is compatible
            // with that.

            if (transportType == SipTransportType.UDP || transportType == SipTransportType.Unspecified)
            {
                foreach (ISipTransport transport in base.Transports)
                {
                    if (transport.TransportType == SipTransportType.UDP)
                    {
                        return(transport);
                    }
                }

                return(null);
            }

            // Otherwise match the transport.

            foreach (ISipTransport transport in base.Transports)
            {
                if (transport.TransportType == transportType)
                {
                    return(transport);
                }
            }

            return(null);
        }
Exemple #8
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="statusCode">The SIP response status code.</param>
        /// <param name="reasonPhrase">The reason phrase (or <c>null</c>).</param>
        /// <param name="sipVersion">The SIP version string (or <c>null</c>).</param>
        public SipResponse(int statusCode, string reasonPhrase, string sipVersion)
            : base(false, sipVersion)
        {
            if (statusCode < 0)
            {
                throw new SipException("Cannot assign stack specific error codes to a SIP response.");
            }

            if (statusCode < 100 || statusCode >= 700)
            {
                throw new SipException("Invalid status code [{0}].", statusCode);
            }

            this.status       = (SipStatus)statusCode;
            this.reasonPhrase = reasonPhrase != null ? reasonPhrase : SipHelper.GetReasonPhrase(statusCode);
        }
Exemple #9
0
        /// <summary>
        /// Appends the header onto a <see cref="StringBuilder" /> in a format
        /// suitable for serializing into a SIP message.
        /// </summary>
        /// <param name="sb">The <see cref="StringBuilder" />.</param>
        /// <param name="useCompactForm">Pass <c>true</c> to render header names using the long form.</param>
        /// <remarks>
        /// <note>Multi-valued headers will be rendered as multiple lines of SIP headers.</note>
        /// <note>Headers with no values will not be rendered.</note>
        /// </remarks>
        public void Serialize(StringBuilder sb, bool useCompactForm)
        {
            string headerName;
            string compactName;

            if (values.Length == 0)
            {
                return;
            }

            headerName = name;
            if (useCompactForm)
            {
                compactName = SipHelper.GetCompactHeader(headerName);
                if (compactName != null)
                {
                    headerName = compactName;
                }
            }

            if (isSpecial || renderMultiLine.ContainsKey(name))
            {
                // Render as separate header lines

                for (int i = 0; i < values.Length; i++)
                {
                    sb.AppendFormat("{0}: {1}\r\n", headerName, values[i]);
                }
            }
            else
            {
                // Render as comma separated values

                sb.AppendFormat("{0}: ", headerName);
                for (int i = 0; i < values.Length; i++)
                {
                    if (i > 0)
                    {
                        sb.Append(", ");
                    }

                    sb.Append(values[i]);
                }

                sb.Append("\r\n");
            }
        }
Exemple #10
0
        /// <summary>
        /// Asynchronously transmits the message passed to the destination
        /// indicated by the <see paramref="remoteEP" /> parameter.
        /// </summary>
        /// <param name="remoteEP">The destination SIP endpoint's <see cref="NetworkBinding" />.</param>
        /// <param name="message">The <see cref="SipMessage" /> to be transmitted.</param>
        /// <exception cref="SipTransportException">Thrown if the remote endpoint rejected the message or timed out.</exception>
        public void Send(NetworkBinding remoteEP, SipMessage message)
        {
            if (disabled)
            {
                return;
            }

            if ((traceMode & SipTraceMode.Send) != 0)
            {
                SipHelper.Trace(string.Format("UDP: sending to {0}", remoteEP), message);
            }

            try
            {
                sock.SendTo(message.ToArray(), remoteEP);
            }
            catch (SocketException e)
            {
                // $todo(jeff.lill):
                //
                // This is just copied from the TCP transport.  It probably
                // doesn't apply here.

                switch ((SocketError)e.ErrorCode)
                {
                case SocketError.ConnectionAborted:
                case SocketError.ConnectionRefused:
                case SocketError.ConnectionReset:
                case SocketError.HostDown:
                case SocketError.HostNotFound:
                case SocketError.HostUnreachable:

                    throw new SipTransportException(SipTransportException.ErrorType.Rejected, e.Message, e);

                case SocketError.TimedOut:

                    throw new SipTransportException(SipTransportException.ErrorType.Timeout, e.Message, e);

                default:

                    throw;
                }
            }
        }
Exemple #11
0
        /// <summary>
        /// References the named <see cref="SipHeader" /> if it's present in the collection.
        /// </summary>
        /// <param name="name">Case insensitive name of the desired header.</param>
        /// <returns>The header instance if one exists, <c>null</c> otherwise.</returns>
        public new SipHeader this[string name]
        {
            get
            {
                SipHeader header;
                string    longForm;

                longForm = SipHelper.GetLongHeader(name);
                if (longForm != null)
                {
                    name = longForm;
                }

                if (this.TryGetValue(name, out header))
                {
                    return(header);
                }
                else
                {
                    return(null);
                }
            }

            set
            {
                string longForm;

                longForm = SipHelper.GetLongHeader(name);
                if (longForm != null)
                {
                    name = longForm;
                    if (!base.ContainsKey(name))
                    {
                        hasCompactHeaders = true;
                    }
                }

                base[name] = value;
            }
        }
Exemple #12
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);
                }
            }
        }
Exemple #13
0
        /// <summary>
        /// Called when a packet is received by the socket from a remote endpoint.
        /// </summary>
        /// <param name="ar">The <see cref="IAsyncResult" />.</param>
        private void OnReceive(IAsyncResult ar)
        {
            byte[]     packet   = null;
            IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
            int        cb;
            SipMessage message;

            recvPending = false;

            if (sock == null || recvBuf == null)
            {
                return;
            }

            try
            {
                cb = sock.EndReceiveFrom(ar, ref recvEP);
            }
            catch (Exception e)
            {
                // Log the exception if the socket appears to be open and then submit
                // another receive request.

                if (sock.IsOpen)
                {
                    SysLog.LogException(e);

                    try
                    {
                        recvEP = new IPEndPoint(IPAddress.Any, 0);
                        sock.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref recvEP, onRecv, null);
                    }
                    catch (Exception e2)
                    {
                        SysLog.LogException(e2, "SIP UDP transport is no longer able to receive packets.");
                    }
                }

                return;
            }

            // $todo(jeff.lill): This is where I need to add source filtering.

            // Make a copy of what we received before initiating the next packet receive.

            try
            {
                packet   = Helper.Extract(recvBuf, 0, cb);
                remoteEP = (IPEndPoint)recvEP;
            }
            catch (Exception e)
            {
                SysLog.LogException(e);
            }

            // Initiate the receive of the next message

            lock (syncLock)
            {
                if (sock == null || !sock.IsOpen)
                {
                    return;
                }

                try
                {
                    recvEP      = new IPEndPoint(IPAddress.Any, 0);
                    recvPending = true;
                    sock.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref recvEP, onRecv, null);
                }
                catch (Exception e)
                {
                    SysLog.LogException(e);
                }
            }

            // Parse and dispatch the message

            if (packet == null)
            {
                return;
            }

            // It looks like SIP clients like X-Lite send 4 byte CRLF CRLF messages
            // periodically over UDP to keep NAT mappings alive.  I'm going to
            // ignore these messages.

            if (packet.Length == 4)
            {
                return;
            }

            // Looks like we have a real message.

            try
            {
                try
                {
                    message = SipMessage.Parse(packet, true);
                }
                catch (Exception e)
                {
                    SipHelper.Trace(string.Format("UDP: UNPARSABLE message received  from {0}: [{1}]", remoteEP, e.Message), Helper.FromUTF8(packet));
                    throw;
                }

                message.SourceTransport = this;
                message.RemoteEndpoint  = remoteEP;
            }
            catch (SipException e)
            {
                e.BadPacket      = packet;
                e.SourceEndpoint = remoteEP;
                SysLog.LogException(e);
                return;
            }
            catch (Exception e)
            {
                SipException sipException;

                sipException                = new SipException("Error parsing SIP message.", e);
                sipException.Transport      = string.Format("UDP [{0}]", localEP);
                sipException.BadPacket      = packet;
                sipException.SourceEndpoint = remoteEP;

                SysLog.LogException(sipException);
                return;
            }

            if (disabled)
            {
                return;
            }

            if ((traceMode & SipTraceMode.Receive) != 0)
            {
                SipHelper.Trace(string.Format("UDP: received from {0}", remoteEP), message);
            }

            router.Route(this, message);
        }
Exemple #14
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);
        }
Exemple #15
0
        /// <summary>
        /// Adds a name/value pair to the collection, handling multi-value and
        /// special headers.
        /// </summary>
        /// <param name="name">The header name.</param>
        /// <param name="value">The header value.</param>
        /// <returns>
        /// The <see cref="SipHeader" /> instance that actually is actually present collection.
        /// </returns>
        public SipHeader Add(string name, string value)
        {
            SipHeader header;
            string    longForm;

            longForm = SipHelper.GetLongHeader(name);
            if (longForm != null)
            {
                hasCompactHeaders = true;
                name = longForm;
            }

            if (specialHeaders.ContainsKey(name))
            {
                if (this.ContainsKey(name))
                {
                    throw new NotImplementedException(string.Format("LillTek SIP stack does not support multiple instances of the header [{0}].", name));
                }

                this.Add(name, header = new SipHeader(name, value, true));
                return(header);
            }

            if (value.IndexOf(',') != -1)
            {
                // We have a multi-valued header.

                var values = value.Split(',');

                for (int i = 0; i < values.Length; i++)
                {
                    values[i] = values[i].Trim();
                }

                if (this.TryGetValue(name, out header))
                {
                    for (int i = 0; i < values.Length; i++)
                    {
                        header.Append(values[i]);
                    }

                    return(header);
                }
                else
                {
                    this.Add(name, header = new SipHeader(name, values));
                    return(header);
                }
            }
            else
            {
                // Single value header.

                if (this.TryGetValue(name, out header))
                {
                    header.Append(value);
                    return(header);
                }
                else
                {
                    this.Add(name, header = new SipHeader(name, value));
                    return(header);
                }
            }
        }
Exemple #16
0
            private void OnReceive(IAsyncResult ar)
            {
                List <SipMessage> received = null;

                try
                {
                    using (TimedLock.Lock(transport))
                    {
                        int       cb;
                        int       pos;
                        int       cbContents;
                        SipHeader contentLength;
                        byte[]    packet;

                        try
                        {
                            if (sock == null)
                            {
                                return;
                            }

                            if (contentBuf == null)
                            {
                                // We're reading a message envelope.

                                // Read packets into headerBuf until we can find the CRLFCRLF
                                // sequence marking the end of the message headers.

                                cb = sock.EndReceive(ar);
                                if (cb == 0)
                                {
                                    // The socket has been closed on by the remote element.

                                    CloseAndRemove();
                                    return;
                                }

                                cbRecv += cb;

tryAgain:

                                // Remove any leading CR or LF characters by shifting the
                                // buffer contents.  I know this isn't super efficient but
                                // we'll probably never actually see packets with this
                                // in the wild.

                                for (pos = 0; pos < cbRecv; pos++)
                                {
                                    if (headerBuf[pos] != 0x0D && headerBuf[pos] != 0x0A)
                                    {
                                        break;
                                    }
                                }

                                if (pos != 0)
                                {
                                    if (pos == cbRecv)
                                    {
                                        // No data remaining in the buffer

                                        cbRecv = 0;
                                        sock.BeginReceive(headerBuf, 0, headerBuf.Length, SocketFlags.None, onRecv, null);
                                        return;
                                    }

                                    Array.Copy(headerBuf, pos, headerBuf, 0, headerBuf.Length - pos);
                                    cbRecv -= pos;
                                }

                                // Scan the message for the CRLFCRLF sequence terminating the
                                // message envelope.

                                pos = Helper.IndexOf(headerBuf, CRLFCRLF, 0, cbRecv);
                                if (pos != -1)
                                {
                                    // We've got the message envelope

                                    pos += 4;   // Advance past the CRLFCRLF

                                    // Parse the message headers and then get the Content-Length header

                                    packet = Helper.Extract(headerBuf, 0, pos);

                                    try
                                    {
                                        message = SipMessage.Parse(packet, false);
                                    }
                                    catch (Exception e)
                                    {
                                        SipHelper.Trace(string.Format("TCP: UNPARSABLE message received  from {0}: [{1}]", remoteEP, e.Message), Helper.FromUTF8(packet));
                                        throw;
                                    }

                                    contentLength = message[SipHeader.ContentLength];

                                    if (contentLength == null || !int.TryParse(contentLength.Text, out cbContents) || cbContents < 0)
                                    {
                                        var e = new SipException("Malformed SIP message: Invalid or missing [Content-Length] header from streaming transport.");

                                        e.Transport      = "TCP";
                                        e.BadPacket      = packet;
                                        e.SourceEndpoint = remoteEP;
                                        throw e;
                                    }

                                    if (cbContents > MaxContentSize)
                                    {
                                        var e = new SipException("Invalid SIP message: [Content-Length={0}] exceeds [{1}].", cbContents, MaxContentSize);

                                        e.Transport      = "TCP";
                                        e.BadPacket      = packet;
                                        e.SourceEndpoint = remoteEP;
                                        throw e;
                                    }

                                    if (pos + cbContents <= cbRecv)
                                    {
                                        // We already have the message contents, so extract the contents,
                                        // add them to the message, and then queue the message for delivery
                                        // once we leave the lock.

                                        message.Contents = Helper.Extract(headerBuf, pos, cbContents);

                                        if (received == null)
                                        {
                                            received = new List <SipMessage>();
                                        }

                                        received.Add(message);
                                        message = null;

                                        // Shift any remaining data to the left in headerBuf,
                                        // adjust cbRecv, and the loop to look for another
                                        // message.

                                        pos += cbContents;
                                        cb   = cbRecv - pos; // Bytes remaining in the buffer

                                        if (cb == 0)
                                        {
                                            // No more data left in the buffer

                                            cbRecv = 0;
                                            sock.BeginReceive(headerBuf, 0, headerBuf.Length, SocketFlags.None, onRecv, null);
                                            return;
                                        }

                                        Array.Copy(headerBuf, pos, headerBuf, 0, cb);
                                        cbRecv = cb;
                                        goto tryAgain;
                                    }

                                    // We don't have all of the message contents, so allocate a buffer for
                                    // the contents, copy what we have already into this buffer, and then
                                    // initiate a receive operation to read the remaining data.

                                    contentBuf = new byte[cbContents];
                                    cbRecv     = cbRecv - pos; // Content bytes remaining in the buffer

                                    Array.Copy(headerBuf, pos, contentBuf, 0, cbRecv);
                                    sock.BeginReceiveAll(contentBuf, cbRecv, cbContents - cbRecv, SocketFlags.None, onRecv, null);
                                    return;
                                }

                                // Throw an error if the header buffer is full and we still haven't
                                // found the end of the envelope.

                                if (cbRecv >= headerBuf.Length)
                                {
                                    var e = new SipException("Malformed SIP message: Read [{0}] bytes and have not yet encountered end of headers.", headerBuf.Length);

                                    e.Transport      = "TCP";
                                    e.SourceEndpoint = remoteEP;
                                    throw e;
                                }

                                // Continue receiving header data.

                                sock.BeginReceive(headerBuf, cbRecv, headerBuf.Length - cbRecv, SocketFlags.None, onRecv, null);
                            }
                            else
                            {
                                // We're in the process of reading the message contents.
                                // Complete the contents receive operation and queue the
                                // message for delivery after we leave the lock.

                                sock.EndReceiveAll(ar);
                                message.Contents = contentBuf;

                                if (received == null)
                                {
                                    received = new List <SipMessage>();
                                }

                                received.Add(message);

                                // Reset and start reading the next message envelope.

                                message    = null;
                                contentBuf = null;
                                cbRecv     = 0;

                                sock.BeginReceive(headerBuf, 0, headerBuf.Length, SocketFlags.None, onRecv, null);
                            }
                        }
                        catch (SocketException)
                        {
                            CloseAndRemove();
                        }
                        catch (Exception e)
                        {
                            SysLog.LogException(e);
                            CloseAndRemove();
                        }
                    }
                }
                finally
                {
                    // Deliver any queued messages (outside of the lock)

                    if (received != null)
                    {
                        foreach (var message in received)
                        {
                            message.SourceTransport = transport;
                            message.RemoteEndpoint  = remoteEP;

                            if ((transport.traceMode & SipTraceMode.Receive) != 0)
                            {
                                SipHelper.Trace(string.Format("TCP: received from {0}", remoteEP), message);
                            }

                            transport.router.Route(transport, message);
                        }
                    }
                }
            }
Exemple #17
0
        /// <summary>
        /// Asynchronously transmits the message passed to the destination
        /// indicated by the <see paramref="remoteEP" /> parameter.
        /// </summary>
        /// <param name="remoteEP">The destination SIP endpoint's <see cref="NetworkBinding" />.</param>
        /// <param name="message">The <see cref="SipMessage" /> to be transmitted.</param>
        /// <remarks>
        /// Note that this method will go to some lengths to send the message
        /// down an existing connection to this endpoint.
        /// </remarks>
        /// <exception cref="SipTransportException">Thrown if the remote endpoint rejected the message or timed out.</exception>
        public void Send(NetworkBinding remoteEP, SipMessage message)
        {
            string         key = remoteEP.ToString();
            EnhancedSocket sock;
            Connection     con;

            using (TimedLock.Lock(this))
            {
                if (listener == null)
                {
                    throw new ObjectDisposedException("Transport is closed.");
                }

                if (disabled)
                {
                    return;
                }

                if ((traceMode & SipTraceMode.Send) != 0)
                {
                    SipHelper.Trace(string.Format("TCP: sending to {0}", remoteEP), message);
                }

                // Send the message down an existing connection to this
                // endpoint (if there is one).

                if (connections.TryGetValue(key, out con))
                {
                    con.Send(message);
                    return;
                }
            }

            // Otherwise establish a connection to the endpoint and transmit
            // the message.  Note that I'm establishing the connection outside
            // of the lock so processing on other connections can continue
            // while the connection is established.

            try
            {
                sock = new EnhancedSocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                sock.Connect(remoteEP);
            }
            catch (SocketException e)
            {
                switch ((SocketError)e.ErrorCode)
                {
                case SocketError.ConnectionAborted:
                case SocketError.ConnectionRefused:
                case SocketError.ConnectionReset:
                case SocketError.HostDown:
                case SocketError.HostNotFound:
                case SocketError.HostUnreachable:

                    throw new SipTransportException(SipTransportException.ErrorType.Rejected, e.Message, e);

                case SocketError.TimedOut:

                    throw new SipTransportException(SipTransportException.ErrorType.Timeout, e.Message, e);

                default:

                    throw;
                }
            }

            using (TimedLock.Lock(this))
            {
                if (listener == null)
                {
                    // Transport must have been closed while we were outside of the lock.

                    sock.ShutdownAndClose();
                    throw new ObjectDisposedException("Transport is closed.");
                }

                if (connections.TryGetValue(key, out con))
                {
                    // Another connection to this endpoint must have been established
                    // while we were outside of the lock.  Close the new socket and
                    // use the existing connection.

                    sock.ShutdownAndClose();

                    con.Send(message);
                    return;
                }

                // Add the new connection to the collection and then
                // send the message.

                con = new Connection(this, sock, remoteEP);
                connections.Add(key, con);
                con.Send(message);
            }
        }