コード例 #1
0
        /// <summary>
        /// Parse a message value.
        /// </summary>
        /// <param name="name">Name of header being parsed.</param>
        /// <param name="reader">Reader containing the string that should be parsed.</param>
        /// <returns>Newly created header.</returns>
        /// <exception cref="ParseException">Header value is malformed.</exception>
        public IHeader Parse(string name, ITextReader reader)
        {
            ViaEntry entry = ParseEntry(reader);

            if (reader.Current != ',')
            {
                return new Via {
                           entry
                }
            }
            ;

            var via = new Via {
                entry
            };

            while (reader.Current == ',')
            {
                reader.ConsumeWhiteSpaces(',');

                entry = ParseEntry(reader);
                via.Add(entry);
            }
            return(via);
        }

        #endregion
    }
コード例 #2
0
        private void OnRequest(object sender, RequestEventArgs e)
        {
            /* RFC 3581 4.
             *  When a server compliant to this specification (which can be a proxy
             *  or UAS) receives a request, it examines the topmost Via header field
             *  value.  If this Via header field value contains an "rport" parameter
             *  with no value, it MUST set the value of the parameter to the source
             *  port of the request.  This is analogous to the way in which a server
             *  will insert the "received" parameter into the topmost Via header
             *  field value.  In fact, the server MUST insert a "received" parameter
             *  containing the source IP address that the request came from, even if
             *  it is identical to the value of the "sent-by" component.  Note that
             *  this processing takes place independent of the transport protocol.
             */

            ViaEntry via = e.Request.Via.First;
            var      ep  = e.RemoteEndPoint as IPEndPoint;

            if (ep != null)
            {
                via.Received = ep.Address.ToString();
                if (via.RportWanted)
                {
                    via.Rport = ep.Port;
                }
            }


            /* RFC 3261 18.2.1.
             *  When the server transport receives a request over any transport, it
             *  MUST examine the value of the "sent-by" parameter in the top Via
             *  header field value.  If the host portion of the "sent-by" parameter
             *  contains a domain name, or if it contains an IP address that differs
             *  from the packet source address, the server MUST add a "received"
             *  parameter to that Via header field value.  This parameter MUST
             *  contain the source address from which the packet was received.  This
             *  is to assist the server transport layer in sending the response,
             *  since it must be sent to the source IP address from which the request
             *  came.
             *
             *  Next, the server transport attempts to match the request to a server
             *  transaction.  It does so using the matching rules described in
             *  Section 17.2.3.  If a matching server transaction is found, the
             *  request is passed to that transaction for processing.  If no match is
             *  found, the request is passed to the core, which may decide to
             *  construct a new server transaction for that request.  Note that when
             *  a UAS core sends a 2xx response to INVITE, the server transaction is
             *  destroyed.  This means that when the ACK arrives, there will be no
             *  matching server transaction, and based on this rule, the ACK is
             *  passed to the UAS core, where it is processed.
             */
            //_logger.Debug(GetMessage(e.Request));
            RequestReceived(this, e);
        }
コード例 #3
0
        public static ViaEntry ParseEntry(ITextReader reader)
        {
            //SIP/2.0/UDP erlang.bell-telephone.com:5060;branch=z9hG4bK87asdks7
            var entry = new ViaEntry();

            // read SIP/
            reader.ConsumeWhiteSpaces();
            entry.Protocol = reader.ReadUntil("/ \t");
            if (entry.Protocol == null)
            {
                throw new FormatException("Expected Via header to start with 'SIP' or 'SIPS'.");
            }
            reader.ConsumeWhiteSpaces('/');

            // read 2.0/
            entry.SipVersion = reader.ReadUntil("/ \t");
            if (entry.SipVersion == null)
            {
                throw new FormatException("Expected to find sip version in Via header.");
            }
            reader.ConsumeWhiteSpaces('/');

            // read UDP or TCP
            entry.Transport = reader.ReadWord();
            if (entry.Transport == null)
            {
                throw new FormatException("Expected to find transport protocol after sip version in Via header.");
            }
            reader.ConsumeWhiteSpaces();

            entry.Domain = reader.ReadUntil(";: \t");
            if (entry.Domain == null)
            {
                throw new FormatException("Failed to find domain in via header.");
            }
            reader.ConsumeWhiteSpaces();

            if (reader.Current == ':')
            {
                reader.Read();
                reader.ConsumeWhiteSpaces();
                string temp = reader.ReadToEnd("; \t");
                reader.ConsumeWhiteSpaces();
                int port;
                if (!int.TryParse(temp, out port))
                {
                    throw new FormatException("Invalid port specified.");
                }
                entry.Port = port;
            }

            UriParser.ParseParameters(entry.Parameters, reader);
            string rport = entry.Parameters["rport"];

            if (!string.IsNullOrEmpty(rport)) //parameter can exist, but be empty. = rport requested.
            {
                int value;
                if (!int.TryParse(rport, out value))
                {
                    throw new FormatException("RPORT is not a number.");
                }
                entry.Rport = value;
            }

            return(entry);
        }
