using System.Net; using System.Net.Sockets; using MoShiro.Protocol; namespace MoShiro.Backend.Application; /// /// 自动识别 Agent 的对外地址:Agent 上报本机的公网地址,注册请求的来源地址作为补充(来源是私网或隧道出口时不用)。 /// 管理员手填的(*_manual)永远优先;已经选中的自动地址还在候选里就不换,不会因为网卡顺序变化来回改。 /// public static class AgentAddressPolicy { /// 返回要写入的 (public_host, ipv6_host);没有候选的那一项是 null(保持不变)。 public static (string? PublicHost, string? Ipv6Host) Choose(AgentRecord current, IReadOnlyList reported, string? observed) { var v4 = new List(); var v6 = new List(); foreach (string text in reported.Append(observed ?? "")) { if (!IPAddress.TryParse(text, out IPAddress? address)) { continue; } if (address.AddressFamily == AddressFamily.InterNetwork && PublicAddress.IsPublicIpv4(address) && !v4.Contains(address.ToString())) { v4.Add(address.ToString()); } else if (address.AddressFamily == AddressFamily.InterNetworkV6 && PublicAddress.IsGlobalIpv6(address) && !v6.Contains(address.ToString())) { v6.Add(address.ToString()); } } return (current.PublicHostManual ? null : Pick(v4, current.PublicHost), current.Ipv6HostManual ? null : Pick(v6, current.Ipv6Host)); } private static string? Pick(List candidates, string current) => candidates.Count == 0 ? null : candidates.Contains(current) ? current : candidates[0]; }