NetworkUtils.cs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. // **********************************************************************************
  2. // CassiniDev - http://cassinidev.codeplex.com
  3. //
  4. // Copyright (c) 2010 Sky Sanders. All rights reserved.
  5. //
  6. // This source code is subject to terms and conditions of the Microsoft Public
  7. // License (Ms-PL). A copy of the license can be found in the license.txt file
  8. // included in this distribution.
  9. //
  10. // You must not remove this notice, or any other, from this software.
  11. //
  12. // **********************************************************************************
  13. #region
  14. using System;
  15. using System.Collections.Generic;
  16. using System.Linq;
  17. using System.Net;
  18. using System.Net.NetworkInformation;
  19. using System.Text.RegularExpressions;
  20. using System.Threading;
  21. #endregion
  22. namespace CassiniDev
  23. {
  24. public static class CassiniNetworkUtils
  25. {
  26. public static IPAddress[] GetLocalAddresses()
  27. {
  28. string strHostName = Dns.GetHostName();
  29. IPHostEntry ipEntry = Dns.GetHostEntry(strHostName);
  30. return ipEntry.AddressList;
  31. }
  32. /// <summary>
  33. /// Returns first available port on the specified IP address.
  34. /// The port scan excludes ports that are open on ANY loopback adapter.
  35. ///
  36. /// If the address upon which a port is requested is an 'ANY' address all
  37. /// ports that are open on ANY IP are excluded.
  38. /// </summary>
  39. /// <param name="rangeStart"></param>
  40. /// <param name="rangeEnd"></param>
  41. /// <param name="ip">The IP address upon which to search for available port.</param>
  42. /// <param name="includeIdlePorts">If true includes ports in TIME_WAIT state in results.
  43. /// TIME_WAIT state is typically cool down period for recently released ports.</param>
  44. /// <returns></returns>
  45. public static int GetAvailablePort(int rangeStart, int rangeEnd, IPAddress ip, bool includeIdlePorts)
  46. {
  47. IPGlobalProperties ipProps = IPGlobalProperties.GetIPGlobalProperties();
  48. // if the ip we want a port on is an 'any' or loopback port we need to exclude all ports that are active on any IP
  49. Func<IPAddress, bool> isIpAnyOrLoopBack = i => IPAddress.Any.Equals(i) ||
  50. IPAddress.IPv6Any.Equals(i) ||
  51. IPAddress.Loopback.Equals(i) ||
  52. IPAddress.IPv6Loopback.
  53. Equals(i);
  54. // get all active ports on specified IP.
  55. List<ushort> excludedPorts = new List<ushort>();
  56. // if a port is open on an 'any' or 'loopback' interface then include it in the excludedPorts
  57. excludedPorts.AddRange(from n in ipProps.GetActiveTcpConnections()
  58. where
  59. n.LocalEndPoint.Port >= rangeStart &&
  60. n.LocalEndPoint.Port <= rangeEnd && (
  61. isIpAnyOrLoopBack(ip) ||
  62. n.LocalEndPoint.Address.Equals(ip) ||
  63. isIpAnyOrLoopBack(n.LocalEndPoint.Address)) &&
  64. (!includeIdlePorts || n.State != TcpState.TimeWait)
  65. select (ushort) n.LocalEndPoint.Port);
  66. excludedPorts.AddRange(from n in ipProps.GetActiveTcpListeners()
  67. where n.Port >= rangeStart && n.Port <= rangeEnd && (
  68. isIpAnyOrLoopBack(ip) ||
  69. n.Address.Equals(ip) ||
  70. isIpAnyOrLoopBack(n.Address))
  71. select (ushort) n.Port);
  72. excludedPorts.AddRange(from n in ipProps.GetActiveUdpListeners()
  73. where n.Port >= rangeStart && n.Port <= rangeEnd && (
  74. isIpAnyOrLoopBack(ip) ||
  75. n.Address.Equals(ip) ||
  76. isIpAnyOrLoopBack(n.Address))
  77. select (ushort) n.Port);
  78. excludedPorts.Sort();
  79. for (int port = rangeStart; port <= rangeEnd; port++)
  80. {
  81. if (!excludedPorts.Contains((ushort) port))
  82. {
  83. return port;
  84. }
  85. }
  86. return 0;
  87. }
  88. ///<summary>
  89. /// Returns the first IPV4 address available for this host.
  90. /// This is typically an external IP
  91. ///</summary>
  92. ///<returns></returns>
  93. public static IPAddress GetExternalIPV4()
  94. {
  95. return GetIPAdresses().ToList()
  96. .FirstOrDefault(i => i.ToString().IndexOf(":") == -1);
  97. }
  98. ///<summary>
  99. ///</summary>
  100. ///<returns></returns>
  101. public static string GetHostName()
  102. {
  103. return Dns.GetHostName();
  104. }
  105. ///<summary>
  106. /// Gets all IP addresses for this host
  107. ///</summary>
  108. public static IPAddress[] GetIPAdresses()
  109. {
  110. return Dns.GetHostEntry(GetHostName()).AddressList;
  111. }
  112. /// <summary>
  113. /// Gently polls specified IP:Port to determine if it is available.
  114. /// </summary>
  115. /// <param name="ipAddress"></param>
  116. /// <param name="port"></param>
  117. public static bool IsPortAvailable(IPAddress ipAddress, int port)
  118. {
  119. bool portAvailable = false;
  120. for (int i = 0; i < 5; i++)
  121. {
  122. portAvailable = GetAvailablePort(port, port, ipAddress, true) == port;
  123. if (portAvailable)
  124. {
  125. break;
  126. }
  127. // be a little patient and wait for the port if necessary,
  128. // the previous occupant may have just vacated
  129. Thread.Sleep(100);
  130. }
  131. return portAvailable;
  132. }
  133. /// <summary>
  134. /// Combine the RootUrl of the running web application with the relative url
  135. /// specified.
  136. /// </summary>
  137. /// <param name="rootUrl"></param>
  138. /// <param name="relativeUrl"></param>
  139. /// <returns></returns>
  140. public static string NormalizeUrl(string rootUrl, string relativeUrl)
  141. {
  142. relativeUrl = relativeUrl.TrimStart('/');
  143. if (!rootUrl.EndsWith("/"))
  144. {
  145. rootUrl += "/";
  146. }
  147. return new Uri(rootUrl + relativeUrl).ToString();
  148. }
  149. ///<summary>
  150. ///</summary>
  151. ///<param name="ipString"></param>
  152. ///<returns></returns>
  153. public static IPAddress ParseIPString(string ipString)
  154. {
  155. if (string.IsNullOrEmpty(ipString))
  156. {
  157. ipString = "loopback";
  158. }
  159. ipString = ipString.Trim().ToLower();
  160. switch (ipString)
  161. {
  162. case "any":
  163. return IPAddress.Any;
  164. case "loopback":
  165. return IPAddress.Loopback;
  166. case "ipv6any":
  167. return IPAddress.IPv6Any;
  168. case "ipv6loopback":
  169. return IPAddress.IPv6Loopback;
  170. default:
  171. IPAddress result;
  172. IPAddress.TryParse(ipString, out result);
  173. return result;
  174. }
  175. }
  176. /// <summary>
  177. /// <para>
  178. /// Hostnames are composed of series of labels concatenated with dots, as are all domain names[1].
  179. /// For example, "en.wikipedia.org" is a hostname. Each label must be between 1 and 63 characters long,
  180. /// and the entire hostname has a maximum of 255 characters.</para>
  181. /// <para>
  182. /// The Internet standards (Request for Comments) for protocols mandate that component hostname
  183. /// labels may contain only the ASCII letters 'a' through 'z' (in a case-insensitive manner), the digits
  184. /// '0' through '9', and the hyphen. The original specification of hostnames in RFC 952, mandated that
  185. /// labels could not start with a digit or with a hyphen, and must not end with a hyphen. However, a
  186. /// subsequent specification (RFC 1123) permitted hostname labels to start with digits. No other symbols,
  187. /// punctuation characters, or blank spaces are permitted.</para>
  188. /// </summary>
  189. /// <param name="hostname"></param>
  190. /// <returns></returns>
  191. /// http://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names
  192. public static bool ValidateHostName(string hostname)
  193. {
  194. Regex hostnameRx =
  195. new Regex(
  196. @"^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$");
  197. return hostnameRx.IsMatch(hostname);
  198. }
  199. }
  200. }