/// <summary> /// Raises <b>AorUpdated</b> event. /// </summary> /// <param name="registration">SIP registration.</param> private void OnAorUpdated(SIP_Registration registration) { if (this.AorUpdated != null) { this.AorUpdated(this, new SIP_RegistrationEventArgs(registration)); } }
/// <summary> /// Default constructor. /// </summary> /// <param name="registration">SIP reggistration.</param> /// <exception cref="ArgumentNullException">Is raised when <b>registration</b> is null reference.</exception> public SIP_RegistrationEventArgs(SIP_Registration registration) { if (registration == null) { throw new ArgumentNullException("registration"); } m_pRegistration = registration; }
/// <summary> /// Adds specified registration to collection. /// </summary> /// <param name="registration">Registration to add.</param> public void Add(SIP_Registration registration) { lock (m_pRegistrations){ if (Contains(registration.AOR)) { throw new ArgumentException("Registration with specified registration name already exists !"); } m_pRegistrations.Add(registration); } }
/// <summary> /// Add or updates specified SIP registration info. /// </summary> /// <param name="aor">Registration address of record.</param> /// <param name="contacts">Registration address of record contacts to update.</param> /// <param name="flow">SIP proxy local data flow what accpeted this contact registration.</param> public void SetRegistration(string aor, SIP_t_ContactParam[] contacts, SIP_Flow flow) { lock (m_pRegistrations){ SIP_Registration registration = m_pRegistrations[aor]; if (registration == null) { registration = new SIP_Registration("system", aor); m_pRegistrations.Add(registration); OnAorRegistered(registration); } registration.AddOrUpdateBindings(flow, "", 1, contacts); } }
/// <summary> /// Default constructor. /// </summary> /// <param name="owner">Owner registration.</param> /// <param name="contactUri">Contact URI what can be used to contact the registration.</param> /// <exception cref="ArgumentNullException">Is raised when <b>owner</b> or <b>contactUri</b> is null reference.</exception> internal SIP_RegistrationBinding(SIP_Registration owner, AbsoluteUri contactUri) { if (owner == null) { throw new ArgumentNullException("owner"); } if (contactUri == null) { throw new ArgumentNullException("contactUri"); } m_pRegistration = owner; m_ContactURI = contactUri; }
/// <summary> /// Removes all expired registrations from the collection. /// </summary> public void RemoveExpired() { lock (m_pRegistrations){ for (int i = 0; i < m_pRegistrations.Count; i++) { SIP_Registration registration = m_pRegistrations[i]; // Force registration to remove all its expired contacts. registration.RemoveExpiredBindings(); // No bindings left, so we need to remove that registration. if (registration.Bindings.Length == 0) { m_pRegistrations.Remove(registration); i--; } } } }
/// <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="addRecordRoute">If true Record-Route header field is added.</param> internal void ForwardRequest(bool statefull, SIP_RequestReceivedEventArgs e, bool addRecordRoute) { SIP_RequestContext requestContext = new SIP_RequestContext(this, e.Request, e.Flow); SIP_Request request = e.Request; SIP_Uri route = null; /* RFC 3261 16. * 1. Validate the request (Section 16.3) * 1. Reasonable Syntax * 2. URI scheme (NOTE: We do it later) * 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) * x. Custom handling (non-RFC) * 1. Process custom request handlers. * 2. URI scheme * 4. Forward the request (Section 16.6) */ #region 1. Validate the request (Section 16.3) // 1.1 Reasonable Syntax. // SIP_Message parsing have done it. // 1.2 URI scheme check. // We do it later. // 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. /* If an element requires credentials before forwarding a request, * the request MUST be inspected as described in Section 22.3. That * section also defines what the element must do if the inspection * fails. */ // We need to auth all foreign calls. if (!SIP_Utils.IsSipOrSipsUri(request.RequestLine.Uri.ToString()) || !this.OnIsLocalUri(((SIP_Uri)request.RequestLine.Uri).Host)) { // If To: field is registrar AOR and request-URI is local registration contact, skip authentication. bool skipAuth = false; if (request.To.Address.IsSipOrSipsUri) { SIP_Registration registration = m_pRegistrar.GetRegistration(((SIP_Uri)request.To.Address.Uri).Address); if (registration != null) { if (registration.GetBinding(request.RequestLine.Uri) != null) { skipAuth = true; } } } if (!skipAuth) { string userName = null; // We need to pass-through ACK. if (request.RequestLine.Method == SIP_Methods.ACK) { } else if (!AuthenticateRequest(e, out userName)) { return; } requestContext.SetUser(userName); } } #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. * * This will only happen when the element sending the request to the * proxy (which may have been an endpoint) is a strict router. This * rewrite on receive is necessary to enable backwards compatibility * with those elements. It also allows elements following this * specification to preserve the Request-URI through strict-routing * proxies (see Section 12.2.1.1). * * This requirement does not obligate a proxy to keep state in order * to detect URIs it previously placed in Record-Route header fields. * Instead, a proxy need only place enough information in those URIs * to recognize them as values it provided when they later appear. * * If the Request-URI contains a maddr parameter, the proxy MUST check * to see if its value is in the set of addresses or domains the proxy * is configured to be responsible for. If the Request-URI has a maddr * parameter with a value the proxy is responsible for, and the request * was received using the port and transport indicated (explicitly or by * default) in the Request-URI, the proxy MUST strip the maddr and any * non-default port or transport parameter and continue processing as if * those values had not been present in the request. * * A request may arrive with a maddr matching the proxy, but on a * port or transport different from that indicated in the URI. Such * a request needs to be forwarded to the proxy using the indicated * port and transport. * * If the first value in the Route header field indicates this proxy, * the proxy MUST remove that value from the request. */ // Strict route - handle it. if ((request.RequestLine.Uri is SIP_Uri) && IsRecordRoute(((SIP_Uri)request.RequestLine.Uri)) && request.Route.GetAllValues().Length > 0) { 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(); } // Check if Route header field indicates this proxy. if (request.Route.GetAllValues().Length > 0) { route = (SIP_Uri)request.Route.GetTopMostValue().Address.Uri; // We consider loose-route always ours, because otherwise this message never reach here. if (route.Param_Lr) { request.Route.RemoveTopMostValue(); } // It's our route, remove it. else if (IsLocalRoute(route)) { request.Route.RemoveTopMostValue(); } } #endregion #region REGISTER request processing if (e.Request.RequestLine.Method == SIP_Methods.REGISTER) { SIP_Uri requestUri = (SIP_Uri)e.Request.RequestLine.Uri; // REGISTER is meant for us. if (this.OnIsLocalUri(requestUri.Host)) { if ((m_ProxyMode & SIP_ProxyMode.Registrar) != 0) { m_pRegistrar.Register(e); return; } else { e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x405_Method_Not_Allowed, e.Request)); return; } } // Forward REGISTER. // else{ } #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 if (e.Request.RequestLine.Uri is SIP_Uri) { SIP_Uri requestUri = (SIP_Uri)e.Request.RequestLine.Uri; // Proxy is not responsible for the domain in the Request-URI. if (!this.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); } } requestContext.Targets.Add(new SIP_ProxyTarget(requestUri, targetFlow)); } // Proxy is responsible for the domain in the Request-URI. else { // 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) { requestContext.Targets.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 (!this.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 (requestContext.Targets.Count == 0) { e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x480_Temporarily_Unavailable, e.Request)); return; } } } #endregion #region x. Custom handling // x.1 Process custom request handlers, if any. foreach (SIP_ProxyHandler handler in this.Handlers) { try{ SIP_ProxyHandler h = handler; // Reusing existing handler not allowed, create new instance of handler. if (!handler.IsReusable) { h = (SIP_ProxyHandler)System.Activator.CreateInstance(handler.GetType()); } if (h.ProcessRequest(requestContext)) { // Handler processed request, we are done. return; } } catch (Exception x) { m_pStack.OnError(x); } } // x.2 URI scheme. // If no targets and request-URI non-SIP, reject request because custom handlers should have been handled it. if (requestContext.Targets.Count == 0 && !SIP_Utils.IsSipOrSipsUri(request.RequestLine.Uri.ToString())) { e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x416_Unsupported_URI_Scheme, e.Request)); return; } #endregion #region 4. Forward the request (Section 16.6) #region Statefull if (statefull) { SIP_ProxyContext proxyContext = this.CreateProxyContext(requestContext, e.ServerTransaction, request, addRecordRoute); 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 = requestContext.Targets[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-" + Net_Utils.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 (requestContext.Targets[0].Flow != null) { m_pStack.TransportLayer.SendRequest(requestContext.Targets[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> /// Handles REGISTER method. /// </summary> /// <param name="e">Request event arguments.</param> internal void Register(SIP_RequestReceivedEventArgs e) { /* RFC 3261 10.3 Processing REGISTER Requests. * 1. The registrar inspects the Request-URI to determine whether it * has access to bindings for the domain identified in the * Request-URI. If not, and if the server also acts as a proxy * server, the server SHOULD forward the request to the addressed * domain, following the general behavior for proxying messages * described in Section 16. * * 2. To guarantee that the registrar supports any necessary extensions, * the registrar MUST process the Require header field. * * 3. A registrar SHOULD authenticate the UAC. * * 4. The registrar SHOULD determine if the authenticated user is * authorized to modify registrations for this address-of-record. * For example, a registrar might consult an authorization * database that maps user names to a list of addresses-of-record * for which that user has authorization to modify bindings. If * the authenticated user is not authorized to modify bindings, * the registrar MUST return a 403 (Forbidden) and skip the * remaining steps. * * 5. The registrar extracts the address-of-record from the To header * field of the request. If the address-of-record is not valid * for the domain in the Request-URI, the registrar MUST send a * 404 (Not Found) response and skip the remaining steps. The URI * MUST then be converted to a canonical form. To do that, all * URI parameters MUST be removed (including the user-param), and * any escaped characters MUST be converted to their unescaped * form. The result serves as an index into the list of bindings. * * 6. The registrar checks whether the request contains the Contact * header field. If not, it skips to the last step. If the * Contact header field is present, the registrar checks if there * is one Contact field value that contains the special value "*" * and an Expires field. If the request has additional Contact * fields or an expiration time other than zero, the request is * invalid, and the server MUST return a 400 (Invalid Request) and * skip the remaining steps. If not, the registrar checks whether * the Call-ID agrees with the value stored for each binding. If * not, it MUST remove the binding. If it does agree, it MUST * remove the binding only if the CSeq in the request is higher * than the value stored for that binding. Otherwise, the update * MUST be aborted and the request fails. * * 7. The registrar now processes each contact address in the Contact * header field in turn. For each address, it determines the * expiration interval as follows: * * - If the field value has an "expires" parameter, that value * MUST be taken as the requested expiration. * * - If there is no such parameter, but the request has an * Expires header field, that value MUST be taken as the requested expiration. * * - If there is neither, a locally-configured default value MUST * be taken as the requested expiration. * * The registrar MAY choose an expiration less than the requested * expiration interval. If and only if the requested expiration * interval is greater than zero AND smaller than one hour AND * less than a registrar-configured minimum, the registrar MAY * reject the registration with a response of 423 (Interval Too * Brief). This response MUST contain a Min-Expires header field * that states the minimum expiration interval the registrar is * willing to honor. It then skips the remaining steps. * * For each address, the registrar then searches the list of * current bindings using the URI comparison rules. If the * binding does not exist, it is tentatively added. If the * binding does exist, the registrar checks the Call-ID value. If * the Call-ID value in the existing binding differs from the * Call-ID value in the request, the binding MUST be removed if * the expiration time is zero and updated otherwise. If they are * the same, the registrar compares the CSeq value. If the value * is higher than that of the existing binding, it MUST update or * remove the binding as above. If not, the update MUST be * aborted and the request fails. * * This algorithm ensures that out-of-order requests from the same * UA are ignored. * * Each binding record records the Call-ID and CSeq values from * the request. * * The binding updates MUST be committed (that is, made visible to * the proxy or redirect server) if and only if all binding * updates and additions succeed. If any one of them fails (for * example, because the back-end database commit failed), the * request MUST fail with a 500 (Server Error) response and all * tentative binding updates MUST be removed. * * 8. The registrar returns a 200 (OK) response. The response MUST * contain Contact header field values enumerating all current * bindings. Each Contact value MUST feature an "expires" * parameter indicating its expiration interval chosen by the * registrar. The response SHOULD include a Date header field. */ SIP_ServerTransaction transaction = e.ServerTransaction; SIP_Request request = e.Request; SIP_Uri to = null; string userName = ""; // Probably we need to do validate in SIP stack. #region Validate request if (SIP_Utils.IsSipOrSipsUri(request.To.Address.Uri.ToString())) { to = (SIP_Uri)request.To.Address.Uri; } else { transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x400_Bad_Request + ": To: value must be SIP or SIPS URI.", request)); return; } #endregion #region 1. Check if we are responsible for Request-URI domain // if(m_pProxy.OnIsLocalUri(e.Request.Uri)){ // } // TODO: #endregion #region 2. Check that all required extentions supported #endregion #region 3. Authenticate request if (!m_pProxy.AuthenticateRequest(e, out userName)) { return; } #endregion #region 4. Check if user user is authorized to modify registrations // We do this in next step(5.). #endregion #region 5. Check if address of record exists if (!m_pProxy.OnAddressExists(to.Address)) { transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x404_Not_Found, request)); return; } else if (!OnCanRegister(userName, to.Address)) { transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x403_Forbidden, request)); return; } #endregion #region 6. Process * Contact if exists // Check if we have star contact. SIP_t_ContactParam starContact = null; foreach (SIP_t_ContactParam c in request.Contact.GetAllValues()) { if (c.IsStarContact) { starContact = c; break; } } // We have star contact. if (starContact != null) { if (request.Contact.GetAllValues().Length > 1) { transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x400_Bad_Request + ": RFC 3261 10.3.6 -> If star(*) present, only 1 contact allowed.", request)); return; } else if (starContact.Expires != 0) { transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x400_Bad_Request + ": RFC 3261 10.3.6 -> star(*) contact parameter 'expires' value must be always '0'.", request)); return; } // Remove bindings. SIP_Registration reg = m_pRegistrations[to.Address]; if (reg != null) { foreach (SIP_RegistrationBinding b in reg.Bindings) { if (request.CallID != b.CallID || request.CSeq.SequenceNumber > b.CSeqNo) { b.Remove(); } } } } #endregion #region 7. Process Contact values if (starContact == null) { bool newReg = false; SIP_Registration reg = m_pRegistrations[to.Address]; if (reg == null) { newReg = true; reg = new SIP_Registration(userName, to.Address); m_pRegistrations.Add(reg); } // We may do updates in batch only. // We just validate all values then do update(this ensures that update doesn't fail). // Check expires and CSeq. foreach (SIP_t_ContactParam c in request.Contact.GetAllValues()) { if (c.Expires == -1) { c.Expires = request.Expires; } if (c.Expires == -1) { c.Expires = m_pProxy.Stack.MinimumExpireTime; } // We must accept 0 values - means remove contact. if (c.Expires != 0 && c.Expires < m_pProxy.Stack.MinimumExpireTime) { SIP_Response resp = m_pStack.CreateResponse(SIP_ResponseCodes.x423_Interval_Too_Brief, request); resp.MinExpires = m_pProxy.Stack.MinimumExpireTime; transaction.SendResponse(resp); return; } SIP_RegistrationBinding currentBinding = reg.GetBinding(c.Address.Uri); if (currentBinding != null && currentBinding.CallID == request.CallID && request.CSeq.SequenceNumber < currentBinding.CSeqNo) { transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x400_Bad_Request + ": CSeq value out of order.", request)); return; } } // Do binding updates. reg.AddOrUpdateBindings(e.ServerTransaction.Flow, request.CallID, request.CSeq.SequenceNumber, request.Contact.GetAllValues()); // Raise AOR change events. if (newReg) { OnAorRegistered(reg); } else { OnAorUpdated(reg); } } #endregion #region 8. Create 200 OK response and return all current bindings SIP_Response response = m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, request); response.Date = DateTime.Now; SIP_Registration registration = m_pRegistrations[to.Address]; if (registration != null) { foreach (SIP_RegistrationBinding b in registration.Bindings) { // Don't list expired bindings what wait to be disposed. if (b.TTL > 1) { response.Header.Add("Contact:", b.ToContactValue()); } } } // Add Authentication-Info:, then client knows next nonce. response.AuthenticationInfo.Add("qop=\"auth\",nextnonce=\"" + m_pStack.DigestNonceManager.CreateNonce() + "\""); transaction.SendResponse(response); #endregion }