コード例 #4
0
        /// <summary>
        /// Create a new request
        /// </summary>
        /// <param name="method">Sip method.</param>
        /// <param name="from">Who is dialing?</param>
        /// <param name="to">Destination</param>
        /// <returns>Request object.</returns>
        /// <seealso cref="SipMethod"/>
        public IRequest CreateRequest(string method, Contact from, Contact to)
        {
            // A valid SIP request formulated by a UAC MUST, at a minimum, contain
            // the following header fields: To, From, CSeq, Call-ID, Max-Forwards,
            // and Via; all of these header fields are mandatory in all SIP
            // requests.  These six header fields are the fundamental building
            // blocks of a SIP message, as they jointly provide for most of the
            // critical message routing services including the addressing of
            // messages, the routing of responses, limiting message propagation,
            // ordering of messages, and the unique identification of transactions.
            // These header fields are in addition to the mandatory request line,
            // which contains the method, Request-URI, and SIP version.

            // The initial Request-URI of the message SHOULD be set to the value of
            // the URI in the To field.  One notable exception is the REGISTER
            // method; behavior for setting the Request-URI of REGISTER is given in
            // Section 10.
            var request = new Request(method, from.Uri, "SIP/2.0");

            request.From = from;

            // A request outside of a dialog MUST NOT contain a To tag; the tag in
            // the To field of a request identifies the peer of the dialog.  Since
            // no dialog is established, no tag is present.
            // For further information on the To header field, see RFC 3261 Section 20.39.
            request.To = to;


            // The Call-ID header field acts as a unique identifier to group
            // together a series of messages.  It MUST be the same for all requests
            // and responses sent by either UA in a dialog.  It SHOULD be the same
            // in each registration from a UA.
            // In a new request created by a UAC outside of any dialog, the Call-ID
            // header field MUST be selected by the UAC as a globally unique
            // identifier over space and time unless overridden by method-specific
            // behavior.  All SIP UAs must have a means to guarantee that the Call-
            // ID header fields they produce will not be inadvertently generated by
            // any other UA.
            request.CallId = CreateCallId();

            // The CSeq header field serves as a way to identify and order
            // transactions.  It consists of a sequence number and a method.  The
            // method MUST match that of the request.  For non-REGISTER requests
            // outside of a dialog, the sequence number value is arbitrary.  The
            // sequence number value MUST be expressible as a 32-bit unsigned
            // integer and MUST be less than 2**31.  As long as it follows the above
            // guidelines, a client may use any mechanism it would like to select
            // CSeq header field values.
            request.CSeq = new CSeq(GetNextSequenceNumber(), method);


            request.MaxForwards = 70;


            // When the UAC creates a request, it MUST insert a Via into that
            // request.  The protocol name and protocol version in the header field
            // MUST be SIP and 2.0, respectively.  The Via header field value MUST
            // contain a branch parameter.  This parameter is used to identify the
            // transaction created by that request.  This parameter is used by both
            // the client and the server.
            //
            // The branch parameter value MUST be unique across space and time for
            // all requests sent by the UA.  The exceptions to this rule are CANCEL
            // and ACK for non-2xx responses.  As discussed below, a CANCEL request
            // will have the same value of the branch parameter as the request it
            // cancels.  As discussed in Section 17.1.1.3, an ACK for a non-2xx
            // response will also have the same branch ID as the INVITE whose
            // response it acknowledges.
            //
            //    The uniqueness property of the branch ID parameter, to facilitate
            //    its use as a transaction ID, was not part of RFC 2543.
            //
            // The branch ID inserted by an element compliant with this
            // specification MUST always begin with the characters "z9hG4bK".  These
            // 7 characters are used as a magic cookie (7 is deemed sufficient to
            // ensure that an older RFC 2543 implementation would not pick such a
            // value), so that servers receiving the request can determine
            // specification (that is, globally unique).  Beyond this requirement,
            // the precise format of the branch token is implementation-defined.
            //
            // The Via header maddr, ttl, and sent-by components will be set when
            // the request is processed by the transport layer (Section 18).
            //
            // Via processing for proxies is described in Section 16.6 Item 8 and
            // Section 16.7 Item 3.
            var viaEntry = new ViaEntry(_endPoint.Address.ToString(), CreateBranch());

            viaEntry.RportWanted = true;
            request.Via.Add(viaEntry);

            // The Contact header field provides a SIP or SIPS URI that can be used
            // to contact that specific instance of the UA for subsequent requests.
            // The Contact header field MUST be present and contain exactly one SIP
            // or SIPS URI in any request that can result in the establishment of a
            // dialog.  For the methods defined in this specification, that includes
            // only the INVITE request.  For these requests, the scope of the
            // Contact is global.  That is, the Contact header field value contains
            // the URI at which the UA would like to receive requests, and this URI
            // MUST be valid even if used in subsequent requests outside of any
            // dialogs.
            //
            // If the Request-URI or top Route header field value contains a SIPS
            // URI, the Contact header field MUST contain a SIPS URI as well.
            request.Contact = _contact;

            return(request);
        }
