Files
Stationeers-RemoteControl/Vendor/EmbedIO-3.5.2/Utilities/IPParser.cs

210 lines
7.0 KiB
C#

using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using Swan.Logging;
namespace EmbedIO.Utilities
{
/// <summary>
/// Provides standard methods to parse IP address strings.
/// </summary>
public static class IPParser
{
/// <summary>
/// Parses the specified IP address.
/// </summary>
/// <param name="address">The IP address.</param>
/// <returns>A collection of <see cref="IPAddress"/> parsed correctly from <paramref name="address"/>.</returns>
public static async Task<IEnumerable<IPAddress>> ParseAsync(string address)
{
if (address == null)
return Enumerable.Empty<IPAddress>();
if (IPAddress.TryParse(address, out var ip))
return new List<IPAddress> { ip };
try
{
return await Dns.GetHostAddressesAsync(address).ConfigureAwait(false);
}
catch (SocketException socketEx)
{
socketEx.Log(nameof(IPParser));
}
catch
{
// Ignore
}
if (IsCidrNotation(address))
return ParseCidrNotation(address);
return IsSimpleIPRange(address) ? TryParseSimpleIPRange(address) : Enumerable.Empty<IPAddress>();
}
/// <summary>
/// Determines whether the IP-range string is in CIDR notation.
/// </summary>
/// <param name="range">The IP-range string.</param>
/// <returns>
/// <c>true</c> if the IP-range string is CIDR notation; otherwise, <c>false</c>.
/// </returns>
public static bool IsCidrNotation(string range)
{
if (string.IsNullOrWhiteSpace(range))
return false;
var parts = range.Split('/');
if (parts.Length != 2)
return false;
var prefix = parts[0];
var prefixLen = parts[1];
var prefixParts = prefix.Split('.');
if (prefixParts.Length != 4)
return false;
return byte.TryParse(prefixLen, out var len) && len <= 32;
}
/// <summary>
/// Parse IP-range string in CIDR notation. For example "12.15.0.0/16".
/// </summary>
/// <param name="range">The IP-range string.</param>
/// <returns>A collection of <see cref="IPAddress"/> parsed correctly from <paramref name="range"/>.</returns>
public static IEnumerable<IPAddress> ParseCidrNotation(string range)
{
if (!IsCidrNotation(range))
return Enumerable.Empty<IPAddress>();
var parts = range.Split('/');
var prefix = parts[0];
if (!byte.TryParse(parts[1], out var prefixLen))
return Enumerable.Empty<IPAddress>();
var prefixParts = prefix.Split('.');
if (prefixParts.Select(x => byte.TryParse(x, out _)).Any(x => !x))
return Enumerable.Empty<IPAddress>();
uint ip = 0;
for (var i = 0; i < 4; i++)
{
ip <<= 8;
ip += uint.Parse(prefixParts[i], NumberFormatInfo.InvariantInfo);
}
var shiftBits = (byte)(32 - prefixLen);
var ip1 = (ip >> shiftBits) << shiftBits;
if ((ip1 & ip) != ip1) // Check correct subnet address
return Enumerable.Empty<IPAddress>();
var ip2 = ip1 >> shiftBits;
for (var k = 0; k < shiftBits; k++)
{
ip2 = (ip2 << 1) + 1;
}
var beginIP = new byte[4];
var endIP = new byte[4];
for (var i = 0; i < 4; i++)
{
beginIP[i] = (byte)((ip1 >> ((3 - i) * 8)) & 255);
endIP[i] = (byte)((ip2 >> ((3 - i) * 8)) & 255);
}
return GetAllIPAddresses(beginIP, endIP);
}
/// <summary>
/// Determines whether the IP-range string is in simple IP range notation.
/// </summary>
/// <param name="range">The IP-range string.</param>
/// <returns>
/// <c>true</c> if the IP-range string is in simple IP range notation; otherwise, <c>false</c>.
/// </returns>
public static bool IsSimpleIPRange(string range)
{
if (string.IsNullOrWhiteSpace(range))
return false;
var parts = range.Split('.');
if (parts.Length != 4)
return false;
foreach (var part in parts)
{
var rangeParts = part.Split('-');
if (rangeParts.Length < 1 || rangeParts.Length > 2)
return false;
if (!byte.TryParse(rangeParts[0], out _) ||
(rangeParts.Length > 1 && !byte.TryParse(rangeParts[1], out _)))
return false;
}
return true;
}
/// <summary>
/// Tries to parse IP-range string "12.15-16.1-30.10-255"
/// </summary>
/// <param name="range">The IP-range string.</param>
/// <returns>A collection of <see cref="IPAddress"/> parsed correctly from <paramref name="range"/>.</returns>
public static IEnumerable<IPAddress> TryParseSimpleIPRange(string range)
{
if (!IsSimpleIPRange(range))
return Enumerable.Empty<IPAddress>();
var beginIP = new byte[4];
var endIP = new byte[4];
var parts = range.Split('.');
for (var i = 0; i < 4; i++)
{
var rangeParts = parts[i].Split('-');
beginIP[i] = byte.Parse(rangeParts[0], NumberFormatInfo.InvariantInfo);
endIP[i] = (rangeParts.Length == 1) ? beginIP[i] : byte.Parse(rangeParts[1], NumberFormatInfo.InvariantInfo);
}
return GetAllIPAddresses(beginIP, endIP);
}
private static IEnumerable<IPAddress> GetAllIPAddresses(byte[] beginIP, byte[] endIP)
{
for (var i = 0; i < 4; i++)
{
if (endIP[i] < beginIP[i])
return Enumerable.Empty<IPAddress>();
}
var capacity = 1;
for (var i = 0; i < 4; i++)
capacity *= endIP[i] - beginIP[i] + 1;
var ips = new List<IPAddress>(capacity);
for (int i0 = beginIP[0]; i0 <= endIP[0]; i0++)
{
for (int i1 = beginIP[1]; i1 <= endIP[1]; i1++)
{
for (int i2 = beginIP[2]; i2 <= endIP[2]; i2++)
{
for (int i3 = beginIP[3]; i3 <= endIP[3]; i3++)
{
ips.Add(new IPAddress(new[] { (byte)i0, (byte)i1, (byte)i2, (byte)i3 }));
}
}
}
}
return ips;
}
}
}