feat: 1.新增在 VPN 配置中添加了一条规则,2.修复全局代理无法使用

This commit is contained in:
2025-11-18 05:15:06 -08:00
parent 59aa67d456
commit 679c303457
4 changed files with 766 additions and 318 deletions
+13 -10
View File
@@ -15,7 +15,8 @@ enum KRCountry {
ru('俄罗斯'),
id('印度尼西亚'),
tr('土耳其'),
br('巴西');
br('巴西'),
other('全局代理'); // ✅ 新增:全局代理模式(所有流量走代理,不添加国家直连规则)
final String kr_name;
const KRCountry(this.kr_name);
@@ -30,7 +31,7 @@ enum KRCountry {
static KRCountry? kr_fromCode(String code) {
try {
return KRCountry.values.firstWhere(
(country) => country.kr_code == code.toLowerCase(),
(country) => country.kr_code == code.toLowerCase(),
);
} catch (e) {
return null;
@@ -53,7 +54,7 @@ class KRCountryUtil {
static Future<void> kr_init() async {
try {
final String? kr_savedCountry =
await _kr_storage.kr_readData(key: _kr_countryKey);
await _kr_storage.kr_readData(key: _kr_countryKey);
if (kr_savedCountry != null) {
final KRCountry? kr_country = KRCountry.kr_fromCode(kr_savedCountry);
if (kr_country != null) {
@@ -66,7 +67,7 @@ class KRCountryUtil {
} else {
kr_currentCountry.value = KRCountry.cn;
}
} catch (err) {
KRLogUtil.kr_e('初始化国家设置失败: $err', tag: 'CountryUtil');
kr_currentCountry.value = KRCountry.cn;
@@ -106,9 +107,9 @@ class KRCountryUtil {
return kr_currentCountry.value.kr_code;
}
static String kr_getCurrentCountryName() {
static String kr_getCurrentCountryName() {
return kr_getCountryName(kr_currentCountry.value);
}
/// 获取国家名称
@@ -131,12 +132,14 @@ class KRCountryUtil {
return AppTranslations.kr_country.tr;
case KRCountry.br:
return AppTranslations.kr_country.br;
case KRCountry.other:
return '全局代理'; // ✅ 新增:全局代理
}
}
/// 获取所有支持的国家列表
static List<KRCountry> kr_getSupportedCountries() {
if (AppConfig().kr_is_daytime == false) {
if (AppConfig().kr_is_daytime == false) {
return KRCountry.values.where((element) => element != KRCountry.cn).toList();
}
return KRCountry.values;
@@ -146,9 +149,9 @@ class KRCountryUtil {
static List<Map<String, String>> kr_getCountryInfoList() {
return KRCountry.values
.map((country) => {
'code': country.kr_code,
'name': country.kr_countryName,
})
'code': country.kr_code,
'name': country.kr_countryName,
})
.toList();
}
}
+439
View File
@@ -0,0 +1,439 @@
import 'dart:io';
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
/// Windows DNS 管理工具类
///
/// 用于在 Windows 平台上管理系统 DNS 设置
/// 主要功能:
/// 1. 备份原始 DNS 设置
/// 2. 恢复 DNS 设置
/// 3. 兜底设置为国内公共 DNS (223.5.5.5 和 114.114.114.114)
class KRWindowsDnsUtil {
/// 私有构造函数
KRWindowsDnsUtil._();
/// 单例实例
static final KRWindowsDnsUtil _instance = KRWindowsDnsUtil._();
/// 工厂构造函数
factory KRWindowsDnsUtil() => _instance;
/// 获取实例的静态方法
static KRWindowsDnsUtil get instance => _instance;
/// 原始 DNS 服务器地址(连接前备份)
List<String>? _originalDnsServers;
/// 主网络接口名称
String? _primaryInterfaceName;
/// 备份当前 DNS 设置
///
/// 在连接 VPN 之前调用,保存原始 DNS 配置
/// 返回:true-成功,false-失败
Future<bool> kr_backupDnsSettings() async {
if (!Platform.isWindows) {
KRLogUtil.kr_w('❌ 非 Windows 平台,跳过 DNS 备份', tag: 'WindowsDNS');
return false;
}
try {
KRLogUtil.kr_i('📦 开始备份 Windows DNS 设置...', tag: 'WindowsDNS');
// 1. 获取主网络接口
final interfaceName = await _kr_getPrimaryNetworkInterface();
if (interfaceName == null) {
KRLogUtil.kr_e('❌ 无法获取主网络接口', tag: 'WindowsDNS');
return false;
}
_primaryInterfaceName = interfaceName;
KRLogUtil.kr_i('🔍 主网络接口: $_primaryInterfaceName', tag: 'WindowsDNS');
// 2. 获取当前 DNS 服务器
final dnsServers = await _kr_getCurrentDnsServers(interfaceName);
if (dnsServers.isEmpty) {
KRLogUtil.kr_w('⚠️ 当前 DNS 为空,可能是自动获取', tag: 'WindowsDNS');
_originalDnsServers = []; // 空列表表示 DHCP 自动获取
} else {
_originalDnsServers = dnsServers;
KRLogUtil.kr_i('✅ 已备份 DNS: ${dnsServers.join(", ")}', tag: 'WindowsDNS');
}
return true;
} catch (e) {
KRLogUtil.kr_e('❌ 备份 DNS 设置失败: $e', tag: 'WindowsDNS');
return false;
}
}
/// 恢复原始 DNS 设置
///
/// 在断开 VPN 后调用,恢复备份的 DNS 配置
/// 如果恢复失败,会自动调用兜底机制设置为公共DNS
/// 返回:true-成功,false-失败
Future<bool> kr_restoreDnsSettings() async {
if (!Platform.isWindows) {
KRLogUtil.kr_w('❌ 非 Windows 平台,跳过 DNS 恢复', tag: 'WindowsDNS');
return false;
}
try {
KRLogUtil.kr_i('🔄 开始恢复 Windows DNS 设置...', tag: 'WindowsDNS');
// 1. 检查是否有备份
if (_primaryInterfaceName == null) {
KRLogUtil.kr_w('⚠️ 没有备份的网络接口,尝试自动检测', tag: 'WindowsDNS');
_primaryInterfaceName = await _kr_getPrimaryNetworkInterface();
if (_primaryInterfaceName == null) {
KRLogUtil.kr_e('❌ 无法检测网络接口,执行兜底恢复', tag: 'WindowsDNS');
return await _kr_fallbackRestoreDns();
}
}
// 2. 恢复原始 DNS
if (_originalDnsServers == null) {
KRLogUtil.kr_w('⚠️ 没有备份的 DNS,执行兜底恢复', tag: 'WindowsDNS');
return await _kr_fallbackRestoreDns();
}
if (_originalDnsServers!.isEmpty) {
// 原本是 DHCP 自动获取
KRLogUtil.kr_i('🔄 恢复为 DHCP 自动获取 DNS', tag: 'WindowsDNS');
final success = await _kr_setDnsToAuto(_primaryInterfaceName!);
if (!success) {
KRLogUtil.kr_w('⚠️ 恢复 DHCP 失败,执行兜底恢复', tag: 'WindowsDNS');
return await _kr_fallbackRestoreDns();
}
} else {
// 恢复指定的 DNS 服务器
KRLogUtil.kr_i('🔄 恢复原始 DNS: ${_originalDnsServers!.join(", ")}', tag: 'WindowsDNS');
final success = await _kr_setDnsServers(
_primaryInterfaceName!,
_originalDnsServers!,
);
if (!success) {
KRLogUtil.kr_w('⚠️ 恢复原始 DNS 失败,执行兜底恢复', tag: 'WindowsDNS');
return await _kr_fallbackRestoreDns();
}
}
// 3. 验证 DNS 是否恢复成功
await Future.delayed(const Duration(milliseconds: 500));
final currentDns = await _kr_getCurrentDnsServers(_primaryInterfaceName!);
KRLogUtil.kr_i('✅ 当前 DNS: ${currentDns.join(", ")}', tag: 'WindowsDNS');
// 4. 额外验证:确认 DNS 不再指向 127.0.0.1sing-box 的本地 DNS
final hasLocalhost = currentDns.any((dns) => dns.startsWith('127.'));
if (hasLocalhost) {
KRLogUtil.kr_w('⚠️ DNS 仍包含 127.0.0.1,可能未完全恢复', tag: 'WindowsDNS');
KRLogUtil.kr_w('⚠️ 执行兜底恢复', tag: 'WindowsDNS');
return await _kr_fallbackRestoreDns();
}
return true;
} catch (e) {
KRLogUtil.kr_e('❌ 恢复 DNS 设置失败: $e', tag: 'WindowsDNS');
KRLogUtil.kr_w('⚠️ 执行兜底恢复', tag: 'WindowsDNS');
return await _kr_fallbackRestoreDns();
}
}
/// 兜底恢复:强制设置为国内公共 DNS
///
/// 当正常恢复失败时,设置为安全的公共 DNS 服务器
/// - 主 DNS: 223.5.5.5 (阿里云)
/// - 备用 DNS: 114.114.114.114 (114DNS)
Future<bool> _kr_fallbackRestoreDns() async {
try {
KRLogUtil.kr_w('🆘 执行 DNS 兜底恢复机制', tag: 'WindowsDNS');
KRLogUtil.kr_i('🔧 设置为国内公共 DNS: 223.5.5.5, 114.114.114.114', tag: 'WindowsDNS');
// 1. 获取主网络接口(如果还没有)
if (_primaryInterfaceName == null) {
_primaryInterfaceName = await _kr_getPrimaryNetworkInterface();
if (_primaryInterfaceName == null) {
KRLogUtil.kr_e('❌ 无法检测网络接口,兜底恢复失败', tag: 'WindowsDNS');
return false;
}
}
// 2. 设置为公共 DNS
final fallbackDns = ['223.5.5.5', '114.114.114.114'];
final success = await _kr_setDnsServers(_primaryInterfaceName!, fallbackDns);
if (success) {
KRLogUtil.kr_i('✅ 兜底 DNS 设置成功', tag: 'WindowsDNS');
// 3. 验证设置
await Future.delayed(const Duration(milliseconds: 500));
final currentDns = await _kr_getCurrentDnsServers(_primaryInterfaceName!);
KRLogUtil.kr_i('✅ 验证当前 DNS: ${currentDns.join(", ")}', tag: 'WindowsDNS');
return true;
} else {
KRLogUtil.kr_e('❌ 兜底 DNS 设置失败', tag: 'WindowsDNS');
return false;
}
} catch (e) {
KRLogUtil.kr_e('❌ 兜底恢复失败: $e', tag: 'WindowsDNS');
return false;
}
}
/// 获取主网络接口名称
///
/// 通过 netsh 命令查找处于"已连接"状态的主网络接口
/// 返回:接口名称,失败返回 null
Future<String?> _kr_getPrimaryNetworkInterface() async {
try {
// 使用 netsh 获取接口列表
final result = await Process.run('netsh', ['interface', 'show', 'interface']);
if (result.exitCode != 0) {
KRLogUtil.kr_e('❌ 获取网络接口失败: ${result.stderr}', tag: 'WindowsDNS');
return null;
}
final output = result.stdout.toString();
final lines = output.split('\n');
// 收集所有已连接的接口
final connectedInterfaces = <String>[];
// 查找所有"已连接"的接口
// 支持中文和英文 Windows 系统
for (var line in lines) {
// 中文: "已连接", 英文: "Connected", "Enabled"
if (line.contains('已连接') ||
line.contains('Connected') ||
line.toLowerCase().contains('enabled')) {
// 跳过表头行
if (line.contains('Admin') ||
line.contains('管理') ||
line.contains('State') ||
line.contains('状态')) {
continue;
}
// 解析接口名称(最后一列)
final parts = line.trim().split(RegExp(r'\s{2,}'));
if (parts.length >= 4) {
final interfaceName = parts.last.trim();
// 排除空接口名
if (interfaceName.isNotEmpty && interfaceName.length > 1) {
connectedInterfaces.add(interfaceName);
}
}
}
}
if (connectedInterfaces.isEmpty) {
KRLogUtil.kr_w('⚠️ 未找到已连接的网络接口', tag: 'WindowsDNS');
return null;
}
// 🔧 优化:优先选择有线网络(以太网),然后才是 Wi-Fi
// 有线网络通常更稳定
String? selectedInterface;
for (var interface in connectedInterfaces) {
final lowerName = interface.toLowerCase();
// 优先选择以太网
if (lowerName.contains('ethernet') ||
lowerName.contains('以太网') ||
lowerName.contains('lan') ||
lowerName.contains('local')) {
selectedInterface = interface;
KRLogUtil.kr_i('🔍 选择有线网络接口: $interface', tag: 'WindowsDNS');
break;
}
}
// 如果没有有线网络,选择第一个(通常是 Wi-Fi)
selectedInterface ??= connectedInterfaces.first;
if (selectedInterface != connectedInterfaces.first) {
KRLogUtil.kr_d('🔍 选择网络接口: $selectedInterface', tag: 'WindowsDNS');
} else {
KRLogUtil.kr_i('🔍 选择网络接口: $selectedInterface', tag: 'WindowsDNS');
}
return selectedInterface;
} catch (e) {
KRLogUtil.kr_e('❌ 获取网络接口异常: $e', tag: 'WindowsDNS');
return null;
}
}
/// 获取指定接口的当前 DNS 服务器
///
/// 参数:
/// - interfaceName: 网络接口名称
///
/// 返回:DNS 服务器列表
Future<List<String>> _kr_getCurrentDnsServers(String interfaceName) async {
try {
final result = await Process.run('netsh', [
'interface',
'ipv4',
'show',
'dnsservers',
'name="$interfaceName"',
]);
if (result.exitCode != 0) {
KRLogUtil.kr_e('❌ 获取 DNS 失败: ${result.stderr}', tag: 'WindowsDNS');
return [];
}
final output = result.stdout.toString();
final dnsServers = <String>[];
// 解析 DNS 服务器地址
final lines = output.split('\n');
for (var line in lines) {
// 查找 IP 地址格式的行
final ipMatch = RegExp(r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b').firstMatch(line);
if (ipMatch != null) {
final ip = ipMatch.group(0)!;
// 排除本地回环地址
if (!ip.startsWith('127.')) {
dnsServers.add(ip);
}
}
}
KRLogUtil.kr_d('🔍 当前 DNS: ${dnsServers.join(", ")}', tag: 'WindowsDNS');
return dnsServers;
} catch (e) {
KRLogUtil.kr_e('❌ 获取 DNS 异常: $e', tag: 'WindowsDNS');
return [];
}
}
/// 设置指定接口的 DNS 服务器
///
/// 参数:
/// - interfaceName: 网络接口名称
/// - dnsServers: DNS 服务器列表
///
/// 返回:true-成功,false-失败
Future<bool> _kr_setDnsServers(String interfaceName, List<String> dnsServers) async {
try {
if (dnsServers.isEmpty) {
KRLogUtil.kr_w('⚠️ DNS 列表为空,无法设置', tag: 'WindowsDNS');
return false;
}
// 1. 设置主 DNS
KRLogUtil.kr_i('🔧 设置主 DNS: ${dnsServers[0]}', tag: 'WindowsDNS');
var result = await Process.run('netsh', [
'interface',
'ipv4',
'set',
'dnsservers',
'name="$interfaceName"',
'source=static',
'address=${dnsServers[0]}',
'validate=no',
]);
if (result.exitCode != 0) {
KRLogUtil.kr_e('❌ 设置主 DNS 失败: ${result.stderr}', tag: 'WindowsDNS');
return false;
}
// 2. 设置备用 DNS(如果有)
if (dnsServers.length > 1) {
for (int i = 1; i < dnsServers.length; i++) {
KRLogUtil.kr_i('🔧 设置备用 DNS ${i}: ${dnsServers[i]}', tag: 'WindowsDNS');
result = await Process.run('netsh', [
'interface',
'ipv4',
'add',
'dnsservers',
'name="$interfaceName"',
'address=${dnsServers[i]}',
'index=${i + 1}',
'validate=no',
]);
if (result.exitCode != 0) {
KRLogUtil.kr_w('⚠️ 设置备用 DNS $i 失败: ${result.stderr}', tag: 'WindowsDNS');
// 继续设置下一个,不中断
}
}
}
// 3. 刷新 DNS 缓存
await _kr_flushDnsCache();
KRLogUtil.kr_i('✅ DNS 服务器设置完成', tag: 'WindowsDNS');
return true;
} catch (e) {
KRLogUtil.kr_e('❌ 设置 DNS 异常: $e', tag: 'WindowsDNS');
return false;
}
}
/// 设置 DNS 为自动获取(DHCP
///
/// 参数:
/// - interfaceName: 网络接口名称
///
/// 返回:true-成功,false-失败
Future<bool> _kr_setDnsToAuto(String interfaceName) async {
try {
KRLogUtil.kr_i('🔧 设置 DNS 为自动获取 (DHCP)', tag: 'WindowsDNS');
final result = await Process.run('netsh', [
'interface',
'ipv4',
'set',
'dnsservers',
'name="$interfaceName"',
'source=dhcp',
]);
if (result.exitCode != 0) {
KRLogUtil.kr_e('❌ 设置 DHCP 失败: ${result.stderr}', tag: 'WindowsDNS');
return false;
}
// 刷新 DNS 缓存
await _kr_flushDnsCache();
KRLogUtil.kr_i('✅ DNS 已设置为自动获取', tag: 'WindowsDNS');
return true;
} catch (e) {
KRLogUtil.kr_e('❌ 设置 DHCP 异常: $e', tag: 'WindowsDNS');
return false;
}
}
/// 刷新 DNS 缓存
///
/// 执行 ipconfig /flushdns 命令清空 DNS 解析缓存
Future<void> _kr_flushDnsCache() async {
try {
KRLogUtil.kr_i('🔄 刷新 DNS 缓存...', tag: 'WindowsDNS');
final result = await Process.run('ipconfig', ['/flushdns']);
if (result.exitCode == 0) {
KRLogUtil.kr_i('✅ DNS 缓存已刷新', tag: 'WindowsDNS');
} else {
KRLogUtil.kr_w('⚠️ 刷新 DNS 缓存失败: ${result.stderr}', tag: 'WindowsDNS');
}
} catch (e) {
KRLogUtil.kr_w('⚠️ 刷新 DNS 缓存异常: $e', tag: 'WindowsDNS');
}
}
/// 清除备份数据
///
/// 在应用退出或不需要时调用
void kr_clearBackup() {
_originalDnsServers = null;
_primaryInterfaceName = null;
KRLogUtil.kr_d('🗑️ 已清除 DNS 备份数据', tag: 'WindowsDNS');
}
}