Files
DP44/Common/DTS.Common/Utils/NetworkUtils.cs
2026-04-17 14:55:32 -04:00

239 lines
9.9 KiB
C#

using DTS.Common.Utilities.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace DTS.Common.Utils
{
//FB 18512 This calss contains utility methods, they are moved from Netwroking module
public static class NetworkUtils
{
/// <summary>
/// returns true if the network interface is up, false otherwise
/// stolen from FWTU
/// </summary>
/// <param name="ip"></param>
/// <returns></returns>
public static bool IsNetworkInterfaceUp(IPAddress ip)
{
foreach (var a in NetworkInterface.GetAllNetworkInterfaces())
{
try
{
var ipp = a.GetIPProperties();
if (ipp.MulticastAddresses.Count == 0)
continue; // most of VPN adapters will be skipped
if (!a.SupportsMulticast)
continue; // multicast is meaningless for this type of connection
if (OperationalStatus.Up != a.OperationalStatus)
continue; // this adapter is off or not connected
if (null == ipp.GetIPv4Properties())
continue; // IPv4 is not configured on this adapter
if (a.NetworkInterfaceType == NetworkInterfaceType.Loopback)
continue;
var addressBytes = ip.GetAddressBytes();
if (!AllowInternalNICIPs && 4 == addressBytes.Length && addressBytes[0] == 169 && addressBytes[1] == 254) { continue; }
if (a.GetIPProperties().UnicastAddresses.Any(i => i.Address.AddressFamily == AddressFamily.InterNetwork && IPAddress.Equals(i.Address, ip)))
{
return true;
}
}
catch (Exception)
{
continue;
}
}
return false;
}
/// <summary>
/// Indicates whether any network connection is available.
/// Filter connections below a specified speed, as well as virtual network cards.
/// </summary>
/// <param name="minimumSpeed">The minimum speed required. Passing 0 will not filter connection using speed.</param>
/// <returns>
/// <c>true</c> if a network connection is available; otherwise, <c>false</c>.
/// </returns>
private static List<NetworkInterface> GetAvailableNetworkInterfaces(long minimumSpeed = 115200)
{
if (!NetworkInterface.GetIsNetworkAvailable())
return null;
var niList = new List<NetworkInterface>();
foreach (var ni in NetworkInterface.GetAllNetworkInterfaces())
{
// discard because of standard reasons
if ((ni.NetworkInterfaceType == NetworkInterfaceType.Loopback)
|| (ni.NetworkInterfaceType == NetworkInterfaceType.Tunnel))
continue;
// this allow to filter modems, serial, etc.
// use 10000000 as a minimum speed for most cases
if (ni.Speed < minimumSpeed)
continue;
// discard virtual cards (virtual box, virtual pc, etc.)
if (ni.Description.ToLower().Contains("virtual")
|| ni.Name.ToLower().Contains("virtual")
|| ni.Description.ToLower().Contains("bluethooth")
|| ni.Name.ToLower().Contains("bluethooth"))
continue;
// discard "Microsoft Loopback Adapter", it will not show as NetworkInterfaceType.Loopback but as Ethernet Card.
if (ni.Description.Equals("Microsoft Loopback Adapter", StringComparison.OrdinalIgnoreCase))
continue;
niList.Add(ni);
}
return niList;
}
private static string GetMACAddress(string id)
{
var macAddress = string.Empty;
var nics = GetAvailableNetworkInterfaces();
if (nics == null)
return macAddress;
foreach (var nic in nics)
{
if (nic.Id != id) continue;
var pa = BitConverter.ToString(nic.GetPhysicalAddress().GetAddressBytes());
pa = pa.Replace("-", ":");
if (string.IsNullOrEmpty(pa)) continue;
macAddress = pa;
break;
}
return macAddress;
}
private static bool InvalidNIC(NetworkInterface ni)
{
return OperationalStatus.Up != ni.OperationalStatus || !ni.SupportsMulticast;
}
public static bool AllowInternalNICIPs { get; set; } = false;
public static List<HostInfo> GetAvailableHosts(bool supportMulticastOnly = false)
{
var nics = GetAvailableNetworkInterfaces();
if (nics == null) { return new List<HostInfo>(); }
var hosts = new List<HostInfo>();
foreach (NetworkInterface ni in nics)
{
if (supportMulticastOnly && InvalidNIC(ni))
{
continue;
}
//exclude wireshark network interface, it claims it can ping ips lyingly
if (ni.Description.Contains("Npcap")) { continue; }
foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses)
{
var addressBytes = ip.Address.GetAddressBytes();
if ( !AllowInternalNICIPs && 4 == addressBytes.Length && addressBytes[0] == 169 && addressBytes[1] == 254)
{
APILogger.Log($"skipping {ni.Name}\\{ni.Description} as it has a 169.254 address");
continue;//internal only
}
if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
{
GetStartAndEndAddress(ip.Address, ip.IPv4Mask, out var startAddress, out var endAddress);
hosts.Add(new HostInfo
{
HostIpAddress = ip.Address.ToString(),
HostNetworkId = ni.Id,
HostMacAddress = GetMACAddress(ni.Id),
StartAddress = startAddress,
EndAddress = endAddress
});
}
}
}
return hosts;
}
/// <summary>
/// given an IP address and a mask returns a ip address as a string for the first address that is accessible and a string for the last address that is accessible
/// anything between the start address and end address after doing a byte-by-byte comparison should be addressable
/// will return empty strings if something bad happens. Is designed for 4 byte addresses but isn't necessarily limited.
/// </summary>
/// <param name="ip"></param>
/// <param name="mask"></param>
/// <param name="startAddress"></param>
/// <param name="endAddress"></param>
private static void GetStartAndEndAddress(IPAddress ip, IPAddress mask, out string startAddress, out string endAddress)
{
try
{
var ipByte = ip.GetAddressBytes();
var maskBytes = mask.GetAddressBytes();
var startBytes = new byte[ipByte.Length];
var endBytes = new byte[ipByte.Length];
for (var i = 0; i < ipByte.Length; i++)
{
startBytes[i] = (byte)(ipByte[i] & maskBytes[i]);
endBytes[i] = (byte)(ipByte[i] | ~maskBytes[i]);
}
var startIP = new IPAddress(startBytes);
var endIP = new IPAddress(endBytes);
startAddress = startIP.ToString();
endAddress = endIP.ToString();
}
catch (Exception ex)
{
startAddress = string.Empty;
endAddress = string.Empty;
APILogger.Log(ex);
}
}
//FB 25658 Changed the method signature to return bool to determine the connection string has IP in it and it's not USB
/// <summary>
/// Try to parse connectionString and retrieve the IP address.
/// </summary>
/// <param name="connectionString"> DAS connectionString which might include the IP address or might be a USB connection string</param>
/// <param name="ipAddress">Return the parsed IP address from connectionString</param>
/// <returns>Returns true if was able to parse and get the IP otherwise false including the valid USB connection string </returns>
public static bool TryParseConnectionString(string connectionString, out string ipAddress)
{
ipAddress = null;
if (string.IsNullOrEmpty(connectionString))
{
return false;
}
try
{
var uri = new Uri(connectionString);
if (IPAddress.TryParse(uri.Host, out _))
{
ipAddress = uri.Host;
return true;
}
}
catch
{
var ip = connectionString.Split(':')[0];
if (IPAddress.TryParse(ip, out _))
{
ipAddress = ip;
return true;
}
}
return false;
}
}
}