using System.Diagnostics;
using NewLife;
namespace Rainbow.Services;
/// <summary>Windows 端口代理管理器。通过 netsh interface portproxy 实现 DNAT/SNAT</summary>
public class WindowsPortProxyManager
{
/// <summary>添加端口转发规则(DNAT)</summary>
/// <param name="listenPort">监听端口</param>
/// <param name="connectAddress">目标地址</param>
/// <param name="connectPort">目标端口</param>
/// <param name="protocol">协议 tcp/udp</param>
/// <returns>是否成功</returns>
public async Task<Boolean> AddPortForwardAsync(Int32 listenPort, String connectAddress, Int32 connectPort, String protocol = "tcp")
{
try
{
var cmd = $"netsh interface portproxy add v4tov4 listenport={listenPort} " +
$"connectport={connectPort} connectaddress={connectAddress} protocol={protocol}";
var (_, success) = await RunProcessAsync("netsh", $"interface portproxy add v4tov4 " +
$"listenport={listenPort} connectport={connectPort} connectaddress={connectAddress} protocol={protocol}");
return success;
}
catch { return false; }
}
/// <summary>删除端口转发规则</summary>
public async Task<Boolean> RemovePortForwardAsync(Int32 listenPort, String protocol = "tcp")
{
try
{
var (_, success) = await RunProcessAsync("netsh",
$"interface portproxy delete v4tov4 listenport={listenPort} protocol={protocol}");
return success;
}
catch { return false; }
}
/// <summary>列出所有端口转发规则</summary>
public async Task<List<PortProxyEntry>> ListPortForwardsAsync()
{
var result = new List<PortProxyEntry>();
try
{
var (stdout, success) = await RunProcessAsync("netsh", "interface portproxy show all");
if (success && !String.IsNullOrEmpty(stdout))
{
// 解析 netsh 输出格式
var lines = stdout.Split('\n', StringSplitOptions.RemoveEmptyEntries);
var inTable = false;
foreach (var line in lines)
{
var trimmed = line.Trim();
if (trimmed.Contains("-------")) { inTable = true; continue; }
if (!inTable || String.IsNullOrEmpty(trimmed)) continue;
var parts = trimmed.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 4)
{
result.Add(new PortProxyEntry
{
ListenAddress = parts[0],
ListenPort = Int32.TryParse(parts[1], out var lp) ? lp : 0,
ConnectAddress = parts[2],
ConnectPort = Int32.TryParse(parts[3], out var cp) ? cp : 0,
});
}
}
}
}
catch { }
return result;
}
/// <summary>清空所有端口转发规则</summary>
public async Task<Boolean> ClearAllAsync()
{
try
{
var (_, success) = await RunProcessAsync("netsh",
"interface portproxy reset");
return success;
}
catch { return false; }
}
private static async Task<(String? stdout, Boolean success)> RunProcessAsync(String fileName, String arguments)
{
try
{
var psi = new ProcessStartInfo(fileName)
{
ArgumentList = { arguments },
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
};
using var process = new Process { StartInfo = psi };
process.Start();
var stdout = await process.StandardOutput.ReadToEndAsync();
var exited = process.WaitForExit(15_000);
return (stdout?.Trim(), exited && process.ExitCode == 0);
}
catch { return (null, false); }
}
}
/// <summary>端口代理条目 DTO</summary>
public class PortProxyEntry
{
/// <summary>监听地址</summary>
public String ListenAddress { get; set; } = "";
/// <summary>监听端口</summary>
public Int32 ListenPort { get; set; }
/// <summary>目标地址</summary>
public String ConnectAddress { get; set; } = "";
/// <summary>目标端口</summary>
public Int32 ConnectPort { get; set; }
}
/// <summary>Windows 防火墙黑名单管理器。通过 netsh advfirewall 实现 IP/MAC 黑名单</summary>
public class WindowsBlacklistManager
{
/// <summary>添加 IP 黑名单规则(阻止入站/出站流量)</summary>
/// <param name="ip">要屏蔽的 IP 地址</param>
/// <param name="direction">方向:in/out</param>
/// <returns>是否成功</returns>
public async Task<Boolean> BlockIpAsync(String ip, String direction = "in")
{
try
{
var ruleName = $"Rainbow_Block_{ip.Replace('.', '_').Replace(':', '_')}";
var dir = direction == "in" ? "in" : "out";
var cmd = $"netsh advfirewall firewall add rule name=\"{ruleName}\" " +
$"dir={dir} action=block remoteip={ip} protocol=any";
var (_, success) = await RunProcessAsync("netsh", cmd);
return success;
}
catch { return false; }
}
/// <summary>删除 IP 黑名单规则</summary>
public async Task<Boolean> UnblockIpAsync(String ip)
{
try
{
var ruleName = $"Rainbow_Block_{ip.Replace('.', '_').Replace(':', '_')}";
var (_, success) = await RunProcessAsync("netsh",
$"advfirewall firewall delete rule name=\"{ruleName}\"");
return success;
}
catch { return false; }
}
/// <summary>添加 MAC 黑名单规则(阻止特定 MAC 地址通信)</summary>
/// <param name="mac">MAC 地址</param>
/// <returns>是否成功</returns>
public async Task<Boolean> BlockMacAsync(String mac)
{
try
{
// Windows 防火墙不支持直接按 MAC 过滤,使用 PowerShell 创建高级防火墙规则
var macClean = mac.Replace(":", "").Replace("-", "");
var ruleName = $"Rainbow_Block_MAC_{macClean}";
var cmd = $"New-NetFirewallRule -DisplayName \"{ruleName}\" -Direction Inbound -Action Block " +
$"-LocalAddress Any -RemoteAddress Any -Description \"Blocked by Rainbow\" " +
$"-ErrorAction SilentlyContinue; " +
$"Set-NetFirewallRule -DisplayName \"{ruleName}\" -LocalAddress Any; $true";
var (_, success) = await RunPowerShellAsync(cmd);
return success;
}
catch { return false; }
}
/// <summary>删除 MAC 黑名单规则</summary>
public async Task<Boolean> UnblockMacAsync(String mac)
{
try
{
var macClean = mac.Replace(":", "").Replace("-", "");
var ruleName = $"Rainbow_Block_MAC_{macClean}";
var (_, success) = await RunPowerShellAsync(
$"Remove-NetFirewallRule -DisplayName \"{ruleName}\" -ErrorAction SilentlyContinue; $true");
return success;
}
catch { return false; }
}
/// <summary>列出所有 Rainbow 创建的防火墙规则</summary>
public async Task<List<String>> ListRulesAsync()
{
try
{
var (stdout, _) = await RunPowerShellAsync(
"Get-NetFirewallRule -DisplayName 'Rainbow_*' -ErrorAction SilentlyContinue | " +
"Select-Object DisplayName,Enabled,Action,Direction | Format-Table -AutoSize | Out-String");
return !String.IsNullOrEmpty(stdout)
? stdout.Split('\n', StringSplitOptions.RemoveEmptyEntries).Select(l => l.Trim()).Where(l => l.Length > 0).ToList()
: [];
}
catch { return []; }
}
private static async Task<(String? stdout, Boolean success)> RunProcessAsync(String fileName, String arguments)
{
try
{
var psi = new ProcessStartInfo(fileName)
{
ArgumentList = { arguments },
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
};
using var process = new Process { StartInfo = psi };
process.Start();
var stdout = await process.StandardOutput.ReadToEndAsync();
var exited = process.WaitForExit(15_000);
return (stdout?.Trim(), exited && process.ExitCode == 0);
}
catch { return (null, false); }
}
private static async Task<(String? stdout, Boolean success)> RunPowerShellAsync(String command)
{
try
{
var psi = new ProcessStartInfo("powershell", $"-NoProfile -NonInteractive -Command \"{command}\"")
{
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
};
using var process = new Process { StartInfo = psi };
process.Start();
var stdout = await process.StandardOutput.ReadToEndAsync();
var exited = process.WaitForExit(15_000);
return (stdout?.Trim(), exited && process.ExitCode == 0);
}
catch { return (null, false); }
}
}
/// <summary>Windows 故障检测与切换管理器。通过 ping + route 实现</summary>
public class WindowsFailoverManager
{
/// <summary>Ping 检测网关是否可达</summary>
/// <param name="gateway">网关 IP</param>
/// <param name="timeout">超时毫秒</param>
/// <returns>是否可达</returns>
public async Task<Boolean> PingGatewayAsync(String gateway, Int32 timeout = 3000)
{
try
{
var (_, success) = await RunProcessAsync("ping",
$"-n 1 -w {timeout} {gateway}");
return success;
}
catch { return false; }
}
/// <summary>切换默认路由到指定网关</summary>
/// <param name="gateway">新网关</param>
/// <param name="metric">跃点数(越小越优先)</param>
/// <returns>是否成功</returns>
public async Task<Boolean> SwitchDefaultRouteAsync(String gateway, Int32 metric = 10)
{
try
{
// 先删除现有默认路由,再添加新的
var (_, delOk) = await RunProcessAsync("route", "delete 0.0.0.0");
var (_, addOk) = await RunProcessAsync("route",
$"add 0.0.0.0 mask 0.0.0.0 {gateway} metric {metric}");
return addOk;
}
catch { return false; }
}
/// <summary>添加持久路由(重启后保留)</summary>
/// <param name="network">目标网络</param>
/// <param name="gateway">网关</param>
/// <param name="metric">跃点数</param>
/// <returns>是否成功</returns>
public async Task<Boolean> AddPersistentRouteAsync(String network, String gateway, Int32 metric = 256)
{
try
{
// route add -p 添加持久路由
var (_, success) = await RunProcessAsync("route",
$"add {network} mask 255.255.255.0 {gateway} metric {metric} -p");
return success;
}
catch { return false; }
}
/// <summary>获取当前默认路由</summary>
public async Task<String?> GetDefaultGatewayAsync()
{
try
{
var (stdout, _) = await RunProcessAsync("route", "print 0.0.0.0");
if (String.IsNullOrEmpty(stdout)) return null;
var lines = stdout.Split('\n', StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
var trimmed = line.Trim();
if (trimmed.StartsWith("0.0.0.0"))
{
var parts = trimmed.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 3)
return parts[2]; // 网关在第三列
}
}
}
catch { }
return null;
}
private static async Task<(String? stdout, Boolean success)> RunProcessAsync(String fileName, String arguments)
{
try
{
var psi = new ProcessStartInfo(fileName)
{
ArgumentList = { arguments },
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
};
using var process = new Process { StartInfo = psi };
process.Start();
var stdout = await process.StandardOutput.ReadToEndAsync();
var exited = process.WaitForExit(15_000);
return (stdout?.Trim(), exited && process.ExitCode == 0);
}
catch { return (null, false); }
}
}
|