#nullable enable using System; using System.Net; using System.Net.Sockets; namespace MediaBrowser.Common.Net { /// /// An object that holds and IP address and subnet mask. /// public class IPNetAddress : IPObject { /// /// Represents an IPNetAddress that has no value. /// public static readonly IPNetAddress None = new IPNetAddress(IPAddress.None); /// /// IPv4 multicast address. /// public static readonly IPAddress SSDPMulticastIPv4 = IPAddress.Parse("239.255.255.250"); /// /// IPv6 local link multicast address. /// public static readonly IPAddress SSDPMulticastIPv6LinkLocal = IPAddress.Parse("ff02::C"); /// /// IPv6 site local multicast address. /// public static readonly IPAddress SSDPMulticastIPv6SiteLocal = IPAddress.Parse("ff05::C"); /// /// IP4Loopback address host. /// public static readonly IPNetAddress IP4Loopback = IPNetAddress.Parse("127.0.0.1/8"); /// /// IP6Loopback address host. /// public static readonly IPNetAddress IP6Loopback = IPNetAddress.Parse("::1"); /// /// Object's IP address. /// private IPAddress _address; /// /// Initializes a new instance of the class. /// /// Address to assign. public IPNetAddress(IPAddress address) { _address = address ?? throw new ArgumentNullException(nameof(address)); PrefixLength = (byte)(address.AddressFamily == AddressFamily.InterNetwork ? 32 : 128); } /// /// Initializes a new instance of the class. /// /// IP Address. /// Mask as a CIDR. public IPNetAddress(IPAddress address, byte prefixLength) { if (address?.IsIPv4MappedToIPv6 ?? throw new ArgumentNullException(nameof(address))) { _address = address.MapToIPv4(); } else { _address = address; } PrefixLength = prefixLength; } /// /// Gets or sets the object's IP address. /// public override IPAddress Address { get { return _address; } set { _address = value ?? IPAddress.None; } } /// public override byte PrefixLength { get; set; } /// /// Try to parse the address and subnet strings into an IPNetAddress object. /// /// IP address to parse. Can be CIDR or X.X.X.X notation. /// Resultant object. /// True if the values parsed successfully. False if not, resulting in the IP being null. public static bool TryParse(string addr, out IPNetAddress ip) { if (!string.IsNullOrEmpty(addr)) { addr = addr.Trim(); // Try to parse it as is. if (IPAddress.TryParse(addr, out IPAddress? res)) { ip = new IPNetAddress(res); return true; } // Is it a network? string[] tokens = addr.Split("/"); if (tokens.Length == 2) { tokens[0] = tokens[0].TrimEnd(); tokens[1] = tokens[1].TrimStart(); if (IPAddress.TryParse(tokens[0], out res)) { // Is the subnet part a cidr? if (byte.TryParse(tokens[1], out byte cidr)) { ip = new IPNetAddress(res, cidr); return true; } // Is the subnet in x.y.a.b form? if (IPAddress.TryParse(tokens[1], out IPAddress? mask)) { ip = new IPNetAddress(res, MaskToCidr(mask)); return true; } } } } ip = None; return false; } /// /// Parses the string provided, throwing an exception if it is badly formed. /// /// String to parse. /// IPNetAddress object. public static IPNetAddress Parse(string addr) { if (TryParse(addr, out IPNetAddress o)) { return o; } throw new ArgumentException("Unable to recognise object :" + addr); } /// public override bool Contains(IPAddress address) { if (address == null) { throw new ArgumentNullException(nameof(address)); } if (address.IsIPv4MappedToIPv6) { address = address.MapToIPv4(); } var altAddress = NetworkAddressOf(address, PrefixLength); return NetworkAddress.Address.Equals(altAddress.Address) && NetworkAddress.PrefixLength >= altAddress.PrefixLength; } /// public override bool Contains(IPObject address) { if (address is IPHost addressObj && addressObj.HasAddress) { foreach (IPAddress addr in addressObj.GetAddresses()) { if (Contains(addr)) { return true; } } } else if (address is IPNetAddress netaddrObj) { // Have the same network address, but different subnets? if (NetworkAddress.Address.Equals(netaddrObj.NetworkAddress.Address)) { return NetworkAddress.PrefixLength <= netaddrObj.PrefixLength; } var altAddress = NetworkAddressOf(netaddrObj.Address, PrefixLength); return NetworkAddress.Address.Equals(altAddress.Address); } return false; } /// public override bool Equals(IPObject? other) { if (other is IPNetAddress otherObj && !Address.Equals(IPAddress.None) && !otherObj.Address.Equals(IPAddress.None)) { return Address.Equals(otherObj.Address) && PrefixLength == otherObj.PrefixLength; } return false; } /// public override bool Equals(IPAddress ip) { if (ip != null && !ip.Equals(IPAddress.None) && !Address.Equals(IPAddress.None)) { return ip.Equals(Address); } return false; } /// public override string ToString() { return ToString(false); } /// /// Returns a textual representation of this object. /// /// Set to true, if the subnet is to be excluded as part of the address. /// String representation of this object. public string ToString(bool shortVersion) { if (!Address.Equals(IPAddress.None)) { if (Address.Equals(IPAddress.Any)) { return "Any IP4 Address"; } if (Address.Equals(IPAddress.IPv6Any)) { return "Any IP6 Address"; } if (Address.Equals(IPAddress.Broadcast)) { return "Any Address"; } if (shortVersion) { return Address.ToString(); } return $"{Address}/{PrefixLength}"; } return string.Empty; } /// protected override IPObject CalculateNetworkAddress() { var value = NetworkAddressOf(_address, PrefixLength); return new IPNetAddress(value.Address, value.PrefixLength); } } }