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.
 
 
 
wormhole-client/coverage.html

1693 lines
64 KiB

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>wormhole-client: Go Coverage Report</title>
<style>
body {
background: black;
color: rgb(80, 80, 80);
}
body, pre, #legend span {
font-family: Menlo, monospace;
font-weight: bold;
}
#topbar {
background: black;
position: fixed;
top: 0; left: 0; right: 0;
height: 42px;
border-bottom: 1px solid rgb(80, 80, 80);
}
#content {
margin-top: 50px;
}
#nav, #legend {
float: left;
margin-left: 10px;
}
#legend {
margin-top: 12px;
}
#nav {
margin-top: 10px;
}
#legend span {
margin: 0 5px;
}
.cov0 { color: rgb(192, 0, 0) }
.cov1 { color: rgb(128, 128, 128) }
.cov2 { color: rgb(116, 140, 131) }
.cov3 { color: rgb(104, 152, 134) }
.cov4 { color: rgb(92, 164, 137) }
.cov5 { color: rgb(80, 176, 140) }
.cov6 { color: rgb(68, 188, 143) }
.cov7 { color: rgb(56, 200, 146) }
.cov8 { color: rgb(44, 212, 149) }
.cov9 { color: rgb(32, 224, 152) }
.cov10 { color: rgb(20, 236, 155) }
</style>
</head>
<body>
<div id="topbar">
<div id="nav">
<select id="files">
<option value="file0">github.com/azoic/wormhole-client/cmd/wormhole-client/main.go (0.0%)</option>
<option value="file1">github.com/azoic/wormhole-client/internal/client/client.go (31.9%)</option>
<option value="file2">github.com/azoic/wormhole-client/internal/config/config.go (90.9%)</option>
<option value="file3">github.com/azoic/wormhole-client/internal/proxy/socks5.go (22.2%)</option>
<option value="file4">github.com/azoic/wormhole-client/internal/proxy/stats.go (96.2%)</option>
<option value="file5">github.com/azoic/wormhole-client/internal/system/proxy.go (0.0%)</option>
<option value="file6">github.com/azoic/wormhole-client/pkg/dns/proxy.go (0.0%)</option>
<option value="file7">github.com/azoic/wormhole-client/pkg/logger/logger.go (0.0%)</option>
</select>
</div>
<div id="legend">
<span>not tracked</span>
<span class="cov0">no coverage</span>
<span class="cov1">low coverage</span>
<span class="cov2">*</span>
<span class="cov3">*</span>
<span class="cov4">*</span>
<span class="cov5">*</span>
<span class="cov6">*</span>
<span class="cov7">*</span>
<span class="cov8">*</span>
<span class="cov9">*</span>
<span class="cov10">high coverage</span>
</div>
</div>
<div id="content">
<pre class="file" id="file0" style="display: none">package main
import (
"flag"
"fmt"
"log"
"os"
"github.com/azoic/wormhole-client/internal/client"
)
var (
version = "v1.0.0"
buildTime = "unknown"
)
func main() <span class="cov0" title="0">{
configPath := flag.String("config", "configs/client.yaml", "Configuration file path")
mode := flag.String("mode", "http", "Client mode: http, global, transparent")
showVersion := flag.Bool("version", false, "Show version information")
flag.Parse()
if *showVersion </span><span class="cov0" title="0">{
fmt.Printf("Wormhole SOCKS5 Client %s\n", version)
fmt.Printf("Build time: %s\n", buildTime)
os.Exit(0)
}</span>
<span class="cov0" title="0">fmt.Printf("🚀 Starting Wormhole SOCKS5 Client %s\n", version)
fmt.Printf("📄 Config: %s\n", *configPath)
fmt.Printf("🔧 Mode: %s\n", *mode)
// TODO: 实现完整的客户端逻辑
cli := client.NewClient(*mode)
if err := cli.Start(*configPath); err != nil </span><span class="cov0" title="0">{
log.Fatalf("Client failed: %v", err)
}</span>
}
</pre>
<pre class="file" id="file1" style="display: none">package client
import (
"fmt"
"os"
"os/signal"
"syscall"
"github.com/azoic/wormhole-client/internal/config"
"github.com/azoic/wormhole-client/internal/proxy"
"github.com/azoic/wormhole-client/internal/system"
"github.com/azoic/wormhole-client/pkg/dns"
"github.com/azoic/wormhole-client/pkg/logger"
)
type Client struct {
mode string
config *config.Config
socks5Proxy *proxy.SOCKS5Proxy
dnsProxy *dns.DNSProxy
systemProxyMgr *system.SystemProxyManager
}
func NewClient(mode string) *Client <span class="cov10" title="3">{
return &amp;Client{
mode: mode,
systemProxyMgr: system.NewSystemProxyManager(),
}
}</span>
func (c *Client) Start(configPath string) error <span class="cov6" title="2">{
// 加载配置
cfg, err := config.LoadConfig(configPath)
if err != nil </span><span class="cov1" title="1">{
return fmt.Errorf("failed to load config: %v", err)
}</span>
<span class="cov1" title="1">if err := cfg.Validate(); err != nil </span><span class="cov0" title="0">{
return fmt.Errorf("invalid config: %v", err)
}</span>
<span class="cov1" title="1">c.config = cfg
// 设置日志级别
logger.SetLevel(cfg.LogLevel)
logger.Info("🚀 Starting Wormhole SOCKS5 Client")
logger.Info("📄 Config loaded from: %s", configPath)
logger.Info("🔧 Mode: %s", c.mode)
logger.Info("🎯 SOCKS5 Server: %s", cfg.GetServerAddr())
// 创建SOCKS5代理客户端
c.socks5Proxy = proxy.NewSOCKS5Proxy(
cfg.GetServerAddr(),
cfg.Server.Username,
cfg.Server.Password,
cfg.Timeout,
)
// 设置信号处理
c.setupSignalHandler()
switch c.mode </span>{
case "http":<span class="cov0" title="0">
return c.startHTTPProxy()</span>
case "global":<span class="cov0" title="0">
return c.startGlobalProxy()</span>
case "transparent":<span class="cov0" title="0">
return c.startTransparentProxy()</span>
default:<span class="cov1" title="1">
return fmt.Errorf("unsupported mode: %s", c.mode)</span>
}
}
func (c *Client) startHTTPProxy() error <span class="cov0" title="0">{
logger.Info("🌐 Starting HTTP proxy mode...")
logger.Info("💡 Setting up HTTP proxy on port %d", c.config.Proxy.LocalPort)
server := c.socks5Proxy.CreateHTTPProxy(c.config.Proxy.LocalPort)
logger.Info("✅ HTTP proxy started on :%d", c.config.Proxy.LocalPort)
logger.Info("💡 Configure your browser to use HTTP proxy: 127.0.0.1:%d", c.config.Proxy.LocalPort)
return server.ListenAndServe()
}</span>
func (c *Client) startGlobalProxy() error <span class="cov0" title="0">{
logger.Info("🌍 Starting global proxy mode...")
logger.Info("💡 This will configure system-wide proxy settings")
logger.Info("⚠ Requires administrator privileges")
// 启动HTTP代理服务器
server := c.socks5Proxy.CreateHTTPProxy(c.config.Proxy.LocalPort)
go func() </span><span class="cov0" title="0">{
if err := server.ListenAndServe(); err != nil </span><span class="cov0" title="0">{
logger.Error("HTTP proxy server failed: %v", err)
}</span>
}()
// 设置系统代理
<span class="cov0" title="0">httpProxy := fmt.Sprintf("127.0.0.1:%d", c.config.Proxy.LocalPort)
httpsProxy := httpProxy
if err := c.systemProxyMgr.SetGlobalProxy(httpProxy, httpsProxy, ""); err != nil </span><span class="cov0" title="0">{
logger.Error("Failed to set system proxy: %v", err)
logger.Warn("You may need to run with administrator privileges")
logger.Info("Manual setup: Set HTTP/HTTPS proxy to %s", httpProxy)
}</span> else<span class="cov0" title="0"> {
logger.Info("✅ System proxy configured successfully")
}</span>
// 启动DNS代理(如果启用)
<span class="cov0" title="0">if c.config.GlobalProxy.DNSProxy </span><span class="cov0" title="0">{
logger.Info("🔍 Starting DNS proxy on port %d", c.config.GlobalProxy.DNSPort)
c.dnsProxy = dns.NewDNSProxy("8.8.8.8:53", c.config.GlobalProxy.DNSPort)
if err := c.dnsProxy.Start(); err != nil </span><span class="cov0" title="0">{
logger.Warn("Failed to start DNS proxy: %v", err)
}</span> else<span class="cov0" title="0"> {
logger.Info("✅ DNS proxy started")
}</span>
}
<span class="cov0" title="0">logger.Info("🎉 Global proxy mode started successfully")
logger.Info("📍 HTTP/HTTPS Proxy: %s", httpProxy)
if c.dnsProxy != nil </span><span class="cov0" title="0">{
logger.Info("📍 DNS Proxy: 127.0.0.1:%d", c.config.GlobalProxy.DNSPort)
}</span>
// 保持运行
<span class="cov0" title="0">select </span>{}
}
func (c *Client) startTransparentProxy() error <span class="cov0" title="0">{
logger.Info("🔍 Starting transparent proxy mode...")
logger.Info("💡 This will intercept network traffic transparently")
logger.Info("⚠ Requires root privileges and iptables support")
logger.Error("❌ Transparent proxy mode is not yet implemented")
return fmt.Errorf("transparent proxy mode not implemented")
}</span>
func (c *Client) setupSignalHandler() <span class="cov1" title="1">{
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
go func() </span><span class="cov1" title="1">{
&lt;-sigChan
logger.Info("🛑 Shutting down...")
c.cleanup()
os.Exit(0)
}</span>()
}
func (c *Client) cleanup() <span class="cov0" title="0">{
// 恢复系统代理设置
if c.systemProxyMgr != nil </span><span class="cov0" title="0">{
if err := c.systemProxyMgr.RestoreProxy(); err != nil </span><span class="cov0" title="0">{
logger.Error("Failed to restore system proxy: %v", err)
}</span>
}
// 停止DNS代理
<span class="cov0" title="0">if c.dnsProxy != nil </span><span class="cov0" title="0">{
if err := c.dnsProxy.Stop(); err != nil </span><span class="cov0" title="0">{
logger.Error("Failed to stop DNS proxy: %v", err)
}</span>
}
<span class="cov0" title="0">logger.Info("✅ Cleanup completed")</span>
}
</pre>
<pre class="file" id="file2" style="display: none">package config
import (
"fmt"
"io/ioutil"
"time"
"gopkg.in/yaml.v3"
)
// Config 客户端配置结构
type Config struct {
ServiceType string `yaml:"serviceType"`
Server Server `yaml:"server"`
Proxy Proxy `yaml:"proxy"`
GlobalProxy GlobalProxy `yaml:"globalProxy"`
TransparentProxy TransparentProxy `yaml:"transparentProxy"`
LogLevel string `yaml:"logLevel"`
Timeout time.Duration `yaml:"timeout"`
}
// Server SOCKS5服务器配置
type Server struct {
Address string `yaml:"address"`
Port int `yaml:"port"`
Username string `yaml:"username"`
Password string `yaml:"password"`
}
// Proxy 代理模式配置
type Proxy struct {
Mode string `yaml:"mode"`
LocalPort int `yaml:"localPort"`
}
// GlobalProxy 全局代理配置
type GlobalProxy struct {
Enabled bool `yaml:"enabled"`
DNSProxy bool `yaml:"dnsProxy"`
DNSPort int `yaml:"dnsPort"`
Routing Routing `yaml:"routing"`
}
// TransparentProxy 透明代理配置
type TransparentProxy struct {
Enabled bool `yaml:"enabled"`
Port int `yaml:"port"`
DNSPort int `yaml:"dnsPort"`
ModifyDNS bool `yaml:"modifyDNS"`
ModifyRoute bool `yaml:"modifyRoute"`
}
// Routing 路由规则配置
type Routing struct {
BypassLocal bool `yaml:"bypassLocal"`
BypassPrivate bool `yaml:"bypassPrivate"`
BypassDomains []string `yaml:"bypassDomains"`
ForceDomains []string `yaml:"forceDomains"`
}
// LoadConfig 从文件加载配置
func LoadConfig(configPath string) (*Config, error) <span class="cov5" title="2">{
data, err := ioutil.ReadFile(configPath)
if err != nil </span><span class="cov0" title="0">{
return nil, fmt.Errorf("failed to read config file: %v", err)
}</span>
<span class="cov5" title="2">var config Config
if err := yaml.Unmarshal(data, &amp;config); err != nil </span><span class="cov0" title="0">{
return nil, fmt.Errorf("failed to parse config file: %v", err)
}</span>
// 设置默认值
<span class="cov5" title="2">if config.LogLevel == "" </span><span class="cov1" title="1">{
config.LogLevel = "info"
}</span>
<span class="cov5" title="2">if config.Timeout == 0 </span><span class="cov1" title="1">{
config.Timeout = 30 * time.Second
}</span>
<span class="cov5" title="2">if config.Proxy.LocalPort == 0 </span><span class="cov1" title="1">{
config.Proxy.LocalPort = 8080
}</span>
<span class="cov5" title="2">return &amp;config, nil</span>
}
// GetServerAddr 获取服务器地址
func (c *Config) GetServerAddr() string <span class="cov1" title="1">{
return fmt.Sprintf("%s:%d", c.Server.Address, c.Server.Port)
}</span>
// Validate 验证配置
func (c *Config) Validate() error <span class="cov10" title="4">{
if c.Server.Address == "" </span><span class="cov1" title="1">{
return fmt.Errorf("server address is required")
}</span>
<span class="cov8" title="3">if c.Server.Port &lt;= 0 || c.Server.Port &gt; 65535 </span><span class="cov1" title="1">{
return fmt.Errorf("invalid server port: %d", c.Server.Port)
}</span>
<span class="cov5" title="2">validModes := map[string]bool{"http": true, "global": true, "transparent": true}
if !validModes[c.Proxy.Mode] </span><span class="cov1" title="1">{
return fmt.Errorf("invalid proxy mode: %s", c.Proxy.Mode)
}</span>
<span class="cov1" title="1">return nil</span>
}
</pre>
<pre class="file" id="file3" style="display: none">package proxy
import (
"context"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"strconv"
"sync"
"time"
"github.com/azoic/wormhole-client/pkg/logger"
)
// SOCKS5Proxy SOCKS5代理客户端
type SOCKS5Proxy struct {
serverAddr string
username string
password string
timeout time.Duration
connPool *connectionPool
stats *ProxyStats
}
// connectionPool 连接池
type connectionPool struct {
connections chan net.Conn
maxSize int
mutex sync.Mutex
}
// NewSOCKS5Proxy 创建SOCKS5代理客户端
func NewSOCKS5Proxy(serverAddr, username, password string, timeout time.Duration) *SOCKS5Proxy <span class="cov6" title="6">{
return &amp;SOCKS5Proxy{
serverAddr: serverAddr,
username: username,
password: password,
timeout: timeout,
connPool: &amp;connectionPool{
connections: make(chan net.Conn, 10),
maxSize: 10,
},
stats: NewProxyStats(),
}
}</span>
// CreateHTTPProxy 创建HTTP代理服务器
func (p *SOCKS5Proxy) CreateHTTPProxy(localPort int) *http.Server <span class="cov1" title="1">{
proxyHandler := &amp;httpProxyHandler{
socks5Proxy: p,
}
// 创建ServeMux来处理不同的路径
mux := http.NewServeMux()
mux.Handle("/", proxyHandler)
mux.HandleFunc("/stats", p.handleStats)
mux.HandleFunc("/health", p.handleHealth)
server := &amp;http.Server{
Addr: fmt.Sprintf(":%d", localPort),
Handler: mux,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 120 * time.Second,
MaxHeaderBytes: 1 &lt;&lt; 20, // 1MB
}
return server
}</span>
// httpProxyHandler HTTP代理处理器
type httpProxyHandler struct {
socks5Proxy *SOCKS5Proxy
}
func (h *httpProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) <span class="cov0" title="0">{
// 统计连接
h.socks5Proxy.stats.IncrementConnections()
defer h.socks5Proxy.stats.DecrementActiveConnections()
logger.Debug("Processing request: %s %s from %s", r.Method, r.URL.String(), r.RemoteAddr)
if r.Method == http.MethodConnect </span><span class="cov0" title="0">{
h.handleHTTPSProxy(w, r)
}</span> else<span class="cov0" title="0"> {
h.handleHTTPProxy(w, r)
}</span>
}
// handleHTTPSProxy 处理HTTPS代理请求 (CONNECT方法)
func (h *httpProxyHandler) handleHTTPSProxy(w http.ResponseWriter, r *http.Request) <span class="cov0" title="0">{
ctx, cancel := context.WithTimeout(r.Context(), h.socks5Proxy.timeout)
defer cancel()
destConn, err := h.socks5Proxy.DialTCPWithContext(ctx, r.Host)
if err != nil </span><span class="cov0" title="0">{
h.socks5Proxy.stats.IncrementFailedRequests()
h.socks5Proxy.stats.IncrementSOCKS5Error("connection_failed")
logger.Error("Failed to connect via SOCKS5 to %s: %v", r.Host, err)
http.Error(w, "Bad Gateway", http.StatusBadGateway)
return
}</span>
<span class="cov0" title="0">defer destConn.Close()
// 发送200 Connection established响应
w.WriteHeader(http.StatusOK)
hijacker, ok := w.(http.Hijacker)
if !ok </span><span class="cov0" title="0">{
h.socks5Proxy.stats.IncrementFailedRequests()
logger.Error("Hijacking not supported")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}</span>
<span class="cov0" title="0">clientConn, _, err := hijacker.Hijack()
if err != nil </span><span class="cov0" title="0">{
h.socks5Proxy.stats.IncrementFailedRequests()
logger.Error("Failed to hijack connection: %v", err)
return
}</span>
<span class="cov0" title="0">defer clientConn.Close()
h.socks5Proxy.stats.IncrementSuccessfulRequests()
logger.Debug("Established HTTPS tunnel to %s", r.Host)
// 双向数据转发
var wg sync.WaitGroup
wg.Add(2)
go func() </span><span class="cov0" title="0">{
defer wg.Done()
written := h.copyData(clientConn, destConn, "client-&gt;server")
h.socks5Proxy.stats.AddBytesTransferred(written, 0)
}</span>()
<span class="cov0" title="0">go func() </span><span class="cov0" title="0">{
defer wg.Done()
written := h.copyData(destConn, clientConn, "server-&gt;client")
h.socks5Proxy.stats.AddBytesTransferred(0, written)
}</span>()
<span class="cov0" title="0">wg.Wait()
logger.Debug("HTTPS tunnel to %s closed", r.Host)</span>
}
// handleHTTPProxy 处理HTTP代理请求
func (h *httpProxyHandler) handleHTTPProxy(w http.ResponseWriter, r *http.Request) <span class="cov0" title="0">{
ctx, cancel := context.WithTimeout(r.Context(), h.socks5Proxy.timeout)
defer cancel()
// 确保URL包含Host
if r.URL.Host == "" </span><span class="cov0" title="0">{
r.URL.Host = r.Host
}</span>
<span class="cov0" title="0">if r.URL.Scheme == "" </span><span class="cov0" title="0">{
r.URL.Scheme = "http"
}</span>
// 通过SOCKS5连接到目标服务器
<span class="cov0" title="0">destConn, err := h.socks5Proxy.DialTCPWithContext(ctx, r.Host)
if err != nil </span><span class="cov0" title="0">{
h.socks5Proxy.stats.IncrementFailedRequests()
h.socks5Proxy.stats.IncrementSOCKS5Error("connection_failed")
logger.Error("Failed to connect via SOCKS5 to %s: %v", r.Host, err)
http.Error(w, "Bad Gateway", http.StatusBadGateway)
return
}</span>
<span class="cov0" title="0">defer destConn.Close()
// 发送HTTP请求
if err := r.Write(destConn); err != nil </span><span class="cov0" title="0">{
h.socks5Proxy.stats.IncrementFailedRequests()
logger.Error("Failed to write request to %s: %v", r.Host, err)
http.Error(w, "Bad Gateway", http.StatusBadGateway)
return
}</span>
// 设置响应头
<span class="cov0" title="0">w.Header().Set("Via", "1.1 wormhole-proxy")
// 使用自定义ResponseWriter来统计字节数
statsWriter := &amp;statsResponseWriter{
ResponseWriter: w,
stats: h.socks5Proxy.stats,
}
// 读取响应并返回给客户端
written, err := io.Copy(statsWriter, destConn)
if err != nil </span><span class="cov0" title="0">{
h.socks5Proxy.stats.IncrementFailedRequests()
logger.Error("Failed to copy response from %s: %v", r.Host, err)
return
}</span>
<span class="cov0" title="0">h.socks5Proxy.stats.IncrementSuccessfulRequests()
h.socks5Proxy.stats.AddBytesTransferred(0, written)
logger.Debug("HTTP request to %s completed, %d bytes", r.Host, written)</span>
}
// statsResponseWriter 带统计功能的ResponseWriter
type statsResponseWriter struct {
http.ResponseWriter
stats *ProxyStats
}
func (w *statsResponseWriter) Write(data []byte) (int, error) <span class="cov0" title="0">{
n, err := w.ResponseWriter.Write(data)
if n &gt; 0 </span><span class="cov0" title="0">{
w.stats.AddBytesTransferred(int64(n), 0)
}</span>
<span class="cov0" title="0">return n, err</span>
}
// copyData 数据复制,带方向标识和字节统计
func (h *httpProxyHandler) copyData(dst, src net.Conn, direction string) int64 <span class="cov0" title="0">{
defer dst.Close()
defer src.Close()
written, err := io.Copy(dst, src)
if err != nil </span><span class="cov0" title="0">{
logger.Debug("Copy %s finished with error: %v, bytes: %d", direction, err, written)
}</span> else<span class="cov0" title="0"> {
logger.Debug("Copy %s finished successfully, bytes: %d", direction, written)
}</span>
<span class="cov0" title="0">return written</span>
}
// handleStats 处理统计信息请求
func (p *SOCKS5Proxy) handleStats(w http.ResponseWriter, r *http.Request) <span class="cov0" title="0">{
if r.Method != http.MethodGet </span><span class="cov0" title="0">{
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}</span>
<span class="cov0" title="0">stats := p.stats.GetStats()
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
if err := json.NewEncoder(w).Encode(stats); err != nil </span><span class="cov0" title="0">{
logger.Error("Failed to encode stats: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}</span>
}
// handleHealth 处理健康检查请求
func (p *SOCKS5Proxy) handleHealth(w http.ResponseWriter, r *http.Request) <span class="cov0" title="0">{
if r.Method != http.MethodGet </span><span class="cov0" title="0">{
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}</span>
<span class="cov0" title="0">stats := p.stats.GetStats()
health := map[string]interface{}{
"status": "healthy",
"uptime": stats.Uptime.String(),
"active_connections": stats.ActiveConnections,
"success_rate": stats.GetSuccessRate(),
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(health); err != nil </span><span class="cov0" title="0">{
logger.Error("Failed to encode health: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}</span>
}
// GetStats 获取代理统计信息
func (p *SOCKS5Proxy) GetStats() ProxyStatsSnapshot <span class="cov0" title="0">{
return p.stats.GetStats()
}</span>
// DialTCP 通过SOCKS5连接到目标地址
func (p *SOCKS5Proxy) DialTCP(address string) (net.Conn, error) <span class="cov0" title="0">{
return p.DialTCPWithContext(context.Background(), address)
}</span>
// DialTCPWithContext 通过SOCKS5连接到目标地址(带上下文)
func (p *SOCKS5Proxy) DialTCPWithContext(ctx context.Context, address string) (net.Conn, error) <span class="cov1" title="1">{
// 连接到SOCKS5代理服务器
dialer := &amp;net.Dialer{
Timeout: p.timeout,
}
conn, err := dialer.DialContext(ctx, "tcp", p.serverAddr)
if err != nil </span><span class="cov1" title="1">{
return nil, fmt.Errorf("failed to connect to SOCKS5 server: %v", err)
}</span>
// 设置连接超时
<span class="cov0" title="0">deadline, ok := ctx.Deadline()
if ok </span><span class="cov0" title="0">{
conn.SetDeadline(deadline)
}</span>
// 执行SOCKS5握手
<span class="cov0" title="0">if err := p.performSOCKS5Handshake(conn, address); err != nil </span><span class="cov0" title="0">{
conn.Close()
return nil, fmt.Errorf("SOCKS5 handshake failed: %v", err)
}</span>
// 清除deadline,让连接正常使用
<span class="cov0" title="0">conn.SetDeadline(time.Time{})
logger.Debug("Successfully connected to %s via SOCKS5 proxy", address)
return conn, nil</span>
}
// performSOCKS5Handshake 执行SOCKS5握手协议
func (p *SOCKS5Proxy) performSOCKS5Handshake(conn net.Conn, targetAddr string) error <span class="cov0" title="0">{
// 设置握手超时
deadline := time.Now().Add(p.timeout)
conn.SetDeadline(deadline)
// 第一步:发送认证方法选择
authMethods := []byte{0x05, 0x02, 0x00, 0x02} // 版本5,2个方法,无认证+用户名密码认证
if _, err := conn.Write(authMethods); err != nil </span><span class="cov0" title="0">{
return fmt.Errorf("failed to send auth methods: %v", err)
}</span>
// 读取服务器响应
<span class="cov0" title="0">response := make([]byte, 2)
if _, err := io.ReadFull(conn, response); err != nil </span><span class="cov0" title="0">{
return fmt.Errorf("failed to read auth response: %v", err)
}</span>
<span class="cov0" title="0">if response[0] != 0x05 </span><span class="cov0" title="0">{
return fmt.Errorf("invalid SOCKS version: %d", response[0])
}</span>
// 第二步:处理认证
<span class="cov0" title="0">switch response[1] </span>{
case 0x00:<span class="cov0" title="0"> // 无认证
logger.Debug("SOCKS5 server requires no authentication")</span>
case 0x02:<span class="cov0" title="0"> // 用户名密码认证
if err := p.performUserPassAuth(conn); err != nil </span><span class="cov0" title="0">{
return fmt.Errorf("user/pass authentication failed: %v", err)
}</span>
case 0xFF:<span class="cov0" title="0"> // 无可接受的认证方法
return fmt.Errorf("no acceptable authentication methods")</span>
default:<span class="cov0" title="0">
return fmt.Errorf("unsupported authentication method: %d", response[1])</span>
}
// 第三步:发送连接请求
<span class="cov0" title="0">connectReq, err := p.buildConnectRequest(targetAddr)
if err != nil </span><span class="cov0" title="0">{
return fmt.Errorf("failed to build connect request: %v", err)
}</span>
<span class="cov0" title="0">if _, err := conn.Write(connectReq); err != nil </span><span class="cov0" title="0">{
return fmt.Errorf("failed to send connect request: %v", err)
}</span>
// 第四步:读取连接响应
<span class="cov0" title="0">return p.readConnectResponse(conn)</span>
}
// buildConnectRequest 构建连接请求
func (p *SOCKS5Proxy) buildConnectRequest(targetAddr string) ([]byte, error) <span class="cov7" title="8">{
host, portStr, err := net.SplitHostPort(targetAddr)
if err != nil </span><span class="cov1" title="1">{
return nil, fmt.Errorf("invalid target address: %v", err)
}</span>
// 解析端口号
<span class="cov7" title="7">portNum, err := parsePort(portStr)
if err != nil </span><span class="cov3" title="2">{
return nil, fmt.Errorf("invalid port: %v", err)
}</span>
<span class="cov6" title="5">var connectReq []byte
// 检测地址类型并构建请求
if ip := net.ParseIP(host); ip != nil </span><span class="cov4" title="3">{
if ip4 := ip.To4(); ip4 != nil </span><span class="cov1" title="1">{
// IPv4地址
connectReq = []byte{0x05, 0x01, 0x00, 0x01}
connectReq = append(connectReq, ip4...)
}</span> else<span class="cov3" title="2"> if ip6 := ip.To16(); ip6 != nil </span><span class="cov3" title="2">{
// IPv6地址
connectReq = []byte{0x05, 0x01, 0x00, 0x04}
connectReq = append(connectReq, ip6...)
}</span>
} else<span class="cov3" title="2"> {
// 域名
if len(host) &gt; 255 </span><span class="cov0" title="0">{
return nil, fmt.Errorf("domain name too long: %d", len(host))
}</span>
<span class="cov3" title="2">connectReq = []byte{0x05, 0x01, 0x00, 0x03}
connectReq = append(connectReq, byte(len(host)))
connectReq = append(connectReq, []byte(host)...)</span>
}
// 添加端口
<span class="cov6" title="5">connectReq = append(connectReq, byte(portNum&gt;&gt;8), byte(portNum&amp;0xFF))
return connectReq, nil</span>
}
// readConnectResponse 读取连接响应
func (p *SOCKS5Proxy) readConnectResponse(conn net.Conn) error <span class="cov0" title="0">{
// 读取响应头部
header := make([]byte, 4)
if _, err := io.ReadFull(conn, header); err != nil </span><span class="cov0" title="0">{
return fmt.Errorf("failed to read connect response header: %v", err)
}</span>
<span class="cov0" title="0">if header[0] != 0x05 </span><span class="cov0" title="0">{
return fmt.Errorf("invalid SOCKS version in response: %d", header[0])
}</span>
<span class="cov0" title="0">if header[1] != 0x00 </span><span class="cov0" title="0">{
return fmt.Errorf("connection failed, status: %d (%s)", header[1], getSOCKS5ErrorMessage(header[1]))
}</span>
// 读取绑定地址和端口
<span class="cov0" title="0">addrType := header[3]
switch addrType </span>{
case 0x01:<span class="cov0" title="0"> // IPv4
skipBytes := make([]byte, 6) // 4字节IP + 2字节端口
_, err := io.ReadFull(conn, skipBytes)
return err</span>
case 0x03:<span class="cov0" title="0"> // 域名
lenByte := make([]byte, 1)
if _, err := io.ReadFull(conn, lenByte); err != nil </span><span class="cov0" title="0">{
return err
}</span>
<span class="cov0" title="0">skipBytes := make([]byte, int(lenByte[0])+2) // 域名长度 + 2字节端口
_, err := io.ReadFull(conn, skipBytes)
return err</span>
case 0x04:<span class="cov0" title="0"> // IPv6
skipBytes := make([]byte, 18) // 16字节IP + 2字节端口
_, err := io.ReadFull(conn, skipBytes)
return err</span>
default:<span class="cov0" title="0">
return fmt.Errorf("unsupported address type: %d", addrType)</span>
}
}
// performUserPassAuth 执行用户名密码认证
func (p *SOCKS5Proxy) performUserPassAuth(conn net.Conn) error <span class="cov0" title="0">{
// 发送用户名密码
authData := []byte{0x01} // 子协议版本
authData = append(authData, byte(len(p.username)))
authData = append(authData, []byte(p.username)...)
authData = append(authData, byte(len(p.password)))
authData = append(authData, []byte(p.password)...)
if _, err := conn.Write(authData); err != nil </span><span class="cov0" title="0">{
return fmt.Errorf("failed to send credentials: %v", err)
}</span>
// 读取认证结果
<span class="cov0" title="0">authResult := make([]byte, 2)
if _, err := io.ReadFull(conn, authResult); err != nil </span><span class="cov0" title="0">{
return fmt.Errorf("failed to read auth result: %v", err)
}</span>
<span class="cov0" title="0">if authResult[0] != 0x01 </span><span class="cov0" title="0">{
return fmt.Errorf("invalid auth response version: %d", authResult[0])
}</span>
<span class="cov0" title="0">if authResult[1] != 0x00 </span><span class="cov0" title="0">{
return fmt.Errorf("authentication failed")
}</span>
<span class="cov0" title="0">logger.Debug("SOCKS5 authentication successful")
return nil</span>
}
// getSOCKS5ErrorMessage 获取SOCKS5错误消息
func getSOCKS5ErrorMessage(code byte) string <span class="cov7" title="9">{
switch code </span>{
case 0x01:<span class="cov1" title="1">
return "general SOCKS server failure"</span>
case 0x02:<span class="cov1" title="1">
return "connection not allowed by ruleset"</span>
case 0x03:<span class="cov1" title="1">
return "network unreachable"</span>
case 0x04:<span class="cov1" title="1">
return "host unreachable"</span>
case 0x05:<span class="cov1" title="1">
return "connection refused"</span>
case 0x06:<span class="cov1" title="1">
return "TTL expired"</span>
case 0x07:<span class="cov1" title="1">
return "command not supported"</span>
case 0x08:<span class="cov1" title="1">
return "address type not supported"</span>
default:<span class="cov1" title="1">
return "unknown error"</span>
}
}
// parsePort 解析端口号
func parsePort(portStr string) (int, error) <span class="cov10" title="17">{
if portStr == "" </span><span class="cov1" title="1">{
return 80, nil // 默认HTTP端口
}</span>
<span class="cov9" title="16">port, err := strconv.Atoi(portStr)
if err != nil </span><span class="cov3" title="2">{
return 0, fmt.Errorf("invalid port format: %s", portStr)
}</span>
<span class="cov9" title="14">if port &lt; 1 || port &gt; 65535 </span><span class="cov5" title="4">{
return 0, fmt.Errorf("port out of range: %d", port)
}</span>
<span class="cov8" title="10">return port, nil</span>
}
</pre>
<pre class="file" id="file4" style="display: none">package proxy
import (
"sync"
"sync/atomic"
"time"
)
// ProxyStats 代理统计信息
type ProxyStats struct {
StartTime time.Time
TotalConnections int64
ActiveConnections int64
SuccessfulRequests int64
FailedRequests int64
BytesSent int64
BytesReceived int64
SOCKS5Errors map[string]int64
mutex sync.RWMutex
}
// NewProxyStats 创建新的统计实例
func NewProxyStats() *ProxyStats <span class="cov4" title="14">{
return &amp;ProxyStats{
StartTime: time.Now(),
SOCKS5Errors: make(map[string]int64),
}
}</span>
// IncrementConnections 增加连接计数
func (s *ProxyStats) IncrementConnections() <span class="cov9" title="1004">{
atomic.AddInt64(&amp;s.TotalConnections, 1)
atomic.AddInt64(&amp;s.ActiveConnections, 1)
}</span>
// DecrementActiveConnections 减少活跃连接计数
func (s *ProxyStats) DecrementActiveConnections() <span class="cov1" title="1">{
atomic.AddInt64(&amp;s.ActiveConnections, -1)
}</span>
// IncrementSuccessfulRequests 增加成功请求计数
func (s *ProxyStats) IncrementSuccessfulRequests() <span class="cov10" title="1005">{
atomic.AddInt64(&amp;s.SuccessfulRequests, 1)
}</span>
// IncrementFailedRequests 增加失败请求计数
func (s *ProxyStats) IncrementFailedRequests() <span class="cov1" title="2">{
atomic.AddInt64(&amp;s.FailedRequests, 1)
}</span>
// AddBytesTransferred 添加传输字节数
func (s *ProxyStats) AddBytesTransferred(sent, received int64) <span class="cov9" title="1002">{
atomic.AddInt64(&amp;s.BytesSent, sent)
atomic.AddInt64(&amp;s.BytesReceived, received)
}</span>
// IncrementSOCKS5Error 增加SOCKS5错误计数
func (s *ProxyStats) IncrementSOCKS5Error(errorType string) <span class="cov9" title="1003">{
s.mutex.Lock()
defer s.mutex.Unlock()
s.SOCKS5Errors[errorType]++
}</span>
// GetStats 获取统计快照
func (s *ProxyStats) GetStats() ProxyStatsSnapshot <span class="cov3" title="9">{
s.mutex.RLock()
defer s.mutex.RUnlock()
errors := make(map[string]int64)
for k, v := range s.SOCKS5Errors </span><span class="cov2" title="3">{
errors[k] = v
}</span>
<span class="cov3" title="9">return ProxyStatsSnapshot{
StartTime: s.StartTime,
Uptime: time.Since(s.StartTime),
TotalConnections: atomic.LoadInt64(&amp;s.TotalConnections),
ActiveConnections: atomic.LoadInt64(&amp;s.ActiveConnections),
SuccessfulRequests: atomic.LoadInt64(&amp;s.SuccessfulRequests),
FailedRequests: atomic.LoadInt64(&amp;s.FailedRequests),
BytesSent: atomic.LoadInt64(&amp;s.BytesSent),
BytesReceived: atomic.LoadInt64(&amp;s.BytesReceived),
SOCKS5Errors: errors,
}</span>
}
// ProxyStatsSnapshot 统计快照
type ProxyStatsSnapshot struct {
StartTime time.Time `json:"start_time"`
Uptime time.Duration `json:"uptime"`
TotalConnections int64 `json:"total_connections"`
ActiveConnections int64 `json:"active_connections"`
SuccessfulRequests int64 `json:"successful_requests"`
FailedRequests int64 `json:"failed_requests"`
BytesSent int64 `json:"bytes_sent"`
BytesReceived int64 `json:"bytes_received"`
SOCKS5Errors map[string]int64 `json:"socks5_errors"`
}
// GetSuccessRate 获取成功率
func (s *ProxyStatsSnapshot) GetSuccessRate() float64 <span class="cov1" title="2">{
total := s.SuccessfulRequests + s.FailedRequests
if total == 0 </span><span class="cov1" title="1">{
return 0
}</span>
<span class="cov1" title="1">return float64(s.SuccessfulRequests) / float64(total) * 100</span>
}
// GetTotalBytes 获取总传输字节数
func (s *ProxyStatsSnapshot) GetTotalBytes() int64 <span class="cov1" title="1">{
return s.BytesSent + s.BytesReceived
}</span>
// GetAverageConnectionsPerHour 获取每小时平均连接数
func (s *ProxyStatsSnapshot) GetAverageConnectionsPerHour() float64 <span class="cov1" title="1">{
hours := s.Uptime.Hours()
if hours == 0 </span><span class="cov0" title="0">{
return 0
}</span>
<span class="cov1" title="1">return float64(s.TotalConnections) / hours</span>
}
</pre>
<pre class="file" id="file5" style="display: none">package system
import (
"fmt"
"os/exec"
"runtime"
"strings"
"github.com/azoic/wormhole-client/pkg/logger"
)
// SystemProxyManager 系统代理管理器
type SystemProxyManager struct {
originalSettings map[string]string
}
// NewSystemProxyManager 创建系统代理管理器
func NewSystemProxyManager() *SystemProxyManager <span class="cov0" title="0">{
return &amp;SystemProxyManager{
originalSettings: make(map[string]string),
}
}</span>
// SetGlobalProxy 设置全局代理
func (s *SystemProxyManager) SetGlobalProxy(httpProxy, httpsProxy, socksProxy string) error <span class="cov0" title="0">{
switch runtime.GOOS </span>{
case "darwin":<span class="cov0" title="0">
return s.setMacOSProxy(httpProxy, httpsProxy, socksProxy)</span>
case "windows":<span class="cov0" title="0">
return s.setWindowsProxy(httpProxy, httpsProxy, socksProxy)</span>
case "linux":<span class="cov0" title="0">
return s.setLinuxProxy(httpProxy, httpsProxy, socksProxy)</span>
default:<span class="cov0" title="0">
return fmt.Errorf("unsupported operating system: %s", runtime.GOOS)</span>
}
}
// RestoreProxy 恢复原始代理设置
func (s *SystemProxyManager) RestoreProxy() error <span class="cov0" title="0">{
switch runtime.GOOS </span>{
case "darwin":<span class="cov0" title="0">
return s.restoreMacOSProxy()</span>
case "windows":<span class="cov0" title="0">
return s.restoreWindowsProxy()</span>
case "linux":<span class="cov0" title="0">
return s.restoreLinuxProxy()</span>
default:<span class="cov0" title="0">
return fmt.Errorf("unsupported operating system: %s", runtime.GOOS)</span>
}
}
// macOS 代理设置
func (s *SystemProxyManager) setMacOSProxy(httpProxy, httpsProxy, socksProxy string) error <span class="cov0" title="0">{
logger.Info("Setting macOS system proxy...")
// 获取网络服务名称
networkService, err := s.getMacOSNetworkService()
if err != nil </span><span class="cov0" title="0">{
return fmt.Errorf("failed to get network service: %v", err)
}</span>
// 保存原始设置
<span class="cov0" title="0">if err := s.saveMacOSOriginalSettings(networkService); err != nil </span><span class="cov0" title="0">{
logger.Warn("Failed to save original proxy settings: %v", err)
}</span>
// 设置HTTP代理
<span class="cov0" title="0">if httpProxy != "" </span><span class="cov0" title="0">{
if err := s.runCommand("networksetup", "-setwebproxy", networkService,
s.parseProxyHost(httpProxy), s.parseProxyPort(httpProxy)); err != nil </span><span class="cov0" title="0">{
return fmt.Errorf("failed to set HTTP proxy: %v", err)
}</span>
<span class="cov0" title="0">if err := s.runCommand("networksetup", "-setwebproxystate", networkService, "on"); err != nil </span><span class="cov0" title="0">{
return fmt.Errorf("failed to enable HTTP proxy: %v", err)
}</span>
}
// 设置HTTPS代理
<span class="cov0" title="0">if httpsProxy != "" </span><span class="cov0" title="0">{
if err := s.runCommand("networksetup", "-setsecurewebproxy", networkService,
s.parseProxyHost(httpsProxy), s.parseProxyPort(httpsProxy)); err != nil </span><span class="cov0" title="0">{
return fmt.Errorf("failed to set HTTPS proxy: %v", err)
}</span>
<span class="cov0" title="0">if err := s.runCommand("networksetup", "-setsecurewebproxystate", networkService, "on"); err != nil </span><span class="cov0" title="0">{
return fmt.Errorf("failed to enable HTTPS proxy: %v", err)
}</span>
}
// 设置SOCKS代理
<span class="cov0" title="0">if socksProxy != "" </span><span class="cov0" title="0">{
if err := s.runCommand("networksetup", "-setsocksfirewallproxy", networkService,
s.parseProxyHost(socksProxy), s.parseProxyPort(socksProxy)); err != nil </span><span class="cov0" title="0">{
return fmt.Errorf("failed to set SOCKS proxy: %v", err)
}</span>
<span class="cov0" title="0">if err := s.runCommand("networksetup", "-setsocksfirewallproxystate", networkService, "on"); err != nil </span><span class="cov0" title="0">{
return fmt.Errorf("failed to enable SOCKS proxy: %v", err)
}</span>
}
<span class="cov0" title="0">logger.Info("macOS system proxy configured successfully")
return nil</span>
}
// 获取macOS网络服务名称
func (s *SystemProxyManager) getMacOSNetworkService() (string, error) <span class="cov0" title="0">{
output, err := exec.Command("networksetup", "-listallnetworkservices").Output()
if err != nil </span><span class="cov0" title="0">{
return "", err
}</span>
<span class="cov0" title="0">lines := strings.Split(string(output), "\n")
for _, line := range lines </span><span class="cov0" title="0">{
line = strings.TrimSpace(line)
if line != "" &amp;&amp; !strings.HasPrefix(line, "*") &amp;&amp; !strings.Contains(line, "An asterisk") </span><span class="cov0" title="0">{
// 优先选择Wi-Fi,否则选择第一个可用的服务
if strings.Contains(strings.ToLower(line), "wi-fi") || strings.Contains(strings.ToLower(line), "wifi") </span><span class="cov0" title="0">{
return line, nil
}</span>
}
}
// 如果没找到Wi-Fi,返回第一个可用的服务
<span class="cov0" title="0">for _, line := range lines </span><span class="cov0" title="0">{
line = strings.TrimSpace(line)
if line != "" &amp;&amp; !strings.HasPrefix(line, "*") &amp;&amp; !strings.Contains(line, "An asterisk") </span><span class="cov0" title="0">{
return line, nil
}</span>
}
<span class="cov0" title="0">return "", fmt.Errorf("no network service found")</span>
}
// 保存macOS原始代理设置
func (s *SystemProxyManager) saveMacOSOriginalSettings(networkService string) error <span class="cov0" title="0">{
// 保存HTTP代理状态
if output, err := exec.Command("networksetup", "-getwebproxy", networkService).Output(); err == nil </span><span class="cov0" title="0">{
s.originalSettings["http_proxy"] = string(output)
}</span>
// 保存HTTPS代理状态
<span class="cov0" title="0">if output, err := exec.Command("networksetup", "-getsecurewebproxy", networkService).Output(); err == nil </span><span class="cov0" title="0">{
s.originalSettings["https_proxy"] = string(output)
}</span>
// 保存SOCKS代理状态
<span class="cov0" title="0">if output, err := exec.Command("networksetup", "-getsocksfirewallproxy", networkService).Output(); err == nil </span><span class="cov0" title="0">{
s.originalSettings["socks_proxy"] = string(output)
}</span>
<span class="cov0" title="0">s.originalSettings["network_service"] = networkService
return nil</span>
}
// 恢复macOS代理设置
func (s *SystemProxyManager) restoreMacOSProxy() error <span class="cov0" title="0">{
networkService, exists := s.originalSettings["network_service"]
if !exists </span><span class="cov0" title="0">{
return fmt.Errorf("no network service information saved")
}</span>
<span class="cov0" title="0">logger.Info("Restoring macOS system proxy...")
// 关闭所有代理
s.runCommand("networksetup", "-setwebproxystate", networkService, "off")
s.runCommand("networksetup", "-setsecurewebproxystate", networkService, "off")
s.runCommand("networksetup", "-setsocksfirewallproxystate", networkService, "off")
logger.Info("macOS system proxy restored")
return nil</span>
}
// Windows 代理设置(简化实现)
func (s *SystemProxyManager) setWindowsProxy(httpProxy, httpsProxy, socksProxy string) error <span class="cov0" title="0">{
logger.Warn("Windows proxy setting not fully implemented")
return fmt.Errorf("Windows proxy setting not implemented yet")
}</span>
func (s *SystemProxyManager) restoreWindowsProxy() error <span class="cov0" title="0">{
logger.Warn("Windows proxy restoration not fully implemented")
return nil
}</span>
// Linux 代理设置(简化实现)
func (s *SystemProxyManager) setLinuxProxy(httpProxy, httpsProxy, socksProxy string) error <span class="cov0" title="0">{
logger.Warn("Linux proxy setting not fully implemented")
return fmt.Errorf("Linux proxy setting not implemented yet")
}</span>
func (s *SystemProxyManager) restoreLinuxProxy() error <span class="cov0" title="0">{
logger.Warn("Linux proxy restoration not fully implemented")
return nil
}</span>
// 辅助函数
func (s *SystemProxyManager) runCommand(name string, args ...string) error <span class="cov0" title="0">{
cmd := exec.Command(name, args...)
output, err := cmd.CombinedOutput()
if err != nil </span><span class="cov0" title="0">{
logger.Error("Command failed: %s %v, output: %s", name, args, string(output))
return err
}</span>
<span class="cov0" title="0">return nil</span>
}
func (s *SystemProxyManager) parseProxyHost(proxy string) string <span class="cov0" title="0">{
// 简单解析,格式: host:port
parts := strings.Split(proxy, ":")
if len(parts) &gt;= 1 </span><span class="cov0" title="0">{
return parts[0]
}</span>
<span class="cov0" title="0">return proxy</span>
}
func (s *SystemProxyManager) parseProxyPort(proxy string) string <span class="cov0" title="0">{
// 简单解析,格式: host:port
parts := strings.Split(proxy, ":")
if len(parts) &gt;= 2 </span><span class="cov0" title="0">{
return parts[1]
}</span>
<span class="cov0" title="0">return "8080"</span> // 默认端口
}
</pre>
<pre class="file" id="file6" style="display: none">package dns
import (
"fmt"
"net"
"strings"
"sync"
"time"
"github.com/azoic/wormhole-client/pkg/logger"
)
// DNSProxy DNS代理服务器
type DNSProxy struct {
upstreamDNS string
localPort int
server *net.UDPConn
cache *dnsCache
running bool
mutex sync.RWMutex
}
// dnsCache DNS缓存
type dnsCache struct {
entries map[string]*cacheEntry
mutex sync.RWMutex
}
type cacheEntry struct {
response []byte
expiry time.Time
}
// NewDNSProxy 创建DNS代理
func NewDNSProxy(upstreamDNS string, localPort int) *DNSProxy <span class="cov0" title="0">{
return &amp;DNSProxy{
upstreamDNS: upstreamDNS,
localPort: localPort,
cache: &amp;dnsCache{
entries: make(map[string]*cacheEntry),
},
}
}</span>
// Start 启动DNS代理服务器
func (d *DNSProxy) Start() error <span class="cov0" title="0">{
d.mutex.Lock()
defer d.mutex.Unlock()
if d.running </span><span class="cov0" title="0">{
return fmt.Errorf("DNS proxy is already running")
}</span>
<span class="cov0" title="0">addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", d.localPort))
if err != nil </span><span class="cov0" title="0">{
return fmt.Errorf("failed to resolve UDP address: %v", err)
}</span>
<span class="cov0" title="0">d.server, err = net.ListenUDP("udp", addr)
if err != nil </span><span class="cov0" title="0">{
return fmt.Errorf("failed to start DNS proxy server: %v", err)
}</span>
<span class="cov0" title="0">d.running = true
logger.Info("DNS proxy started on port %d", d.localPort)
// 启动清理缓存的goroutine
go d.cleanupCache()
// 处理DNS请求
go d.handleRequests()
return nil</span>
}
// Stop 停止DNS代理服务器
func (d *DNSProxy) Stop() error <span class="cov0" title="0">{
d.mutex.Lock()
defer d.mutex.Unlock()
if !d.running </span><span class="cov0" title="0">{
return nil
}</span>
<span class="cov0" title="0">d.running = false
if d.server != nil </span><span class="cov0" title="0">{
d.server.Close()
}</span>
<span class="cov0" title="0">logger.Info("DNS proxy stopped")
return nil</span>
}
// handleRequests 处理DNS请求
func (d *DNSProxy) handleRequests() <span class="cov0" title="0">{
buffer := make([]byte, 512) // DNS消息最大512字节(UDP)
for d.isRunning() </span><span class="cov0" title="0">{
n, clientAddr, err := d.server.ReadFromUDP(buffer)
if err != nil </span><span class="cov0" title="0">{
if d.isRunning() </span><span class="cov0" title="0">{
logger.Error("Failed to read DNS request: %v", err)
}</span>
<span class="cov0" title="0">continue</span>
}
<span class="cov0" title="0">go d.processRequest(buffer[:n], clientAddr)</span>
}
}
// processRequest 处理单个DNS请求
func (d *DNSProxy) processRequest(request []byte, clientAddr *net.UDPAddr) <span class="cov0" title="0">{
// 简单的DNS请求解析
domain := d.extractDomain(request)
logger.Debug("DNS request for domain: %s", domain)
// 检查缓存
if cachedResponse := d.getFromCache(domain); cachedResponse != nil </span><span class="cov0" title="0">{
logger.Debug("Serving %s from cache", domain)
d.server.WriteToUDP(cachedResponse, clientAddr)
return
}</span>
// 转发到上游DNS服务器
<span class="cov0" title="0">response, err := d.forwardToUpstream(request)
if err != nil </span><span class="cov0" title="0">{
logger.Error("Failed to forward DNS request: %v", err)
return
}</span>
// 缓存响应
<span class="cov0" title="0">d.addToCache(domain, response)
// 返回响应给客户端
d.server.WriteToUDP(response, clientAddr)</span>
}
// extractDomain 从DNS请求中提取域名(简化实现)
func (d *DNSProxy) extractDomain(request []byte) string <span class="cov0" title="0">{
if len(request) &lt; 12 </span><span class="cov0" title="0">{
return ""
}</span>
// 跳过DNS头部(12字节)
<span class="cov0" title="0">offset := 12
var domain strings.Builder
for offset &lt; len(request) </span><span class="cov0" title="0">{
length := int(request[offset])
if length == 0 </span><span class="cov0" title="0">{
break</span>
}
<span class="cov0" title="0">offset++
if offset+length &gt; len(request) </span><span class="cov0" title="0">{
break</span>
}
<span class="cov0" title="0">if domain.Len() &gt; 0 </span><span class="cov0" title="0">{
domain.WriteByte('.')
}</span>
<span class="cov0" title="0">domain.Write(request[offset : offset+length])
offset += length</span>
}
<span class="cov0" title="0">return domain.String()</span>
}
// forwardToUpstream 转发DNS请求到上游服务器
func (d *DNSProxy) forwardToUpstream(request []byte) ([]byte, error) <span class="cov0" title="0">{
conn, err := net.Dial("udp", d.upstreamDNS)
if err != nil </span><span class="cov0" title="0">{
return nil, fmt.Errorf("failed to connect to upstream DNS: %v", err)
}</span>
<span class="cov0" title="0">defer conn.Close()
// 设置超时
conn.SetDeadline(time.Now().Add(5 * time.Second))
// 发送请求
if _, err := conn.Write(request); err != nil </span><span class="cov0" title="0">{
return nil, fmt.Errorf("failed to send DNS request: %v", err)
}</span>
// 读取响应
<span class="cov0" title="0">response := make([]byte, 512)
n, err := conn.Read(response)
if err != nil </span><span class="cov0" title="0">{
return nil, fmt.Errorf("failed to read DNS response: %v", err)
}</span>
<span class="cov0" title="0">return response[:n], nil</span>
}
// getFromCache 从缓存获取DNS响应
func (d *DNSProxy) getFromCache(domain string) []byte <span class="cov0" title="0">{
d.cache.mutex.RLock()
defer d.cache.mutex.RUnlock()
entry, exists := d.cache.entries[domain]
if !exists || time.Now().After(entry.expiry) </span><span class="cov0" title="0">{
return nil
}</span>
<span class="cov0" title="0">return entry.response</span>
}
// addToCache 添加DNS响应到缓存
func (d *DNSProxy) addToCache(domain string, response []byte) <span class="cov0" title="0">{
d.cache.mutex.Lock()
defer d.cache.mutex.Unlock()
// 设置缓存过期时间(5分钟)
expiry := time.Now().Add(5 * time.Minute)
d.cache.entries[domain] = &amp;cacheEntry{
response: make([]byte, len(response)),
expiry: expiry,
}
copy(d.cache.entries[domain].response, response)
}</span>
// cleanupCache 清理过期的缓存条目
func (d *DNSProxy) cleanupCache() <span class="cov0" title="0">{
ticker := time.NewTicker(1 * time.Minute)
defer ticker.Stop()
for </span><span class="cov0" title="0">{
select </span>{
case &lt;-ticker.C:<span class="cov0" title="0">
if !d.isRunning() </span><span class="cov0" title="0">{
return
}</span>
<span class="cov0" title="0">d.cache.mutex.Lock()
now := time.Now()
for domain, entry := range d.cache.entries </span><span class="cov0" title="0">{
if now.After(entry.expiry) </span><span class="cov0" title="0">{
delete(d.cache.entries, domain)
}</span>
}
<span class="cov0" title="0">d.cache.mutex.Unlock()</span>
}
}
}
// isRunning 检查DNS代理是否在运行
func (d *DNSProxy) isRunning() bool <span class="cov0" title="0">{
d.mutex.RLock()
defer d.mutex.RUnlock()
return d.running
}</span>
</pre>
<pre class="file" id="file7" style="display: none">package logger
import (
"fmt"
"log"
"os"
"strings"
"time"
)
type LogLevel int
const (
DEBUG LogLevel = iota
INFO
WARN
ERROR
)
type Logger struct {
level LogLevel
logger *log.Logger
}
var defaultLogger *Logger
func init() <span class="cov0" title="0">{
defaultLogger = New("INFO")
}</span>
// New 创建新的日志记录器
func New(levelStr string) *Logger <span class="cov0" title="0">{
level := parseLogLevel(levelStr)
logger := log.New(os.Stdout, "", 0)
return &amp;Logger{
level: level,
logger: logger,
}
}</span>
// SetLevel 设置日志级别
func SetLevel(levelStr string) <span class="cov0" title="0">{
defaultLogger.level = parseLogLevel(levelStr)
}</span>
func parseLogLevel(levelStr string) LogLevel <span class="cov0" title="0">{
switch strings.ToUpper(levelStr) </span>{
case "DEBUG":<span class="cov0" title="0">
return DEBUG</span>
case "INFO":<span class="cov0" title="0">
return INFO</span>
case "WARN", "WARNING":<span class="cov0" title="0">
return WARN</span>
case "ERROR":<span class="cov0" title="0">
return ERROR</span>
default:<span class="cov0" title="0">
return INFO</span>
}
}
func (l *Logger) log(level LogLevel, format string, args ...interface{}) <span class="cov0" title="0">{
if level &lt; l.level </span><span class="cov0" title="0">{
return
}</span>
<span class="cov0" title="0">levelStr := ""
switch level </span>{
case DEBUG:<span class="cov0" title="0">
levelStr = "DEBUG"</span>
case INFO:<span class="cov0" title="0">
levelStr = "INFO"</span>
case WARN:<span class="cov0" title="0">
levelStr = "WARN"</span>
case ERROR:<span class="cov0" title="0">
levelStr = "ERROR"</span>
}
<span class="cov0" title="0">timestamp := time.Now().Format("2006-01-02 15:04:05")
message := fmt.Sprintf(format, args...)
logLine := fmt.Sprintf("[%s] [%s] %s", timestamp, levelStr, message)
l.logger.Println(logLine)</span>
}
// Debug 调试日志
func (l *Logger) Debug(format string, args ...interface{}) <span class="cov0" title="0">{
l.log(DEBUG, format, args...)
}</span>
// Info 信息日志
func (l *Logger) Info(format string, args ...interface{}) <span class="cov0" title="0">{
l.log(INFO, format, args...)
}</span>
// Warn 警告日志
func (l *Logger) Warn(format string, args ...interface{}) <span class="cov0" title="0">{
l.log(WARN, format, args...)
}</span>
// Error 错误日志
func (l *Logger) Error(format string, args ...interface{}) <span class="cov0" title="0">{
l.log(ERROR, format, args...)
}</span>
// 全局日志函数
func Debug(format string, args ...interface{}) <span class="cov0" title="0">{
defaultLogger.Debug(format, args...)
}</span>
func Info(format string, args ...interface{}) <span class="cov0" title="0">{
defaultLogger.Info(format, args...)
}</span>
func Warn(format string, args ...interface{}) <span class="cov0" title="0">{
defaultLogger.Warn(format, args...)
}</span>
func Error(format string, args ...interface{}) <span class="cov0" title="0">{
defaultLogger.Error(format, args...)
}</span>
</pre>
</div>
</body>
<script>
(function() {
var files = document.getElementById('files');
var visible;
files.addEventListener('change', onChange, false);
function select(part) {
if (visible)
visible.style.display = 'none';
visible = document.getElementById(part);
if (!visible)
return;
files.value = part;
visible.style.display = 'block';
location.hash = part;
}
function onChange() {
select(files.value);
window.scrollTo(0, 0);
}
if (location.hash != "") {
select(location.hash.substr(1));
}
if (!visible) {
select("file0");
}
})();
</script>
</html>