refactor: 拆分 proxy.go 为平台特定文件,兼容 macOS 和 Linux\n\n- proxy.go: 保留通用类型和辅助函数\n- proxy_windows.go: 通过注册表读取 Windows 系统代理\n- proxy_darwin.go: 通过 scutil --proxy 读取 macOS 系统代理\n- proxy_linux.go: 通过 gsettings/kreadconfig 读取 Linux 系统代理"
This commit is contained in:
@@ -4,8 +4,6 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
// ProxyInfo 保存当前系统代理信息
|
||||
@@ -25,8 +23,8 @@ type ProxyInfo struct {
|
||||
}
|
||||
|
||||
// GetCurrentProxy 获取本机当前的代理设置
|
||||
// 优先读取 Windows 系统代理(注册表),同时也读取环境变量中的代理配置
|
||||
// 支持检测 Clash、V2Ray 等常见代理工具的配置
|
||||
// 优先读取系统代理(Windows 注册表 / macOS scutil / Linux 环境变量),
|
||||
// 同时也读取环境变量中的代理配置
|
||||
func GetCurrentProxy() (*ProxyInfo, error) {
|
||||
info := &ProxyInfo{}
|
||||
|
||||
@@ -34,9 +32,8 @@ func GetCurrentProxy() (*ProxyInfo, error) {
|
||||
info.HTTPProxy = getEnvProxy("HTTP_PROXY", "http_proxy")
|
||||
info.HTTPSProxy = getEnvProxy("HTTPS_PROXY", "https_proxy")
|
||||
|
||||
// 2. 从 Windows 注册表读取系统代理设置
|
||||
if err := readWindowsSystemProxy(info); err != nil {
|
||||
// 注册表读取失败不算致命错误,环境变量中可能仍有代理信息
|
||||
// 2. 从系统设置读取代理(平台相关)
|
||||
if err := readSystemProxy(info); err != nil {
|
||||
fmt.Printf("读取系统代理设置时出错: %v\n", err)
|
||||
}
|
||||
|
||||
@@ -54,42 +51,6 @@ func GetCurrentProxy() (*ProxyInfo, error) {
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// readWindowsSystemProxy 从 Windows 注册表读取系统代理设置
|
||||
func readWindowsSystemProxy(info *ProxyInfo) error {
|
||||
key, err := registry.OpenKey(
|
||||
registry.CURRENT_USER,
|
||||
`Software\Microsoft\Windows\CurrentVersion\Internet Settings`,
|
||||
registry.QUERY_VALUE,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("无法打开注册表键: %w", err)
|
||||
}
|
||||
defer key.Close()
|
||||
|
||||
// 读取代理是否启用 (ProxyEnable: 0=关闭, 1=开启)
|
||||
proxyEnable, _, err := key.GetIntegerValue("ProxyEnable")
|
||||
if err != nil {
|
||||
return fmt.Errorf("无法读取 ProxyEnable: %w", err)
|
||||
}
|
||||
info.Enabled = proxyEnable == 1
|
||||
|
||||
// 读取代理服务器地址
|
||||
proxyServer, _, err := key.GetStringValue("ProxyServer")
|
||||
if err == nil && proxyServer != "" {
|
||||
info.Server = proxyServer
|
||||
// 根据端口推断代理协议
|
||||
info.Protocol = guessProtocol(proxyServer)
|
||||
}
|
||||
|
||||
// 读取代理绕过列表
|
||||
bypass, _, err := key.GetStringValue("ProxyOverride")
|
||||
if err == nil {
|
||||
info.Bypass = bypass
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getEnvProxy 依次检查多个环境变量名,返回第一个非空值
|
||||
func getEnvProxy(names ...string) string {
|
||||
for _, name := range names {
|
||||
@@ -104,11 +65,9 @@ func getEnvProxy(names ...string) string {
|
||||
// 例如 "http://127.0.0.1:7890" -> "127.0.0.1:7890"
|
||||
func extractServerFromURL(proxyURL string) string {
|
||||
s := proxyURL
|
||||
// 去掉协议前缀
|
||||
if idx := strings.Index(s, "://"); idx != -1 {
|
||||
s = s[idx+3:]
|
||||
}
|
||||
// 去掉尾部斜杠
|
||||
s = strings.TrimRight(s, "/")
|
||||
return s
|
||||
}
|
||||
@@ -127,14 +86,14 @@ func extractProtocolFromURL(proxyURL string) string {
|
||||
// V2Ray 默认: 10808(socks5), 10809(http)
|
||||
func guessProtocol(server string) string {
|
||||
knownPorts := map[string]string{
|
||||
"7890": "http", // Clash HTTP
|
||||
"7891": "socks5", // Clash SOCKS5
|
||||
"7892": "http", // Clash mixed
|
||||
"10808": "socks5", // V2Ray SOCKS5
|
||||
"10809": "http", // V2Ray HTTP
|
||||
"1080": "socks5", // 通用 SOCKS5
|
||||
"1081": "http", // 通用 HTTP
|
||||
"8080": "http", // 通用 HTTP
|
||||
"7890": "http",
|
||||
"7891": "socks5",
|
||||
"7892": "http",
|
||||
"10808": "socks5",
|
||||
"10809": "http",
|
||||
"1080": "socks5",
|
||||
"1081": "http",
|
||||
"8080": "http",
|
||||
}
|
||||
|
||||
parts := strings.Split(server, ":")
|
||||
|
||||
82
internal/proxy_darwin.go
Normal file
82
internal/proxy_darwin.go
Normal file
@@ -0,0 +1,82 @@
|
||||
//go:build darwin
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// readSystemProxy 从 macOS 系统偏好设置读取代理配置
|
||||
// 使用 scutil --proxy 命令获取系统代理信息
|
||||
func readSystemProxy(info *ProxyInfo) error {
|
||||
out, err := exec.Command("scutil", "--proxy").Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("执行 scutil --proxy 失败: %w", err)
|
||||
}
|
||||
|
||||
lines := strings.Split(string(out), "\n")
|
||||
settings := make(map[string]string)
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
parts := strings.SplitN(line, " : ", 2)
|
||||
if len(parts) == 2 {
|
||||
settings[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
|
||||
}
|
||||
}
|
||||
|
||||
// 检查 HTTP 代理
|
||||
if settings["HTTPEnable"] == "1" {
|
||||
host := settings["HTTPProxy"]
|
||||
port := settings["HTTPPort"]
|
||||
if host != "" {
|
||||
info.Enabled = true
|
||||
info.Server = host
|
||||
if port != "" && port != "0" {
|
||||
info.Server = host + ":" + port
|
||||
}
|
||||
info.Protocol = "http"
|
||||
}
|
||||
}
|
||||
|
||||
// 检查 HTTPS 代理
|
||||
if !info.Enabled && settings["HTTPSEnable"] == "1" {
|
||||
host := settings["HTTPSProxy"]
|
||||
port := settings["HTTPSPort"]
|
||||
if host != "" {
|
||||
info.Enabled = true
|
||||
info.Server = host
|
||||
if port != "" && port != "0" {
|
||||
info.Server = host + ":" + port
|
||||
}
|
||||
info.Protocol = "http"
|
||||
}
|
||||
}
|
||||
|
||||
// 检查 SOCKS 代理
|
||||
if !info.Enabled && settings["SOCKSEnable"] == "1" {
|
||||
host := settings["SOCKSProxy"]
|
||||
port := settings["SOCKSPort"]
|
||||
if host != "" {
|
||||
info.Enabled = true
|
||||
info.Server = host
|
||||
if port != "" && port != "0" {
|
||||
info.Server = host + ":" + port
|
||||
}
|
||||
info.Protocol = "socks5"
|
||||
}
|
||||
}
|
||||
|
||||
// 如果通过以上方式检测到了代理,根据端口再次猜测协议
|
||||
if info.Enabled && info.Server != "" {
|
||||
info.Protocol = guessProtocol(info.Server)
|
||||
}
|
||||
|
||||
// 读取绕过列表
|
||||
if exceptions, ok := settings["ExceptionsList"]; ok {
|
||||
info.Bypass = exceptions
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
122
internal/proxy_linux.go
Normal file
122
internal/proxy_linux.go
Normal file
@@ -0,0 +1,122 @@
|
||||
//go:build linux
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// readSystemProxy 从 Linux 系统读取代理配置
|
||||
// 优先尝试 GNOME (gsettings),失败则回退到环境变量
|
||||
func readSystemProxy(info *ProxyInfo) error {
|
||||
// 尝试通过 gsettings 读取 GNOME 系统代理
|
||||
if err := readGnomeProxy(info); err == nil && info.Enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 尝试通过 KDE 配置读取代理
|
||||
if err := readKDEProxy(info); err == nil && info.Enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Linux 上环境变量已由 GetCurrentProxy 中读取,此处无需额外操作
|
||||
return nil
|
||||
}
|
||||
|
||||
// readGnomeProxy 通过 gsettings 读取 GNOME 桌面环境的代理配置
|
||||
func readGnomeProxy(info *ProxyInfo) error {
|
||||
// 检查 gsettings 是否可用
|
||||
if _, err := exec.LookPath("gsettings"); err != nil {
|
||||
return fmt.Errorf("gsettings 不可用: %w", err)
|
||||
}
|
||||
|
||||
// 读取代理模式
|
||||
mode, err := gsettingsGet("org.gnome.system.proxy", "mode")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if mode != "'manual'" {
|
||||
return nil // 代理未启用
|
||||
}
|
||||
|
||||
// 读取 HTTP 代理
|
||||
host, err := gsettingsGet("org.gnome.system.proxy.http", "host")
|
||||
if err == nil && host != "" && host != "''" {
|
||||
host = strings.Trim(host, "'")
|
||||
port, _ := gsettingsGet("org.gnome.system.proxy.http", "port")
|
||||
port = strings.TrimSpace(port)
|
||||
if port != "" && port != "0" {
|
||||
info.Enabled = true
|
||||
info.Server = host + ":" + port
|
||||
info.Protocol = guessProtocol(info.Server)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// 读取 SOCKS 代理
|
||||
host, err = gsettingsGet("org.gnome.system.proxy.socks", "host")
|
||||
if err == nil && host != "" && host != "''" {
|
||||
host = strings.Trim(host, "'")
|
||||
port, _ := gsettingsGet("org.gnome.system.proxy.socks", "port")
|
||||
port = strings.TrimSpace(port)
|
||||
if port != "" && port != "0" {
|
||||
info.Enabled = true
|
||||
info.Server = host + ":" + port
|
||||
info.Protocol = "socks5"
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// readKDEProxy 通过 kreadconfig5/kreadconfig6 读取 KDE 代理配置
|
||||
func readKDEProxy(info *ProxyInfo) error {
|
||||
// 尝试 kreadconfig5 或 kreadconfig6
|
||||
var cmd string
|
||||
if _, err := exec.LookPath("kreadconfig6"); err == nil {
|
||||
cmd = "kreadconfig6"
|
||||
} else if _, err := exec.LookPath("kreadconfig5"); err == nil {
|
||||
cmd = "kreadconfig5"
|
||||
} else {
|
||||
return fmt.Errorf("kreadconfig 不可用")
|
||||
}
|
||||
|
||||
// 读取代理类型
|
||||
proxyType, err := kdeGet(cmd, "Proxy Settings", "ProxyType")
|
||||
if err != nil || proxyType != "1" {
|
||||
return nil // 0 = 无代理, 1 = 手动
|
||||
}
|
||||
|
||||
// 读取 HTTP 代理
|
||||
httpProxy, err := kdeGet(cmd, "Proxy Settings", "httpProxy")
|
||||
if err == nil && httpProxy != "" {
|
||||
info.Enabled = true
|
||||
info.Server = extractServerFromURL(httpProxy)
|
||||
info.Protocol = guessProtocol(info.Server)
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// gsettingsGet 执行 gsettings get 命令
|
||||
func gsettingsGet(schema, key string) (string, error) {
|
||||
out, err := exec.Command("gsettings", "get", schema, key).Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(string(out)), nil
|
||||
}
|
||||
|
||||
// kdeGet 执行 kreadconfig 命令读取 KDE 配置
|
||||
func kdeGet(cmd, group, key string) (string, error) {
|
||||
out, err := exec.Command(cmd, "--file", "kioslaverc", "--group", group, "--key", key).Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(string(out)), nil
|
||||
}
|
||||
43
internal/proxy_windows.go
Normal file
43
internal/proxy_windows.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
// readSystemProxy 从 Windows 注册表读取系统代理设置
|
||||
func readSystemProxy(info *ProxyInfo) error {
|
||||
key, err := registry.OpenKey(
|
||||
registry.CURRENT_USER,
|
||||
`Software\Microsoft\Windows\CurrentVersion\Internet Settings`,
|
||||
registry.QUERY_VALUE,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("无法打开注册表键: %w", err)
|
||||
}
|
||||
defer key.Close()
|
||||
|
||||
// 读取代理是否启用 (ProxyEnable: 0=关闭, 1=开启)
|
||||
proxyEnable, _, err := key.GetIntegerValue("ProxyEnable")
|
||||
if err != nil {
|
||||
return fmt.Errorf("无法读取 ProxyEnable: %w", err)
|
||||
}
|
||||
info.Enabled = proxyEnable == 1
|
||||
|
||||
// 读取代理服务器地址
|
||||
proxyServer, _, err := key.GetStringValue("ProxyServer")
|
||||
if err == nil && proxyServer != "" {
|
||||
info.Server = proxyServer
|
||||
// 根据端口推断代理协议
|
||||
info.Protocol = guessProtocol(proxyServer)
|
||||
}
|
||||
|
||||
// 读取代理绕过列表
|
||||
bypass, _, err := key.GetStringValue("ProxyOverride")
|
||||
if err == nil {
|
||||
info.Bypass = bypass
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user