/// <summary> /// New transaction ACK requests are for 2xx responses, i.e. INVITE accepted and dialogue being created. /// </summary> /// <remarks> /// From RFC 3261 Chapter 17.1.1.3 - ACK for non-2xx final responses /// /// IMPORTANT: /// an ACK for a non-2xx response will also have the same branch ID as the INVITE whose response it acknowledges. /// /// The ACK request constructed by the client transaction MUST contain /// values for the Call-ID, From, and Request-URI that are equal to the /// values of those header fields in the request passed to the transport /// by the client transaction (call this the "original request"). The To /// header field in the ACK MUST equal the To header field in the /// response being acknowledged, and therefore will usually differ from /// the To header field in the original request by the addition of the /// tag parameter. The ACK MUST contain a single Via header field, and /// this MUST be equal to the top Via header field of the original /// request. The CSeq header field in the ACK MUST contain the same /// value for the sequence number as was present in the original request, /// but the method parameter MUST be equal to "ACK". /// /// If the INVITE request whose response is being acknowledged had Route /// header fields, those header fields MUST appear in the ACK. This is /// to ensure that the ACK can be routed properly through any downstream /// stateless proxies. /// /// From RFC 3261 Chapter 13.2.2.4 - ACK for 2xx final responses /// /// IMPORTANT: /// an ACK for a 2xx final response is a new transaction and has a new branch ID. /// /// The UAC core MUST generate an ACK request for each 2xx received from /// the transaction layer. The header fields of the ACK are constructed /// in the same way as for any request sent within a dialog (see Section /// 12) with the exception of the CSeq and the header fields related to /// authentication. The sequence number of the CSeq header field MUST be /// the same as the INVITE being acknowledged, but the CSeq method MUST /// be ACK. The ACK MUST contain the same credentials as the INVITE. If /// the 2xx contains an offer (based on the rules above), the ACK MUST /// carry an answer in its body. If the offer in the 2xx response is not /// acceptable, the UAC core MUST generate a valid answer in the ACK and /// then send a BYE immediately. /// </remarks> private SIPRequest GetNewTransactionACKRequest(SIPResponse sipResponse, SIPURI ackURI, SIPEndPoint localSIPEndPoint) { SIPRequest ackRequest = new SIPRequest(SIPMethodsEnum.ACK, ackURI.ToString()); ackRequest.LocalSIPEndPoint = localSIPEndPoint; SIPHeader header = new SIPHeader(TransactionRequest.Header.From, sipResponse.Header.To, sipResponse.Header.CSeq, sipResponse.Header.CallId); header.CSeqMethod = SIPMethodsEnum.ACK; header.AuthenticationHeader = TransactionRequest.Header.AuthenticationHeader; header.ProxySendFrom = base.TransactionRequest.Header.ProxySendFrom; // If the UAS supplies a desired Record-Route list use that first. Otherwise fall back to any Route list used in the original transaction. if (sipResponse.Header.RecordRoutes != null) { header.Routes = sipResponse.Header.RecordRoutes.Reversed(); } else if (base.TransactionRequest.Header.Routes != null) { header.Routes = base.TransactionRequest.Header.Routes; } ackRequest.Header = header; SIPViaHeader viaHeader = new SIPViaHeader(localSIPEndPoint, CallProperties.CreateBranchId()); ackRequest.Header.Vias.PushViaHeader(viaHeader); return(ackRequest); }
/// <summary> /// Used to create a SIP response for a request when it was not possible to parse the incoming SIP request. /// The response generated by this method may or may not make it back to the requester. Because the SIP /// request could not be parsed there are no Via headers available and without those the return network /// path is missing. Instead a new Via header is generated that may get through if the requester is only /// one SIP hop away. /// </summary> /// <param name="localSIPEndPoint">The local SIP end point the request was received on.</param> /// <param name="remoteSIPEndPoint">The remote SIP end point the request was received on.</param> /// <param name="responseCode">The response code to set on the response.</param> /// <param name="reasonPhrase">Optional reason phrase to set on the response (keep short).</param> public static SIPResponse GetResponse(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteSIPEndPoint, SIPResponseStatusCodesEnum responseCode, string reasonPhrase) { try { SIPResponse response = new SIPResponse(responseCode, reasonPhrase); response.SetSendFromHints(localSIPEndPoint); SIPSchemesEnum sipScheme = (localSIPEndPoint.Protocol == SIPProtocolsEnum.tls) ? SIPSchemesEnum.sips : SIPSchemesEnum.sip; SIPFromHeader from = new SIPFromHeader(null, new SIPURI(sipScheme, localSIPEndPoint), null); SIPToHeader to = new SIPToHeader(null, new SIPURI(sipScheme, localSIPEndPoint), null); int cSeq = 1; string callId = CallProperties.CreateNewCallId(); response.Header = new SIPHeader(from, to, cSeq, callId); response.Header.CSeqMethod = SIPMethodsEnum.NONE; response.Header.Vias.PushViaHeader(new SIPViaHeader( new SIPEndPoint(localSIPEndPoint.Protocol, remoteSIPEndPoint.GetIPEndPoint()), CallProperties.CreateBranchId())); response.Header.MaxForwards = Int32.MinValue; response.Header.Allow = m_allowedSIPMethods; return(response); } catch (Exception excp) { logger.LogError("Exception SIPResponse.GetResponse. " + excp.Message); throw; } }
/// <summary> /// Duplicates an existing SIP request, typically one that received an unauthorised response, to an /// authenticated version. The CSeq and Via branch ID are also incremented so /// that the request will not be flagged as a retransmit. /// </summary> /// <param name="authenticationChallenges">The challenges to authenticate the request against. Typically /// the challenges come from a SIP response.</param> /// <param name="username">The username to authenticate with.</param> /// <param name="password">The password to authenticate with.</param> /// <returns>A SIP request that is a duplicate of the original but with an authentication header added and /// the state header values updated so as not to be flagged as a retransmit.</returns> public SIPRequest DuplicateAndAuthenticate(List <SIPAuthenticationHeader> authenticationChallenges, string username, string password) { var dupRequest = this.Copy(); dupRequest.Header.Vias.TopViaHeader.Branch = CallProperties.CreateBranchId(); dupRequest.Header.CSeq = dupRequest.Header.CSeq + 1; dupRequest.Header.AuthenticationHeaders.Clear(); // RFC8760 (which introduces SHA256/512 for SIP) states that multiple authentication headers with different digest algorithms // can be included in a SIP request. When testing this with the latest versions (Jul 2021) of Asterisk v18.5.0 and FreeSWITCH v1.10.6 // request authentication failed if the MD5 digest was not first and it's almost certain the subsequent SHA256 digest was ignored. // As a consequence the logic below will only use a SHA256 digest IFF the UAS put an authentication challenge with the digest // algorithm explicitly set to SHA-256. // See https://github.com/sipsorcery-org/sipsorcery/issues/525. bool useSHA256 = authenticationChallenges.Any(x => x.SIPDigest.DigestAlgorithm == DigestAlgorithmsEnum.SHA256); if (useSHA256) { var sha256AuthHeader = SIPAuthChallenge.GetAuthenticationHeader(authenticationChallenges, this.URI, this.Method, username, password, DigestAlgorithmsEnum.SHA256); dupRequest.Header.AuthenticationHeaders.Add(sha256AuthHeader); } else { var md5AuthHeader = SIPAuthChallenge.GetAuthenticationHeader(authenticationChallenges, this.URI, this.Method, username, password); dupRequest.Header.AuthenticationHeaders.Add(md5AuthHeader); } return(dupRequest); }
/// <summary> /// New transaction ACK requests are for 2xx responses, i.e. INVITE accepted and dialogue being created. /// </summary> /// <remarks> /// From RFC 3261 Chapter 17.1.1.3 - ACK for non-2xx final responses /// /// IMPORTANT: /// an ACK for a non-2xx response will also have the same branch ID as the INVITE whose response it acknowledges. /// /// The ACK request constructed by the client transaction MUST contain /// values for the Call-ID, From, and Request-URI that are equal to the /// values of those header fields in the request passed to the transport /// by the client transaction (call this the "original request"). The To /// header field in the ACK MUST equal the To header field in the /// response being acknowledged, and therefore will usually differ from /// the To header field in the original request by the addition of the /// tag parameter. The ACK MUST contain a single Via header field, and /// this MUST be equal to the top Via header field of the original /// request. The CSeq header field in the ACK MUST contain the same /// value for the sequence number as was present in the original request, /// but the method parameter MUST be equal to "ACK". /// /// If the INVITE request whose response is being acknowledged had Route /// header fields, those header fields MUST appear in the ACK. This is /// to ensure that the ACK can be routed properly through any downstream /// stateless proxies. /// /// From RFC 3261 Chapter 13.2.2.4 - ACK for 2xx final responses /// /// IMPORTANT: /// an ACK for a 2xx final response is a new transaction and has a new branch ID. /// /// The UAC core MUST generate an ACK request for each 2xx received from /// the transaction layer. The header fields of the ACK are constructed /// in the same way as for any request sent within a dialog (see Section /// 12) with the exception of the CSeq and the header fields related to /// authentication. The sequence number of the CSeq header field MUST be /// the same as the INVITE being acknowledged, but the CSeq method MUST /// be ACK. The ACK MUST contain the same credentials as the INVITE. If /// the 2xx contains an offer (based on the rules above), the ACK MUST /// carry an answer in its body. If the offer in the 2xx response is not /// acceptable, the UAC core MUST generate a valid answer in the ACK and /// then send a BYE immediately. /// </remarks> private SIPRequest GetNewTransactionACKRequest(SIPResponse sipResponse, SIPURI ackURI, SIPEndPoint localSIPEndPoint) { SIPRequest ackRequest = new SIPRequest(SIPMethodsEnum.ACK, ackURI.ToString()); ackRequest.LocalSIPEndPoint = localSIPEndPoint; SIPHeader header = new SIPHeader(TransactionRequest.Header.From, sipResponse.Header.To, sipResponse.Header.CSeq, sipResponse.Header.CallId); header.CSeqMethod = SIPMethodsEnum.ACK; header.AuthenticationHeader = TransactionRequest.Header.AuthenticationHeader; header.ProxySendFrom = base.TransactionRequest.Header.ProxySendFrom; if (sipResponse.Header.RecordRoutes != null) { header.Routes = sipResponse.Header.RecordRoutes.Reversed(); } ackRequest.Header = header; SIPViaHeader viaHeader = new SIPViaHeader(localSIPEndPoint, CallProperties.CreateBranchId()); ackRequest.Header.Vias.PushViaHeader(viaHeader); return(ackRequest); }
/// <summary> /// Helper method to create a SIP response for a SIP request. This method can be thought of as creating a /// vanilla (or no frills) response for a request. It's suitable for generating error responses. For requests that /// require an action such as creating a call or registering a contact the response will require additional /// information and this method will not be suitable. /// </summary> /// <param name="sipRequest">The SIP request to create the response for.</param> /// <param name="responseCode">The response code.</param> /// <param name="reasonPhrase">Optional reason phrase to set on the response (needs to be short).</param> /// <returns>A SIP response object.</returns> public static SIPResponse GetResponse(SIPRequest sipRequest, SIPResponseStatusCodesEnum responseCode, string reasonPhrase) { try { SIPResponse response = new SIPResponse(responseCode, reasonPhrase); response.SetSendFromHints(sipRequest.LocalSIPEndPoint); if (reasonPhrase != null) { response.ReasonPhrase = reasonPhrase; } SIPHeader requestHeader = sipRequest.Header; SIPFromHeader from = (requestHeader == null || requestHeader.From != null) ? requestHeader.From : new SIPFromHeader(null, new SIPURI(sipRequest.URI.Scheme, sipRequest.LocalSIPEndPoint), null); SIPToHeader to = (requestHeader == null || requestHeader.To != null) ? requestHeader.To : new SIPToHeader(null, new SIPURI(sipRequest.URI.Scheme, sipRequest.LocalSIPEndPoint), null); int cSeq = (requestHeader == null || requestHeader.CSeq != -1) ? requestHeader.CSeq : 1; string callId = (requestHeader == null || requestHeader.CallId != null) ? requestHeader.CallId : CallProperties.CreateNewCallId(); response.Header = new SIPHeader(from, to, cSeq, callId); response.Header.CSeqMethod = (requestHeader != null) ? requestHeader.CSeqMethod : SIPMethodsEnum.NONE; if (requestHeader == null || requestHeader.Vias == null || requestHeader.Vias.Length == 0) { response.Header.Vias.PushViaHeader(new SIPViaHeader(sipRequest.RemoteSIPEndPoint, CallProperties.CreateBranchId())); } else { response.Header.Vias = requestHeader.Vias; } response.Header.MaxForwards = Int32.MinValue; response.Header.Allow = m_allowedSIPMethods; return(response); } catch (Exception excp) { logger.LogError("Exception SIPResponse.GetResponse. " + excp.Message); throw excp; } }
public string CreateBranchId() { string routeStr = (Header.Routes != null) ? Header.Routes.ToString() : null; string toTagStr = (Header.To != null) ? Header.To.ToTag : null; string fromTagStr = (Header.From != null) ? Header.From.FromTag : null; string topViaStr = (Header.Vias != null && Header.Vias.TopViaHeader != null) ? Header.Vias.TopViaHeader.ToString() : null; return(CallProperties.CreateBranchId( SIPConstants.SIP_BRANCH_MAGICCOOKIE, toTagStr, fromTagStr, Header.CallId, URI.ToString(), topViaStr, Header.CSeq, routeStr, Header.ProxyRequire, null)); }
private SIPRequest GetByeRequest(SIPEndPoint localSIPEndPoint) { SIPRequest byeRequest = new SIPRequest(SIPMethodsEnum.BYE, RemoteTarget); SIPFromHeader byeFromHeader = SIPFromHeader.ParseFromHeader(LocalUserField.ToString()); SIPToHeader byeToHeader = SIPToHeader.ParseToHeader(RemoteUserField.ToString()); int cseq = CSeq + 1; SIPHeader byeHeader = new SIPHeader(byeFromHeader, byeToHeader, cseq, CallId); byeHeader.CSeqMethod = SIPMethodsEnum.BYE; byeRequest.Header = byeHeader; byeRequest.Header.Routes = RouteSet; byeRequest.Header.ProxySendFrom = ProxySendFrom; SIPViaHeader viaHeader = new SIPViaHeader(localSIPEndPoint, CallProperties.CreateBranchId()); byeRequest.Header.Vias.PushViaHeader(viaHeader); return(byeRequest); }
private SIPRequest GetDummyINVITERequest(SIPURI dummyURI) { string dummyFrom = "<sip:[email protected]>"; string dummyContact = "sip:127.0.0.1:1234"; SIPRequest inviteRequest = new SIPRequest(SIPMethodsEnum.INVITE, dummyURI); SIPHeader inviteHeader = new SIPHeader(SIPFromHeader.ParseFromHeader(dummyFrom), new SIPToHeader(null, dummyURI, null), 1, CallProperties.CreateNewCallId()); inviteHeader.From.FromTag = CallProperties.CreateNewTag(); inviteHeader.Contact = SIPContactHeader.ParseContactHeader(dummyContact); inviteHeader.CSeqMethod = SIPMethodsEnum.INVITE; inviteHeader.UserAgent = "unittest"; inviteRequest.Header = inviteHeader; SIPViaHeader viaHeader = new SIPViaHeader("127.0.0.1", 1234, CallProperties.CreateBranchId(), SIPProtocolsEnum.udp); inviteRequest.Header.Vias.PushViaHeader(viaHeader); inviteRequest.Body = "dummy"; inviteRequest.Header.ContentLength = inviteRequest.Body.Length; inviteRequest.Header.ContentType = "application/sdp"; return(inviteRequest); }