using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using NewLife;
using NewLife.Log;
namespace AgentExpansion;
/// <summary>局域网扫æåœ°å€è§£æžã€‚ä»Žæœ¬æœºç½‘å¡æˆ–é…置的Networksè§£æžå‡ºå¾…探测的局域网地å€</summary>
/// <remarks>åªæ‰«æ RFC1918 ç§æœ‰ç½‘段,链路本地ã€å›žçޝã€å¤šæ’ã€ä¿ç•™åœ°å€ä»¥åŠå…¬ç½‘地å€ä¸€å¾‹æŽ’除</remarks>
internal static class LanScan
{
/// <summary>åˆ¤æ–æ˜¯å¦å±€åŸŸç½‘地å€</summary>
/// <remarks>
/// 仅放行 10.0.0.0/8ã€172.16.0.0/12ã€192.168.0.0/16。
/// 其余地å€ä¸€å¾‹æŽ’除,包括:链路本地 169.254.0.0/16(APIPA,掩ç 多为 /16,展开åŽåœ°å€æžå¤šï¼‰ã€
/// 回环 127.0.0.0/8ã€æœ¬ç½‘络 0.0.0.0/8ã€è¿è¥å•†çº§ NAT 100.64.0.0/10ã€
/// å¤šæ’ 224.0.0.0/4ã€ä¿ç•™ 240.0.0.0/4ã€ä»¥åŠ 192.0.2.0/24 ç‰æ–‡æ¡£æµ‹è¯•网段和任æ„公网地å€ã€‚
/// </remarks>
/// <param name="address">待判æ–çš„ IPv4 地å€</param>
/// <returns>是å¦å±žäºŽå±€åŸŸç½‘网段</returns>
public static Boolean IsLanAddress(IPAddress? address)
{
if (address == null) return false;
if (address.AddressFamily != AddressFamily.InterNetwork) return false;
var bytes = address.GetAddressBytes();
if (bytes.Length != 4) return false;
// 10.0.0.0/8
if (bytes[0] == 10) return true;
// 172.16.0.0/12
if (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] <= 31) return true;
// 192.168.0.0/16
if (bytes[0] == 192 && bytes[1] == 168) return true;
return false;
}
/// <summary>è§£æžé…ç½®çš„ç›®æ ‡ç½‘æ®µã€‚æ”¯æŒ 192.168.1.0/24ã€192.168.1.* 以åŠå•个地å€</summary>
/// <param name="text">网段文本,逗å·åˆ†å·ç©ºç™½åˆ†éš”</param>
/// <param name="log">日志。用于æç¤ºè¢«å¿½ç•¥çš„éžå±€åŸŸç½‘网段</param>
/// <returns>局域网网段集åˆ</returns>
public static List<NetworkRange> ParseNetworks(String? text, ILog? log)
{
var list = new List<NetworkRange>();
if (text.IsNullOrEmpty()) return list;
var parts = text.Split([',', ';', '\r', '\n', '\t', ' '], StringSplitOptions.RemoveEmptyEntries);
foreach (var part in parts)
{
if (TryParseCidr(part, out var range) ||
TryParseWildcard(part, out range) ||
TryParseSingle(part, out range))
{
// 与本机网å¡åŒä¸€å¥—过滤,é¿å…è¯¯é…æˆ 169.254.0.0/16ã€0.0.0.0/0 ç‰éžå±€åŸŸç½‘网段åŽå…¨ç½‘段扫æ
if (!IsLanAddress(range.Start))
{
log?.Warn("ç›®æ ‡ç½‘æ®µ {0} 䏿˜¯å±€åŸŸç½‘地å€ï¼Œå·²å¿½ç•¥", part);
continue;
}
list.Add(range);
}
}
return list;
}
/// <summary>èŽ·å–æœ¬æœºæ‰€æœ‰å±€åŸŸç½‘网段</summary>
/// <returns>æœ¬æœºç½‘å¡æ‰€åœ¨çš„局域网网段</returns>
public static List<NetworkRange> GetLocalRanges()
{
var list = new List<NetworkRange>();
foreach (var nic in NetworkInterface.GetAllNetworkInterfaces())
{
if (nic.OperationalStatus != OperationalStatus.Up) continue;
if (nic.NetworkInterfaceType is NetworkInterfaceType.Loopback or NetworkInterfaceType.Tunnel) continue;
var props = nic.GetIPProperties();
if (props == null) continue;
foreach (var unicast in props.UnicastAddresses)
{
if (unicast.Address.AddressFamily != AddressFamily.InterNetwork) continue;
if (unicast.IPv4Mask == null) continue;
//if (unicast.PrefixOrigin == PrefixOrigin.WellKnown) continue;
// åªæ‰«æå±€åŸŸç½‘ç½‘æ®µã€‚æ— çº¿ç›´è¿žã€è“牙ã€è™šæ‹Ÿç½‘å¡ç‰å¸¸å¸¸å›žè½ 169.254.0.0/16,掩ç 还是 /16,
// 一旦å‚与扫æä¼šå±•å¼€æˆå…万多个地å€ï¼Œæ—¢æ— æ„义åˆä¼šåˆ·çˆ†æ—¥å¿—
if (!IsLanAddress(unicast.Address)) continue;
var prefix = GetPrefixLength(unicast.IPv4Mask);
if (prefix <= 0) continue;
list.Add(NetworkRange.FromCidr(unicast.Address, prefix));
}
}
return list;
}
/// <summary>网段去é‡ã€‚å¤šå¼ ç½‘å¡å¯èƒ½æŽ¥å…¥åŒä¸€ç½‘段</summary>
/// <param name="ranges">原始网段集åˆ</param>
/// <returns>去é‡åŽçš„网段集åˆ</returns>
public static List<NetworkRange> Distinct(List<NetworkRange> ranges)
{
var list = new List<NetworkRange>();
foreach (var range in ranges)
{
if (!list.Any(e => e.Start.Equals(range.Start) && e.End.Equals(range.End))) list.Add(range);
}
return list;
}
/// <summary>枚举待探测地å€</summary>
/// <param name="ranges">ç›®æ ‡ç½‘æ®µ</param>
/// <param name="maxHosts">最大主机数。0或负数表示ä¸é™åˆ¶</param>
/// <param name="log">æ—¥å¿—ã€‚è¶…è¿‡ä¸Šé™æ—¶ç»™å‡ºæç¤º</param>
/// <returns>待探测地å€</returns>
public static IEnumerable<IPAddress> GetTargets(List<NetworkRange> ranges, Int32 maxHosts, ILog? log)
{
var count = 0;
foreach (var range in ranges)
{
foreach (var address in range.GetAddresses())
{
yield return address;
count++;
if (maxHosts > 0 && count >= maxHosts)
{
log?.Warn("ç›®æ ‡åœ°å€è¶…è¿‡ä¸Šé™ {0},本轮仅扫æå‰ {0} 个地å€ï¼Œè¯·è°ƒæ•´MaxHosts或收窄Networks", maxHosts);
yield break;
}
}
}
}
private static Int32 GetPrefixLength(IPAddress mask)
{
var bytes = mask.GetAddressBytes();
var count = 0;
foreach (var item in bytes)
{
var value = item;
for (var i = 0; i < 8; i++)
{
if ((value & 0x80) == 0x80) count++;
value <<= 1;
}
}
return count;
}
private static Boolean TryParseSingle(String text, out NetworkRange range)
{
range = default;
if (!IPAddress.TryParse(text, out var address)) return false;
range = NetworkRange.FromSingle(address);
return true;
}
private static Boolean TryParseCidr(String text, out NetworkRange range)
{
range = default;
var p = text.IndexOf('/');
if (p <= 0) return false;
if (!IPAddress.TryParse(text[..p], out var address)) return false;
if (!Int32.TryParse(text[(p + 1)..], out var prefix)) return false;
if (prefix < 0 || prefix > 32) return false;
range = NetworkRange.FromCidr(address, prefix);
return true;
}
private static Boolean TryParseWildcard(String text, out NetworkRange range)
{
range = default;
if (!text.Contains('*')) return false;
var parts = text.Split('.');
if (parts.Length != 4) return false;
var startBytes = new Byte[4];
var endBytes = new Byte[4];
var stars = 0;
for (var i = 0; i < 4; i++)
{
if (parts[i] == "*")
{
startBytes[i] = 0;
endBytes[i] = 255;
stars++;
}
else if (Byte.TryParse(parts[i], out var value))
{
startBytes[i] = value;
endBytes[i] = value;
}
else
{
return false;
}
}
var start = new IPAddress(startBytes);
// 通é…符全部ä½äºŽæœ«å°¾æ—¶ç‰ä»·äºŽ CIDR,交由 FromCidr 处ç†ï¼Œé¡ºå¸¦æŽ’除网络地å€ä¸Žå¹¿æ’地å€
var trailing = 0;
for (var i = 3; i >= 0 && parts[i] == "*"; i--) trailing++;
range = stars == trailing && trailing < 4
? NetworkRange.FromCidr(start, (4 - trailing) * 8)
: new NetworkRange(start, new IPAddress(endBytes));
return true;
}
}
/// <summary>IP地å€åŒºé—´</summary>
internal readonly struct NetworkRange
{
/// <summary>实例化</summary>
/// <param name="start">起始地å€</param>
/// <param name="end">结æŸåœ°å€</param>
public NetworkRange(IPAddress start, IPAddress end)
{
Start = start;
End = end;
}
/// <summary>起始地å€</summary>
public IPAddress Start { get; }
/// <summary>结æŸåœ°å€</summary>
public IPAddress End { get; }
/// <summary>枚举区间内所有地å€</summary>
/// <returns>区间内地å€</returns>
public IEnumerable<IPAddress> GetAddresses()
{
var start = ToUInt32(Start);
var end = ToUInt32(End);
if (end < start) yield break;
for (var i = start; i <= end; i++)
{
yield return ToIPAddress(i);
if (i == UInt32.MaxValue) break;
}
}
/// <summary>从å•ä¸ªåœ°å€æž„é€ åŒºé—´</summary>
/// <param name="address">地å€</param>
/// <returns>区间</returns>
public static NetworkRange FromSingle(IPAddress address) => new(address, address);
/// <summary>从CIDRæž„é€ åŒºé—´ï¼Œè‡ªåŠ¨æŽ’é™¤ç½‘ç»œåœ°å€ä¸Žå¹¿æ’地å€</summary>
/// <param name="address">ä»»æ„地å€</param>
/// <param name="prefix">å‰ç¼€é•¿åº¦</param>
/// <returns>区间</returns>
public static NetworkRange FromCidr(IPAddress address, Int32 prefix)
{
var ip = ToUInt32(address);
var mask = prefix == 0 ? 0U : UInt32.MaxValue << (32 - prefix);
var network = ip & mask;
var broadcast = network | ~mask;
var start = prefix >= 31 ? network : network + 1;
var end = prefix >= 31 ? broadcast : broadcast - 1;
return new NetworkRange(ToIPAddress(start), ToIPAddress(end));
}
private static UInt32 ToUInt32(IPAddress address)
{
var bytes = address.GetAddressBytes();
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
return BitConverter.ToUInt32(bytes, 0);
}
private static IPAddress ToIPAddress(UInt32 value)
{
var bytes = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
return new IPAddress(bytes);
}
}
|