PageRenderTime 57ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/src/app/SIPPacketMangler.cs

https://github.com/sipsorcery/sipsorcery
C# | 188 lines | 128 code | 22 blank | 38 comment | 46 complexity | 621e3f4e64dc28b2974ee11ec7d0ff49 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause
  1. // ============================================================================
  2. // FileName: SIPPacketMangler.cs
  3. //
  4. // Description:
  5. // A class containing functionality to mangle the Contact header and SDP payloads
  6. // of SIP messages.
  7. //
  8. // Author(s):
  9. // Aaron Clauson (aaron@sipsorcery.com)
  10. //
  11. // History:
  12. // 14 Sep 2008 Aaron Clauson Created, Hobart, Australia
  13. // (most methods extracted from StatefulProxyCore).
  14. //
  15. // License:
  16. // BSD 3-Clause "New" or "Revised" License, see included LICENSE.md file.
  17. // ============================================================================
  18. using System;
  19. using System.Net;
  20. using System.Net.Sockets;
  21. using System.Text.RegularExpressions;
  22. using Microsoft.Extensions.Logging;
  23. using SIPSorcery.Net;
  24. using SIPSorcery.Sys;
  25. namespace SIPSorcery.SIP.App
  26. {
  27. public class SIPPacketMangler
  28. {
  29. private static ILogger logger = Log.Logger;
  30. public static string MangleSDP(string sdpBody, string publicIPAddress, out bool wasMangled)
  31. {
  32. wasMangled = false;
  33. try
  34. {
  35. if (sdpBody != null && publicIPAddress != null)
  36. {
  37. IPAddress addr = SDP.GetSDPRTPEndPoint(sdpBody).Address;
  38. //rj2: need to consider publicAddress and IPv6 for mangling
  39. IPAddress pubaddr = IPAddress.Parse(publicIPAddress);
  40. string sdpAddress = addr.ToString();
  41. // 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.
  42. if (IPSocket.IsPrivateAddress(sdpAddress) && publicIPAddress != sdpAddress
  43. && pubaddr.AddressFamily == AddressFamily.InterNetworkV6
  44. && addr.AddressFamily == AddressFamily.InterNetworkV6)
  45. {
  46. string mangledSDP = Regex.Replace(sdpBody, @"c=IN IP6 (?<ipaddress>([:a-fA-F0-9]+))", "c=IN IP6" + publicIPAddress, RegexOptions.Singleline);
  47. wasMangled = true;
  48. return mangledSDP;
  49. }
  50. else if (IPSocket.IsPrivateAddress(sdpAddress) && publicIPAddress != sdpAddress
  51. && pubaddr.AddressFamily == AddressFamily.InterNetwork
  52. && addr.AddressFamily == AddressFamily.InterNetwork)
  53. {
  54. //logger.LogDebug("MangleSDP replacing private " + sdpAddress + " with " + publicIPAddress + ".");
  55. string mangledSDP = Regex.Replace(sdpBody, @"c=IN IP4 (?<ipaddress>(\d+\.){3}\d+)", "c=IN IP4 " + publicIPAddress, RegexOptions.Singleline);
  56. wasMangled = true;
  57. return mangledSDP;
  58. }
  59. }
  60. else
  61. {
  62. logger.LogWarning("Mangle SDP was called with an empty body or public IP address.");
  63. }
  64. return sdpBody;
  65. }
  66. catch (Exception excp)
  67. {
  68. logger.LogError("Exception MangleSDP. " + excp.Message);
  69. return sdpBody;
  70. }
  71. }
  72. /// <summary>
  73. /// Mangles private IP addresses in a SIP request replacing them with the IP address the packet was received on.
  74. /// </summary>
  75. /// <param name="sipRequest">The unmangled SIP request.</param>
  76. /// <returns>The mangled SIP request</returns>
  77. public static void MangleSIPRequest(SIPRequest sipRequest)
  78. {
  79. try
  80. {
  81. string bottomViaIPAddress = sipRequest.Header.Vias.BottomViaHeader.ReceivedFromIPAddress;
  82. if (sipRequest.Header.Contact != null && sipRequest.Header.Contact.Count == 1 && bottomViaIPAddress != null)
  83. {
  84. string contactHost = sipRequest.Header.Contact[0].ContactURI.Host;
  85. // Only mangle if the host is a private IP address and there is something to change.
  86. // For example the server could be on the same private subnet in which case it can't help.
  87. if (IPSocket.IsPrivateAddress(contactHost) && contactHost != bottomViaIPAddress)
  88. {
  89. string origContact = sipRequest.Header.Contact[0].ContactURI.Host;
  90. sipRequest.Header.Contact[0].ContactURI.Host = sipRequest.Header.Vias.BottomViaHeader.ReceivedFromAddress;
  91. //logger.LogDebug("Contact URI identified as containing private address for " + sipRequest.Method + " " + origContact + " adjusting to use bottom via " + bottomViaHost + ".");
  92. //FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.Registrar, SIPMonitorServerTypesEnum.ContactRegisterInProgress, "Contact on " + sipRequest.Method + " " + origContact + " had private address adjusted to " + bottomViaHost + ".", username));
  93. }
  94. }
  95. if (sipRequest.Body != null && bottomViaIPAddress != null)
  96. {
  97. bool wasMangled = false;
  98. string mangledSDP = MangleSDP(sipRequest.Body, bottomViaIPAddress, out wasMangled);
  99. if (wasMangled)
  100. {
  101. sipRequest.Body = mangledSDP;
  102. sipRequest.Header.ContentLength = sipRequest.Body.Length;
  103. logger.LogDebug("SDP mangled for " + sipRequest.Method.ToString() + " request from " + sipRequest.RemoteSIPEndPoint.ToString() + ", adjusted address " + bottomViaIPAddress + ".");
  104. }
  105. }
  106. }
  107. catch (Exception excp)
  108. {
  109. logger.LogError("Exception MangleSIPRequest. " + excp.Message);
  110. }
  111. }
  112. /// <summary>
  113. /// Mangles private IP addresses in a SIP response replacing them with the IP address the packet was received on.
  114. /// </summary>
  115. /// <param name="sipResponse">The unmangled SIP response.</param>
  116. /// <returns>The mangled SIP response</returns>
  117. public static void MangleSIPResponse(SIPResponse sipResponse, SIPEndPoint remoteEndPoint)
  118. {
  119. try
  120. {
  121. if (sipResponse.Header.Contact != null && sipResponse.Header.Contact.Count > 0)
  122. {
  123. string contactHost = sipResponse.Header.Contact[0].ContactURI.Host;
  124. // Only mangle if the host is a private IP address and there is something to change.
  125. // For example the server could be on the same private subnet in which case it can't help.
  126. if (IPSocket.IsPrivateAddress(contactHost) && contactHost != remoteEndPoint.Address.ToString())
  127. {
  128. SIPURI origContact = sipResponse.Header.Contact[0].ContactURI;
  129. sipResponse.Header.Contact[0].ContactURI = new SIPURI(origContact.Scheme, remoteEndPoint);
  130. //logger.LogDebug("INVITE response Contact URI identified as containing private address, original " + origContact + " adjusted to " + remoteEndPoint.ToString() + ".");
  131. //FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.Registrar, SIPMonitorServerTypesEnum.ContactRegisterInProgress, "INVITE Response contact adjusted from " + origContact + " to " + remoteEndPoint.ToString() + ".", username));
  132. }
  133. }
  134. if (sipResponse.Body != null)
  135. {
  136. bool wasMangled = false;
  137. string mangledSDP = MangleSDP(sipResponse.Body, remoteEndPoint.Address.ToString(), out wasMangled);
  138. if (wasMangled)
  139. {
  140. sipResponse.Body = mangledSDP;
  141. sipResponse.Header.ContentLength = sipResponse.Body.Length;
  142. logger.LogDebug("SDP mangled for " + sipResponse.Status.ToString() + " response from " + sipResponse.RemoteSIPEndPoint.ToString() + ", adjusted address " + remoteEndPoint.Address.ToString() + ".");
  143. }
  144. }
  145. }
  146. catch (Exception excp)
  147. {
  148. logger.LogError("Exception MangleSIPResponse. " + excp.Message);
  149. }
  150. }
  151. public static IPAddress GetRequestIPAddress(SIPRequest sipRequest)
  152. {
  153. IPAddress requestIPAddress = null;
  154. string remoteUAStr = sipRequest.Header.ProxyReceivedFrom;
  155. if (!remoteUAStr.IsNullOrBlank())
  156. {
  157. requestIPAddress = SIPEndPoint.ParseSIPEndPoint(remoteUAStr).Address;
  158. }
  159. else if (sipRequest.RemoteSIPEndPoint != null)
  160. {
  161. requestIPAddress = sipRequest.RemoteSIPEndPoint.Address;
  162. }
  163. return requestIPAddress;
  164. }
  165. }
  166. }