/// <summary> /// This method is called when new request is received. /// </summary> /// <param name="e">Request event arguments.</param> private void OnRequestReceived(SIP_RequestReceivedEventArgs e) { SIP_Request request = e.Request; try{ #region Statefull // Statefull if ((m_ProxyMode & SIP_ProxyMode.Statefull) != 0) { // Statefull proxy is transaction statefull proxy only, // what don't create dialogs and keep dialog state. /* RFC 3261 16.10. * StateFull proxy: * If a matching response context is found, the element MUST * immediately return a 200 (OK) response to the CANCEL request. * * If a response context is not found, the element does not have any * knowledge of the request to apply the CANCEL to. It MUST statelessly * forward the CANCEL request (it may have statelessly forwarded the * associated request previously). */ if (e.Request.RequestLine.Method == SIP_Methods.CANCEL) { // Don't do server transaction before we get CANCEL matching transaction. SIP_ServerTransaction trToCancel = m_pStack.TransactionLayer.MatchCancelToTransaction(e.Request); if (trToCancel != null) { trToCancel.Cancel(); e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, request)); } else { ForwardRequest(false, e, true); } } // ACK never creates transaction, it's always passed directly to transport layer. else if (e.Request.RequestLine.Method == SIP_Methods.ACK) { ForwardRequest(false, e, true); } else { ForwardRequest(true, e, true); } } #endregion #region B2BUA // B2BUA else if ((m_ProxyMode & SIP_ProxyMode.B2BUA) != 0) { m_pB2BUA.OnRequestReceived(e); } #endregion #region Stateless // Stateless else if ((m_ProxyMode & SIP_ProxyMode.Stateless) != 0) { // Stateless proxy don't do transaction, just forwards all. ForwardRequest(false, e, true); } #endregion #region Proxy won't accept command else { e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x501_Not_Implemented, request)); } #endregion } catch (Exception x) { try{ m_pStack.TransportLayer.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error + ": " + x.Message, e.Request)); } catch { // Skip transport layer exception if send fails. } // Don't raise OnError for transport errors. if (!(x is SIP_TransportException)) { m_pStack.OnError(x); } } }
/// <summary> /// This method is called when SIP stack received new message. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Event data.</param> private void m_pStack_RequestReceived(object sender, SIP_RequestReceivedEventArgs e) { // TODO: Performance: rise events on thread pool or see if this method called on pool aready, then we may not keep lock for events ? if (e.Request.RequestLine.Method == SIP_Methods.CANCEL) { /* RFC 3261 9.2. * If the UAS did not find a matching transaction for the CANCEL * according to the procedure above, it SHOULD respond to the CANCEL * with a 481 (Call Leg/Transaction Does Not Exist). * * Regardless of the method of the original request, as long as the * CANCEL matched an existing transaction, the UAS answers the CANCEL * request itself with a 200 (OK) response. */ SIP_ServerTransaction trToCancel = m_pStack.TransactionLayer.MatchCancelToTransaction(e.Request); if (trToCancel != null) { trToCancel.Cancel(); e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, e.Request)); } else { e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist, e.Request)); } } else if (e.Request.RequestLine.Method == SIP_Methods.BYE) { /* RFC 3261 15.1.2. * If the BYE does not match an existing dialog, the UAS core SHOULD generate a 481 * (Call/Transaction Does Not Exist) response and pass that to the server transaction. */ // TODO: SIP_Dialog dialog = m_pStack.TransactionLayer.MatchDialog(e.Request); if (dialog != null) { e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, e.Request)); dialog.Terminate(); } else { e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist, e.Request)); } } else if (e.Request.RequestLine.Method == SIP_Methods.INVITE) { // Supress INVITE retransmissions. e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x100_Trying, e.Request)); // Create call. SIP_UA_Call call = new SIP_UA_Call(this, e.ServerTransaction); call.StateChanged += new EventHandler(Call_StateChanged); m_pCalls.Add(call); OnIncomingCall(call); } else { OnRequestReceived(e); } }
/// <summary> /// This method is called when new request is received. /// </summary> /// <param name="e">Request event arguments.</param> internal void OnRequestReceived(SIP_RequestReceivedEventArgs e) { SIP_Request request = e.Request; if (request.RequestLine.Method == SIP_Methods.CANCEL) { /* RFC 3261 9.2. * If the UAS did not find a matching transaction for the CANCEL * according to the procedure above, it SHOULD respond to the CANCEL * with a 481 (Call Leg/Transaction Does Not Exist). * * Regardless of the method of the original request, as long as the * CANCEL matched an existing transaction, the UAS answers the CANCEL * request itself with a 200 (OK) response. */ SIP_ServerTransaction trToCancel = m_pProxy.Stack.TransactionLayer.MatchCancelToTransaction(e.Request); if (trToCancel != null) { trToCancel.Cancel(); //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x200_Ok)); } else { //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist)); } } // We never should ge BYE here, because transport layer must match it to dialog. else if (request.RequestLine.Method == SIP_Methods.BYE) { /* RFC 3261 15.1.2. * If the BYE does not match an existing dialog, the UAS core SHOULD generate a 481 * (Call/Transaction Does Not Exist) response and pass that to the server transaction. */ //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist)); } // We never should ge ACK here, because transport layer must match it to dialog. else if (request.RequestLine.Method == SIP_Methods.ACK) { // ACK is response less request, so we may not return error to it. } // B2BUA must respond to OPTIONS request, not to forward it. else if (request.RequestLine.Method == SIP_Methods.OPTIONS) /* * SIP_Response response = e.Request.CreateResponse(SIP_ResponseCodes.x200_Ok); * // Add Allow to non ACK response. * if(e.Request.RequestLine.Method != SIP_Methods.ACK){ * response.Allow.Add("INVITE,ACK,OPTIONS,CANCEL,BYE,PRACK,MESSAGE,UPDATE"); * } * // Add Supported to 2xx non ACK response. * if(response.StatusCodeType == SIP_StatusCodeType.Success && e.Request.RequestLine.Method != SIP_Methods.ACK){ * response.Supported.Add("100rel,timer"); * } * e.ServerTransaction.SendResponse(response);*/ { } // We never should get PRACK here, because transport layer must match it to dialog. else if (request.RequestLine.Method == SIP_Methods.PRACK) { //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist)); } // We never should get UPDATE here, because transport layer must match it to dialog. else if (request.RequestLine.Method == SIP_Methods.UPDATE) { //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist)); } else { /* draft-marjou-sipping-b2bua-00 4.1.3. * When the UAS of the B2BUA receives an upstream SIP request, its * associated UAC generates a new downstream SIP request with its new * Via, Max-Forwards, Call-Id, CSeq, and Contact header fields. Route * header fields of the upstream request are copied in the downstream * request, except the first Route header if it is under the * responsibility of the B2BUA. Record-Route header fields of the * upstream request are not copied in the new downstream request, as * Record-Route is only meaningful for the upstream dialog. The UAC * SHOULD copy other header fields and body from the upstream request * into this downstream request before sending it. */ SIP_Request b2buaRequest = e.Request.Copy(); b2buaRequest.Via.RemoveAll(); b2buaRequest.MaxForwards = 70; b2buaRequest.CallID = SIP_t_CallID.CreateCallID().CallID; b2buaRequest.CSeq.SequenceNumber = 1; b2buaRequest.Contact.RemoveAll(); // b2buaRequest.Contact.Add(m_pProxy.CreateContact(b2buaRequest.To.Address).ToStringValue()); if (b2buaRequest.Route.Count > 0 && m_pProxy.IsLocalRoute(SIP_Uri.Parse(b2buaRequest.Route.GetTopMostValue().Address.Uri.ToString()))) { b2buaRequest.Route.RemoveTopMostValue(); } b2buaRequest.RecordRoute.RemoveAll(); // Remove our Authorization header if it's there. foreach (SIP_SingleValueHF <SIP_t_Credentials> header in b2buaRequest.ProxyAuthorization.HeaderFields) { try{ Auth_HttpDigest digest = new Auth_HttpDigest(header.ValueX.AuthData, b2buaRequest.RequestLine.Method); if (m_pProxy.Stack.Realm == digest.Realm) { b2buaRequest.ProxyAuthorization.Remove(header); } } catch { // We don't care errors here. This can happen if remote server xxx auth method here and // we don't know how to parse it, so we leave it as is. } } //--- Add/replace default fields. ------------------------------------------ b2buaRequest.Allow.RemoveAll(); b2buaRequest.Supported.RemoveAll(); // Accept to non ACK,BYE request. if (request.RequestLine.Method != SIP_Methods.ACK && request.RequestLine.Method != SIP_Methods.BYE) { b2buaRequest.Allow.Add("INVITE,ACK,OPTIONS,CANCEL,BYE,PRACK"); } // Supported to non ACK request. if (request.RequestLine.Method != SIP_Methods.ACK) { b2buaRequest.Supported.Add("100rel,timer"); } // Remove Require: header. b2buaRequest.Require.RemoveAll(); // RFC 4028 7.4. For re-INVITE and UPDATE we need to add Session-Expires and Min-SE: headers. if (request.RequestLine.Method == SIP_Methods.INVITE || request.RequestLine.Method == SIP_Methods.UPDATE) { b2buaRequest.SessionExpires = new SIP_t_SessionExpires(m_pProxy.Stack.SessionExpries, "uac"); b2buaRequest.MinSE = new SIP_t_MinSE(m_pProxy.Stack.MinimumSessionExpries); } // Forward request. //m_pProxy.ForwardRequest(true,e,b2buaRequest,false); } }
/// <summary> /// This method is called when new request is received. /// </summary> /// <param name="e">Request event arguments.</param> private void OnRequestReceived(SIP_RequestReceivedEventArgs e) { /* RFC 3261 16.12. ????????? Forward does all thse steps. * 1. The proxy will inspect the Request-URI. If it indicates a * resource owned by this proxy, the proxy will replace it with * the results of running a location service. Otherwise, the * proxy will not change the Request-URI. * * 2. The proxy will inspect the URI in the topmost Route header * field value. If it indicates this proxy, the proxy removes it * from the Route header field (this route node has been reached). * * 3. The proxy will forward the request to the resource indicated * by the URI in the topmost Route header field value or in the * Request-URI if no Route header field is present. The proxy * determines the address, port and transport to use when * forwarding the request by applying the procedures in [4] to that URI. */ SIP_Request request = e.Request; try { #region Registrar // Registrar if ((m_ProxyMode & SIP_ProxyMode.Registrar) != 0 && request.RequestLine.Method == SIP_Methods.REGISTER) { m_pRegistrar.Register(e); } #endregion #region Presence /* * // Presence * else if((m_ProxyMode & SIP_ProxyMode.Presence) != 0 && (request.Method == "SUBSCRIBE" || request.Method == "NOTIFY")){ * * } */ #endregion #region Statefull // Statefull else if ((m_ProxyMode & SIP_ProxyMode.Statefull) != 0) { // Statefull proxy is transaction statefull proxy only, // what don't create dialogs and keep dialog state. /* RFC 3261 16.10. * StateFull proxy: * If a matching response context is found, the element MUST * immediately return a 200 (OK) response to the CANCEL request. * * If a response context is not found, the element does not have any * knowledge of the request to apply the CANCEL to. It MUST statelessly * forward the CANCEL request (it may have statelessly forwarded the * associated request previously). */ if (e.Request.RequestLine.Method == SIP_Methods.CANCEL) { // Don't do server transaction before we get CANCEL matching transaction. SIP_ServerTransaction trToCancel = m_pStack.TransactionLayer.MatchCancelToTransaction(e.Request); if (trToCancel != null) { trToCancel.Cancel(); e.ServerTransaction.SendResponse(m_pStack.CreateResponse( SIP_ResponseCodes.x200_Ok, request)); } else { ForwardRequest(false, e); } } // ACK never creates transaction, it's always passed directly to transport layer. else if (e.Request.RequestLine.Method == SIP_Methods.ACK) { ForwardRequest(false, e); } else { ForwardRequest(true, e); } } #endregion #region B2BUA // B2BUA else if ((m_ProxyMode & SIP_ProxyMode.B2BUA) != 0) { m_pB2BUA.OnRequestReceived(e); } #endregion #region Stateless // Stateless else if ((m_ProxyMode & SIP_ProxyMode.Stateless) != 0) { // Stateless proxy don't do transaction, just forwards all. ForwardRequest(false, e); } #endregion #region Proxy won't accept command else { e.ServerTransaction.SendResponse( m_pStack.CreateResponse(SIP_ResponseCodes.x501_Not_Implemented, request)); } #endregion } catch (Exception x) { try { m_pStack.TransportLayer.SendResponse( m_pStack.CreateResponse( SIP_ResponseCodes.x500_Server_Internal_Error + ": " + x.Message, e.Request)); } catch { // Skip transport layer exception if send fails. } // Don't raise OnError for transport errors. if (!(x is SIP_TransportException)) { m_pStack.OnError(x); } } }