/// <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 }
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); }
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); }
/// <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); }
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); }