/// <summary> /// Is called by SIP proxy when SIP proxy needs to get gateways for non-SIP URI. /// </summary> /// <param name="uriScheme">Non-SIP URI scheme which gateways to get.</param> /// <param name="userName">Authenticated user name.</param> /// <returns>Returns event data.</returns> protected SIP_GatewayEventArgs OnGetGateways(string uriScheme, string userName) { SIP_GatewayEventArgs e = new SIP_GatewayEventArgs(uriScheme, userName); if (GetGateways != null) { GetGateways(e); } return(e); }
/// <summary> /// Forwards specified request to target recipient. /// </summary> /// <param name="statefull">Specifies if request is sent statefully or statelessly.</param> /// <param name="e">Request event arguments.</param> /// <param name="request">SIP request to forward.</param> /// <param name="addRecordRoute">Specifies if Record-Route header filed is added.</param> internal void ForwardRequest(bool statefull, SIP_RequestReceivedEventArgs e, SIP_Request request, bool addRecordRoute) { List <SIP_ProxyTarget> targetSet = new List <SIP_ProxyTarget>(); List <NetworkCredential> credentials = new List <NetworkCredential>(); SIP_Uri route = null; /* RFC 3261 16. * 1. Validate the request (Section 16.3) * 1. Reasonable Syntax * 2. URI scheme * 3. Max-Forwards * 4. (Optional) Loop Detection * 5. Proxy-Require * 6. Proxy-Authorization * 2. Preprocess routing information (Section 16.4) * 3. Determine target(s) for the request (Section 16.5) * 4. Forward the request (Section 16.6) */ #region 1. Validate the request (Section 16.3) // 1.1 Reasonable Syntax. // SIP_Message will do it. // 1.2 URI scheme check. if (!SIP_Utils.IsSipOrSipsUri(request.RequestLine.Uri.ToString())) { // TODO: SIP_GatewayEventArgs eArgs = OnGetGateways("uriScheme", "userName"); // No suitable gateway or authenticated user has no access. if (eArgs.Gateways.Count == 0) { e.ServerTransaction.SendResponse( m_pStack.CreateResponse(SIP_ResponseCodes.x416_Unsupported_URI_Scheme, e.Request)); return; } } // 1.3 Max-Forwards. if (request.MaxForwards <= 0) { e.ServerTransaction.SendResponse(m_pStack.CreateResponse( SIP_ResponseCodes.x483_Too_Many_Hops, request)); return; } // 1.4 (Optional) Loop Detection. // Skip. // 1.5 Proxy-Require. // TODO: // 1.6 Proxy-Authorization. // We need to auth all foreign calls. if (!SIP_Utils.IsSipOrSipsUri(request.RequestLine.Uri.ToString()) || !OnIsLocalUri(((SIP_Uri)request.RequestLine.Uri).Host)) { // We need to pass-through ACK. if (request.RequestLine.Method == SIP_Methods.ACK) { } else if (!AuthenticateRequest(e)) { return; } } #endregion #region 2. Preprocess routing information (Section 16.4). /* * The proxy MUST inspect the Request-URI of the request. If the * Request-URI of the request contains a value this proxy previously * placed into a Record-Route header field (see Section 16.6 item 4), * the proxy MUST replace the Request-URI in the request with the last * value from the Route header field, and remove that value from the * Route header field. The proxy MUST then proceed as if it received * this modified request. * * If the first value in the Route header field indicates this proxy, * the proxy MUST remove that value from the request. */ // Strict route. if (SIP_Utils.IsSipOrSipsUri(request.RequestLine.Uri.ToString()) && IsLocalRoute(((SIP_Uri)request.RequestLine.Uri))) { request.RequestLine.Uri = request.Route.GetAllValues()[request.Route.GetAllValues().Length - 1].Address.Uri; SIP_t_AddressParam[] routes = request.Route.GetAllValues(); route = (SIP_Uri)routes[routes.Length - 1].Address.Uri; request.Route.RemoveLastValue(); } // Loose route. else if (request.Route.GetAllValues().Length > 0 && IsLocalRoute(SIP_Uri.Parse(request.Route.GetTopMostValue().Address.Uri.ToString()))) { route = (SIP_Uri)request.Route.GetTopMostValue().Address.Uri; request.Route.RemoveTopMostValue(); } #endregion #region 3. Determine target(s) for the request (Section 16.5) /* 3. Determine target(s) for the request (Section 16.5) * Next, the proxy calculates the target(s) of the request. The set of * targets will either be predetermined by the contents of the request * or will be obtained from an abstract location service. Each target * in the set is represented as a URI. * * If the domain of the Request-URI indicates a domain this element is * not responsible for, the Request-URI MUST be placed into the target * set as the only target, and the element MUST proceed to the task of * Request Forwarding (Section 16.6). * * If the target set for the request has not been predetermined as * described above, this implies that the element is responsible for the * domain in the Request-URI, and the element MAY use whatever mechanism * it desires to determine where to send the request. Any of these * mechanisms can be modeled as accessing an abstract Location Service. * This may consist of obtaining information from a location service * created by a SIP Registrar, reading a database, consulting a presence * server, utilizing other protocols, or simply performing an * algorithmic substitution on the Request-URI. When accessing the * location service constructed by a registrar, the Request-URI MUST * first be canonicalized as described in Section 10.3 before being used * as an index. The output of these mechanisms is used to construct the * target set. */ // Non-SIP // Foreign SIP // Local SIP // FIX ME: we may have tel: here SIP_Uri requestUri = (SIP_Uri)e.Request.RequestLine.Uri; // Proxy is not responsible for the domain in the Request-URI. if (!OnIsLocalUri(requestUri.Host)) { /* NAT traversal. * When we do record routing, store request sender flow info and request target flow info. * Now the tricky part, how proxy later which flow is target (because both sides can send requests). * Sender-flow will store from-tag to flow and target-flow will store flowID only (Because we don't know to-tag). * Later if request to-tag matches(incoming request), use that flow, otherwise(outgoing request) other flow. * * flowInfo: sender-flow "/" target-flow * sender-flow = from-tag ":" flowID * target-flow = flowID */ SIP_Flow targetFlow = null; string flowInfo = (route != null && route.Parameters["flowInfo"] != null) ? route.Parameters["flowInfo"].Value : null; if (flowInfo != null && request.To.Tag != null) { string flow1Tag = flowInfo.Substring(0, flowInfo.IndexOf(':')); string flow1ID = flowInfo.Substring(flowInfo.IndexOf(':') + 1, flowInfo.IndexOf('/') - flowInfo.IndexOf(':') - 1); string flow2ID = flowInfo.Substring(flowInfo.IndexOf('/') + 1); if (flow1Tag == request.To.Tag) { targetFlow = m_pStack.TransportLayer.GetFlow(flow1ID); } else { ; targetFlow = m_pStack.TransportLayer.GetFlow(flow2ID); } } targetSet.Add(new SIP_ProxyTarget(requestUri, targetFlow)); } // Proxy is responsible for the domain in the Request-URI. else { // TODO: tel: //SIP_Uri requestUri = SIP_Uri.Parse(e.Request.Uri); // Try to get AOR from registrar. SIP_Registration registration = m_pRegistrar.GetRegistration(requestUri.Address); // We have AOR specified in request-URI in registrar server. if (registration != null) { // Add all AOR SIP contacts to target set. foreach (SIP_RegistrationBinding binding in registration.Bindings) { if (binding.ContactURI is SIP_Uri && binding.TTL > 0) { targetSet.Add(new SIP_ProxyTarget((SIP_Uri)binding.ContactURI, binding.Flow)); } } } // We don't have AOR specified in request-URI in registrar server. else { // If the Request-URI indicates a resource at this proxy that does not // exist, the proxy MUST return a 404 (Not Found) response. if (!OnAddressExists(requestUri.Address)) { e.ServerTransaction.SendResponse( m_pStack.CreateResponse(SIP_ResponseCodes.x404_Not_Found, e.Request)); return; } } } // If the target set remains empty after applying all of the above, the proxy MUST return an error response, // which SHOULD be the 480 (Temporarily Unavailable) response. if (targetSet.Count == 0) { e.ServerTransaction.SendResponse( m_pStack.CreateResponse(SIP_ResponseCodes.x480_Temporarily_Unavailable, e.Request)); return; } #endregion #region 4. Forward the request (Section 16.6) #region Statefull if (statefull) { // Create proxy context that will be responsible for forwarding request. SIP_ProxyContext proxyContext = new SIP_ProxyContext(this, e.ServerTransaction, request, addRecordRoute, m_ForkingMode, (ProxyMode & SIP_ProxyMode.B2BUA) != 0, false, false, targetSet.ToArray(), credentials.ToArray()); m_pProxyContexts.Add(proxyContext); proxyContext.Start(); } #endregion #region Stateless else { /* RFC 3261 16.6 Request Forwarding. * For each target, the proxy forwards the request following these steps: * 1. Make a copy of the received request * 2. Update the Request-URI * 3. Update the Max-Forwards header field * 4. Optionally add a Record-route header field value * 5. Optionally add additional header fields * 6. Postprocess routing information * 7. Determine the next-hop address, port, and transport * 8. Add a Via header field value * 9. Add a Content-Length header field if necessary * 10. Forward the new request */ /* RFC 3261 16.11 Stateless Proxy. * o A stateless proxy MUST choose one and only one target from the target set. This choice * MUST only rely on fields in the message and time-invariant properties of the server. In * particular, a retransmitted request MUST be forwarded to the same destination each time * it is processed. Furthermore, CANCEL and non-Routed ACK requests MUST generate the same * choice as their associated INVITE. * * However, a stateless proxy cannot simply use a random number generator to compute * the first component of the branch ID, as described in Section 16.6 bullet 8. * This is because retransmissions of a request need to have the same value, and * a stateless proxy cannot tell a retransmission from the original request. * * We just use: "z9hG4bK-" + md5(topmost branch) */ bool isStrictRoute = false; SIP_Hop[] hops = null; #region 1. Make a copy of the received request SIP_Request forwardRequest = request.Copy(); #endregion #region 2. Update the Request-URI forwardRequest.RequestLine.Uri = targetSet[0].TargetUri; #endregion #region 3. Update the Max-Forwards header field forwardRequest.MaxForwards--; #endregion #region 4. Optionally add a Record-route header field value #endregion #region 5. Optionally add additional header fields #endregion #region 6. Postprocess routing information /* 6. Postprocess routing information. * * If the copy contains a Route header field, the proxy MUST inspect the URI in its first value. * If that URI does not contain an lr parameter, the proxy MUST modify the copy as follows: * - The proxy MUST place the Request-URI into the Route header * field as the last value. * * - The proxy MUST then place the first Route header field value * into the Request-URI and remove that value from the Route header field. */ if (forwardRequest.Route.GetAllValues().Length > 0 && !forwardRequest.Route.GetTopMostValue().Parameters.Contains("lr")) { forwardRequest.Route.Add(forwardRequest.RequestLine.Uri.ToString()); forwardRequest.RequestLine.Uri = SIP_Utils.UriToRequestUri(forwardRequest.Route.GetTopMostValue().Address.Uri); forwardRequest.Route.RemoveTopMostValue(); isStrictRoute = true; } #endregion #region 7. Determine the next-hop address, port, and transport /* 7. Determine the next-hop address, port, and transport. * The proxy MAY have a local policy to send the request to a * specific IP address, port, and transport, independent of the * values of the Route and Request-URI. Such a policy MUST NOT be * used if the proxy is not certain that the IP address, port, and * transport correspond to a server that is a loose router. * However, this mechanism for sending the request through a * specific next hop is NOT RECOMMENDED; instead a Route header * field should be used for that purpose as described above. * * In the absence of such an overriding mechanism, the proxy * applies the procedures listed in [4] as follows to determine * where to send the request. If the proxy has reformatted the * request to send to a strict-routing element as described in * step 6 above, the proxy MUST apply those procedures to the * Request-URI of the request. Otherwise, the proxy MUST apply * the procedures to the first value in the Route header field, if * present, else the Request-URI. The procedures will produce an * ordered set of (address, port, transport) tuples. * Independently of which URI is being used as input to the * procedures of [4], if the Request-URI specifies a SIPS * resource, the proxy MUST follow the procedures of [4] as if the * input URI were a SIPS URI. * * As described in [4], the proxy MUST attempt to deliver the * message to the first tuple in that set, and proceed through the * set in order until the delivery attempt succeeds. * * For each tuple attempted, the proxy MUST format the message as * appropriate for the tuple and send the request using a new * client transaction as detailed in steps 8 through 10. * * Since each attempt uses a new client transaction, it represents * a new branch. Thus, the branch parameter provided with the Via * header field inserted in step 8 MUST be different for each * attempt. * * If the client transaction reports failure to send the request * or a timeout from its state machine, the proxy continues to the * next address in that ordered set. If the ordered set is * exhausted, the request cannot be forwarded to this element in * the target set. The proxy does not need to place anything in * the response context, but otherwise acts as if this element of * the target set returned a 408 (Request Timeout) final response. */ SIP_Uri uri = null; if (isStrictRoute) { uri = (SIP_Uri)forwardRequest.RequestLine.Uri; } else if (forwardRequest.Route.GetTopMostValue() != null) { uri = (SIP_Uri)forwardRequest.Route.GetTopMostValue().Address.Uri; } else { uri = (SIP_Uri)forwardRequest.RequestLine.Uri; } hops = m_pStack.GetHops(uri, forwardRequest.ToByteData().Length, ((SIP_Uri)forwardRequest.RequestLine.Uri).IsSecure); if (hops.Length == 0) { if (forwardRequest.RequestLine.Method != SIP_Methods.ACK) { e.ServerTransaction.SendResponse( m_pStack.CreateResponse( SIP_ResponseCodes.x503_Service_Unavailable + ": No hop(s) for target.", forwardRequest)); } return; } #endregion #region 8. Add a Via header field value forwardRequest.Via.AddToTop( "SIP/2.0/transport-tl-addign sentBy-tl-assign-it;branch=z9hG4bK-" + Core.ComputeMd5(request.Via.GetTopMostValue().Branch, true)); // Add 'flowID' what received request, you should use the same flow to send response back. // For more info see RFC 3261 18.2.2. forwardRequest.Via.GetTopMostValue().Parameters.Add("flowID", request.Flow.ID); #endregion #region 9. Add a Content-Length header field if necessary // Skip, our SIP_Message class is smart and do it when ever it's needed. #endregion #region 10. Forward the new request try { try { if (targetSet[0].Flow != null) { m_pStack.TransportLayer.SendRequest(targetSet[0].Flow, request); return; } } catch { m_pStack.TransportLayer.SendRequest(request, null, hops[0]); } } catch (SIP_TransportException x) { string dummy = x.Message; if (forwardRequest.RequestLine.Method != SIP_Methods.ACK) { /* RFC 3261 16.9 Handling Transport Errors * If the transport layer notifies a proxy of an error when it tries to * forward a request (see Section 18.4), the proxy MUST behave as if the * forwarded request received a 503 (Service Unavailable) response. */ e.ServerTransaction.SendResponse( m_pStack.CreateResponse( SIP_ResponseCodes.x503_Service_Unavailable + ": Transport error.", forwardRequest)); } } #endregion } #endregion #endregion }
/// <summary> /// Is called by SIP proxy when SIP proxy needs to get gateways for non-SIP URI. /// </summary> /// <param name="uriScheme">Non-SIP URI scheme which gateways to get.</param> /// <param name="userName">Authenticated user name.</param> /// <returns>Returns event data.</returns> protected SIP_GatewayEventArgs OnGetGateways(string uriScheme, string userName) { SIP_GatewayEventArgs e = new SIP_GatewayEventArgs(uriScheme, userName); if (GetGateways != null) { GetGateways(e); } return e; }