#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);
}
}
}