/// <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> /// Default constructor. /// </summary> public SIP_Stack() { m_pTransportLayer = new SIP_TransportLayer(this); m_pTransactionLayer = new SIP_TransactionLayer(this); m_pNonceManager = new Auth_HttpDigest_NonceManager(); m_pProxyServers = new List<SIP_Uri>(); m_pRegistrations = new List<SIP_UA_Registration>(); m_pCredentials = new List<NetworkCredential>(); m_RegisterCallID = SIP_t_CallID.CreateCallID(); m_pLogger = new Logger(); m_pDnsClient = new Dns_Client(); }