package system import ( "fmt" "os" "os/exec" "runtime" "strconv" "strings" "github.com/azoic/wormhole-client/pkg/logger" ) // SystemProxyManager 系统代理管理器 type SystemProxyManager struct { originalSettings map[string]string isEnabled bool } // NewSystemProxyManager 创建系统代理管理器 func NewSystemProxyManager() *SystemProxyManager { return &SystemProxyManager{ originalSettings: make(map[string]string), isEnabled: false, } } // SetGlobalProxy 设置全局代理 func (s *SystemProxyManager) SetGlobalProxy(httpProxy, httpsProxy, socksProxy string) error { logger.Info("Setting system proxy...") logger.Debug("HTTP: %s, HTTPS: %s, SOCKS: %s", httpProxy, httpsProxy, socksProxy) var err error switch runtime.GOOS { case "darwin": err = s.setMacOSProxy(httpProxy, httpsProxy, socksProxy) case "windows": err = s.setWindowsProxy(httpProxy, httpsProxy, socksProxy) case "linux": err = s.setLinuxProxy(httpProxy, httpsProxy, socksProxy) default: return fmt.Errorf("unsupported operating system: %s", runtime.GOOS) } if err == nil { s.isEnabled = true logger.Info("✅ System proxy configured successfully") } return err } // RestoreProxy 恢复原始代理设置 func (s *SystemProxyManager) RestoreProxy() error { if !s.isEnabled { logger.Debug("System proxy was not enabled, nothing to restore") return nil } logger.Info("Restoring system proxy...") var err error switch runtime.GOOS { case "darwin": err = s.restoreMacOSProxy() case "windows": err = s.restoreWindowsProxy() case "linux": err = s.restoreLinuxProxy() default: return fmt.Errorf("unsupported operating system: %s", runtime.GOOS) } if err == nil { s.isEnabled = false logger.Info("✅ System proxy restored successfully") } return err } // IsEnabled 检查代理是否已启用 func (s *SystemProxyManager) IsEnabled() bool { return s.isEnabled } // GetCurrentProxy 获取当前代理设置 func (s *SystemProxyManager) GetCurrentProxy() (map[string]string, error) { switch runtime.GOOS { case "darwin": return s.getMacOSCurrentProxy() case "windows": return s.getWindowsCurrentProxy() case "linux": return s.getLinuxCurrentProxy() default: return nil, fmt.Errorf("unsupported operating system: %s", runtime.GOOS) } } // ===================== macOS 实现 ===================== // macOS 代理设置 func (s *SystemProxyManager) setMacOSProxy(httpProxy, httpsProxy, socksProxy string) error { // 获取网络服务名称 networkService, err := s.getMacOSNetworkService() if err != nil { return fmt.Errorf("failed to get network service: %v", err) } logger.Debug("Using network service: %s", networkService) // 保存原始设置 if err := s.saveMacOSOriginalSettings(networkService); err != nil { logger.Warn("Failed to save original proxy settings: %v", err) } // 设置HTTP代理 if httpProxy != "" { host, port := s.parseProxyHostPort(httpProxy) if err := s.runCommand("networksetup", "-setwebproxy", networkService, host, port); err != nil { return fmt.Errorf("failed to set HTTP proxy: %v", err) } if err := s.runCommand("networksetup", "-setwebproxystate", networkService, "on"); err != nil { return fmt.Errorf("failed to enable HTTP proxy: %v", err) } logger.Debug("HTTP proxy set to %s", httpProxy) } // 设置HTTPS代理 if httpsProxy != "" { host, port := s.parseProxyHostPort(httpsProxy) if err := s.runCommand("networksetup", "-setsecurewebproxy", networkService, host, port); err != nil { return fmt.Errorf("failed to set HTTPS proxy: %v", err) } if err := s.runCommand("networksetup", "-setsecurewebproxystate", networkService, "on"); err != nil { return fmt.Errorf("failed to enable HTTPS proxy: %v", err) } logger.Debug("HTTPS proxy set to %s", httpsProxy) } // 设置SOCKS代理 if socksProxy != "" { host, port := s.parseProxyHostPort(socksProxy) if err := s.runCommand("networksetup", "-setsocksfirewallproxy", networkService, host, port); err != nil { return fmt.Errorf("failed to set SOCKS proxy: %v", err) } if err := s.runCommand("networksetup", "-setsocksfirewallproxystate", networkService, "on"); err != nil { return fmt.Errorf("failed to enable SOCKS proxy: %v", err) } logger.Debug("SOCKS proxy set to %s", socksProxy) } return nil } // 获取macOS网络服务名称 func (s *SystemProxyManager) getMacOSNetworkService() (string, error) { output, err := exec.Command("networksetup", "-listallnetworkservices").Output() if err != nil { return "", fmt.Errorf("failed to list network services: %v", err) } lines := strings.Split(string(output), "\n") // 优先查找Wi-Fi服务 for _, line := range lines { line = strings.TrimSpace(line) if line != "" && !strings.HasPrefix(line, "*") && !strings.Contains(line, "An asterisk") { lower := strings.ToLower(line) if strings.Contains(lower, "wi-fi") || strings.Contains(lower, "wifi") { return line, nil } } } // 查找以太网服务 for _, line := range lines { line = strings.TrimSpace(line) if line != "" && !strings.HasPrefix(line, "*") && !strings.Contains(line, "An asterisk") { lower := strings.ToLower(line) if strings.Contains(lower, "ethernet") || strings.Contains(lower, "thunderbolt") { return line, nil } } } // 返回第一个可用的服务 for _, line := range lines { line = strings.TrimSpace(line) if line != "" && !strings.HasPrefix(line, "*") && !strings.Contains(line, "An asterisk") { return line, nil } } return "", fmt.Errorf("no active network service found") } // 保存macOS原始代理设置 func (s *SystemProxyManager) saveMacOSOriginalSettings(networkService string) error { commands := map[string][]string{ "http_proxy": {"-getwebproxy", networkService}, "https_proxy": {"-getsecurewebproxy", networkService}, "socks_proxy": {"-getsocksfirewallproxy", networkService}, } for key, args := range commands { if output, err := exec.Command("networksetup", args...).Output(); err == nil { s.originalSettings[key] = strings.TrimSpace(string(output)) } } s.originalSettings["network_service"] = networkService logger.Debug("Saved original proxy settings for %s", networkService) return nil } // 恢复macOS代理设置 func (s *SystemProxyManager) restoreMacOSProxy() error { networkService, exists := s.originalSettings["network_service"] if !exists { return fmt.Errorf("no network service information saved") } // 关闭所有代理 commands := [][]string{ {"-setwebproxystate", networkService, "off"}, {"-setsecurewebproxystate", networkService, "off"}, {"-setsocksfirewallproxystate", networkService, "off"}, } for _, args := range commands { if err := s.runCommand("networksetup", args...); err != nil { logger.Warn("Failed to restore proxy setting: %v", err) } } return nil } // 获取macOS当前代理设置 func (s *SystemProxyManager) getMacOSCurrentProxy() (map[string]string, error) { networkService, err := s.getMacOSNetworkService() if err != nil { return nil, err } result := make(map[string]string) // 获取HTTP代理 if output, err := exec.Command("networksetup", "-getwebproxy", networkService).Output(); err == nil { result["http"] = strings.TrimSpace(string(output)) } // 获取HTTPS代理 if output, err := exec.Command("networksetup", "-getsecurewebproxy", networkService).Output(); err == nil { result["https"] = strings.TrimSpace(string(output)) } // 获取SOCKS代理 if output, err := exec.Command("networksetup", "-getsocksfirewallproxy", networkService).Output(); err == nil { result["socks"] = strings.TrimSpace(string(output)) } return result, nil } // ===================== Windows 实现 ===================== // Windows 代理设置 func (s *SystemProxyManager) setWindowsProxy(httpProxy, httpsProxy, socksProxy string) error { // 保存当前设置 if err := s.saveWindowsOriginalSettings(); err != nil { logger.Warn("Failed to save Windows proxy settings: %v", err) } // 设置HTTP/HTTPS代理 if httpProxy != "" { // 使用 netsh 或注册表设置代理 proxyServer := httpProxy if httpsProxy != "" && httpsProxy != httpProxy { proxyServer = fmt.Sprintf("http=%s;https=%s", httpProxy, httpsProxy) } // 使用PowerShell设置代理 cmd := fmt.Sprintf(` $regPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" Set-ItemProperty -Path $regPath -Name ProxyEnable -Value 1 Set-ItemProperty -Path $regPath -Name ProxyServer -Value "%s" `, proxyServer) if err := s.runPowerShell(cmd); err != nil { return fmt.Errorf("failed to set Windows proxy: %v", err) } logger.Debug("Windows HTTP proxy set to %s", proxyServer) } return nil } // 保存Windows原始代理设置 func (s *SystemProxyManager) saveWindowsOriginalSettings() error { // 读取当前注册表设置 cmd := ` $regPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" $enable = Get-ItemProperty -Path $regPath -Name ProxyEnable -ErrorAction SilentlyContinue $server = Get-ItemProperty -Path $regPath -Name ProxyServer -ErrorAction SilentlyContinue Write-Output "ProxyEnable:$($enable.ProxyEnable)" Write-Output "ProxyServer:$($server.ProxyServer)" ` output, err := s.runPowerShellWithOutput(cmd) if err != nil { return err } lines := strings.Split(output, "\n") for _, line := range lines { if strings.HasPrefix(line, "ProxyEnable:") { s.originalSettings["windows_proxy_enable"] = strings.TrimPrefix(line, "ProxyEnable:") } else if strings.HasPrefix(line, "ProxyServer:") { s.originalSettings["windows_proxy_server"] = strings.TrimPrefix(line, "ProxyServer:") } } return nil } // 恢复Windows代理设置 func (s *SystemProxyManager) restoreWindowsProxy() error { enable := s.originalSettings["windows_proxy_enable"] server := s.originalSettings["windows_proxy_server"] cmd := fmt.Sprintf(` $regPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" Set-ItemProperty -Path $regPath -Name ProxyEnable -Value %s Set-ItemProperty -Path $regPath -Name ProxyServer -Value "%s" `, strings.TrimSpace(enable), strings.TrimSpace(server)) return s.runPowerShell(cmd) } // 获取Windows当前代理设置 func (s *SystemProxyManager) getWindowsCurrentProxy() (map[string]string, error) { cmd := ` $regPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" $enable = Get-ItemProperty -Path $regPath -Name ProxyEnable -ErrorAction SilentlyContinue $server = Get-ItemProperty -Path $regPath -Name ProxyServer -ErrorAction SilentlyContinue Write-Output "ProxyEnable:$($enable.ProxyEnable)" Write-Output "ProxyServer:$($server.ProxyServer)" ` output, err := s.runPowerShellWithOutput(cmd) if err != nil { return nil, err } result := make(map[string]string) lines := strings.Split(output, "\n") for _, line := range lines { if strings.HasPrefix(line, "ProxyEnable:") { result["enabled"] = strings.TrimSpace(strings.TrimPrefix(line, "ProxyEnable:")) } else if strings.HasPrefix(line, "ProxyServer:") { result["server"] = strings.TrimSpace(strings.TrimPrefix(line, "ProxyServer:")) } } return result, nil } // ===================== Linux 实现 ===================== // Linux 代理设置 func (s *SystemProxyManager) setLinuxProxy(httpProxy, httpsProxy, socksProxy string) error { // 保存当前环境变量 s.saveLinuxOriginalSettings() envVars := []string{} if httpProxy != "" { envVars = append(envVars, fmt.Sprintf("export http_proxy=%s", httpProxy)) envVars = append(envVars, fmt.Sprintf("export HTTP_PROXY=%s", httpProxy)) } if httpsProxy != "" { envVars = append(envVars, fmt.Sprintf("export https_proxy=%s", httpsProxy)) envVars = append(envVars, fmt.Sprintf("export HTTPS_PROXY=%s", httpsProxy)) } if socksProxy != "" { envVars = append(envVars, fmt.Sprintf("export socks_proxy=%s", socksProxy)) envVars = append(envVars, fmt.Sprintf("export SOCKS_PROXY=%s", socksProxy)) } // 写入到 /etc/environment (需要root权限) envContent := strings.Join(envVars, "\n") + "\n" // 尝试写入系统环境变量文件 if err := s.writeLinuxSystemProxy(envContent); err != nil { logger.Warn("Failed to write system proxy settings: %v", err) logger.Info("Please manually add the following to your shell profile:") for _, env := range envVars { logger.Info(" %s", env) } } // 设置当前会话的环境变量 for _, env := range envVars { parts := strings.SplitN(strings.TrimPrefix(env, "export "), "=", 2) if len(parts) == 2 { os.Setenv(parts[0], parts[1]) } } return nil } // 保存Linux原始代理设置 func (s *SystemProxyManager) saveLinuxOriginalSettings() { envVars := []string{"http_proxy", "https_proxy", "socks_proxy", "HTTP_PROXY", "HTTPS_PROXY", "SOCKS_PROXY"} for _, env := range envVars { if value := os.Getenv(env); value != "" { s.originalSettings["linux_"+env] = value } } } // 恢复Linux代理设置 func (s *SystemProxyManager) restoreLinuxProxy() error { // 清除当前会话的环境变量 envVars := []string{"http_proxy", "https_proxy", "socks_proxy", "HTTP_PROXY", "HTTPS_PROXY", "SOCKS_PROXY"} for _, env := range envVars { originalKey := "linux_" + env if originalValue, exists := s.originalSettings[originalKey]; exists { os.Setenv(env, originalValue) } else { os.Unsetenv(env) } } logger.Info("Environment variables restored (session only)") logger.Info("Note: You may need to manually remove proxy settings from system files") return nil } // 获取Linux当前代理设置 func (s *SystemProxyManager) getLinuxCurrentProxy() (map[string]string, error) { result := make(map[string]string) envVars := map[string]string{ "http": "http_proxy", "https": "https_proxy", "socks": "socks_proxy", } for key, env := range envVars { if value := os.Getenv(env); value != "" { result[key] = value } else if value := os.Getenv(strings.ToUpper(env)); value != "" { result[key] = value } } return result, nil } // ===================== 辅助函数 ===================== // 运行系统命令 func (s *SystemProxyManager) runCommand(name string, args ...string) error { cmd := exec.Command(name, args...) output, err := cmd.CombinedOutput() if err != nil { logger.Error("Command failed: %s %v, output: %s", name, args, string(output)) return fmt.Errorf("command '%s %s' failed: %v", name, strings.Join(args, " "), err) } logger.Debug("Command succeeded: %s %v", name, args) return nil } // 运行PowerShell命令 (Windows) func (s *SystemProxyManager) runPowerShell(script string) error { cmd := exec.Command("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", script) output, err := cmd.CombinedOutput() if err != nil { logger.Error("PowerShell command failed: %s, output: %s", script, string(output)) return fmt.Errorf("PowerShell command failed: %v", err) } return nil } // 运行PowerShell命令并获取输出 (Windows) func (s *SystemProxyManager) runPowerShellWithOutput(script string) (string, error) { cmd := exec.Command("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", script) output, err := cmd.Output() if err != nil { return "", fmt.Errorf("PowerShell command failed: %v", err) } return string(output), nil } // 写入Linux系统代理设置 func (s *SystemProxyManager) writeLinuxSystemProxy(content string) error { // 尝试写入 /etc/environment envFile := "/etc/environment" // 检查是否有写权限 if _, err := os.Stat(envFile); os.IsNotExist(err) { return fmt.Errorf("file %s does not exist", envFile) } // 备份原文件 if err := exec.Command("cp", envFile, envFile+".wormhole.backup").Run(); err != nil { logger.Warn("Failed to backup %s: %v", envFile, err) } // 追加代理设置 f, err := os.OpenFile(envFile, os.O_APPEND|os.O_WRONLY, 0644) if err != nil { return err } defer f.Close() _, err = f.WriteString("\n# Wormhole SOCKS5 Client Proxy Settings\n" + content) return err } // 解析代理地址 func (s *SystemProxyManager) parseProxyHostPort(proxy string) (host, port string) { // 移除协议前缀 proxy = strings.TrimPrefix(proxy, "http://") proxy = strings.TrimPrefix(proxy, "https://") proxy = strings.TrimPrefix(proxy, "socks5://") parts := strings.Split(proxy, ":") if len(parts) >= 2 { host = parts[0] port = parts[1] } else { host = proxy port = "8080" // 默认端口 } // 验证端口 if _, err := strconv.Atoi(port); err != nil { port = "8080" } return host, port }