diff options
Diffstat (limited to 'Emby.Server.Implementations/Networking')
6 files changed, 2736 insertions, 40 deletions
diff --git a/Emby.Server.Implementations/Networking/IPNetwork/BigIntegerExt.cs b/Emby.Server.Implementations/Networking/IPNetwork/BigIntegerExt.cs new file mode 100644 index 000000000..afb202fa3 --- /dev/null +++ b/Emby.Server.Implementations/Networking/IPNetwork/BigIntegerExt.cs @@ -0,0 +1,168 @@ +using System.Collections.Generic; + +namespace System.Net +{ + using System; + using System.Numerics; + using System.Text; + + /// <summary> + /// Extension methods to convert <see cref="System.Numerics.BigInteger"/> + /// instances to hexadecimal, octal, and binary strings. + /// </summary> + public static class BigIntegerExtensions + { + /// <summary> + /// Converts a <see cref="BigInteger"/> to a binary string. + /// </summary> + /// <param name="bigint">A <see cref="BigInteger"/>.</param> + /// <returns> + /// A <see cref="System.String"/> containing a binary + /// representation of the supplied <see cref="BigInteger"/>. + /// </returns> + public static string ToBinaryString(this BigInteger bigint) + { + var bytes = bigint.ToByteArray(); + var idx = bytes.Length - 1; + + // Create a StringBuilder having appropriate capacity. + var base2 = new StringBuilder(bytes.Length * 8); + + // Convert first byte to binary. + var binary = Convert.ToString(bytes[idx], 2); + + // Ensure leading zero exists if value is positive. + if (binary[0] != '0' && bigint.Sign == 1) + { + base2.Append('0'); + } + + // Append binary string to StringBuilder. + base2.Append(binary); + + // Convert remaining bytes adding leading zeros. + for (idx--; idx >= 0; idx--) + { + base2.Append(Convert.ToString(bytes[idx], 2).PadLeft(8, '0')); + } + + return base2.ToString(); + } + + /// <summary> + /// Converts a <see cref="BigInteger"/> to a hexadecimal string. + /// </summary> + /// <param name="bigint">A <see cref="BigInteger"/>.</param> + /// <returns> + /// A <see cref="System.String"/> containing a hexadecimal + /// representation of the supplied <see cref="BigInteger"/>. + /// </returns> + public static string ToHexadecimalString(this BigInteger bigint) + { + return bigint.ToString("X"); + } + + /// <summary> + /// Converts a <see cref="BigInteger"/> to a octal string. + /// </summary> + /// <param name="bigint">A <see cref="BigInteger"/>.</param> + /// <returns> + /// A <see cref="System.String"/> containing an octal + /// representation of the supplied <see cref="BigInteger"/>. + /// </returns> + public static string ToOctalString(this BigInteger bigint) + { + var bytes = bigint.ToByteArray(); + var idx = bytes.Length - 1; + + // Create a StringBuilder having appropriate capacity. + var base8 = new StringBuilder(((bytes.Length / 3) + 1) * 8); + + // Calculate how many bytes are extra when byte array is split + // into three-byte (24-bit) chunks. + var extra = bytes.Length % 3; + + // If no bytes are extra, use three bytes for first chunk. + if (extra == 0) + { + extra = 3; + } + + // Convert first chunk (24-bits) to integer value. + int int24 = 0; + for (; extra != 0; extra--) + { + int24 <<= 8; + int24 += bytes[idx--]; + } + + // Convert 24-bit integer to octal without adding leading zeros. + var octal = Convert.ToString(int24, 8); + + // Ensure leading zero exists if value is positive. + if (octal[0] != '0') + { + if (bigint.Sign == 1) + { + base8.Append('0'); + } + } + + // Append first converted chunk to StringBuilder. + base8.Append(octal); + + // Convert remaining 24-bit chunks, adding leading zeros. + for (; idx >= 0; idx -= 3) + { + int24 = (bytes[idx] << 16) + (bytes[idx - 1] << 8) + bytes[idx - 2]; + base8.Append(Convert.ToString(int24, 8).PadLeft(8, '0')); + } + + return base8.ToString(); + } + + /// <summary> + /// + /// Reverse a Positive BigInteger ONLY + /// Bitwise ~ operator + /// + /// Input : FF FF FF FF + /// Width : 4 + /// Result : 00 00 00 00 + /// + /// + /// Input : 00 00 00 00 + /// Width : 4 + /// Result : FF FF FF FF + /// + /// Input : FF FF FF FF + /// Width : 8 + /// Result : FF FF FF FF 00 00 00 00 + /// + /// + /// Input : 00 00 00 00 + /// Width : 8 + /// Result : FF FF FF FF FF FF FF FF + /// + /// </summary> + /// <param name="input"></param> + /// <param name="width"></param> + /// <returns></returns> + public static BigInteger PositiveReverse(this BigInteger input, int width) + { + + var result = new List<byte>(); + var bytes = input.ToByteArray(); + var work = new byte[width]; + Array.Copy(bytes, 0, work, 0, bytes.Length - 1); // Length -1 : positive BigInteger + + for (int i = 0; i < work.Length; i++) + { + result.Add((byte)(~work[i])); + } + result.Add(0); // positive BigInteger + return new BigInteger(result.ToArray()); + + } + } +}
\ No newline at end of file diff --git a/Emby.Server.Implementations/Networking/IPNetwork/IPAddressCollection.cs b/Emby.Server.Implementations/Networking/IPNetwork/IPAddressCollection.cs new file mode 100644 index 000000000..2b31a0a32 --- /dev/null +++ b/Emby.Server.Implementations/Networking/IPNetwork/IPAddressCollection.cs @@ -0,0 +1,104 @@ +using System.Collections; +using System.Collections.Generic; +using System.Numerics; + +namespace System.Net +{ + public class IPAddressCollection : IEnumerable<IPAddress>, IEnumerator<IPAddress> + { + + private IPNetwork _ipnetwork; + private BigInteger _enumerator; + + internal IPAddressCollection(IPNetwork ipnetwork) + { + this._ipnetwork = ipnetwork; + this._enumerator = -1; + } + + + #region Count, Array, Enumerator + + public BigInteger Count + { + get + { + return this._ipnetwork.Total; + } + } + + public IPAddress this[BigInteger i] + { + get + { + if (i >= this.Count) + { + throw new ArgumentOutOfRangeException("i"); + } + byte width = this._ipnetwork.AddressFamily == Sockets.AddressFamily.InterNetwork ? (byte)32 : (byte)128; + IPNetworkCollection ipn = this._ipnetwork.Subnet(width); + return ipn[i].Network; + } + } + + #endregion + + #region IEnumerable Members + + IEnumerator<IPAddress> IEnumerable<IPAddress>.GetEnumerator() + { + return this; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this; + } + + #region IEnumerator<IPNetwork> Members + + public IPAddress Current + { + get { return this[this._enumerator]; } + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + // nothing to dispose + return; + } + + #endregion + + #region IEnumerator Members + + object IEnumerator.Current + { + get { return this.Current; } + } + + public bool MoveNext() + { + this._enumerator++; + if (this._enumerator >= this.Count) + { + return false; + } + return true; + + } + + public void Reset() + { + this._enumerator = -1; + } + + #endregion + + #endregion + } +}
\ No newline at end of file diff --git a/Emby.Server.Implementations/Networking/IPNetwork/IPNetwork.cs b/Emby.Server.Implementations/Networking/IPNetwork/IPNetwork.cs new file mode 100644 index 000000000..6d7785b90 --- /dev/null +++ b/Emby.Server.Implementations/Networking/IPNetwork/IPNetwork.cs @@ -0,0 +1,2170 @@ +using System.Collections.Generic; +using System.IO; +using System.Net.Sockets; +using System.Numerics; +using System.Text.RegularExpressions; + +namespace System.Net +{ + /// <summary> + /// IP Network utility class. + /// Use IPNetwork.Parse to create instances. + /// </summary> + public class IPNetwork : IComparable<IPNetwork> + { + + #region properties + + //private uint _network; + private BigInteger _ipaddress; + private AddressFamily _family; + //private uint _netmask; + //private uint _broadcast; + //private uint _firstUsable; + //private uint _lastUsable; + //private uint _usable; + private byte _cidr; + + #endregion + + #region accessors + + private BigInteger _network + { + get + { + BigInteger uintNetwork = this._ipaddress & this._netmask; + return uintNetwork; + } + } + + /// <summary> + /// Network address + /// </summary> + public IPAddress Network + { + get + { + + return IPNetwork.ToIPAddress(this._network, this._family); + } + } + + /// <summary> + /// Address Family + /// </summary> + public AddressFamily AddressFamily + { + get + { + return this._family; + } + } + + private BigInteger _netmask + { + get + { + return IPNetwork.ToUint(this._cidr, this._family); + } + } + + /// <summary> + /// Netmask + /// </summary> + public IPAddress Netmask + { + get + { + return IPNetwork.ToIPAddress(this._netmask, this._family); + } + } + + private BigInteger _broadcast + { + get + { + + int width = this._family == Sockets.AddressFamily.InterNetwork ? 4 : 16; + BigInteger uintBroadcast = this._network + this._netmask.PositiveReverse(width); + return uintBroadcast; + } + } + + /// <summary> + /// Broadcast address + /// </summary> + public IPAddress Broadcast + { + get + { + if (this._family == Sockets.AddressFamily.InterNetworkV6) + { + return null; + } + return IPNetwork.ToIPAddress(this._broadcast, this._family); + } + } + + /// <summary> + /// First usable IP adress in Network + /// </summary> + public IPAddress FirstUsable + { + get + { + BigInteger fisrt = this._family == Sockets.AddressFamily.InterNetworkV6 + ? this._network + : (this.Usable <= 0) ? this._network : this._network + 1; + return IPNetwork.ToIPAddress(fisrt, this._family); + } + } + + /// <summary> + /// Last usable IP adress in Network + /// </summary> + public IPAddress LastUsable + { + get + { + BigInteger last = this._family == Sockets.AddressFamily.InterNetworkV6 + ? this._broadcast + : (this.Usable <= 0) ? this._network : this._broadcast - 1; + return IPNetwork.ToIPAddress(last, this._family); + } + } + + /// <summary> + /// Number of usable IP adress in Network + /// </summary> + public BigInteger Usable + { + get + { + + if (this._family == Sockets.AddressFamily.InterNetworkV6) + { + return this.Total; + } + byte[] mask = new byte[] { 0xff, 0xff, 0xff, 0xff, 0x00 }; + BigInteger bmask = new BigInteger(mask); + BigInteger usableIps = (_cidr > 30) ? 0 : ((bmask >> _cidr) - 1); + return usableIps; + } + } + + /// <summary> + /// Number of IP adress in Network + /// </summary> + public BigInteger Total + { + get + { + + int max = this._family == Sockets.AddressFamily.InterNetwork ? 32 : 128; + BigInteger count = BigInteger.Pow(2, (max - _cidr)); + return count; + } + } + + + /// <summary> + /// The CIDR netmask notation + /// </summary> + public byte Cidr + { + get + { + return this._cidr; + } + } + + #endregion + + #region constructor + +#if TRAVISCI + public +#else + internal +#endif + + IPNetwork(BigInteger ipaddress, AddressFamily family, byte cidr) + { + + int maxCidr = family == Sockets.AddressFamily.InterNetwork ? 32 : 128; + if (cidr > maxCidr) + { + throw new ArgumentOutOfRangeException("cidr"); + } + + this._ipaddress = ipaddress; + this._family = family; + this._cidr = cidr; + + } + + #endregion + + #region parsers + + /// <summary> + /// 192.168.168.100 - 255.255.255.0 + /// + /// Network : 192.168.168.0 + /// Netmask : 255.255.255.0 + /// Cidr : 24 + /// Start : 192.168.168.1 + /// End : 192.168.168.254 + /// Broadcast : 192.168.168.255 + /// </summary> + /// <param name="ipaddress"></param> + /// <param name="netmask"></param> + /// <returns></returns> + public static IPNetwork Parse(string ipaddress, string netmask) + { + + IPNetwork ipnetwork = null; + IPNetwork.InternalParse(false, ipaddress, netmask, out ipnetwork); + return ipnetwork; + } + + /// <summary> + /// 192.168.168.100/24 + /// + /// Network : 192.168.168.0 + /// Netmask : 255.255.255.0 + /// Cidr : 24 + /// Start : 192.168.168.1 + /// End : 192.168.168.254 + /// Broadcast : 192.168.168.255 + /// </summary> + /// <param name="ipaddress"></param> + /// <param name="cidr"></param> + /// <returns></returns> + public static IPNetwork Parse(string ipaddress, byte cidr) + { + + IPNetwork ipnetwork = null; + IPNetwork.InternalParse(false, ipaddress, cidr, out ipnetwork); + return ipnetwork; + + } + + /// <summary> + /// 192.168.168.100 255.255.255.0 + /// + /// Network : 192.168.168.0 + /// Netmask : 255.255.255.0 + /// Cidr : 24 + /// Start : 192.168.168.1 + /// End : 192.168.168.254 + /// Broadcast : 192.168.168.255 + /// </summary> + /// <param name="ipaddress"></param> + /// <param name="netmask"></param> + /// <returns></returns> + public static IPNetwork Parse(IPAddress ipaddress, IPAddress netmask) + { + + IPNetwork ipnetwork = null; + IPNetwork.InternalParse(false, ipaddress, netmask, out ipnetwork); + return ipnetwork; + + } + + /// <summary> + /// 192.168.0.1/24 + /// 192.168.0.1 255.255.255.0 + /// + /// Network : 192.168.0.0 + /// Netmask : 255.255.255.0 + /// Cidr : 24 + /// Start : 192.168.0.1 + /// End : 192.168.0.254 + /// Broadcast : 192.168.0.255 + /// </summary> + /// <param name="network"></param> + /// <returns></returns> + public static IPNetwork Parse(string network) + { + + IPNetwork ipnetwork = null; + IPNetwork.InternalParse(false, network, out ipnetwork); + return ipnetwork; + + } + + #endregion + + #region TryParse + + + + /// <summary> + /// 192.168.168.100 - 255.255.255.0 + /// + /// Network : 192.168.168.0 + /// Netmask : 255.255.255.0 + /// Cidr : 24 + /// Start : 192.168.168.1 + /// End : 192.168.168.254 + /// Broadcast : 192.168.168.255 + /// </summary> + /// <param name="ipaddress"></param> + /// <param name="netmask"></param> + /// <returns></returns> + public static bool TryParse(string ipaddress, string netmask, out IPNetwork ipnetwork) + { + + IPNetwork ipnetwork2 = null; + IPNetwork.InternalParse(true, ipaddress, netmask, out ipnetwork2); + bool parsed = (ipnetwork2 != null); + ipnetwork = ipnetwork2; + return parsed; + + } + + + + /// <summary> + /// 192.168.168.100/24 + /// + /// Network : 192.168.168.0 + /// Netmask : 255.255.255.0 + /// Cidr : 24 + /// Start : 192.168.168.1 + /// End : 192.168.168.254 + /// Broadcast : 192.168.168.255 + /// </summary> + /// <param name="ipaddress"></param> + /// <param name="cidr"></param> + /// <returns></returns> + public static bool TryParse(string ipaddress, byte cidr, out IPNetwork ipnetwork) + { + + IPNetwork ipnetwork2 = null; + IPNetwork.InternalParse(true, ipaddress, cidr, out ipnetwork2); + bool parsed = (ipnetwork2 != null); + ipnetwork = ipnetwork2; + return parsed; + + } + + /// <summary> + /// 192.168.0.1/24 + /// 192.168.0.1 255.255.255.0 + /// + /// Network : 192.168.0.0 + /// Netmask : 255.255.255.0 + /// Cidr : 24 + /// Start : 192.168.0.1 + /// End : 192.168.0.254 + /// Broadcast : 192.168.0.255 + /// </summary> + /// <param name="network"></param> + /// <param name="ipnetwork"></param> + /// <returns></returns> + public static bool TryParse(string network, out IPNetwork ipnetwork) + { + + IPNetwork ipnetwork2 = null; + IPNetwork.InternalParse(true, network, out ipnetwork2); + bool parsed = (ipnetwork2 != null); + ipnetwork = ipnetwork2; + return parsed; + + } + + /// <summary> + /// 192.168.0.1/24 + /// 192.168.0.1 255.255.255.0 + /// + /// Network : 192.168.0.0 + /// Netmask : 255.255.255.0 + /// Cidr : 24 + /// Start : 192.168.0.1 + /// End : 192.168.0.254 + /// Broadcast : 192.168.0.255 + /// </summary> + /// <param name="ipaddress"></param> + /// <param name="netmask"></param> + /// <param name="ipnetwork"></param> + /// <returns></returns> + public static bool TryParse(IPAddress ipaddress, IPAddress netmask, out IPNetwork ipnetwork) + { + + IPNetwork ipnetwork2 = null; + IPNetwork.InternalParse(true, ipaddress, netmask, out ipnetwork2); + bool parsed = (ipnetwork2 != null); + ipnetwork = ipnetwork2; + return parsed; + + } + + + #endregion + + #region InternalParse + + /// <summary> + /// 192.168.168.100 - 255.255.255.0 + /// + /// Network : 192.168.168.0 + /// Netmask : 255.255.255.0 + /// Cidr : 24 + /// Start : 192.168.168.1 + /// End : 192.168.168.254 + /// Broadcast : 192.168.168.255 + /// </summary> + /// <param name="ipaddress"></param> + /// <param name="netmask"></param> + /// <returns></returns> + private static void InternalParse(bool tryParse, string ipaddress, string netmask, out IPNetwork ipnetwork) + { + + if (string.IsNullOrEmpty(ipaddress)) + { + if (tryParse == false) + { + throw new ArgumentNullException("ipaddress"); + } + ipnetwork = null; + return; + } + + if (string.IsNullOrEmpty(netmask)) + { + if (tryParse == false) + { + throw new ArgumentNullException("netmask"); + } + ipnetwork = null; + return; + } + + IPAddress ip = null; + bool ipaddressParsed = IPAddress.TryParse(ipaddress, out ip); + if (ipaddressParsed == false) + { + if (tryParse == false) + { + throw new ArgumentException("ipaddress"); + } + ipnetwork = null; + return; + } + + IPAddress mask = null; + bool netmaskParsed = IPAddress.TryParse(netmask, out mask); + if (netmaskParsed == false) + { + if (tryParse == false) + { + throw new ArgumentException("netmask"); + } + ipnetwork = null; + return; + } + + IPNetwork.InternalParse(tryParse, ip, mask, out ipnetwork); + } + + private static void InternalParse(bool tryParse, string network, out IPNetwork ipnetwork) + { + + if (string.IsNullOrEmpty(network)) + { + if (tryParse == false) + { + throw new ArgumentNullException("network"); + } + ipnetwork = null; + return; + } + + network = Regex.Replace(network, @"[^0-9a-fA-F\.\/\s\:]+", ""); + network = Regex.Replace(network, @"\s{2,}", " "); + network = network.Trim(); + string[] args = network.Split(new char[] { ' ', '/' }); + byte cidr = 0; + if (args.Length == 1) + { + + if (IPNetwork.TryGuessCidr(args[0], out cidr)) + { + IPNetwork.InternalParse(tryParse, args[0], cidr, out ipnetwork); + return; + } + + if (tryParse == false) + { + throw new ArgumentException("network"); + } + ipnetwork = null; + return; + } + + if (byte.TryParse(args[1], out cidr)) + { + IPNetwork.InternalParse(tryParse, args[0], cidr, out ipnetwork); + return; + } + + IPNetwork.InternalParse(tryParse, args[0], args[1], out ipnetwork); + return; + + } + + + + /// <summary> + /// 192.168.168.100 255.255.255.0 + /// + /// Network : 192.168.168.0 + /// Netmask : 255.255.255.0 + /// Cidr : 24 + /// Start : 192.168.168.1 + /// End : 192.168.168.254 + /// Broadcast : 192.168.168.255 + /// </summary> + /// <param name="ipaddress"></param> + /// <param name="netmask"></param> + /// <returns></returns> + private static void InternalParse(bool tryParse, IPAddress ipaddress, IPAddress netmask, out IPNetwork ipnetwork) + { + + if (ipaddress == null) + { + if (tryParse == false) + { + throw new ArgumentNullException("ipaddress"); + } + ipnetwork = null; + return; + } + + if (netmask == null) + { + if (tryParse == false) + { + throw new ArgumentNullException("netmask"); + } + ipnetwork = null; + return; + } + + BigInteger uintIpAddress = IPNetwork.ToBigInteger(ipaddress); + byte? cidr2 = null; + bool parsed = IPNetwork.TryToCidr(netmask, out cidr2); + if (parsed == false) + { + if (tryParse == false) + { + throw new ArgumentException("netmask"); + } + ipnetwork = null; + return; + } + byte cidr = (byte)cidr2; + + IPNetwork ipnet = new IPNetwork(uintIpAddress, ipaddress.AddressFamily, cidr); + ipnetwork = ipnet; + + return; + } + + + + /// <summary> + /// 192.168.168.100/24 + /// + /// Network : 192.168.168.0 + /// Netmask : 255.255.255.0 + /// Cidr : 24 + /// Start : 192.168.168.1 + /// End : 192.168.168.254 + /// Broadcast : 192.168.168.255 + /// </summary> + /// <param name="ipaddress"></param> + /// <param name="cidr"></param> + /// <returns></returns> + private static void InternalParse(bool tryParse, string ipaddress, byte cidr, out IPNetwork ipnetwork) + { + + if (string.IsNullOrEmpty(ipaddress)) + { + if (tryParse == false) + { + throw new ArgumentNullException("ipaddress"); + } + ipnetwork = null; + return; + } + + + IPAddress ip = null; + bool ipaddressParsed = IPAddress.TryParse(ipaddress, out ip); + if (ipaddressParsed == false) + { + if (tryParse == false) + { + throw new ArgumentException("ipaddress"); + } + ipnetwork = null; + return; + } + + IPAddress mask = null; + bool parsedNetmask = IPNetwork.TryToNetmask(cidr, ip.AddressFamily, out mask); + if (parsedNetmask == false) + { + if (tryParse == false) + { + throw new ArgumentException("cidr"); + } + ipnetwork = null; + return; + } + + + IPNetwork.InternalParse(tryParse, ip, mask, out ipnetwork); + } + + #endregion + + #region converters + + #region ToUint + + /// <summary> + /// Convert an ipadress to decimal + /// 0.0.0.0 -> 0 + /// 0.0.1.0 -> 256 + /// </summary> + /// <param name="ipaddress"></param> + /// <returns></returns> + public static BigInteger ToBigInteger(IPAddress ipaddress) + { + BigInteger? uintIpAddress = null; + IPNetwork.InternalToBigInteger(false, ipaddress, out uintIpAddress); + return (BigInteger)uintIpAddress; + + } + + /// <summary> + /// Convert an ipadress to decimal + /// 0.0.0.0 -> 0 + /// 0.0.1.0 -> 256 + /// </summary> + /// <param name="ipaddress"></param> + /// <returns></returns> + public static bool TryToBigInteger(IPAddress ipaddress, out BigInteger? uintIpAddress) + { + BigInteger? uintIpAddress2 = null; + IPNetwork.InternalToBigInteger(true, ipaddress, out uintIpAddress2); + bool parsed = (uintIpAddress2 != null); + uintIpAddress = uintIpAddress2; + return parsed; + } + +#if TRAVISCI + public +#else + internal +#endif + static void InternalToBigInteger(bool tryParse, IPAddress ipaddress, out BigInteger? uintIpAddress) + { + + if (ipaddress == null) + { + if (tryParse == false) + { + throw new ArgumentNullException("ipaddress"); + } + uintIpAddress = null; + return; + } + + byte[] bytes = ipaddress.GetAddressBytes(); + /// 20180217 lduchosal + /// code impossible to reach, GetAddressBytes returns either 4 or 16 bytes length addresses + /// if (bytes.Length != 4 && bytes.Length != 16) { + /// if (tryParse == false) { + /// throw new ArgumentException("bytes"); + /// } + /// uintIpAddress = null; + /// return; + /// } + + Array.Reverse(bytes); + var unsigned = new List<byte>(bytes); + unsigned.Add(0); + uintIpAddress = new BigInteger(unsigned.ToArray()); + return; + } + + + /// <summary> + /// Convert a cidr to BigInteger netmask + /// </summary> + /// <param name="cidr"></param> + /// <returns></returns> + public static BigInteger ToUint(byte cidr, AddressFamily family) + { + + BigInteger? uintNetmask = null; + IPNetwork.InternalToBigInteger(false, cidr, family, out uintNetmask); + return (BigInteger)uintNetmask; + } + + + /// <summary> + /// Convert a cidr to uint netmask + /// </summary> + /// <param name="cidr"></param> + /// <returns></returns> + public static bool TryToUint(byte cidr, AddressFamily family, out BigInteger? uintNetmask) + { + + BigInteger? uintNetmask2 = null; + IPNetwork.InternalToBigInteger(true, cidr, family, out uintNetmask2); + bool parsed = (uintNetmask2 != null); + uintNetmask = uintNetmask2; + return parsed; + } + + /// <summary> + /// Convert a cidr to uint netmask + /// </summary> + /// <param name="cidr"></param> + /// <returns></returns> +#if TRAVISCI + public +#else + internal +#endif + static void InternalToBigInteger(bool tryParse, byte cidr, AddressFamily family, out BigInteger? uintNetmask) + { + + if (family == AddressFamily.InterNetwork && cidr > 32) + { + if (tryParse == false) + { + throw new ArgumentOutOfRangeException("cidr"); + } + uintNetmask = null; + return; + } + + if (family == AddressFamily.InterNetworkV6 && cidr > 128) + { + if (tryParse == false) + { + throw new ArgumentOutOfRangeException("cidr"); + } + uintNetmask = null; + return; + } + + if (family != AddressFamily.InterNetwork + && family != AddressFamily.InterNetworkV6) + { + if (tryParse == false) + { + throw new NotSupportedException(family.ToString()); + } + uintNetmask = null; + return; + } + + if (family == AddressFamily.InterNetwork) + { + + uintNetmask = cidr == 0 ? 0 : 0xffffffff << (32 - cidr); + return; + } + + BigInteger mask = new BigInteger(new byte[] { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0x00 + }); + + BigInteger masked = cidr == 0 ? 0 : mask << (128 - cidr); + byte[] m = masked.ToByteArray(); + byte[] bmask = new byte[17]; + int copy = m.Length > 16 ? 16 : m.Length; + Array.Copy(m, 0, bmask, 0, copy); + uintNetmask = new BigInteger(bmask); + + + } + + #endregion + + #region ToCidr + + /// <summary> + /// Convert netmask to CIDR + /// 255.255.255.0 -> 24 + /// 255.255.0.0 -> 16 + /// 255.0.0.0 -> 8 + /// </summary> + /// <param name="netmask"></param> + /// <returns></returns> + private static void InternalToCidr(bool tryParse, BigInteger netmask, AddressFamily family, out byte? cidr) + { + + if (!IPNetwork.InternalValidNetmask(netmask, family)) + { + if (tryParse == false) + { + throw new ArgumentException("netmask"); + } + cidr = null; + return; + } + + byte cidr2 = IPNetwork.BitsSet(netmask, family); + cidr = cidr2; + return; + + } + /// <summary> + /// Convert netmask to CIDR + /// 255.255.255.0 -> 24 + /// 255.255.0.0 -> 16 + /// 255.0.0.0 -> 8 + /// </summary> + /// <param name="netmask"></param> + /// <returns></returns> + public static byte ToCidr(IPAddress netmask) + { + byte? cidr = null; + IPNetwork.InternalToCidr(false, netmask, out cidr); + return (byte)cidr; + } + + /// <summary> + /// Convert netmask to CIDR + /// 255.255.255.0 -> 24 + /// 255.255.0.0 -> 16 + /// 255.0.0.0 -> 8 + /// </summary> + /// <param name="netmask"></param> + /// <returns></returns> + public static bool TryToCidr(IPAddress netmask, out byte? cidr) + { + byte? cidr2 = null; + IPNetwork.InternalToCidr(true, netmask, out cidr2); + bool parsed = (cidr2 != null); + cidr = cidr2; + return parsed; + } + + private static void InternalToCidr(bool tryParse, IPAddress netmask, out byte? cidr) + { + + if (netmask == null) + { + if (tryParse == false) + { + throw new ArgumentNullException("netmask"); + } + cidr = null; + return; + } + BigInteger? uintNetmask2 = null; + bool parsed = IPNetwork.TryToBigInteger(netmask, out uintNetmask2); + + /// 20180217 lduchosal + /// impossible to reach code. + /// if (parsed == false) { + /// if (tryParse == false) { + /// throw new ArgumentException("netmask"); + /// } + /// cidr = null; + /// return; + /// } + BigInteger uintNetmask = (BigInteger)uintNetmask2; + + byte? cidr2 = null; + IPNetwork.InternalToCidr(tryParse, uintNetmask, netmask.AddressFamily, out cidr2); + cidr = cidr2; + + return; + + } + + + #endregion + + #region ToNetmask + + /// <summary> + /// Convert CIDR to netmask + /// 24 -> 255.255.255.0 + /// 16 -> 255.255.0.0 + /// 8 -> 255.0.0.0 + /// </summary> + /// <see cref="http://snipplr.com/view/15557/cidr-class-for-ipv4/"/> + /// <param name="cidr"></param> + /// <returns></returns> + public static IPAddress ToNetmask(byte cidr, AddressFamily family) + { + + IPAddress netmask = null; + IPNetwork.InternalToNetmask(false, cidr, family, out netmask); + return netmask; + } + + /// <summary> + /// Convert CIDR to netmask + /// 24 -> 255.255.255.0 + /// 16 -> 255.255.0.0 + /// 8 -> 255.0.0.0 + /// </summary> + /// <see cref="http://snipplr.com/view/15557/cidr-class-for-ipv4/"/> + /// <param name="cidr"></param> + /// <returns></returns> + public static bool TryToNetmask(byte cidr, AddressFamily family, out IPAddress netmask) + { + + IPAddress netmask2 = null; + IPNetwork.InternalToNetmask(true, cidr, family, out netmask2); + bool parsed = (netmask2 != null); + netmask = netmask2; + return parsed; + } + + +#if TRAVISCI + public +#else + internal +#endif + static void InternalToNetmask(bool tryParse, byte cidr, AddressFamily family, out IPAddress netmask) + { + + if (family != AddressFamily.InterNetwork + && family != AddressFamily.InterNetworkV6) + { + if (tryParse == false) + { + throw new ArgumentException("family"); + } + netmask = null; + return; + } + + /// 20180217 lduchosal + /// impossible to reach code, byte cannot be negative : + /// + /// if (cidr < 0) { + /// if (tryParse == false) { + /// throw new ArgumentOutOfRangeException("cidr"); + /// } + /// netmask = null; + /// return; + /// } + + int maxCidr = family == Sockets.AddressFamily.InterNetwork ? 32 : 128; + if (cidr > maxCidr) + { + if (tryParse == false) + { + throw new ArgumentOutOfRangeException("cidr"); + } + netmask = null; + return; + } + + BigInteger mask = IPNetwork.ToUint(cidr, family); + IPAddress netmask2 = IPNetwork.ToIPAddress(mask, family); + netmask = netmask2; + + return; + } + + #endregion + + #endregion + + #region utils + + #region BitsSet + + /// <summary> + /// Count bits set to 1 in netmask + /// </summary> + /// <see cref="http://stackoverflow.com/questions/109023/best-algorithm-to-count-the-number-of-set-bits-in-a-32-bit-integer"/> + /// <param name="netmask"></param> + /// <returns></returns> + private static byte BitsSet(BigInteger netmask, AddressFamily family) + { + + string s = netmask.ToBinaryString(); + return (byte)s.Replace("0", "") + .ToCharArray() + .Length; + + } + + + /// <summary> + /// Count bits set to 1 in netmask + /// </summary> + /// <param name="netmask"></param> + /// <returns></returns> + public static uint BitsSet(IPAddress netmask) + { + BigInteger uintNetmask = IPNetwork.ToBigInteger(netmask); + uint bits = IPNetwork.BitsSet(uintNetmask, netmask.AddressFamily); + return bits; + } + + #endregion + + #region ValidNetmask + + /// <summary> + /// return true if netmask is a valid netmask + /// 255.255.255.0, 255.0.0.0, 255.255.240.0, ... + /// </summary> + /// <see cref="http://www.actionsnip.com/snippets/tomo_atlacatl/calculate-if-a-netmask-is-valid--as2-"/> + /// <param name="netmask"></param> + /// <returns></returns> + public static bool ValidNetmask(IPAddress netmask) + { + + if (netmask == null) + { + throw new ArgumentNullException("netmask"); + } + BigInteger uintNetmask = IPNetwork.ToBigInteger(netmask); + bool valid = IPNetwork.InternalValidNetmask(uintNetmask, netmask.AddressFamily); + return valid; + } + +#if TRAVISCI + public +#else + internal +#endif + static bool InternalValidNetmask(BigInteger netmask, AddressFamily family) + { + + if (family != AddressFamily.InterNetwork + && family != AddressFamily.InterNetworkV6) + { + throw new ArgumentException("family"); + } + + var mask = family == AddressFamily.InterNetwork + ? new BigInteger(0x0ffffffff) + : new BigInteger(new byte[]{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0x00 + }); + + BigInteger neg = ((~netmask) & (mask)); + bool isNetmask = ((neg + 1) & neg) == 0; + return isNetmask; + + } + + #endregion + + #region ToIPAddress + + /// <summary> + /// Transform a uint ipaddress into IPAddress object + /// </summary> + /// <param name="ipaddress"></param> + /// <returns></returns> + public static IPAddress ToIPAddress(BigInteger ipaddress, AddressFamily family) + { + + int width = family == AddressFamily.InterNetwork ? 4 : 16; + byte[] bytes = ipaddress.ToByteArray(); + byte[] bytes2 = new byte[width]; + int copy = bytes.Length > width ? width : bytes.Length; + Array.Copy(bytes, 0, bytes2, 0, copy); + Array.Reverse(bytes2); + + byte[] sized = Resize(bytes2, family); + IPAddress ip = new IPAddress(sized); + return ip; + } + +#if TRAVISCI + public +#else + internal +#endif + static byte[] Resize(byte[] bytes, AddressFamily family) + { + + if (family != AddressFamily.InterNetwork + && family != AddressFamily.InterNetworkV6) + { + throw new ArgumentException("family"); + } + + int width = family == AddressFamily.InterNetwork ? 4 : 16; + + if (bytes.Length > width) + { + throw new ArgumentException("bytes"); + } + + byte[] result = new byte[width]; + Array.Copy(bytes, 0, result, 0, bytes.Length); + return result; + } + + #endregion + + #endregion + + #region contains + + /// <summary> + /// return true if ipaddress is contained in network + /// </summary> + /// <param name="ipaddress"></param> + /// <returns></returns> + public bool Contains(IPAddress ipaddress) + { + + if (ipaddress == null) + { + throw new ArgumentNullException("ipaddress"); + } + + if (AddressFamily != ipaddress.AddressFamily) + { + return false; + } + + BigInteger uintNetwork = _network; + BigInteger uintBroadcast = _broadcast; + BigInteger uintAddress = IPNetwork.ToBigInteger(ipaddress); + + bool contains = (uintAddress >= uintNetwork + && uintAddress <= uintBroadcast); + + return contains; + + } + + [Obsolete("static Contains is deprecated, please use instance Contains.")] + public static bool Contains(IPNetwork network, IPAddress ipaddress) + { + + if (network == null) + { + throw new ArgumentNullException("network"); + } + + return network.Contains(ipaddress); + } + + /// <summary> + /// return true is network2 is fully contained in network + /// </summary> + /// <param name="network2"></param> + /// <returns></returns> + public bool Contains(IPNetwork network2) + { + + if (network2 == null) + { + throw new ArgumentNullException("network2"); + } + + BigInteger uintNetwork = _network; + BigInteger uintBroadcast = _broadcast; + + BigInteger uintFirst = network2._network; + BigInteger uintLast = network2._broadcast; + + bool contains = (uintFirst >= uintNetwork + && uintLast <= uintBroadcast); + + return contains; + } + + [Obsolete("static Contains is deprecated, please use instance Contains.")] + public static bool Contains(IPNetwork network, IPNetwork network2) + { + + if (network == null) + { + throw new ArgumentNullException("network"); + } + + return network.Contains(network2); + } + + #endregion + + #region overlap + + /// <summary> + /// return true is network2 overlap network + /// </summary> + /// <param name="network2"></param> + /// <returns></returns> + public bool Overlap(IPNetwork network2) + { + + if (network2 == null) + { + throw new ArgumentNullException("network2"); + } + + BigInteger uintNetwork = _network; + BigInteger uintBroadcast = _broadcast; + + BigInteger uintFirst = network2._network; + BigInteger uintLast = network2._broadcast; + + bool overlap = + (uintFirst >= uintNetwork && uintFirst <= uintBroadcast) + || (uintLast >= uintNetwork && uintLast <= uintBroadcast) + || (uintFirst <= uintNetwork && uintLast >= uintBroadcast) + || (uintFirst >= uintNetwork && uintLast <= uintBroadcast); + + return overlap; + } + + [Obsolete("static Overlap is deprecated, please use instance Overlap.")] + public static bool Overlap(IPNetwork network, IPNetwork network2) + { + + if (network == null) + { + throw new ArgumentNullException("network"); + } + + return network.Overlap(network2); + } + + #endregion + + #region ToString + + public override string ToString() + { + return string.Format("{0}/{1}", this.Network, this.Cidr); + } + + #endregion + + #region IANA block + + private static readonly Lazy<IPNetwork> _iana_ablock_reserved = new Lazy<IPNetwork>(() => IPNetwork.Parse("10.0.0.0/8")); + private static readonly Lazy<IPNetwork> _iana_bblock_reserved = new Lazy<IPNetwork>(() => IPNetwork.Parse("172.16.0.0/12")); + private static readonly Lazy<IPNetwork> _iana_cblock_reserved = new Lazy<IPNetwork>(() => IPNetwork.Parse("192.168.0.0/16")); + + /// <summary> + /// 10.0.0.0/8 + /// </summary> + /// <returns></returns> + public static IPNetwork IANA_ABLK_RESERVED1 + { + get + { + return _iana_ablock_reserved.Value; + } + } + + /// <summary> + /// 172.12.0.0/12 + /// </summary> + /// <returns></returns> + public static IPNetwork IANA_BBLK_RESERVED1 + { + get + { + return _iana_bblock_reserved.Value; + } + } + + /// <summary> + /// 192.168.0.0/16 + /// </summary> + /// <returns></returns> + public static IPNetwork IANA_CBLK_RESERVED1 + { + get + { + return _iana_cblock_reserved.Value; + } + } + + /// <summary> + /// return true if ipaddress is contained in + /// IANA_ABLK_RESERVED1, IANA_BBLK_RESERVED1, IANA_CBLK_RESERVED1 + /// </summary> + /// <param name="ipaddress"></param> + /// <returns></returns> + public static bool IsIANAReserved(IPAddress ipaddress) + { + + if (ipaddress == null) + { + throw new ArgumentNullException("ipaddress"); + } + + return IPNetwork.IANA_ABLK_RESERVED1.Contains(ipaddress) + || IPNetwork.IANA_BBLK_RESERVED1.Contains(ipaddress) + || IPNetwork.IANA_CBLK_RESERVED1.Contains(ipaddress); + } + + /// <summary> + /// return true if ipnetwork is contained in + /// IANA_ABLK_RESERVED1, IANA_BBLK_RESERVED1, IANA_CBLK_RESERVED1 + /// </summary> + /// <returns></returns> + public bool IsIANAReserved() + { + return IPNetwork.IANA_ABLK_RESERVED1.Contains(this) + || IPNetwork.IANA_BBLK_RESERVED1.Contains(this) + || IPNetwork.IANA_CBLK_RESERVED1.Contains(this); + } + + [Obsolete("static IsIANAReserved is deprecated, please use instance IsIANAReserved.")] + public static bool IsIANAReserved(IPNetwork ipnetwork) + { + + if (ipnetwork == null) + { + throw new ArgumentNullException("ipnetwork"); + } + + return ipnetwork.IsIANAReserved(); + } + + #endregion + + #region Subnet + + /// <summary> + /// Subnet a network into multiple nets of cidr mask + /// Subnet 192.168.0.0/24 into cidr 25 gives 192.168.0.0/25, 192.168.0.128/25 + /// Subnet 10.0.0.0/8 into cidr 9 gives 10.0.0.0/9, 10.128.0.0/9 + /// </summary> + /// <param name="cidr"></param> + /// <returns></returns> + public IPNetworkCollection Subnet(byte cidr) + { + IPNetworkCollection ipnetworkCollection = null; + IPNetwork.InternalSubnet(false, this, cidr, out ipnetworkCollection); + return ipnetworkCollection; + } + + [Obsolete("static Subnet is deprecated, please use instance Subnet.")] + public static IPNetworkCollection Subnet(IPNetwork network, byte cidr) + { + if (network == null) + { + throw new ArgumentNullException("network"); + } + return network.Subnet(cidr); + } + + /// <summary> + /// Subnet a network into multiple nets of cidr mask + /// Subnet 192.168.0.0/24 into cidr 25 gives 192.168.0.0/25, 192.168.0.128/25 + /// Subnet 10.0.0.0/8 into cidr 9 gives 10.0.0.0/9, 10.128.0.0/9 + /// </summary> + /// <param name="cidr"></param> + /// <returns></returns> + public bool TrySubnet(byte cidr, out IPNetworkCollection ipnetworkCollection) + { + IPNetworkCollection inc = null; + IPNetwork.InternalSubnet(true, this, cidr, out inc); + if (inc == null) + { + ipnetworkCollection = null; + return false; + } + + ipnetworkCollection = inc; + return true; + } + + [Obsolete("static TrySubnet is deprecated, please use instance TrySubnet.")] + public static bool TrySubnet(IPNetwork network, byte cidr, out IPNetworkCollection ipnetworkCollection) + { + if (network == null) + { + throw new ArgumentNullException("network"); + } + return network.TrySubnet(cidr, out ipnetworkCollection); + } + +#if TRAVISCI + public +#else + internal +#endif + static void InternalSubnet(bool trySubnet, IPNetwork network, byte cidr, out IPNetworkCollection ipnetworkCollection) + { + + if (network == null) + { + if (trySubnet == false) + { + throw new ArgumentNullException("network"); + } + ipnetworkCollection = null; + return; + } + + int maxCidr = network._family == Sockets.AddressFamily.InterNetwork ? 32 : 128; + if (cidr > maxCidr) + { + if (trySubnet == false) + { + throw new ArgumentOutOfRangeException("cidr"); + } + ipnetworkCollection = null; + return; + } + + if (cidr < network.Cidr) + { + if (trySubnet == false) + { + throw new ArgumentException("cidr"); + } + ipnetworkCollection = null; + return; + } + + ipnetworkCollection = new IPNetworkCollection(network, cidr); + return; + } + + + + #endregion + + #region Supernet + + /// <summary> + /// Supernet two consecutive cidr equal subnet into a single one + /// 192.168.0.0/24 + 192.168.1.0/24 = 192.168.0.0/23 + /// 10.1.0.0/16 + 10.0.0.0/16 = 10.0.0.0/15 + /// 192.168.0.0/24 + 192.168.0.0/25 = 192.168.0.0/24 + /// </summary> + /// <param name="network2"></param> + /// <returns></returns> + public IPNetwork Supernet(IPNetwork network2) + { + IPNetwork supernet = null; + IPNetwork.InternalSupernet(false, this, network2, out supernet); + return supernet; + } + + [Obsolete("static Supernet is deprecated, please use instance Supernet.")] + public static IPNetwork Supernet(IPNetwork network, IPNetwork network2) + { + return network.Supernet(network2); + } + + /// <summary> + /// Try to supernet two consecutive cidr equal subnet into a single one + /// 192.168.0.0/24 + 192.168.1.0/24 = 192.168.0.0/23 + /// 10.1.0.0/16 + 10.0.0.0/16 = 10.0.0.0/15 + /// 192.168.0.0/24 + 192.168.0.0/25 = 192.168.0.0/24 + /// </summary> + /// <param name="network2"></param> + /// <returns></returns> + public bool TrySupernet(IPNetwork network2, out IPNetwork supernet) + { + + IPNetwork outSupernet = null; + IPNetwork.InternalSupernet(true, this, network2, out outSupernet); + bool parsed = (outSupernet != null); + supernet = outSupernet; + return parsed; + } + + [Obsolete("static TrySupernet is deprecated, please use instance TrySupernet.")] + public static bool TrySupernet(IPNetwork network, IPNetwork network2, out IPNetwork supernet) + { + if (network == null) + { + throw new ArgumentNullException("network"); + } + return network.TrySupernet(network2, out supernet); + } + +#if TRAVISCI + public +#else + internal +#endif + static void InternalSupernet(bool trySupernet, IPNetwork network1, IPNetwork network2, out IPNetwork supernet) + { + + if (network1 == null) + { + if (trySupernet == false) + { + throw new ArgumentNullException("network1"); + } + supernet = null; + return; + } + + if (network2 == null) + { + if (trySupernet == false) + { + throw new ArgumentNullException("network2"); + } + supernet = null; + return; + } + + + if (network1.Contains(network2)) + { + supernet = new IPNetwork(network1._network, network1._family, network1.Cidr); + return; + } + + if (network2.Contains(network1)) + { + supernet = new IPNetwork(network2._network, network2._family, network2.Cidr); + return; + } + + if (network1._cidr != network2._cidr) + { + if (trySupernet == false) + { + throw new ArgumentException("cidr"); + } + supernet = null; + return; + } + + IPNetwork first = (network1._network < network2._network) ? network1 : network2; + IPNetwork last = (network1._network > network2._network) ? network1 : network2; + + /// Starting from here : + /// network1 and network2 have the same cidr, + /// network1 does not contain network2, + /// network2 does not contain network1, + /// first is the lower subnet + /// last is the higher subnet + + + if ((first._broadcast + 1) != last._network) + { + if (trySupernet == false) + { + throw new ArgumentOutOfRangeException("network"); + } + supernet = null; + return; + } + + BigInteger uintSupernet = first._network; + byte cidrSupernet = (byte)(first._cidr - 1); + + IPNetwork networkSupernet = new IPNetwork(uintSupernet, first._family, cidrSupernet); + if (networkSupernet._network != first._network) + { + if (trySupernet == false) + { + throw new ArgumentException("network"); + } + supernet = null; + return; + } + supernet = networkSupernet; + return; + } + + #endregion + + #region GetHashCode + + public override int GetHashCode() + { + return string.Format("{0}|{1}|{2}", + this._ipaddress.GetHashCode(), + this._network.GetHashCode(), + this._cidr.GetHashCode()).GetHashCode(); + } + + #endregion + + #region SupernetArray + + /// <summary> + /// Supernet a list of subnet + /// 192.168.0.0/24 + 192.168.1.0/24 = 192.168.0.0/23 + /// 192.168.0.0/24 + 192.168.1.0/24 + 192.168.2.0/24 + 192.168.3.0/24 = 192.168.0.0/22 + /// </summary> + /// <param name="ipnetworks"></param> + /// <param name="supernet"></param> + /// <returns></returns> + public static IPNetwork[] Supernet(IPNetwork[] ipnetworks) + { + IPNetwork[] supernet; + InternalSupernet(false, ipnetworks, out supernet); + return supernet; + } + + /// <summary> + /// Supernet a list of subnet + /// 192.168.0.0/24 + 192.168.1.0/24 = 192.168.0.0/23 + /// 192.168.0.0/24 + 192.168.1.0/24 + 192.168.2.0/24 + 192.168.3.0/24 = 192.168.0.0/22 + /// </summary> + /// <param name="ipnetworks"></param> + /// <param name="supernet"></param> + /// <returns></returns> + public static bool TrySupernet(IPNetwork[] ipnetworks, out IPNetwork[] supernet) + { + bool supernetted = InternalSupernet(true, ipnetworks, out supernet); + return supernetted; + + } + +#if TRAVISCI + public +#else + internal +#endif + static bool InternalSupernet(bool trySupernet, IPNetwork[] ipnetworks, out IPNetwork[] supernet) + { + + if (ipnetworks == null) + { + if (trySupernet == false) + { + throw new ArgumentNullException("ipnetworks"); + } + supernet = null; + return false; + } + + if (ipnetworks.Length <= 0) + { + supernet = new IPNetwork[0]; + return true; + } + + List<IPNetwork> supernetted = new List<IPNetwork>(); + List<IPNetwork> ipns = IPNetwork.Array2List(ipnetworks); + Stack<IPNetwork> current = IPNetwork.List2Stack(ipns); + int previousCount = 0; + int currentCount = current.Count; + + while (previousCount != currentCount) + { + + supernetted.Clear(); + while (current.Count > 1) + { + IPNetwork ipn1 = current.Pop(); + IPNetwork ipn2 = current.Peek(); + + IPNetwork outNetwork = null; + bool success = ipn1.TrySupernet(ipn2, out outNetwork); + if (success) + { + current.Pop(); + current.Push(outNetwork); + } + else + { + supernetted.Add(ipn1); + } + } + if (current.Count == 1) + { + supernetted.Add(current.Pop()); + } + + previousCount = currentCount; + currentCount = supernetted.Count; + current = IPNetwork.List2Stack(supernetted); + + } + supernet = supernetted.ToArray(); + return true; + } + + private static Stack<IPNetwork> List2Stack(List<IPNetwork> list) + { + Stack<IPNetwork> stack = new Stack<IPNetwork>(); + list.ForEach(new Action<IPNetwork>( + delegate (IPNetwork ipn) + { + stack.Push(ipn); + } + )); + return stack; + } + + private static List<IPNetwork> Array2List(IPNetwork[] array) + { + List<IPNetwork> ipns = new List<IPNetwork>(); + ipns.AddRange(array); + IPNetwork.RemoveNull(ipns); + ipns.Sort(new Comparison<IPNetwork>( + delegate (IPNetwork ipn1, IPNetwork ipn2) + { + int networkCompare = ipn1._network.CompareTo(ipn2._network); + if (networkCompare == 0) + { + int cidrCompare = ipn1._cidr.CompareTo(ipn2._cidr); + return cidrCompare; + } + return networkCompare; + } + )); + ipns.Reverse(); + + return ipns; + } + + private static void RemoveNull(List<IPNetwork> ipns) + { + ipns.RemoveAll(new Predicate<IPNetwork>( + delegate (IPNetwork ipn) + { + if (ipn == null) + { + return true; + } + return false; + } + )); + + } + + #endregion + + #region WideSubnet + + public static IPNetwork WideSubnet(string start, string end) + { + + if (string.IsNullOrEmpty(start)) + { + throw new ArgumentNullException("start"); + } + + if (string.IsNullOrEmpty(end)) + { + throw new ArgumentNullException("end"); + } + + IPAddress startIP; + if (!IPAddress.TryParse(start, out startIP)) + { + throw new ArgumentException("start"); + } + + IPAddress endIP; + if (!IPAddress.TryParse(end, out endIP)) + { + throw new ArgumentException("end"); + } + + if (startIP.AddressFamily != endIP.AddressFamily) + { + throw new NotSupportedException("MixedAddressFamily"); + } + + IPNetwork ipnetwork = new IPNetwork(0, startIP.AddressFamily, 0); + for (byte cidr = 32; cidr >= 0; cidr--) + { + IPNetwork wideSubnet = IPNetwork.Parse(start, cidr); + if (wideSubnet.Contains(endIP)) + { + ipnetwork = wideSubnet; + break; + } + } + return ipnetwork; + + } + + public static bool TryWideSubnet(IPNetwork[] ipnetworks, out IPNetwork ipnetwork) + { + IPNetwork ipn = null; + IPNetwork.InternalWideSubnet(true, ipnetworks, out ipn); + if (ipn == null) + { + ipnetwork = null; + return false; + } + ipnetwork = ipn; + return true; + } + + public static IPNetwork WideSubnet(IPNetwork[] ipnetworks) + { + IPNetwork ipn = null; + IPNetwork.InternalWideSubnet(false, ipnetworks, out ipn); + return ipn; + } + + internal static void InternalWideSubnet(bool tryWide, IPNetwork[] ipnetworks, out IPNetwork ipnetwork) + { + + if (ipnetworks == null) + { + if (tryWide == false) + { + throw new ArgumentNullException("ipnetworks"); + } + ipnetwork = null; + return; + } + + + IPNetwork[] nnin = Array.FindAll<IPNetwork>(ipnetworks, new Predicate<IPNetwork>( + delegate (IPNetwork ipnet) { + return ipnet != null; + } + )); + + if (nnin.Length <= 0) + { + if (tryWide == false) + { + throw new ArgumentException("ipnetworks"); + } + ipnetwork = null; + return; + } + + if (nnin.Length == 1) + { + IPNetwork ipn0 = nnin[0]; + ipnetwork = ipn0; + return; + } + + Array.Sort<IPNetwork>(nnin); + IPNetwork nnin0 = nnin[0]; + BigInteger uintNnin0 = nnin0._ipaddress; + + IPNetwork nninX = nnin[nnin.Length - 1]; + IPAddress ipaddressX = nninX.Broadcast; + + AddressFamily family = ipnetworks[0]._family; + foreach (var ipnx in ipnetworks) + { + if (ipnx._family != family) + { + throw new ArgumentException("MixedAddressFamily"); + } + } + + IPNetwork ipn = new IPNetwork(0, family, 0); + for (byte cidr = nnin0._cidr; cidr >= 0; cidr--) + { + IPNetwork wideSubnet = new IPNetwork(uintNnin0, family, cidr); + if (wideSubnet.Contains(ipaddressX)) + { + ipn = wideSubnet; + break; + } + } + + ipnetwork = ipn; + return; + } + + #endregion + + #region Print + + /// <summary> + /// Print an ipnetwork in a clear representation string + /// </summary> + /// <returns></returns> + public string Print() + { + + StringWriter sw = new StringWriter(); + + sw.WriteLine("IPNetwork : {0}", ToString()); + sw.WriteLine("Network : {0}", Network); + sw.WriteLine("Netmask : {0}", Netmask); + sw.WriteLine("Cidr : {0}", Cidr); + sw.WriteLine("Broadcast : {0}", Broadcast); + sw.WriteLine("FirstUsable : {0}", FirstUsable); + sw.WriteLine("LastUsable : {0}", LastUsable); + sw.WriteLine("Usable : {0}", Usable); + + return sw.ToString(); + } + + [Obsolete("static Print is deprecated, please use instance Print.")] + public static string Print(IPNetwork ipnetwork) + { + + if (ipnetwork == null) + { + throw new ArgumentNullException("ipnetwork"); + } + + return ipnetwork.Print(); + } + + #endregion + + #region TryGuessCidr + + /// <summary> + /// + /// Class Leading bits Default netmask + /// A (CIDR /8) 00 255.0.0.0 + /// A (CIDR /8) 01 255.0.0.0 + /// B (CIDR /16) 10 255.255.0.0 + /// C (CIDR /24) 11 255.255.255.0 + /// + /// </summary> + /// <param name="ip"></param> + /// <param name="cidr"></param> + /// <returns></returns> + public static bool TryGuessCidr(string ip, out byte cidr) + { + + IPAddress ipaddress = null; + bool parsed = IPAddress.TryParse(string.Format("{0}", ip), out ipaddress); + if (parsed == false) + { + cidr = 0; + return false; + } + + if (ipaddress.AddressFamily == AddressFamily.InterNetworkV6) + { + cidr = 64; + return true; + } + BigInteger uintIPAddress = IPNetwork.ToBigInteger(ipaddress); + uintIPAddress = uintIPAddress >> 29; + if (uintIPAddress <= 3) + { + cidr = 8; + return true; + } + else if (uintIPAddress <= 5) + { + cidr = 16; + return true; + } + else if (uintIPAddress <= 6) + { + cidr = 24; + return true; + } + + cidr = 0; + return false; + + } + + /// <summary> + /// Try to parse cidr. Have to be >= 0 and <= 32 or 128 + /// </summary> + /// <param name="sidr"></param> + /// <param name="cidr"></param> + /// <returns></returns> + public static bool TryParseCidr(string sidr, AddressFamily family, out byte? cidr) + { + + byte b = 0; + if (!byte.TryParse(sidr, out b)) + { + cidr = null; + return false; + } + + IPAddress netmask = null; + if (!IPNetwork.TryToNetmask(b, family, out netmask)) + { + cidr = null; + return false; + } + + cidr = b; + return true; + } + + #endregion + + #region ListIPAddress + + [Obsolete("static ListIPAddress is deprecated, please use instance ListIPAddress.")] + public static IPAddressCollection ListIPAddress(IPNetwork ipnetwork) + { + return ipnetwork.ListIPAddress(); + } + + public IPAddressCollection ListIPAddress() + { + return new IPAddressCollection(this); + } + + #endregion + + /** + * Need a better way to do it + * +#region TrySubstractNetwork + + public static bool TrySubstractNetwork(IPNetwork[] ipnetworks, IPNetwork substract, out IEnumerable<IPNetwork> result) { + + if (ipnetworks == null) { + result = null; + return false; + } + if (ipnetworks.Length <= 0) { + result = null; + return false; + } + if (substract == null) { + result = null; + return false; + } + var results = new List<IPNetwork>(); + foreach (var ipn in ipnetworks) { + if (!Overlap(ipn, substract)) { + results.Add(ipn); + continue; + } + + var collection = ipn.Subnet(substract.Cidr); + var rtemp = new List<IPNetwork>(); + foreach(var subnet in collection) { + if (subnet != substract) { + rtemp.Add(subnet); + } + } + var supernets = Supernet(rtemp.ToArray()); + results.AddRange(supernets); + } + result = results; + return true; + } +#endregion + * **/ + + #region IComparable<IPNetwork> Members + + public static Int32 Compare(IPNetwork left, IPNetwork right) + { + // two null IPNetworks are equal + if (ReferenceEquals(left, null) && ReferenceEquals(right, null)) return 0; + + // two same IPNetworks are equal + if (ReferenceEquals(left, right)) return 0; + + // null is always sorted first + if (ReferenceEquals(left, null)) return -1; + if (ReferenceEquals(right, null)) return 1; + + // first test the network + var result = left._network.CompareTo(right._network); + if (result != 0) return result; + + // then test the cidr + result = left._cidr.CompareTo(right._cidr); + return result; + } + + public Int32 CompareTo(IPNetwork other) + { + return Compare(this, other); + } + + public Int32 CompareTo(Object obj) + { + // null is at less + if (obj == null) return 1; + + // convert to a proper Cidr object + var other = obj as IPNetwork; + + // type problem if null + if (other == null) + { + throw new ArgumentException( + "The supplied parameter is an invalid type. Please supply an IPNetwork type.", + "obj"); + } + + // perform the comparision + return CompareTo(other); + } + + #endregion + + #region IEquatable<IPNetwork> Members + + public static Boolean Equals(IPNetwork left, IPNetwork right) + { + return Compare(left, right) == 0; + } + + public Boolean Equals(IPNetwork other) + { + return Equals(this, other); + } + + public override Boolean Equals(Object obj) + { + return Equals(this, obj as IPNetwork); + } + + #endregion + + #region Operators + + public static Boolean operator ==(IPNetwork left, IPNetwork right) + { + return Equals(left, right); + } + + public static Boolean operator !=(IPNetwork left, IPNetwork right) + { + return !Equals(left, right); + } + + public static Boolean operator <(IPNetwork left, IPNetwork right) + { + return Compare(left, right) < 0; + } + + public static Boolean operator >(IPNetwork left, IPNetwork right) + { + return Compare(left, right) > 0; + } + + #endregion + + } +}
\ No newline at end of file diff --git a/Emby.Server.Implementations/Networking/IPNetwork/IPNetworkCollection.cs b/Emby.Server.Implementations/Networking/IPNetwork/IPNetworkCollection.cs new file mode 100644 index 000000000..35cff88dc --- /dev/null +++ b/Emby.Server.Implementations/Networking/IPNetwork/IPNetworkCollection.cs @@ -0,0 +1,144 @@ +using System.Collections; +using System.Collections.Generic; +using System.Numerics; + +namespace System.Net +{ + public class IPNetworkCollection : IEnumerable<IPNetwork>, IEnumerator<IPNetwork> + { + + private BigInteger _enumerator; + private byte _cidrSubnet; + private IPNetwork _ipnetwork; + + private byte _cidr + { + get { return this._ipnetwork.Cidr; } + } + private BigInteger _broadcast + { + get { return IPNetwork.ToBigInteger(this._ipnetwork.Broadcast); } + } + private BigInteger _lastUsable + { + get { return IPNetwork.ToBigInteger(this._ipnetwork.LastUsable); } + } + private BigInteger _network + { + get { return IPNetwork.ToBigInteger(this._ipnetwork.Network); } + } +#if TRAVISCI + public +#else + internal +#endif + IPNetworkCollection(IPNetwork ipnetwork, byte cidrSubnet) + { + + int maxCidr = ipnetwork.AddressFamily == Sockets.AddressFamily.InterNetwork ? 32 : 128; + if (cidrSubnet > maxCidr) + { + throw new ArgumentOutOfRangeException("cidrSubnet"); + } + + if (cidrSubnet < ipnetwork.Cidr) + { + throw new ArgumentException("cidr"); + } + + this._cidrSubnet = cidrSubnet; + this._ipnetwork = ipnetwork; + this._enumerator = -1; + } + + #region Count, Array, Enumerator + + public BigInteger Count + { + get + { + BigInteger count = BigInteger.Pow(2, this._cidrSubnet - this._cidr); + return count; + } + } + + public IPNetwork this[BigInteger i] + { + get + { + if (i >= this.Count) + { + throw new ArgumentOutOfRangeException("i"); + } + + BigInteger last = this._ipnetwork.AddressFamily == Sockets.AddressFamily.InterNetworkV6 + ? this._lastUsable : this._broadcast; + BigInteger increment = (last - this._network) / this.Count; + BigInteger uintNetwork = this._network + ((increment + 1) * i); + IPNetwork ipn = new IPNetwork(uintNetwork, this._ipnetwork.AddressFamily, this._cidrSubnet); + return ipn; + } + } + + #endregion + + #region IEnumerable Members + + IEnumerator<IPNetwork> IEnumerable<IPNetwork>.GetEnumerator() + { + return this; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this; + } + + #region IEnumerator<IPNetwork> Members + + public IPNetwork Current + { + get { return this[this._enumerator]; } + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + // nothing to dispose + return; + } + + #endregion + + #region IEnumerator Members + + object IEnumerator.Current + { + get { return this.Current; } + } + + public bool MoveNext() + { + this._enumerator++; + if (this._enumerator >= this.Count) + { + return false; + } + return true; + + } + + public void Reset() + { + this._enumerator = -1; + } + + #endregion + + #endregion + + } +}
\ No newline at end of file diff --git a/Emby.Server.Implementations/Networking/IPNetwork/LICENSE.txt b/Emby.Server.Implementations/Networking/IPNetwork/LICENSE.txt new file mode 100644 index 000000000..45d7392ac --- /dev/null +++ b/Emby.Server.Implementations/Networking/IPNetwork/LICENSE.txt @@ -0,0 +1,24 @@ +Copyright (c) 2015, lduchosal +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs index 60da8a012..20abaf27c 100644 --- a/Emby.Server.Implementations/Networking/NetworkManager.cs +++ b/Emby.Server.Implementations/Networking/NetworkManager.cs @@ -11,6 +11,8 @@ using MediaBrowser.Model.Extensions; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; +using MediaBrowser.Model.System; +using System.Numerics; namespace Emby.Server.Implementations.Networking { @@ -19,27 +21,32 @@ namespace Emby.Server.Implementations.Networking protected ILogger Logger { get; private set; } public event EventHandler NetworkChanged; + public Func<string[]> LocalSubnetsFn { get; set; } - public NetworkManager(ILogger logger) + public NetworkManager(ILogger logger, IEnvironmentInfo environment) { Logger = logger; - try - { - NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged; - } - catch (Exception ex) + // In FreeBSD these events cause a crash + if (environment.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.BSD) { - Logger.ErrorException("Error binding to NetworkAddressChanged event", ex); - } + try + { + NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged; + } + catch (Exception ex) + { + Logger.ErrorException("Error binding to NetworkAddressChanged event", ex); + } - try - { - NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged; - } - catch (Exception ex) - { - Logger.ErrorException("Error binding to NetworkChange_NetworkAvailabilityChanged event", ex); + try + { + NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged; + } + catch (Exception ex) + { + Logger.ErrorException("Error binding to NetworkChange_NetworkAvailabilityChanged event", ex); + } } } @@ -60,6 +67,7 @@ namespace Emby.Server.Implementations.Networking lock (_localIpAddressSyncLock) { _localIpAddresses = null; + _macAddresses = null; } if (NetworkChanged != null) { @@ -67,16 +75,16 @@ namespace Emby.Server.Implementations.Networking } } - private List<IpAddressInfo> _localIpAddresses; + private IpAddressInfo[] _localIpAddresses; private readonly object _localIpAddressSyncLock = new object(); - public List<IpAddressInfo> GetLocalIpAddresses() + public IpAddressInfo[] GetLocalIpAddresses() { lock (_localIpAddressSyncLock) { if (_localIpAddresses == null) { - var addresses = GetLocalIpAddressesInternal().Result.Select(ToIpAddressInfo).ToList(); + var addresses = GetLocalIpAddressesInternal().Result.Select(ToIpAddressInfo).ToArray(); _localIpAddresses = addresses; @@ -120,6 +128,11 @@ namespace Emby.Server.Implementations.Networking public bool IsInPrivateAddressSpace(string endpoint) { + return IsInPrivateAddressSpace(endpoint, true); + } + + private bool IsInPrivateAddressSpace(string endpoint, bool checkSubnets) + { if (string.Equals(endpoint, "::1", StringComparison.OrdinalIgnoreCase)) { return true; @@ -146,12 +159,24 @@ namespace Emby.Server.Implementations.Networking return Is172AddressPrivate(endpoint); } - return endpoint.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) || + if (endpoint.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) || endpoint.StartsWith("127.", StringComparison.OrdinalIgnoreCase) || - endpoint.StartsWith("192.168", StringComparison.OrdinalIgnoreCase) || - endpoint.StartsWith("169.", StringComparison.OrdinalIgnoreCase) || - //endpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase) || - IsInPrivateAddressSpaceAndLocalSubnet(endpoint); + endpoint.StartsWith("169.", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + if (checkSubnets && endpoint.StartsWith("192.168", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + if (checkSubnets && IsInPrivateAddressSpaceAndLocalSubnet(endpoint)) + { + return true; + } + + return false; } public bool IsInPrivateAddressSpaceAndLocalSubnet(string endpoint) @@ -238,9 +263,38 @@ namespace Emby.Server.Implementations.Networking return IsInLocalNetworkInternal(endpoint, true); } - public bool IsInLocalNetworkInternal(string endpoint, bool resolveHost) + public bool IsAddressInSubnets(string addressString, string[] subnets) { - if (string.IsNullOrWhiteSpace(endpoint)) + return IsAddressInSubnets(IPAddress.Parse(addressString), addressString, subnets); + } + + private bool IsAddressInSubnets(IPAddress address, string addressString, string[] subnets) + { + foreach (var subnet in subnets) + { + var normalizedSubnet = subnet.Trim(); + + if (string.Equals(normalizedSubnet, addressString, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + if (normalizedSubnet.IndexOf('/') != -1) + { + var ipnetwork = IPNetwork.Parse(normalizedSubnet); + if (ipnetwork.Contains(address)) + { + return true; + } + } + } + + return false; + } + + private bool IsInLocalNetworkInternal(string endpoint, bool resolveHost) + { + if (string.IsNullOrEmpty(endpoint)) { throw new ArgumentNullException("endpoint"); } @@ -250,11 +304,25 @@ namespace Emby.Server.Implementations.Networking { var addressString = address.ToString(); + var localSubnetsFn = LocalSubnetsFn; + if (localSubnetsFn != null) + { + var localSubnets = localSubnetsFn(); + foreach (var subnet in localSubnets) + { + // only validate if there's at least one valid entry + if (!string.IsNullOrWhiteSpace(subnet)) + { + return IsAddressInSubnets(address, addressString, localSubnets) || IsInPrivateAddressSpace(addressString, false); + } + } + } + int lengthMatch = 100; if (address.AddressFamily == AddressFamily.InterNetwork) { lengthMatch = 4; - if (IsInPrivateAddressSpace(addressString)) + if (IsInPrivateAddressSpace(addressString, true)) { return true; } @@ -262,7 +330,7 @@ namespace Emby.Server.Implementations.Networking else if (address.AddressFamily == AddressFamily.InterNetworkV6) { lengthMatch = 9; - if (IsInPrivateAddressSpace(endpoint)) + if (IsInPrivateAddressSpace(endpoint, true)) { return true; } @@ -353,13 +421,7 @@ namespace Emby.Server.Implementations.Networking return new List<IPAddress>(); } - //if (!_validNetworkInterfaceTypes.Contains(network.NetworkInterfaceType)) - //{ - // return new List<IPAddress>(); - //} - return ipProperties.UnicastAddresses - //.Where(i => i.IsDnsEligible) .Select(i => i.Address) .Where(i => i.AddressFamily == AddressFamily.InterNetwork || i.AddressFamily == AddressFamily.InterNetworkV6) .ToList(); @@ -408,16 +470,40 @@ namespace Emby.Server.Implementations.Networking } } - /// <summary> - /// Returns MAC Address from first Network Card in Computer - /// </summary> - /// <returns>[string] MAC Address</returns> - public string GetMacAddress() + private List<string> _macAddresses; + public List<string> GetMacAddresses() + { + if (_macAddresses == null) + { + _macAddresses = GetMacAddressesInternal(); + } + return _macAddresses; + } + + private List<string> GetMacAddressesInternal() { return NetworkInterface.GetAllNetworkInterfaces() .Where(i => i.NetworkInterfaceType != NetworkInterfaceType.Loopback) - .Select(i => BitConverter.ToString(i.GetPhysicalAddress().GetAddressBytes())) - .FirstOrDefault(); + .Select(i => + { + try + { + var physicalAddress = i.GetPhysicalAddress(); + + if (physicalAddress == null) + { + return null; + } + + return physicalAddress.ToString(); + } + catch (Exception ex) + { + return null; + } + }) + .Where(i => i != null) + .ToList(); } /// <summary> |
