public static string MangleSDP(string sdpBody, string publicIPAddress, out bool wasMangled) { wasMangled = false; try { if (sdpBody != null && publicIPAddress != null) { string sdpAddress = SDP.GetSDPRTPEndPoint(sdpBody).Address.ToString(); // Only mangle if there is something to change. For example the server could be on the same private subnet in which case it can't help. if (IPSocket.IsPrivateAddress(sdpAddress) && publicIPAddress != sdpAddress) { //logger.LogDebug("MangleSDP replacing private " + sdpAddress + " with " + publicIPAddress + "."); string mangledSDP = Regex.Replace(sdpBody, @"c=IN IP4 (?<ipaddress>(\d+\.){3}\d+)", "c=IN IP4 " + publicIPAddress, RegexOptions.Singleline); wasMangled = true; return(mangledSDP); } } else { logger.LogWarning("Mangle SDP was called with an empty body or public IP address."); } return(sdpBody); } catch (Exception excp) { logger.LogError("Exception MangleSDP. " + excp.Message); return(sdpBody); } }
/// <summary> /// This constructor is used to create non-INVITE dialogues for example the dialogues used in SIP event interactions /// where the dialogue is created based on a SUBSCRIBE request. /// </summary> public SIPDialogue( SIPRequest nonInviteRequest, string toTag) { Id = Guid.NewGuid(); CallId = nonInviteRequest.Header.CallId; RouteSet = (nonInviteRequest.Header.RecordRoutes != null) ? nonInviteRequest.Header.RecordRoutes.Reversed() : null; RemoteUserField = nonInviteRequest.Header.From.FromUserField; RemoteTag = nonInviteRequest.Header.From.FromTag; LocalUserField = nonInviteRequest.Header.To.ToUserField; LocalUserField.Parameters.Set("tag", toTag); LocalTag = toTag; CSeq = nonInviteRequest.Header.CSeq; Inserted = DateTime.UtcNow; Direction = SIPCallDirection.Out; // Set the dialogue remote target and take care of mangling if an upstream proxy has indicated it's required. RemoteTarget = nonInviteRequest.Header.Contact[0].ContactURI; ProxySendFrom = nonInviteRequest.Header.ProxyReceivedOn; if (!nonInviteRequest.Header.ProxyReceivedFrom.IsNullOrBlank()) { // Setting the Proxy-ReceivedOn header is how an upstream proxy will let an agent know it should mangle the contact. // Don't mangle private contacts if there is a Record-Route header. If a proxy is putting private IP's in a Record-Route header that's its problem. if (RouteSet == null && IPSocket.IsPrivateAddress(RemoteTarget.Host)) { SIPEndPoint remoteUASIPEndPoint = SIPEndPoint.ParseSIPEndPoint(nonInviteRequest.Header.ProxyReceivedFrom); RemoteTarget.Host = remoteUASIPEndPoint.GetIPEndPoint().ToString(); } } }
/// <summary> /// This constructor is used by client user agents or SIP elements acting in a client user agent role. When /// acting as a client user agent the local fields are contained in the From header and the remote fields are /// in the To header. /// </summary> public SIPDialogue( UACInviteTransaction uacInviteTransaction, string owner, string adminMemberId) { Id = Guid.NewGuid(); CallId = uacInviteTransaction.TransactionRequest.Header.CallId; RouteSet = (uacInviteTransaction.TransactionFinalResponse != null && uacInviteTransaction.TransactionFinalResponse.Header.RecordRoutes != null) ? uacInviteTransaction.TransactionFinalResponse.Header.RecordRoutes.Reversed() : null; LocalUserField = uacInviteTransaction.TransactionFinalResponse.Header.From.FromUserField; LocalTag = uacInviteTransaction.TransactionFinalResponse.Header.From.FromTag; RemoteUserField = uacInviteTransaction.TransactionFinalResponse.Header.To.ToUserField; RemoteTag = uacInviteTransaction.TransactionFinalResponse.Header.To.ToTag; CSeq = uacInviteTransaction.TransactionRequest.Header.CSeq; CDRId = (uacInviteTransaction.CDR != null) ? uacInviteTransaction.CDR.CDRId : Guid.Empty; Owner = owner; AdminMemberId = adminMemberId; ContentType = uacInviteTransaction.TransactionRequest.Header.ContentType; SDP = uacInviteTransaction.TransactionRequest.Body; RemoteSDP = uacInviteTransaction.TransactionFinalResponse.Body; Inserted = DateTimeOffset.UtcNow; Direction = SIPCallDirection.Out; // Set the dialogue remote target and take care of mangling if an upstream proxy has indicated it's required. if (uacInviteTransaction.TransactionFinalResponse != null) { RemoteTarget = new SIPURI(uacInviteTransaction.TransactionRequest.URI.Scheme, uacInviteTransaction.TransactionFinalResponse.RemoteSIPEndPoint.CopyOf()); } else { RemoteTarget = new SIPURI(uacInviteTransaction.TransactionRequest.URI.Scheme, uacInviteTransaction.TransactionRequest.RemoteSIPEndPoint.CopyOf()); } ProxySendFrom = uacInviteTransaction.TransactionFinalResponse.Header.ProxyReceivedOn; if (uacInviteTransaction.TransactionFinalResponse.Header.Contact != null && uacInviteTransaction.TransactionFinalResponse.Header.Contact.Count > 0) { RemoteTarget = uacInviteTransaction.TransactionFinalResponse.Header.Contact[0].ContactURI.CopyOf(); if (!uacInviteTransaction.TransactionFinalResponse.Header.ProxyReceivedFrom.IsNullOrBlank()) { // Setting the Proxy-ReceivedOn header is how an upstream proxy will let an agent know it should mangle the contact. // Don't mangle private contacts if there is a Record-Route header. If a proxy is putting private IP's in a Record-Route header that's its problem. if (RouteSet == null && IPSocket.IsPrivateAddress(RemoteTarget.Host)) { SIPEndPoint remoteUASSIPEndPoint = SIPEndPoint.ParseSIPEndPoint(uacInviteTransaction.TransactionFinalResponse.Header .ProxyReceivedFrom); RemoteTarget.Host = remoteUASSIPEndPoint.GetIPEndPoint().ToString(); } } } }
public void Test172IPRangeIsPrivate() { Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); Assert.IsFalse(IPSocket.IsPrivateAddress("172.15.1.1"), "Public IP address was mistakenly identified as private."); Assert.IsTrue(IPSocket.IsPrivateAddress("172.16.1.1"), "Private IP address was not correctly identified."); Console.WriteLine("-----------------------------------------"); }
public void Test172IPRangeIsPrivate() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); Assert.False(IPSocket.IsPrivateAddress("172.15.1.1"), "Public IP address was mistakenly identified as private."); Assert.True(IPSocket.IsPrivateAddress("172.16.1.1"), "Private IP address was not correctly identified."); logger.LogDebug("-----------------------------------------"); }
private void UACInviteTransaction_TransactionFinalResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse) { try { // BranchId for 2xx responses needs to be a new one, non-2xx final responses use same one as original request. SIPRequest ackRequest = null; if (sipResponse.StatusCode >= 200 && sipResponse.StatusCode < 299) { if (sipResponse.Header.To != null) { m_remoteTag = sipResponse.Header.To.ToTag; } SIPURI ackURI = m_transactionRequest.URI; if (sipResponse.Header.Contact != null && sipResponse.Header.Contact.Count > 0) { ackURI = sipResponse.Header.Contact[0].ContactURI; // Don't mangle private contacts if there is a Record-Route header. If a proxy is putting private IP's in a Record-Route header that's its problem. if ((sipResponse.Header.RecordRoutes == null || sipResponse.Header.RecordRoutes.Length == 0) && IPSocket.IsPrivateAddress(ackURI.Host) && !sipResponse.Header.ProxyReceivedFrom.IsNullOrBlank()) { // Setting the Proxy-ReceivedOn header is how an upstream proxy will let an agent know it should mangle the contact. SIPEndPoint remoteUASSIPEndPoint = SIPEndPoint.ParseSIPEndPoint(sipResponse.Header.ProxyReceivedFrom); ackURI.Host = remoteUASSIPEndPoint.GetIPEndPoint().ToString(); } } // ACK for 2xx response needs to be a new transaction and gets routed based on SIP request fields. ackRequest = GetNewTransactionACKRequest(sipResponse, ackURI, LocalSIPEndPoint); base.SendRequest(ackRequest); } else { // ACK for non 2xx response is part of the INVITE transaction and gets routed to the same endpoint as the INVITE. ackRequest = GetInTransactionACKRequest(sipResponse, m_transactionRequest.URI, LocalSIPEndPoint); base.SendRequest(RemoteEndPoint, ackRequest); } if (UACInviteTransactionFinalResponseReceived != null) { UACInviteTransactionFinalResponseReceived(localSIPEndPoint, remoteEndPoint, sipTransaction, sipResponse); } if (CDR != null) { SIPEndPoint localEP = SIPEndPoint.TryParse(sipResponse.Header.ProxyReceivedOn) ?? localSIPEndPoint; SIPEndPoint remoteEP = SIPEndPoint.TryParse(sipResponse.Header.ProxyReceivedFrom) ?? remoteEndPoint; CDR.Answered(sipResponse.StatusCode, sipResponse.Status, sipResponse.ReasonPhrase, localEP, remoteEP); } } catch (Exception excp) { logger.Error("Exception UACInviteTransaction_TransactionFinalResponseReceived. " + excp.Message); } }
/// <summary> /// Mangles private IP addresses in a SIP request replacing them with the IP address the packet was received on. /// </summary> /// <param name="sipRequest">The unmangled SIP request.</param> /// <returns>The mangled SIP request</returns> public static void MangleSIPRequest(SIPMonitorServerTypesEnum server, SIPRequest sipRequest, string username, SIPMonitorLogDelegate logDelegate) { try { string bottomViaIPAddress = sipRequest.Header.Vias.BottomViaHeader.ReceivedFromIPAddress; if (sipRequest.Header.Contact != null && sipRequest.Header.Contact.Count == 1 && bottomViaIPAddress != null) { string contactHost = sipRequest.Header.Contact[0].ContactURI.Host; // Only mangle if the host is a private IP address and there is something to change. // For example the server could be on the same private subnet in which case it can't help. if (IPSocket.IsPrivateAddress(contactHost) && contactHost != bottomViaIPAddress) { string origContact = sipRequest.Header.Contact[0].ContactURI.Host; sipRequest.Header.Contact[0].ContactURI.Host = sipRequest.Header.Vias.BottomViaHeader.ReceivedFromAddress; //logger.LogDebug("Contact URI identified as containing private address for " + sipRequest.Method + " " + origContact + " adjusting to use bottom via " + bottomViaHost + "."); //FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.Registrar, SIPMonitorServerTypesEnum.ContactRegisterInProgress, "Contact on " + sipRequest.Method + " " + origContact + " had private address adjusted to " + bottomViaHost + ".", username)); } } if (sipRequest.Body != null && bottomViaIPAddress != null) { bool wasMangled = false; string mangledSDP = MangleSDP(sipRequest.Body, bottomViaIPAddress, out wasMangled); if (wasMangled) { sipRequest.Body = mangledSDP; sipRequest.Header.ContentLength = sipRequest.Body.Length; if (logDelegate != null) { logDelegate(new SIPMonitorConsoleEvent(server, SIPMonitorEventTypesEnum.DialPlan, "SDP mangled for " + sipRequest.Method.ToString() + " request from " + sipRequest.RemoteSIPEndPoint.ToString() + ", adjusted address " + bottomViaIPAddress + ".", username)); } } } } catch (Exception excp) { Logger.Logger.Error("Exception MangleSIPRequest. ->" + excp.Message); } }
// private static ILogger logger = Log.Logger; public static string MangleSDP(string sdpBody, string publicIPAddress, out bool wasMangled) { wasMangled = false; try { if (sdpBody != null && publicIPAddress != null) { IPAddress addr = SDP.GetSDPRTPEndPoint(sdpBody).Address; //rj2: need to consider publicAddress and IPv6 for mangling IPAddress pubaddr = IPAddress.Parse(publicIPAddress); string sdpAddress = addr.ToString(); // Only mangle if there is something to change. For example the server could be on the same private subnet in which case it can't help. if (IPSocket.IsPrivateAddress(sdpAddress) && publicIPAddress != sdpAddress && pubaddr.AddressFamily == AddressFamily.InterNetworkV6 && addr.AddressFamily == AddressFamily.InterNetworkV6) { string mangledSDP = Regex.Replace(sdpBody, @"c=IN IP6 (?<ipaddress>([:a-fA-F0-9]+))", "c=IN IP6" + publicIPAddress, RegexOptions.Singleline); wasMangled = true; return(mangledSDP); } else if (IPSocket.IsPrivateAddress(sdpAddress) && publicIPAddress != sdpAddress && pubaddr.AddressFamily == AddressFamily.InterNetwork && addr.AddressFamily == AddressFamily.InterNetwork) { //logger.LogDebug("MangleSDP replacing private " + sdpAddress + " with " + publicIPAddress + "."); string mangledSDP = Regex.Replace(sdpBody, @"c=IN IP4 (?<ipaddress>(\d+\.){3}\d+)", "c=IN IP4 " + publicIPAddress, RegexOptions.Singleline); wasMangled = true; return(mangledSDP); } } else { Logger.Logger.Warn("Mangle SDP was called with an empty body or public IP address."); } return(sdpBody); } catch (Exception excp) { Logger.Logger.Error("Exception MangleSDP. ->" + excp.Message); return(sdpBody); } }
/// <summary> /// Mangles private IP addresses in a SIP response replacing them with the IP address the packet was received on. /// </summary> /// <param name="sipResponse">The unmangled SIP response.</param> /// <returns>The mangled SIP response</returns> public static void MangleSIPResponse(SIPMonitorServerTypesEnum server, SIPResponse sipResponse, SIPEndPoint remoteEndPoint, string username, SIPMonitorLogDelegate logDelegate) { try { if (sipResponse.Header.Contact != null && sipResponse.Header.Contact.Count > 0) { string contactHost = sipResponse.Header.Contact[0].ContactURI.Host; // Only mangle if the host is a private IP address and there is something to change. // For example the server could be on the same private subnet in which case it can't help. if (IPSocket.IsPrivateAddress(contactHost) && contactHost != remoteEndPoint.Address.ToString()) { SIPURI origContact = sipResponse.Header.Contact[0].ContactURI; sipResponse.Header.Contact[0].ContactURI = new SIPURI(origContact.Scheme, remoteEndPoint); //logger.LogDebug("INVITE response Contact URI identified as containing private address, original " + origContact + " adjusted to " + remoteEndPoint.ToString() + "."); //FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.Registrar, SIPMonitorServerTypesEnum.ContactRegisterInProgress, "INVITE Response contact adjusted from " + origContact + " to " + remoteEndPoint.ToString() + ".", username)); } } if (sipResponse.Body != null) { bool wasMangled = false; string mangledSDP = MangleSDP(sipResponse.Body, remoteEndPoint.Address.ToString(), out wasMangled); if (wasMangled) { sipResponse.Body = mangledSDP; sipResponse.Header.ContentLength = sipResponse.Body.Length; if (logDelegate != null) { logDelegate(new SIPMonitorConsoleEvent(server, SIPMonitorEventTypesEnum.DialPlan, "SDP mangled for " + sipResponse.Status.ToString() + " response from " + sipResponse.RemoteSIPEndPoint.ToString() + ", adjusted address " + remoteEndPoint.Address.ToString() + ".", username)); } } } } catch (Exception excp) { Logger.Logger.Error("Exception MangleSIPResponse. ->" + excp.Message); } }
public void Call(SIPCallDescriptor sipCallDescriptor) { CallDescriptor = sipCallDescriptor; m_xmppClient = new XMPPClient("talk.google.com", 5222, "google.com", null, null); m_xmppClient.IsBound += IsBound; m_xmppClient.Answered += Answered; IPEndPoint sdpEndPoint = SDP.GetSDPRTPEndPoint(CallDescriptor.Content); if (IPSocket.IsPrivateAddress(sdpEndPoint.Address.ToString())) { bool wasSDPMangled; CallDescriptor.Content = SIPPacketMangler.MangleSDP(CallDescriptor.Content, CallDescriptor.MangleIPAddress.ToString(), out wasSDPMangled); } ThreadPool.QueueUserWorkItem(delegate { m_xmppClient.Connect(); }); }
/// <summary> /// Sends the ACK request as a new transaction. This is required for 2xx responses. /// </summary> /// <param name="content">The optional content body for the ACK request.</param> /// <param name="contentType">The optional content type.</param> private SIPRequest Get2xxAckRequest(string content, string contentType) { try { var sipResponse = m_transactionFinalResponse; if (sipResponse.Header.To != null) { m_remoteTag = sipResponse.Header.To.ToTag; } SIPURI ackURI = m_transactionRequest.URI; if (sipResponse.Header.Contact != null && sipResponse.Header.Contact.Count > 0) { ackURI = sipResponse.Header.Contact[0].ContactURI; // Don't mangle private contacts if there is a Record-Route header. If a proxy is putting private IP's in a Record-Route header that's its problem. if ((sipResponse.Header.RecordRoutes == null || sipResponse.Header.RecordRoutes.Length == 0) && IPSocket.IsPrivateAddress(ackURI.Host) && !sipResponse.Header.ProxyReceivedFrom.IsNullOrBlank()) { // Setting the Proxy-ReceivedOn header is how an upstream proxy will let an agent know it should mangle the contact. SIPEndPoint remoteUASSIPEndPoint = SIPEndPoint.ParseSIPEndPoint(sipResponse.Header.ProxyReceivedFrom); ackURI.Host = remoteUASSIPEndPoint.GetIPEndPoint().ToString(); } } // ACK for 2xx response needs to be a new transaction and gets routed based on SIP request fields. var ackRequest = GetNewTransactionAcknowledgeRequest(SIPMethodsEnum.ACK, sipResponse, ackURI); if (content.NotNullOrBlank()) { ackRequest.Body = content; ackRequest.Header.ContentLength = ackRequest.Body.Length; ackRequest.Header.ContentType = contentType; } return(ackRequest); } catch (Exception excp) { Logger.Logger.Error($"Exception Get2xxAckRequest. ->{excp.Message}"); throw excp; } }
/// <summary> /// Places an outgoing SIP call. /// </summary> /// <param name="destination">The SIP URI to place a call to. The destination can be a full SIP URI in which case the all will /// be placed anonymously directly to that URI. Alternatively it can be just the user portion of a URI in which case it will /// be sent to the configured SIP server.</param> public void Call(MediaManager mediaManager, string destination) { //_initialisationTask.Wait(_cancelCallTokenSource.Token); _mediaManager = mediaManager; _mediaManager.NewCall(); // Determine if this is a direct anonymous call or whether it should be placed using the pre-configured SIP server account. SIPURI callURI = null; string sipUsername = null; string sipPassword = null; string fromHeader = null; if (destination.Contains("@") || m_sipServer == null) { // Anonymous call direct to SIP server specified in the URI. callURI = SIPURI.ParseSIPURIRelaxed(destination); fromHeader = (new SIPFromHeader(m_sipFromName, SIPURI.ParseSIPURI(SIPFromHeader.DEFAULT_FROM_URI), null)).ToString(); } else { // This call will use the pre-configured SIP account. callURI = SIPURI.ParseSIPURIRelaxed(destination + "@" + m_sipServer); sipUsername = m_sipUsername; sipPassword = m_sipPassword; fromHeader = (new SIPFromHeader(m_sipFromName, new SIPURI(m_sipUsername, m_sipServer, null), null)).ToString(); } StatusMessage("Starting call to " + callURI.ToString() + "."); m_uac = new SIPClientUserAgent(m_sipTransport, null, null, null, null); m_uac.CallTrying += CallTrying; m_uac.CallRinging += CallRinging; m_uac.CallAnswered += CallAnswered; m_uac.CallFailed += CallFailed; // Get the SDP requesting that the public IP address be used if the host on the call destination is not a private IP address. SDP sdp = _mediaManager.GetSDP(!(IPSocket.IsIPAddress(callURI.Host) && IPSocket.IsPrivateAddress(callURI.Host))); System.Diagnostics.Debug.WriteLine(sdp.ToString()); SIPCallDescriptor callDescriptor = new SIPCallDescriptor(sipUsername, sipPassword, callURI.ToString(), fromHeader, null, null, null, null, SIPCallDirection.Out, _sdpMimeContentType, sdp.ToString(), null); m_uac.Call(callDescriptor); }
/// <summary> /// This constructor is used by server user agents or SIP elements acting in a server user agent role. When /// acting as a server user agent the local fields are contained in the To header and the remote fields are /// in the From header. /// </summary> public SIPDialogue( UASInviteTransaction uasInviteTransaction) { Id = Guid.NewGuid(); CallId = uasInviteTransaction.TransactionRequest.Header.CallId; //RouteSet = (uasInviteTransaction.TransactionFinalResponse != null && uasInviteTransaction.TransactionFinalResponse.Header.RecordRoutes != null) ? uasInviteTransaction.TransactionFinalResponse.Header.RecordRoutes.Reversed() : null; RouteSet = (uasInviteTransaction.TransactionFinalResponse != null && uasInviteTransaction.TransactionFinalResponse.Header.RecordRoutes != null) ? uasInviteTransaction.TransactionFinalResponse.Header.RecordRoutes : null; LocalUserField = uasInviteTransaction.TransactionFinalResponse.Header.To.ToUserField; LocalTag = uasInviteTransaction.TransactionFinalResponse.Header.To.ToTag; RemoteUserField = uasInviteTransaction.TransactionFinalResponse.Header.From.FromUserField; RemoteTag = uasInviteTransaction.TransactionFinalResponse.Header.From.FromTag; CSeq = uasInviteTransaction.TransactionRequest.Header.CSeq; CDRId = uasInviteTransaction.CDR != null ? uasInviteTransaction.CDR.CDRId : Guid.Empty; ContentType = uasInviteTransaction.TransactionFinalResponse.Header.ContentType; SDP = uasInviteTransaction.TransactionFinalResponse.Body; RemoteSDP = uasInviteTransaction.TransactionRequest.Body ?? uasInviteTransaction.AckRequest.Body; Inserted = DateTime.UtcNow; Direction = SIPCallDirection.In; if (uasInviteTransaction.m_gotPrack) { CSeq++; } RemoteTarget = new SIPURI(uasInviteTransaction.TransactionRequest.URI.Scheme, uasInviteTransaction.TransactionRequest.RemoteSIPEndPoint.CopyOf()); ProxySendFrom = uasInviteTransaction.TransactionRequest.Header.ProxyReceivedOn; if (uasInviteTransaction.TransactionRequest.Header.Contact != null && uasInviteTransaction.TransactionRequest.Header.Contact.Count > 0) { RemoteTarget = uasInviteTransaction.TransactionRequest.Header.Contact[0].ContactURI.CopyOf(); if (!uasInviteTransaction.TransactionRequest.Header.ProxyReceivedFrom.IsNullOrBlank()) { // Setting the Proxy-ReceivedOn header is how an upstream proxy will let an agent know it should mangle the contact. // Don't mangle private contacts if there is a Record-Route header. If a proxy is putting private IP's in a Record-Route header that's its problem. if (RouteSet == null && IPSocket.IsPrivateAddress(RemoteTarget.Host)) { SIPEndPoint remoteUASSIPEndPoint = SIPEndPoint.ParseSIPEndPoint(uasInviteTransaction.TransactionRequest.Header.ProxyReceivedFrom); RemoteTarget.Host = remoteUASSIPEndPoint.GetIPEndPoint().ToString(); } } } }
/// <summary> /// Places an outgoing SIP call. /// </summary> /// <param name="destination">The SIP URI to place a call to. The destination can be a full SIP URI in which case the all will /// be placed anonymously directly to that URI. Alternatively it can be just the user portion of a URI in which case it will /// be sent to the configured SIP server.</param> public void Call(string destination) { // Determine if this is a direct anonymous call or whether it should be placed using the pre-configured SIP server account. SIPURI callURI = null; string sipUsername = null; string sipPassword = null; string fromHeader = null; if (destination.Contains("@") || m_sipServer == null) { // Anonymous call direct to SIP server specified in the URI. callURI = SIPURI.ParseSIPURIRelaxed(destination); } else { // This call will use the pre-configured SIP account. callURI = SIPURI.ParseSIPURIRelaxed(destination + "@" + m_sipServer); sipUsername = m_sipUsername; sipPassword = m_sipPassword; fromHeader = (new SIPFromHeader(m_sipFromName, new SIPURI(m_sipUsername, m_sipServer, null), null)).ToString(); } StatusMessage("Starting call to " + callURI.ToString() + "."); m_uac = new SIPClientUserAgent(m_sipTransport, null, null, null, null); m_uac.CallTrying += CallTrying; m_uac.CallRinging += CallRinging; m_uac.CallAnswered += CallAnswered; m_uac.CallFailed += CallFailed; _audioChannel = new AudioChannel(); // Get the SDP requesting that the public IP address be used if the host on the call destination is not a private IP address. SDP sdp = _audioChannel.GetSDP(!(IPSocket.IsIPAddress(callURI.Host) && IPSocket.IsPrivateAddress(callURI.Host))); SIPCallDescriptor callDescriptor = new SIPCallDescriptor(sipUsername, sipPassword, callURI.ToString(), fromHeader, null, null, null, null, SIPCallDirection.Out, SDP.SDP_MIME_CONTENTTYPE, sdp.ToString(), null); m_uac.Call(callDescriptor); }
public void Send2xxAckRequest(string content, string contentType) { var sipResponse = m_transactionFinalResponse; if (sipResponse.Header.To != null) { m_remoteTag = sipResponse.Header.To.ToTag; } SIPURI ackURI = m_transactionRequest.URI; if (sipResponse.Header.Contact != null && sipResponse.Header.Contact.Count > 0) { ackURI = sipResponse.Header.Contact[0].ContactURI; // Don't mangle private contacts if there is a Record-Route header. If a proxy is putting private IP's in a Record-Route header that's its problem. if ((sipResponse.Header.RecordRoutes == null || sipResponse.Header.RecordRoutes.Length == 0) && IPSocket.IsPrivateAddress(ackURI.Host) && !sipResponse.Header.ProxyReceivedFrom.IsNullOrBlank()) { // Setting the Proxy-ReceivedOn header is how an upstream proxy will let an agent know it should mangle the contact. SIPEndPoint remoteUASSIPEndPoint = SIPEndPoint.ParseSIPEndPoint(sipResponse.Header.ProxyReceivedFrom); ackURI.Host = remoteUASSIPEndPoint.GetIPEndPoint().ToString(); } } // ACK for 2xx response needs to be a new transaction and gets routed based on SIP request fields. var ackRequest = GetNewTransactionACKRequest(sipResponse, ackURI, LocalSIPEndPoint); if (content.NotNullOrBlank()) { ackRequest.Body = content; ackRequest.Header.ContentLength = ackRequest.Body.Length; ackRequest.Header.ContentType = contentType; } base.SendRequest(ackRequest); }
/// <summary> /// Generates the ACK or PRACK request to acknowledge a response. This method generates the ACK requests /// for INVITE 2xx and PRACK for 1xx responses. The request needs to be sent as part of a new transaction. /// Note for constructing the ACK for INVITE >= 300 responses is <seealso cref="GetInTransactionACKRequest"/>. /// </summary> /// <param name="ackResponse">The response being acknowledged.</param> /// <param name="ackMethod">The acknowledgement request method, either ACK or PRACK.</param> /// <param name="cseq">The SIP CSeq header value to set on the acknowledge request.</param> /// <param name="content">The optional content body for the ACK request.</param> /// <param name="contentType">The optional content type.</param> private SIPRequest GetAcknowledgeRequest(SIPResponse ackResponse, SIPMethodsEnum ackMethod, int cseq, string content, string contentType) { if (ackResponse.Header.To != null) { m_remoteTag = ackResponse.Header.To.ToTag; } SIPURI requestURI = m_transactionRequest.URI.CopyOf(); if (ackResponse.Header.Contact?.Count > 0) { requestURI = ackResponse.Header.Contact[0].ContactURI; // Don't mangle private contacts if there is a Record-Route header. If a proxy is putting private IP's // in a Record-Route header that's its problem. if ((ackResponse.Header.RecordRoutes == null || ackResponse.Header.RecordRoutes.Length == 0) && IPSocket.IsPrivateAddress(requestURI.Host) && !ackResponse.Header.ProxyReceivedFrom.IsNullOrBlank()) { // Setting the Proxy-ReceivedOn header is how an upstream proxy will let an agent know it should // mangle the contact. SIPEndPoint remoteUASSIPEndPoint = SIPEndPoint.ParseSIPEndPoint(ackResponse.Header.ProxyReceivedFrom); requestURI.Host = remoteUASSIPEndPoint.GetIPEndPoint().ToString(); } } // ACK for 2xx response needs to be a new transaction and gets routed based on SIP request fields. var ackRequest = GetNewTxACKRequest(ackMethod, cseq, ackResponse, requestURI); if (content.NotNullOrBlank()) { ackRequest.Body = content; ackRequest.Header.ContentLength = ackRequest.Body.Length; ackRequest.Header.ContentType = contentType; } return(ackRequest); }
private static void SIPTransportRequestReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest) { if (sipRequest.Method == SIPMethodsEnum.BYE) { var rtpJob = (from job in m_rtpJobs.Values where job.UAS.CallRequest.Header.CallId == sipRequest.Header.CallId select job).FirstOrDefault(); if (rtpJob != null) { rtpJob.Stop(); // Call has been hungup by remote end. Console.WriteLine("Call hungup by client: " + localSIPEndPoint + "<-" + remoteEndPoint + " " + sipRequest.URI.ToString() + ".\n"); Publish(rtpJob.QueueName, "BYE request received from " + remoteEndPoint + " for " + sipRequest.URI.ToString() + "."); //Console.WriteLine("Request Received " + localSIPEndPoint + "<-" + remoteEndPoint + "\n" + sipRequest.ToString()); //m_uas.SIPDialogue.Hangup(m_sipTransport, null); SIPResponse okResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); m_sipTransport.SendResponse(okResponse); } else { Console.WriteLine("Unmatched BYE request received for " + sipRequest.URI.ToString() + ".\n"); SIPResponse noCallLegResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist, null); m_sipTransport.SendResponse(noCallLegResponse); } } else if (sipRequest.Method == SIPMethodsEnum.INVITE) { Console.WriteLine("Incoming call request: " + localSIPEndPoint + "<-" + remoteEndPoint + " " + sipRequest.URI.ToString() + ".\n"); Publish(sipRequest.URI.User, "INVITE request received from " + remoteEndPoint + " for " + sipRequest.URI.ToString() + "."); Console.WriteLine(sipRequest.Body); SIPPacketMangler.MangleSIPRequest(SIPMonitorServerTypesEnum.Unknown, sipRequest, null, LogTraceMessage); UASInviteTransaction uasTransaction = m_sipTransport.CreateUASTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, null); var uas = new SIPServerUserAgent(m_sipTransport, null, null, null, SIPCallDirection.In, null, null, LogTraceMessage, uasTransaction); uas.CallCancelled += UASCallCancelled; RTPDiagnosticsJob rtpJob = new RTPDiagnosticsJob(m_rtpListenIPAddress, m_publicIPAddress, uas, sipRequest); string sdpAddress = SDP.GetSDPRTPEndPoint(sipRequest.Body).Address.ToString(); // Only mangle if there is something to change. For example the server could be on the same private subnet in which case it can't help. IPEndPoint expectedRTPEndPoint = new IPEndPoint(rtpJob.RemoteRTPEndPoint.Address, rtpJob.RemoteRTPEndPoint.Port); if (IPSocket.IsPrivateAddress(rtpJob.RemoteRTPEndPoint.Address.ToString())) { expectedRTPEndPoint.Address = remoteEndPoint.Address; } Publish(sipRequest.URI.User, "Advertised RTP remote socket " + rtpJob.RemoteRTPEndPoint + ", expecting from " + expectedRTPEndPoint + "."); m_rtpJobs.Add(rtpJob.RTPListenEndPoint.Port, rtpJob); //ThreadPool.QueueUserWorkItem(delegate { StartRTPListener(rtpJob); }); Console.WriteLine(rtpJob.LocalSDP.ToString()); uas.Answer("application/sdp", rtpJob.LocalSDP.ToString(), CallProperties.CreateNewTag(), null, SIPDialogueTransferModesEnum.NotAllowed); var hangupTimer = new Timer(delegate { if (!rtpJob.StopJob) { if (uas != null && uas.SIPDialogue != null) { if (rtpJob.RTPPacketReceived && !rtpJob.ErrorOnRTPSend) { Publish(sipRequest.URI.User, "Test completed. There were no RTP send or receive errors."); } else if (!rtpJob.RTPPacketReceived) { Publish(sipRequest.URI.User, "Test completed. An error was identified, no RTP packets were received."); } else { Publish(sipRequest.URI.User, "Test completed. An error was identified, there was a problem when attempting to send an RTP packet."); } rtpJob.Stop(); uas.SIPDialogue.Hangup(m_sipTransport, null); } } }, null, HANGUP_TIMEOUT, Timeout.Infinite); } else if (sipRequest.Method == SIPMethodsEnum.CANCEL) { UASInviteTransaction inviteTransaction = (UASInviteTransaction)m_sipTransport.GetTransaction(SIPTransaction.GetRequestTransactionId(sipRequest.Header.Vias.TopViaHeader.Branch, SIPMethodsEnum.INVITE)); if (inviteTransaction != null) { Console.WriteLine("Matching CANCEL request received " + sipRequest.URI.ToString() + ".\n"); Publish(sipRequest.URI.User, "CANCEL request received from " + remoteEndPoint + " for " + sipRequest.URI.ToString() + "."); SIPCancelTransaction cancelTransaction = m_sipTransport.CreateCancelTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, inviteTransaction); cancelTransaction.GotRequest(localSIPEndPoint, remoteEndPoint, sipRequest); } else { Console.WriteLine("No matching transaction was found for CANCEL to " + sipRequest.URI.ToString() + ".\n"); SIPResponse noCallLegResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist, null); m_sipTransport.SendResponse(noCallLegResponse); } } else { Console.WriteLine("SIP " + sipRequest.Method + " request received but no processing has been set up for it, rejecting.\n"); Publish(sipRequest.URI.User, sipRequest.Method + " request received from " + remoteEndPoint + " for " + sipRequest.URI.ToString() + "."); SIPResponse notAllowedResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.MethodNotAllowed, null); m_sipTransport.SendResponse(notAllowedResponse); } }