mirror of
https://github.com/tw93/Mole.git
synced 2026-02-08 04:29:24 +00:00
fix(status): improve proxy detection and add parser tests
This commit is contained in:
@@ -160,7 +160,7 @@ const NetworkHistorySize = 120 // Increased history size for wider graph
|
||||
|
||||
type ProxyStatus struct {
|
||||
Enabled bool
|
||||
Type string // HTTP, SOCKS, System
|
||||
Type string // HTTP, HTTPS, SOCKS, PAC, WPAD, TUN
|
||||
Host string
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -114,23 +116,8 @@ func isNoiseInterface(name string) bool {
|
||||
}
|
||||
|
||||
func collectProxy() ProxyStatus {
|
||||
// Check environment variables first.
|
||||
for _, env := range []string{"https_proxy", "HTTPS_PROXY", "http_proxy", "HTTP_PROXY"} {
|
||||
if val := os.Getenv(env); val != "" {
|
||||
proxyType := "HTTP"
|
||||
if strings.HasPrefix(val, "socks") {
|
||||
proxyType = "SOCKS"
|
||||
}
|
||||
// Extract host.
|
||||
host := val
|
||||
if strings.Contains(host, "://") {
|
||||
host = strings.SplitN(host, "://", 2)[1]
|
||||
}
|
||||
if idx := strings.Index(host, "@"); idx >= 0 {
|
||||
host = host[idx+1:]
|
||||
}
|
||||
return ProxyStatus{Enabled: true, Type: proxyType, Host: host}
|
||||
}
|
||||
if proxy := collectProxyFromEnv(os.Getenv); proxy.Enabled {
|
||||
return proxy
|
||||
}
|
||||
|
||||
// macOS: check system proxy via scutil.
|
||||
@@ -139,14 +126,166 @@ func collectProxy() ProxyStatus {
|
||||
defer cancel()
|
||||
out, err := runCmd(ctx, "scutil", "--proxy")
|
||||
if err == nil {
|
||||
if strings.Contains(out, "HTTPEnable : 1") || strings.Contains(out, "HTTPSEnable : 1") {
|
||||
return ProxyStatus{Enabled: true, Type: "System", Host: "System Proxy"}
|
||||
}
|
||||
if strings.Contains(out, "SOCKSEnable : 1") {
|
||||
return ProxyStatus{Enabled: true, Type: "SOCKS", Host: "System Proxy"}
|
||||
if proxy := collectProxyFromScutilOutput(out); proxy.Enabled {
|
||||
return proxy
|
||||
}
|
||||
}
|
||||
|
||||
if proxy := collectProxyFromTunInterfaces(); proxy.Enabled {
|
||||
return proxy
|
||||
}
|
||||
}
|
||||
|
||||
return ProxyStatus{Enabled: false}
|
||||
}
|
||||
|
||||
func collectProxyFromEnv(getenv func(string) string) ProxyStatus {
|
||||
// Include ALL_PROXY for users running proxy tools that only export a single variable.
|
||||
envKeys := []string{
|
||||
"https_proxy", "HTTPS_PROXY",
|
||||
"http_proxy", "HTTP_PROXY",
|
||||
"all_proxy", "ALL_PROXY",
|
||||
}
|
||||
for _, key := range envKeys {
|
||||
val := strings.TrimSpace(getenv(key))
|
||||
if val == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
proxyType := "HTTP"
|
||||
lower := strings.ToLower(val)
|
||||
if strings.HasPrefix(lower, "socks") {
|
||||
proxyType = "SOCKS"
|
||||
}
|
||||
|
||||
host := parseProxyHost(val)
|
||||
if host == "" {
|
||||
host = val
|
||||
}
|
||||
return ProxyStatus{Enabled: true, Type: proxyType, Host: host}
|
||||
}
|
||||
|
||||
return ProxyStatus{Enabled: false}
|
||||
}
|
||||
|
||||
func collectProxyFromScutilOutput(out string) ProxyStatus {
|
||||
if out == "" {
|
||||
return ProxyStatus{Enabled: false}
|
||||
}
|
||||
|
||||
if scutilProxyEnabled(out, "SOCKSEnable") {
|
||||
host := joinHostPort(scutilProxyValue(out, "SOCKSProxy"), scutilProxyValue(out, "SOCKSPort"))
|
||||
if host == "" {
|
||||
host = "System Proxy"
|
||||
}
|
||||
return ProxyStatus{Enabled: true, Type: "SOCKS", Host: host}
|
||||
}
|
||||
|
||||
if scutilProxyEnabled(out, "HTTPSEnable") {
|
||||
host := joinHostPort(scutilProxyValue(out, "HTTPSProxy"), scutilProxyValue(out, "HTTPSPort"))
|
||||
if host == "" {
|
||||
host = "System Proxy"
|
||||
}
|
||||
return ProxyStatus{Enabled: true, Type: "HTTPS", Host: host}
|
||||
}
|
||||
|
||||
if scutilProxyEnabled(out, "HTTPEnable") {
|
||||
host := joinHostPort(scutilProxyValue(out, "HTTPProxy"), scutilProxyValue(out, "HTTPPort"))
|
||||
if host == "" {
|
||||
host = "System Proxy"
|
||||
}
|
||||
return ProxyStatus{Enabled: true, Type: "HTTP", Host: host}
|
||||
}
|
||||
|
||||
if scutilProxyEnabled(out, "ProxyAutoConfigEnable") {
|
||||
pacURL := scutilProxyValue(out, "ProxyAutoConfigURLString")
|
||||
host := parseProxyHost(pacURL)
|
||||
if host == "" {
|
||||
host = "PAC"
|
||||
}
|
||||
return ProxyStatus{Enabled: true, Type: "PAC", Host: host}
|
||||
}
|
||||
|
||||
if scutilProxyEnabled(out, "ProxyAutoDiscoveryEnable") {
|
||||
return ProxyStatus{Enabled: true, Type: "WPAD", Host: "Auto Discovery"}
|
||||
}
|
||||
|
||||
return ProxyStatus{Enabled: false}
|
||||
}
|
||||
|
||||
func collectProxyFromTunInterfaces() ProxyStatus {
|
||||
stats, err := net.IOCounters(true)
|
||||
if err != nil {
|
||||
return ProxyStatus{Enabled: false}
|
||||
}
|
||||
|
||||
var activeTun []string
|
||||
for _, s := range stats {
|
||||
lower := strings.ToLower(s.Name)
|
||||
if strings.HasPrefix(lower, "utun") || strings.HasPrefix(lower, "tun") {
|
||||
if s.BytesRecv+s.BytesSent > 0 {
|
||||
activeTun = append(activeTun, s.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(activeTun) == 0 {
|
||||
return ProxyStatus{Enabled: false}
|
||||
}
|
||||
sort.Strings(activeTun)
|
||||
host := activeTun[0]
|
||||
if len(activeTun) > 1 {
|
||||
host = activeTun[0] + "+"
|
||||
}
|
||||
return ProxyStatus{Enabled: true, Type: "TUN", Host: host}
|
||||
}
|
||||
|
||||
func scutilProxyEnabled(out, key string) bool {
|
||||
return scutilProxyValue(out, key) == "1"
|
||||
}
|
||||
|
||||
func scutilProxyValue(out, key string) string {
|
||||
prefix := key + " :"
|
||||
for _, line := range strings.Split(out, "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if strings.HasPrefix(line, prefix) {
|
||||
return strings.TrimSpace(strings.TrimPrefix(line, prefix))
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func parseProxyHost(raw string) string {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
target := raw
|
||||
if !strings.Contains(target, "://") {
|
||||
target = "http://" + target
|
||||
}
|
||||
parsed, err := url.Parse(target)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
host := parsed.Host
|
||||
if host == "" {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimPrefix(host, "@")
|
||||
}
|
||||
|
||||
func joinHostPort(host, port string) string {
|
||||
host = strings.TrimSpace(host)
|
||||
port = strings.TrimSpace(port)
|
||||
if host == "" {
|
||||
return ""
|
||||
}
|
||||
if port == "" {
|
||||
return host
|
||||
}
|
||||
if _, err := strconv.Atoi(port); err != nil {
|
||||
return host
|
||||
}
|
||||
return host + ":" + port
|
||||
}
|
||||
|
||||
60
cmd/status/metrics_network_test.go
Normal file
60
cmd/status/metrics_network_test.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestCollectProxyFromEnvSupportsAllProxy(t *testing.T) {
|
||||
env := map[string]string{
|
||||
"ALL_PROXY": "socks5://127.0.0.1:7890",
|
||||
}
|
||||
getenv := func(key string) string {
|
||||
return env[key]
|
||||
}
|
||||
|
||||
got := collectProxyFromEnv(getenv)
|
||||
if !got.Enabled {
|
||||
t.Fatalf("expected proxy enabled")
|
||||
}
|
||||
if got.Type != "SOCKS" {
|
||||
t.Fatalf("expected SOCKS type, got %s", got.Type)
|
||||
}
|
||||
if got.Host != "127.0.0.1:7890" {
|
||||
t.Fatalf("unexpected host: %s", got.Host)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCollectProxyFromScutilOutputPAC(t *testing.T) {
|
||||
out := `
|
||||
<dictionary> {
|
||||
ProxyAutoConfigEnable : 1
|
||||
ProxyAutoConfigURLString : http://127.0.0.1:6152/proxy.pac
|
||||
}`
|
||||
got := collectProxyFromScutilOutput(out)
|
||||
if !got.Enabled {
|
||||
t.Fatalf("expected proxy enabled")
|
||||
}
|
||||
if got.Type != "PAC" {
|
||||
t.Fatalf("expected PAC type, got %s", got.Type)
|
||||
}
|
||||
if got.Host != "127.0.0.1:6152" {
|
||||
t.Fatalf("unexpected host: %s", got.Host)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCollectProxyFromScutilOutputHTTPHostPort(t *testing.T) {
|
||||
out := `
|
||||
<dictionary> {
|
||||
HTTPEnable : 1
|
||||
HTTPProxy : 127.0.0.1
|
||||
HTTPPort : 7890
|
||||
}`
|
||||
got := collectProxyFromScutilOutput(out)
|
||||
if !got.Enabled {
|
||||
t.Fatalf("expected proxy enabled")
|
||||
}
|
||||
if got.Type != "HTTP" {
|
||||
t.Fatalf("expected HTTP type, got %s", got.Type)
|
||||
}
|
||||
if got.Host != "127.0.0.1:7890" {
|
||||
t.Fatalf("unexpected host: %s", got.Host)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user