update news & events
This commit is contained in:
123
TWASys-App/Models/ServerInfo/NetUtils.cs
Normal file
123
TWASys-App/Models/ServerInfo/NetUtils.cs
Normal file
@ -0,0 +1,123 @@
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace TWASys_App.Models.ServerInfo
|
||||
{
|
||||
public class NetUtils
|
||||
{
|
||||
public static string? GetPrimaryPrivateIPv4String()
|
||||
{
|
||||
if (!Socket.OSSupportsIPv4) return null;
|
||||
try
|
||||
{
|
||||
using var s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
||||
s.Connect(new IPEndPoint(IPAddress.Parse("8.8.8.8"), 53));
|
||||
return (s.LocalEndPoint as IPEndPoint)?.Address.ToString();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// fallback: liệt kê NIC
|
||||
return GetAllPrivateIPsStrings().FirstOrDefault(ip => ip.Contains('.'));
|
||||
}
|
||||
}
|
||||
|
||||
public static string? GetPrimaryPrivateIPv6String()
|
||||
{
|
||||
if (!Socket.OSSupportsIPv6) return null;
|
||||
try
|
||||
{
|
||||
using var s = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
|
||||
s.Connect(new IPEndPoint(IPAddress.Parse("2001:4860:4860::8888"), 53));
|
||||
var ip = (s.LocalEndPoint as IPEndPoint)?.Address;
|
||||
return ip is null ? null : FormatIPv6WithScope(ip);
|
||||
}
|
||||
catch
|
||||
{
|
||||
var ip = FirstIPv6_ByEnum();
|
||||
return ip is null ? null : FormatIPv6WithScope(ip);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetAllPrivateIPsStrings()
|
||||
{
|
||||
foreach (var ni in NetworkInterface.GetAllNetworkInterfaces())
|
||||
{
|
||||
if (ni.OperationalStatus != OperationalStatus.Up) continue;
|
||||
if (ni.NetworkInterfaceType is NetworkInterfaceType.Loopback or NetworkInterfaceType.Tunnel) continue;
|
||||
|
||||
var idx = ni.GetIPProperties().GetIPv6Properties()?.Index;
|
||||
|
||||
foreach (var ua in ni.GetIPProperties().UnicastAddresses)
|
||||
{
|
||||
var ip = ua.Address;
|
||||
if (IPAddress.IsLoopback(ip)) continue;
|
||||
|
||||
if (ip.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
if (IsPrivateIPv4(ip)) yield return ip.ToString();
|
||||
}
|
||||
else if (ip.AddressFamily == AddressFamily.InterNetworkV6)
|
||||
{
|
||||
if (IsPrivateIPv6(ip)) yield return FormatIPv6WithScope(ip, idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- PUBLIC IP (string) ----------
|
||||
public static async Task<string?> GetPublicIPv4Async(CancellationToken ct = default) =>
|
||||
await FetchFirstAsync(new[] { "https://api.ipify.org", "https://checkip.amazonaws.com", "https://ipv4.icanhazip.com" }, ct);
|
||||
|
||||
public static async Task<string?> GetPublicIPv6Async(CancellationToken ct = default) =>
|
||||
await FetchFirstAsync(new[] { "https://api6.ipify.org", "https://ipv6.icanhazip.com", "https://ifconfig.co/ip" }, ct);
|
||||
|
||||
public static async Task<string?> GetPublicIpAsync(bool preferIPv6 = false, CancellationToken ct = default) =>
|
||||
preferIPv6 ? await GetPublicIPv6Async(ct) ?? await GetPublicIPv4Async(ct)
|
||||
: await GetPublicIPv4Async(ct) ?? await GetPublicIPv6Async(ct);
|
||||
|
||||
// ---------- helpers ----------
|
||||
static async Task<string?> FetchFirstAsync(IEnumerable<string> urls, CancellationToken ct)
|
||||
{
|
||||
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(3) };
|
||||
foreach (var u in urls)
|
||||
try
|
||||
{
|
||||
var s = (await http.GetStringAsync(u, ct)).Trim();
|
||||
if (!string.IsNullOrWhiteSpace(s)) return s;
|
||||
}
|
||||
catch { /* thử URL kế */ }
|
||||
return null;
|
||||
}
|
||||
|
||||
static IPAddress? FirstIPv6_ByEnum() =>
|
||||
NetworkInterface.GetAllNetworkInterfaces()
|
||||
.Where(n => n.OperationalStatus == OperationalStatus.Up &&
|
||||
n.NetworkInterfaceType != NetworkInterfaceType.Loopback &&
|
||||
n.NetworkInterfaceType != NetworkInterfaceType.Tunnel)
|
||||
.SelectMany(n => n.GetIPProperties().UnicastAddresses)
|
||||
.Select(u => u.Address)
|
||||
.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetworkV6);
|
||||
|
||||
static bool IsPrivateIPv4(IPAddress ip)
|
||||
{
|
||||
var b = ip.GetAddressBytes();
|
||||
return b[0] == 10 || (b[0] == 172 && b[1] is >= 16 and <= 31) || (b[0] == 192 && b[1] == 168);
|
||||
}
|
||||
static bool IsPrivateIPv6(IPAddress ip)
|
||||
{
|
||||
var b = ip.GetAddressBytes();
|
||||
return ((b[0] & 0xFE) == 0xFC) || (b[0] == 0xFE && (b[1] & 0xC0) == 0x80); // fc00::/7, fe80::/10
|
||||
}
|
||||
|
||||
// KHÔNG đụng IPAddress.ScopeId. Nếu link-local, thêm scope = index của NIC.
|
||||
static string FormatIPv6WithScope(IPAddress ip, int? nicIndex = null)
|
||||
{
|
||||
if (ip.AddressFamily != AddressFamily.InterNetworkV6) return ip.ToString();
|
||||
var b = ip.GetAddressBytes();
|
||||
bool linkLocal = b[0] == 0xFE && (b[1] & 0xC0) == 0x80;
|
||||
if (linkLocal && nicIndex is int idx) return $"{ip}%{idx}";
|
||||
return ip.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user