コード例 #5
0
        public void Send(IResponse response)
        {
            /*
             * The server transport uses the value of the top Via header field in
             * order to determine where to send a response.  It MUST follow the
             * following process:
             *
             *    o  If the "sent-protocol" is a reliable transport protocol such as
             *       TCP or SCTP, or TLS over those, the response MUST be sent using
             *       the existing connection to the source of the original request
             *       that created the transaction, if that connection is still open.
             *       This requires the server transport to maintain an association
             *       between server transactions and transport connections.  If that
             *       connection is no longer open, the server SHOULD open a
             *       connection to the IP address in the "received" parameter, if
             *       present, using the port in the "sent-by" value, or the default
             *       port for that transport, if no port is specified.  If that
             *       connection attempt fails, the server SHOULD use the procedures
             *       in [4] for servers in order to determine the IP address and
             *       port to open the connection and send the response to.
             *
             *    o  Otherwise, if the Via header field value contains a "maddr"
             *       parameter, the response MUST be forwarded to the address listed
             *       there, using the port indicated in "sent-by", or port 5060 if
             *       none is present.  If the address is a multicast address, the
             *       response SHOULD be sent using the TTL indicated in the "ttl"
             *       parameter, or with a TTL of 1 if that parameter is not present.
             *
             *    o  Otherwise (for unreliable unicast transports), if the top Via
             *       has a "received" parameter, the response MUST be sent to the
             *       address in the "received" parameter, using the port indicated
             *       in the "sent-by" value, or using port 5060 if none is specified
             *       explicitly.  If this fails, for example, elicits an ICMP "port
             *       unreachable" response, the procedures of Section 5 of [4]
             *       SHOULD be used to determine where to send the response.
             */

            ViaEntry via = response.Via.First;

            if (string.IsNullOrEmpty(via.Received))
            {
                _logger.Warning("Received was not specified in " + response);
                return;
            }

            string targetDomain;
            int    port = 5060;

            if (!string.IsNullOrEmpty(via.SentBy))
            {
                targetDomain = via.SentBy;
                int index = via.SentBy.IndexOf(':');
                if (index > 0 && index < via.SentBy.Length - 1)
                {
                    string temp = via.SentBy.Substring(index + 1);
                    if (!int.TryParse(temp, out port))
                    {
                        port = 5060;
                    }
                }
            }
            else
            {
                targetDomain = via.Received;
            }

            // RFC3841
            if (via.Rport > 0)
            {
                port = via.Rport;
            }

            IPHostEntry entry = Dns.GetHostEntry(targetDomain);

            if (entry.AddressList.Length == 0)
            {
                _logger.Warning("Failed to find host entry for: " + via.Received);
                return;
            }
            EndPoint ep = new IPEndPoint(entry.AddressList[0], port);


            byte[] buffer = _buffers.Dequeue();
            int    count  = _serializer.Serialize(response, buffer);

            _transports[via.Transport].Send(ep, buffer, 0, count);
        }