Esempio n. 1
0
        /// <summary>
        /// Compares the <b>CSeq</b> header in this message with the message
        /// passed.
        /// </summary>
        /// <param name="message">The message to be compared.</param>
        /// <returns><c>true</c> if both headers have <b>CSeq</b> headers and their values match.</returns>
        public bool MatchCSeq(SipMessage message)
        {
            var v1 = this.GetHeader <SipCSeqValue>(SipHeader.CSeq);
            var v2 = message.GetHeader <SipCSeqValue>(SipHeader.CSeq);

            if (v1 == null || v2 == null)
            {
                return(false);
            }

            return(v1.Number == v2.Number && v1.Method == v2.Method);
        }
Esempio n. 2
0
        /// <summary>
        /// Generates a diagnostic trace of the message passed.
        /// </summary>
        /// <param name="title">The trace title.</param>
        /// <param name="message">The message.</param>
        public static void Trace(string title, SipMessage message)
        {
            StringBuilder sb = new StringBuilder(1024);
            SipRequest    request;
            SipResponse   response;
            string        summary;
            string        contentType;
            SipCSeqValue  vCSeq;
            string        cseqMethod;
            string        cseqNumber;

            vCSeq = message.GetHeader <SipCSeqValue>(SipHeader.CSeq);
            if (vCSeq == null)
            {
                cseqMethod = "????";
                cseqNumber = "CSeq:????";
            }
            else
            {
                cseqMethod = vCSeq.Method;
                cseqNumber = "CSeq:" + vCSeq.Number.ToString();
            }

            request = message as SipRequest;
            if (request != null)
            {
                summary = string.Format("{0} Request: {1} {2}", request.MethodText, request.Uri, cseqNumber);
            }
            else
            {
                response = (SipResponse)message;
                summary  = string.Format("{0} Response: {1} ({2}) {3}", cseqMethod, response.StatusCode, response.ReasonPhrase, cseqNumber);
            }

            sb.AppendLine(title);
            sb.AppendLine();
            sb.Append(message.ToString());

            contentType = message.GetHeaderText(SipHeader.ContentType);
            if (message.ContentLength > 0 && message.HasContentType(SipHelper.SdpMimeType))
            {
                sb.AppendLine(Helper.FromUTF8(message.Contents));
            }
            else
            {
                sb.AppendLine(Helper.HexDump(message.Contents, 16, HexDumpOption.ShowAll));
            }

            NetTrace.Write(SipHelper.TraceSubsystem, 0, "SIP: " + title, summary, sb.ToString());
        }
Esempio n. 3
0
        //---------------------------------------------------------------------
        // ISipMessageRouter implementation

        /// <summary>
        /// Routes a <see cref="SipMessage" /> received by an <see cref="ISipTransport" /> to the <see cref="ISipAgent" />
        /// instance that needs to handle it.
        /// </summary>
        /// <param name="transport">The <see cref="ISipTransport" /> that received the message.</param>
        /// <param name="message">The <see cref="SipMessage" /> received by the transport.</param>
        public void Route(ISipTransport transport, SipMessage message)
        {
            // Routing is easy:
            //
            //      Servers get the requests
            //      Clients get the responses.

            if (message is SipRequest)
            {
                serverAgent.OnReceive(transport, message);
            }
            else
            {
                clientAgent.OnReceive(transport, message);
            }
        }
Esempio n. 4
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;
                }
            }
        }
Esempio n. 5
0
        /// <summary>
        /// Performs a deep copy of the internal properties of the message passed
        /// to this instance.
        /// </summary>
        /// <param name="message">The message to be copied.</param>
        internal void CopyFrom(SipMessage message)
        {
            this.isRequest         = message.isRequest;
            this.sipVersion        = message.sipVersion;
            this.sourceTransaction = null;
            this.sourceTransport   = message.sourceTransport;
            this.remoteEP          = message.remoteEP;

            this.contents = null;
            if (message.contents != null)
            {
                this.contents = new byte[message.contents.Length];
                Array.Copy(message.contents, this.contents, message.contents.Length);
            }

            foreach (SipHeader header in message.headers.Values)
            {
                this.headers.Add(header.Name, header.Clone());
            }
        }
Esempio n. 6
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);
            }
        }
Esempio n. 7
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);
                        }
                    }
                }
            }
Esempio n. 8
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);
        }