package client import ( "context" "fmt" "net/http" "os" "os/signal" "syscall" "time" "github.com/azoic/wormhole-client/internal/config" "github.com/azoic/wormhole-client/internal/proxy" "github.com/azoic/wormhole-client/internal/routing" "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 routeMatcher *routing.RouteMatcher httpServer *http.Server ctx context.Context cancel context.CancelFunc } func NewClient(mode string) *Client { ctx, cancel := context.WithCancel(context.Background()) return &Client{ mode: mode, systemProxyMgr: system.NewSystemProxyManager(), ctx: ctx, cancel: cancel, } } func (c *Client) Start(configPath string) error { // 加载配置 cfg, err := config.LoadConfig(configPath) if err != nil { return fmt.Errorf("failed to load config: %v", err) } if err := cfg.Validate(); err != nil { return fmt.Errorf("invalid config: %v", err) } 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, ) // 初始化路由匹配器(如果有路由配置) if c.mode == "global" && len(cfg.GlobalProxy.Routing.BypassDomains) > 0 || len(cfg.GlobalProxy.Routing.ForceDomains) > 0 { c.routeMatcher, err = routing.NewRouteMatcher(&cfg.GlobalProxy.Routing) if err != nil { logger.Warn("Failed to initialize route matcher: %v", err) } else { logger.Info("🛣️ Route matcher initialized") } } // 设置信号处理 c.setupSignalHandler() switch c.mode { case "http": return c.startHTTPProxy() case "global": return c.startGlobalProxy() case "transparent": return c.startTransparentProxy() default: return fmt.Errorf("unsupported mode: %s", c.mode) } } func (c *Client) startHTTPProxy() error { logger.Info("🌐 Starting HTTP proxy mode...") logger.Info("💡 Setting up HTTP proxy on port %d", c.config.Proxy.LocalPort) c.httpServer = 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) logger.Info("📊 Statistics available at: http://127.0.0.1:%d/stats", c.config.Proxy.LocalPort) logger.Info("💚 Health check available at: http://127.0.0.1:%d/health", c.config.Proxy.LocalPort) return c.httpServer.ListenAndServe() } func (c *Client) startGlobalProxy() error { logger.Info("🌍 Starting global proxy mode...") logger.Info("💡 This will configure system-wide proxy settings") logger.Info("⚠️ Requires administrator privileges") // 启动HTTP代理服务器 c.httpServer = c.socks5Proxy.CreateHTTPProxy(c.config.Proxy.LocalPort) errChan := make(chan error, 1) go func() { if err := c.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { errChan <- fmt.Errorf("HTTP proxy server failed: %v", err) } }() // 等待服务器启动 time.Sleep(500 * time.Millisecond) // 设置系统代理 httpProxy := fmt.Sprintf("127.0.0.1:%d", c.config.Proxy.LocalPort) httpsProxy := httpProxy logger.Info("📡 Setting system proxy to %s", httpProxy) if err := c.systemProxyMgr.SetGlobalProxy(httpProxy, httpsProxy, ""); err != nil { logger.Error("Failed to set system proxy: %v", err) logger.Warn("💡 You may need to run with administrator privileges") logger.Info("📋 Manual setup instructions:") logger.Info(" - Set HTTP proxy to: %s", httpProxy) logger.Info(" - Set HTTPS proxy to: %s", httpsProxy) } else { logger.Info("✅ System proxy configured successfully") } // 启动DNS代理(如果启用) if c.config.GlobalProxy.DNSProxy { logger.Info("🔍 Starting DNS proxy on port %d", c.config.GlobalProxy.DNSPort) // 创建DNS代理(通过SOCKS5转发DNS查询) c.dnsProxy = dns.NewDNSProxy("8.8.8.8:53", c.config.GlobalProxy.DNSPort) if err := c.dnsProxy.Start(); err != nil { logger.Warn("Failed to start DNS proxy: %v", err) } else { logger.Info("✅ DNS proxy started") logger.Info("💡 Configure your system DNS to: 127.0.0.1:%d", c.config.GlobalProxy.DNSPort) } } // 显示路由配置信息 if c.routeMatcher != nil { stats := c.routeMatcher.GetStats() logger.Info("🛣️ Routing configuration:") logger.Info(" - Bypass domains: %v", stats["bypass_domains_count"]) logger.Info(" - Force domains: %v", stats["force_domains_count"]) logger.Info(" - Bypass local: %v", stats["bypass_local"]) logger.Info(" - Bypass private: %v", stats["bypass_private"]) } logger.Info("🎉 Global proxy mode started successfully") logger.Info("📍 HTTP/HTTPS Proxy: %s", httpProxy) if c.dnsProxy != nil { logger.Info("📍 DNS Proxy: 127.0.0.1:%d", c.config.GlobalProxy.DNSPort) } logger.Info("📊 Statistics: http://%s/stats", httpProxy) logger.Info("💚 Health check: http://%s/health", httpProxy) logger.Info("🛑 Press Ctrl+C to stop") // 检查是否有错误 select { case err := <-errChan: return err case <-c.ctx.Done(): return nil } } func (c *Client) startTransparentProxy() error { logger.Info("🔍 Starting transparent proxy mode...") logger.Info("💡 This will intercept network traffic transparently") logger.Info("⚠️ Requires root privileges and iptables support") // TODO: 实现透明代理 logger.Error("❌ Transparent proxy mode is not yet implemented") logger.Info("💡 Available alternatives:") logger.Info(" - Use global mode: ./bin/wormhole-client -mode global") logger.Info(" - Use HTTP mode: ./bin/wormhole-client -mode http") return fmt.Errorf("transparent proxy mode not implemented") } func (c *Client) setupSignalHandler() { sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) go func() { <-sigChan logger.Info("🛑 Received shutdown signal...") c.shutdown() }() } func (c *Client) shutdown() { logger.Info("🔄 Shutting down gracefully...") // 停止HTTP服务器 if c.httpServer != nil { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := c.httpServer.Shutdown(ctx); err != nil { logger.Error("Failed to shutdown HTTP server gracefully: %v", err) } else { logger.Info("✅ HTTP server stopped") } } // 恢复系统代理设置 if c.systemProxyMgr != nil && c.systemProxyMgr.IsEnabled() { logger.Info("🔄 Restoring system proxy settings...") if err := c.systemProxyMgr.RestoreProxy(); err != nil { logger.Error("Failed to restore system proxy: %v", err) } } // 停止DNS代理 if c.dnsProxy != nil { logger.Info("🔄 Stopping DNS proxy...") if err := c.dnsProxy.Stop(); err != nil { logger.Error("Failed to stop DNS proxy: %v", err) } else { logger.Info("✅ DNS proxy stopped") } } // 显示统计信息 if c.socks5Proxy != nil { stats := c.socks5Proxy.GetStats() logger.Info("📊 Final statistics:") logger.Info(" - Total connections: %d", stats.TotalConnections) logger.Info(" - Successful requests: %d", stats.SuccessfulRequests) logger.Info(" - Failed requests: %d", stats.FailedRequests) logger.Info(" - Success rate: %.2f%%", stats.GetSuccessRate()) logger.Info(" - Total bytes: %d", stats.GetTotalBytes()) logger.Info(" - Uptime: %v", stats.Uptime) } logger.Info("✅ Cleanup completed") // 取消上下文 c.cancel() // 给一点时间让goroutines优雅退出 time.Sleep(100 * time.Millisecond) os.Exit(0) } // GetStats 获取客户端统计信息 func (c *Client) GetStats() map[string]interface{} { stats := make(map[string]interface{}) if c.socks5Proxy != nil { proxyStats := c.socks5Proxy.GetStats() stats["proxy"] = proxyStats } if c.systemProxyMgr != nil { stats["system_proxy_enabled"] = c.systemProxyMgr.IsEnabled() if currentProxy, err := c.systemProxyMgr.GetCurrentProxy(); err == nil { stats["current_system_proxy"] = currentProxy } } if c.routeMatcher != nil { stats["routing"] = c.routeMatcher.GetStats() } stats["mode"] = c.mode stats["config_file"] = c.config return stats }