You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

558 lines
16 KiB

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
}