174 lines
5.7 KiB
C#
174 lines
5.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Net.NetworkInformation;
|
|
using System.Net.Sockets;
|
|
using Microsoft.AspNetCore.HttpOverrides;
|
|
|
|
namespace AMWD.Common.Utilities
|
|
{
|
|
/// <summary>
|
|
/// Provides some network utils.
|
|
/// </summary>
|
|
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
public static class NetworkHelper
|
|
{
|
|
/// <summary>
|
|
/// Tries to resolve a <paramref name="hostname"/> to its <see cref="IPAddress"/>es.
|
|
/// </summary>
|
|
/// <param name="hostname">The hostname to resolve.</param>
|
|
/// <param name="addressFamily">An address family to use (available: <see cref="AddressFamily.InterNetwork"/> and <see cref="AddressFamily.InterNetworkV6"/>).</param>
|
|
/// <returns>The resolved <see cref="IPAddress"/>es or an empty list.</returns>
|
|
public static List<IPAddress> ResolveHost(string hostname, AddressFamily addressFamily = default)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(hostname))
|
|
return new();
|
|
|
|
if (addressFamily != AddressFamily.InterNetwork && addressFamily != AddressFamily.InterNetworkV6)
|
|
addressFamily = AddressFamily.Unspecified;
|
|
|
|
var ipAddress = ResolveIpAddress(hostname, addressFamily);
|
|
// the name was an ip address, should not happen but experience tells other stories
|
|
if (ipAddress != null)
|
|
return new() { ipAddress };
|
|
|
|
try
|
|
{
|
|
return Dns.GetHostAddresses(hostname)
|
|
.FilterAddressFamily(addressFamily)
|
|
.ToList();
|
|
}
|
|
catch
|
|
{
|
|
return new();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to resolve an interface name to its v4 and v6 <see cref="IPAddress"/>es.
|
|
/// </summary>
|
|
/// <param name="interfaceName">The interface name to resolve.</param>
|
|
/// <param name="addressFamily">An address family to use (available: <see cref="AddressFamily.InterNetwork"/> and <see cref="AddressFamily.InterNetworkV6"/>).</param>
|
|
/// <returns>The resolved <see cref="IPAddress"/>es or an empty list.</returns>
|
|
public static List<IPAddress> ResolveInterface(string interfaceName, AddressFamily addressFamily = default)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(interfaceName))
|
|
return new();
|
|
|
|
if (addressFamily != AddressFamily.InterNetwork && addressFamily != AddressFamily.InterNetworkV6)
|
|
addressFamily = AddressFamily.Unspecified;
|
|
|
|
var ipAddress = ResolveIpAddress(interfaceName, addressFamily);
|
|
// the name was an ip address, should not happen but experience tells other stories
|
|
if (ipAddress != null)
|
|
return new() { ipAddress };
|
|
|
|
try
|
|
{
|
|
return NetworkInterface.GetAllNetworkInterfaces()
|
|
.Where(nic => nic.Name.Equals(interfaceName, StringComparison.OrdinalIgnoreCase))
|
|
.SelectMany(nic => nic.GetIPProperties().UnicastAddresses.Select(uai => uai.Address))
|
|
.FilterAddressFamily(addressFamily)
|
|
.ToList();
|
|
}
|
|
catch
|
|
{
|
|
return new();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parses a CIDR network definition.
|
|
/// </summary>
|
|
/// <param name="network">The network in CIDR.</param>
|
|
/// <returns>The <see cref="IPNetwork"/> or <c>null</c>.</returns>
|
|
public static IPNetwork ParseNetwork(string network)
|
|
{
|
|
TryParseNetwork(network, out var ipNetwork);
|
|
return ipNetwork;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to parse a CIDR network definition.
|
|
/// </summary>
|
|
/// <param name="network">The network in CIDR.</param>
|
|
/// <param name="ipNetwork">The parsed <see cref="IPNetwork"/>.</param>
|
|
/// <returns><c>true</c> on success, otherwise <c>false</c>.</returns>
|
|
public static bool TryParseNetwork(string network, out IPNetwork ipNetwork)
|
|
{
|
|
try
|
|
{
|
|
string[] parts = network.Split('/');
|
|
if (parts.Length != 2)
|
|
throw new ArgumentException($"Invalid network type");
|
|
|
|
var prefix = IPAddress.Parse(parts.First());
|
|
int prefixLength = int.Parse(parts.Last());
|
|
|
|
ipNetwork = new IPNetwork(prefix, prefixLength);
|
|
return true;
|
|
}
|
|
catch
|
|
{
|
|
ipNetwork = null;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Uses the <see cref="IPNetwork"/> and expands the whole subnet.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The network identifier and the broadcast address are included in the response list.
|
|
/// </remarks>
|
|
/// <param name="network">The <see cref="IPNetwork"/> to expand.</param>
|
|
/// <returns>A list with all valid <see cref="IPAddress"/>es.</returns>
|
|
public static List<IPAddress> ExpandNetwork(this IPNetwork network)
|
|
{
|
|
var list = new List<IPAddress>();
|
|
|
|
var ipAddress = network.Prefix;
|
|
while (network.Contains(ipAddress))
|
|
{
|
|
list.Add(ipAddress);
|
|
ipAddress = ipAddress.Increment();
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
private static IPAddress ResolveIpAddress(string address, AddressFamily addressFamily)
|
|
{
|
|
if (IPAddress.TryParse(address, out var ipAddress))
|
|
{
|
|
// the address is whether IPv4 nor IPv6
|
|
if (ipAddress.AddressFamily != AddressFamily.InterNetwork && ipAddress.AddressFamily != AddressFamily.InterNetworkV6)
|
|
return null;
|
|
|
|
// the address does not match the required address family
|
|
if (addressFamily != AddressFamily.Unspecified && ipAddress.AddressFamily != addressFamily)
|
|
return null;
|
|
|
|
return ipAddress;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static IEnumerable<IPAddress> FilterAddressFamily(this IEnumerable<IPAddress> source, AddressFamily addressFamily)
|
|
{
|
|
return source
|
|
.Where(a => a.AddressFamily == AddressFamily.InterNetwork || a.AddressFamily == AddressFamily.InterNetworkV6)
|
|
.Where(a => addressFamily == AddressFamily.Unspecified || a.AddressFamily == addressFamily)
|
|
.OrderBy(a => a.AddressFamily);
|
|
}
|
|
|
|
internal static void SwapBigEndian(byte[] array)
|
|
{
|
|
if (BitConverter.IsLittleEndian)
|
|
Array.Reverse(array);
|
|
}
|
|
}
|
|
}
|