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? _originalDnsServers; /// 主网络接口名称 String? _primaryInterfaceName; /// 备份当前 DNS 设置 /// /// 在连接 VPN 之前调用,保存原始 DNS 配置 /// 返回:true-成功,false-失败 Future 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 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.1(sing-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 _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 _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 = []; // 查找所有"已连接"的接口 // 支持中文和英文 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> _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 = []; // 解析 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 _kr_setDnsServers(String interfaceName, List 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 _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 _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'); } }