Complete wormhole client implementation with SOCKS5 proxy, HTTP proxy mode, global proxy mode, DNS proxy, system integration, configuration management, logging, documentation and tests

main
huyinsong 2 weeks ago
parent a5b72efcf3
commit ea09a07490
  1. 282
      README.md
  2. BIN
      bin/wormhole-client
  3. 213
      docs/usage.md
  4. 2
      go.mod
  5. 4
      go.sum
  6. 171
      internal/client/client.go
  7. 67
      internal/client/client_test.go
  8. 107
      internal/config/config.go
  9. 217
      internal/proxy/socks5.go
  10. 224
      internal/system/proxy.go
  11. 252
      pkg/dns/proxy.go
  12. 121
      pkg/logger/logger.go

@ -1,126 +1,252 @@
# Wormhole SOCKS5 Client
🌐 功能完整的 SOCKS5 代理客户端,支持多种代理模式
[![Go Version](https://img.shields.io/badge/Go-1.21+-00ADD8?style=flat&logo=go)](https://go.dev/)
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Build Status](https://img.shields.io/badge/Build-Passing-brightgreen.svg)]()
## 快速开始
一个功能强大的 SOCKS5 代理客户端,支持多种代理模式,包括 HTTP 代理转换、全局代理设置和透明代理。
## ✨ 功能特性
- 🚀 **HTTP 代理模式**: 将 SOCKS5 代理转换为 HTTP/HTTPS 代理
- 🌍 **全局代理模式**: 自动配置系统级代理设置
- 🔍 **DNS 代理**: 提供 DNS 请求代理和缓存功能
- 🖥 **系统集成**: 支持 macOS 系统代理自动配置和恢复
- 📝 **灵活配置**: 支持 YAML 配置文件和命令行参数
- 🛡 **安全认证**: 支持 SOCKS5 用户名密码认证
- ⚡ **高性能**: 异步处理和连接池优化
## 🚀 快速开始
### 安装
#### 从源码编译
### 构建和运行
```bash
# HTTP 代理模式
make run-http
git clone http://101.34.16.52:3000/huyinsong/wormhole-client.git
cd wormhole-client
make build
```
# 全局代理模式 (需要管理员权限)
make run-global
#### 使用预编译二进制
# 透明代理模式 (需要root权限)
make run-transparent
```bash
# 安装到系统路径
make install
```
### 配置
编辑 `configs/client.yaml` 来自定义客户端设置:
### 基本使用
1. **配置 SOCKS5 服务器信息**
编辑 `configs/client.yaml`:
```yaml
server:
address: your_server_ip
address: your-socks5-server.com
port: 1080
username: your_username
password: your_password
username: your-username
password: your-password
```
proxy:
mode: http
localPort: 8080
2. **启动 HTTP 代理模式**
```bash
./bin/wormhole-client -mode http
```
## 功能特性
3. **配置浏览器代理**
- HTTP 代理: `127.0.0.1:8080`
- HTTPS 代理: `127.0.0.1:8080`
### 🔄 多种代理模式
- ✅ **HTTP 代理** - 简单易用的HTTP代理
- 🔄 **全局代理** - 系统级全局代理设置
- 🔄 **透明代理** - 透明流量拦截和转发
## 📖 使用模式
### 🎯 智能路由
- 🔄 域名/IP分流规则
- 🔄 本地网络绕过
- 🔄 PAC文件支持
- 🔄 自定义路由规则
### HTTP 代理模式
### 🛡 系统集成
- 🔄 自动DNS配置
- 🔄 系统代理设置
- 🔄 iptables规则管理
- 🔄 网络路由配置
最常用的模式,将 SOCKS5 代理转换为 HTTP 代理:
### 🖥 跨平台支持
- ✅ Linux - 完整支持
- 🔄 macOS - 系统代理支持
- 🔄 Windows - WinDivert透明代理
```bash
# 使用默认配置
./bin/wormhole-client -mode http
## 使用模式
# 指定端口
./bin/wormhole-client -mode http -config configs/custom.yaml
```
### HTTP 代理模式
最简单的使用方式,在本地启动HTTP代理:
### 全局代理模式
自动配置系统代理,适合需要全局科学上网的场景:
```bash
make run-http
# macOS 需要管理员权限
sudo ./bin/wormhole-client -mode global
```
然后配置应用程序使用 `http://127.0.0.1:8080` 作为代理。
**特性:**
- 自动配置系统 HTTP/HTTPS 代理
- 可选的 DNS 代理功能
- 程序退出时自动恢复原始设置
- 支持 Ctrl+C 安全退出
### 全局代理模式
配置系统级代理,所有网络流量通过代理:
### 透明代理模式 (开发中)
```bash
sudo make run-global
sudo ./bin/wormhole-client -mode transparent
```
## ⚙ 配置说明
### 完整配置示例
```yaml
# SOCKS5 服务器设置
server:
address: 127.0.0.1
port: 1080
username: admin
password: secure_password_123
# 代理模式设置
proxy:
mode: http
localPort: 8080
# 全局代理设置
globalProxy:
enabled: false
dnsProxy: true
dnsPort: 5353
# 路由规则
routing:
bypassLocal: true # 跳过本地地址
bypassPrivate: true # 跳过私有网络
bypassDomains: # 跳过的域名
- "*.local"
- "*.lan"
forceDomains: # 强制代理的域名
- "*.google.com"
- "*.github.com"
# 日志设置
logLevel: info # debug, info, warn, error
timeout: 30s
```
### 透明代理模式
透明拦截网络流量,无需配置应用程序:
### 命令行选项
```bash
sudo make run-transparent
./bin/wormhole-client [选项]
选项:
-config string
配置文件路径 (默认 "configs/client.yaml")
-mode string
代理模式: http, global, transparent (默认 "http")
-version
显示版本信息
```
## 迁移状态
## 🏗 项目结构
此项目是从 [原始 Wormhole 项目](https://github.com/azoic/wormhole) 拆分出的独立客户端。
```
wormhole-client/
├── cmd/wormhole-client/ # 主程序入口
├── internal/ # 内部模块
│ ├── client/ # 客户端核心逻辑
│ ├── config/ # 配置解析模块
│ ├── proxy/ # SOCKS5 代理实现
│ └── system/ # 系统代理管理
├── pkg/ # 公共模块
│ ├── dns/ # DNS 代理模块
│ └── logger/ # 日志模块
├── configs/ # 配置文件
├── docs/ # 文档
├── scripts/ # 构建脚本
└── bin/ # 编译输出
```
### ✅ 已完成
- [x] 基础项目结构
- [x] HTTP代理模式框架
- [x] 配置管理系统
- [x] 构建系统
## 🔧 开发
### 🔄 进行中
- [ ] 完整的客户端代码迁移
- [ ] 全局代理功能
- [ ] 透明代理实现
- [ ] 智能路由系统
### 编译和测试
### 🎯 计划中
- [ ] GUI客户端
- [ ] 移动端支持
- [ ] 浏览器扩展
- [ ] 高级分流规则
```bash
# 编译
make build
## 开发
# 运行测试
make test
### 系统要求
- Go 1.21+
- Linux: iptables, ip命令
- macOS: 管理员权限
- Windows: WinDivert驱动
# 代码检查
make lint
### 添加依赖
```bash
go get package_name
go mod tidy
# 清理
make clean
# 开发模式运行
make dev
```
### 运行测试
### 添加新功能
1. Fork 本项目
2. 创建功能分支
3. 编写代码和测试
4. 提交 Pull Request
## 📚 文档
- [使用指南](docs/usage.md) - 详细的使用说明
- [配置参考](docs/configuration.md) - 配置参数说明
- [故障排除](docs/troubleshooting.md) - 常见问题解决
## 🐛 故障排除
### 常见问题
1. **连接失败**
- 检查 SOCKS5 服务器地址和端口
- 验证用户名和密码
- 确认网络连通性
2. **权限问题** (macOS)
```bash
sudo ./bin/wormhole-client -mode global
```
3. **端口占用**
- 修改配置文件中的 `localPort`
- 检查端口是否被其他程序占用
### 调试模式
```bash
make test
# 启用详细日志
./bin/wormhole-client -mode http
```
## 许可证
在配置文件中设置 `logLevel: debug` 获取详细信息。
## 🛡 安全说明
- 保护好包含密码的配置文件
- 仅在需要时使用管理员权限
- 确保 SOCKS5 服务器的安全性
- 使用 DNS 代理防止 DNS 泄露
## 📄 许可证
本项目采用 [MIT 许可证](LICENSE)。
## 🤝 贡献
欢迎提交 Issue 和 Pull Request!
## 📞 联系
- 项目地址: http://101.34.16.52:3000/huyinsong/wormhole-client.git
- 问题反馈: 请使用 GitHub Issues
---
MIT License - 详见 [LICENSE](LICENSE) 文件
**⭐ 如果这个项目对你有帮助,请给一个 Star!**

Binary file not shown.

@ -0,0 +1,213 @@
# Wormhole SOCKS5 Client 使用指南
## 概述
Wormhole SOCKS5 Client 是一个功能强大的代理客户端,支持多种代理模式,可以将 SOCKS5 代理转换为 HTTP 代理,并提供全局代理和透明代理功能。
## 功能特性
- ✅ **HTTP 代理模式**: 将 SOCKS5 代理转换为 HTTP/HTTPS 代理
- ✅ **全局代理模式**: 自动配置系统级代理设置
- ✅ **DNS 代理**: 提供 DNS 请求代理和缓存
- ✅ **系统集成**: 支持 macOS 系统代理自动配置
- ⚠ **透明代理模式**: 计划实现(需要 root 权限)
## 安装
### 从源码编译
```bash
git clone http://101.34.16.52:3000/huyinsong/wormhole-client.git
cd wormhole-client
make build
```
### 使用预编译二进制
```bash
# 下载并安装到 /usr/local/bin
make install
```
## 配置文件
配置文件位于 `configs/client.yaml`,包含以下主要配置:
```yaml
# SOCKS5 服务器设置
server:
address: 127.0.0.1
port: 1080
username: admin
password: secure_password_123
# 代理模式设置
proxy:
mode: http # http, global, transparent
localPort: 8080
# 全局代理设置
globalProxy:
enabled: false
dnsProxy: true
dnsPort: 5353
# 分流规则
routing:
bypassLocal: true
bypassPrivate: true
bypassDomains:
- "*.local"
- "*.lan"
forceDomains:
- "*.google.com"
- "*.github.com"
# 日志级别
logLevel: info
timeout: 30s
```
## 使用方法
### 1. HTTP 代理模式
最简单的使用方式,将 SOCKS5 代理转换为 HTTP 代理:
```bash
# 使用默认配置
./bin/wormhole-client -mode http
# 指定配置文件
./bin/wormhole-client -mode http -config /path/to/config.yaml
```
启动后,在浏览器中配置 HTTP 代理:
- 代理地址: `127.0.0.1`
- 代理端口: `8080` (或配置文件中指定的端口)
### 2. 全局代理模式
自动配置系统级代理,所有应用程序都将通过代理访问网络:
```bash
# 需要管理员权限 (macOS)
sudo ./bin/wormhole-client -mode global
```
**注意**:
- macOS 系统需要管理员权限
- 程序退出时会自动恢复原始代理设置
- 可以使用 Ctrl+C 安全退出
### 3. 透明代理模式 (计划实现)
```bash
# 需要 root 权限
sudo ./bin/wormhole-client -mode transparent
```
## 命令行选项
```bash
./bin/wormhole-client [选项]
选项:
-config string
配置文件路径 (默认 "configs/client.yaml")
-mode string
客户端模式: http, global, transparent (默认 "http")
-version
显示版本信息
```
## 使用场景
### 场景 1: 浏览器代理
1. 启动 HTTP 代理模式
2. 在浏览器中配置 HTTP 代理为 `127.0.0.1:8080`
3. 访问被墙网站
### 场景 2: 全局科学上网
1. 以管理员权限启动全局代理模式
2. 系统所有网络请求自动通过代理
3. 支持 DNS 代理,解决 DNS 污染问题
### 场景 3: 开发调试
1. 使用 HTTP 代理模式
2. 配置开发工具使用代理
3. 调试网络请求和响应
## 故障排除
### 连接失败
1. 检查 SOCKS5 服务器地址和端口
2. 验证用户名和密码
3. 确认网络连通性
### 权限问题
```bash
# macOS 全局代理需要管理员权限
sudo ./bin/wormhole-client -mode global
# 检查系统代理设置
networksetup -getwebproxy Wi-Fi
```
### 日志调试
```bash
# 启用详细日志
./bin/wormhole-client -mode http -config configs/debug.yaml
```
在配置文件中设置 `logLevel: debug` 获取详细日志信息。
## 安全注意事项
1. **配置文件安全**: 保护好包含密码的配置文件
2. **管理员权限**: 仅在需要时使用管理员权限
3. **网络安全**: 确保 SOCKS5 服务器的安全性
4. **DNS 泄露**: 使用 DNS 代理防止 DNS 泄露
## 开发和贡献
### 编译和测试
```bash
# 编译
make build
# 运行测试
make test
# 检查代码质量
make lint
# 清理构建文件
make clean
```
### 代码结构
```
├── cmd/wormhole-client/ # 主程序入口
├── internal/ # 内部模块
│ ├── client/ # 客户端核心逻辑
│ ├── config/ # 配置解析
│ ├── proxy/ # SOCKS5 代理实现
│ └── system/ # 系统代理管理
├── pkg/ # 公共模块
│ ├── dns/ # DNS 代理
│ └── logger/ # 日志模块
└── configs/ # 配置文件
```
## 许可证
本项目采用 MIT 许可证,详情请参见 LICENSE 文件。

@ -1,3 +1,5 @@
module github.com/azoic/wormhole-client
go 1.21
require gopkg.in/yaml.v3 v3.0.1

@ -0,0 +1,4 @@
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

@ -2,22 +2,64 @@ package client
import (
"fmt"
"net/http"
"net/url"
"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
mode string
config *config.Config
socks5Proxy *proxy.SOCKS5Proxy
dnsProxy *dns.DNSProxy
systemProxyMgr *system.SystemProxyManager
}
func NewClient(mode string) *Client {
return &Client{mode: mode}
return &Client{
mode: mode,
systemProxyMgr: system.NewSystemProxyManager(),
}
}
func (c *Client) Start(configPath string) error {
fmt.Printf("🎯 Starting client in %s mode...\n", c.mode)
fmt.Printf("📁 Loading config from: %s\n", configPath)
// 加载配置
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,
)
// 设置信号处理
c.setupSignalHandler()
switch c.mode {
case "http":
return c.startHTTPProxy()
@ -31,39 +73,98 @@ func (c *Client) Start(configPath string) error {
}
func (c *Client) startHTTPProxy() error {
fmt.Println("🌐 Starting HTTP proxy mode...")
fmt.Println("💡 Setting up HTTP proxy on :8080")
// 简单的HTTP代理实现示例
proxyURL, _ := url.Parse("socks5://127.0.0.1:1080")
server := &http.Server{
Addr: ":8080",
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("📝 Proxy request: %s %s\n", r.Method, r.URL)
w.WriteHeader(http.StatusOK)
w.Write([]byte(fmt.Sprintf("Proxied via %s", proxyURL)))
}),
}
fmt.Println("✅ HTTP proxy started on :8080")
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()
}
func (c *Client) startGlobalProxy() error {
fmt.Println("🌍 Starting global proxy mode...")
fmt.Println("💡 This will configure system-wide proxy settings")
fmt.Println("⚠ Requires administrator privileges")
// TODO: 实现全局代理设置
select {} // 保持运行
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() {
if err := server.ListenAndServe(); err != nil {
logger.Error("HTTP proxy server failed: %v", err)
}
}()
// 设置系统代理
httpProxy := fmt.Sprintf("127.0.0.1:%d", c.config.Proxy.LocalPort)
httpsProxy := 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: Set HTTP/HTTPS proxy to %s", httpProxy)
} 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)
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("🎉 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)
}
// 保持运行
select {}
}
func (c *Client) startTransparentProxy() error {
fmt.Println("🔍 Starting transparent proxy mode...")
fmt.Println("💡 This will intercept network traffic transparently")
fmt.Println("⚠ Requires root privileges and iptables support")
// TODO: 实现透明代理
select {} // 保持运行
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")
}
func (c *Client) setupSignalHandler() {
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
go func() {
<-sigChan
logger.Info("🛑 Shutting down...")
c.cleanup()
os.Exit(0)
}()
}
func (c *Client) cleanup() {
// 恢复系统代理设置
if c.systemProxyMgr != nil {
if err := c.systemProxyMgr.RestoreProxy(); err != nil {
logger.Error("Failed to restore system proxy: %v", err)
}
}
// 停止DNS代理
if c.dnsProxy != nil {
if err := c.dnsProxy.Stop(); err != nil {
logger.Error("Failed to stop DNS proxy: %v", err)
}
}
logger.Info("✅ Cleanup completed")
}

@ -0,0 +1,67 @@
package client
import (
"os"
"testing"
)
func TestNewClient(t *testing.T) {
client := NewClient("http")
if client == nil {
t.Fatal("NewClient returned nil")
}
if client.mode != "http" {
t.Errorf("Expected mode 'http', got '%s'", client.mode)
}
if client.systemProxyMgr == nil {
t.Error("SystemProxyManager should be initialized")
}
}
func TestClientStart_InvalidConfig(t *testing.T) {
client := NewClient("http")
// 测试不存在的配置文件
err := client.Start("nonexistent.yaml")
if err == nil {
t.Error("Expected error for nonexistent config file")
}
}
func TestClientStart_InvalidMode(t *testing.T) {
// 创建临时配置文件
tmpFile, err := os.CreateTemp("", "test_config_*.yaml")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmpFile.Name())
// 写入基本配置
configContent := `
serviceType: client
server:
address: 127.0.0.1
port: 1080
username: test
password: test
proxy:
mode: http
localPort: 8080
logLevel: info
timeout: 30s
`
if _, err := tmpFile.WriteString(configContent); err != nil {
t.Fatal(err)
}
tmpFile.Close()
// 测试无效模式
client := NewClient("invalid_mode")
err = client.Start(tmpFile.Name())
if err == nil {
t.Error("Expected error for invalid mode")
}
}

@ -0,0 +1,107 @@
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) {
data, err := ioutil.ReadFile(configPath)
if err != nil {
return nil, fmt.Errorf("failed to read config file: %v", err)
}
var config Config
if err := yaml.Unmarshal(data, &config); err != nil {
return nil, fmt.Errorf("failed to parse config file: %v", err)
}
// 设置默认值
if config.LogLevel == "" {
config.LogLevel = "info"
}
if config.Timeout == 0 {
config.Timeout = 30 * time.Second
}
if config.Proxy.LocalPort == 0 {
config.Proxy.LocalPort = 8080
}
return &config, nil
}
// GetServerAddr 获取服务器地址
func (c *Config) GetServerAddr() string {
return fmt.Sprintf("%s:%d", c.Server.Address, c.Server.Port)
}
// Validate 验证配置
func (c *Config) Validate() error {
if c.Server.Address == "" {
return fmt.Errorf("server address is required")
}
if c.Server.Port <= 0 || c.Server.Port > 65535 {
return fmt.Errorf("invalid server port: %d", c.Server.Port)
}
validModes := map[string]bool{"http": true, "global": true, "transparent": true}
if !validModes[c.Proxy.Mode] {
return fmt.Errorf("invalid proxy mode: %s", c.Proxy.Mode)
}
return nil
}

@ -0,0 +1,217 @@
package proxy
import (
"fmt"
"io"
"net"
"net/http"
"time"
"github.com/azoic/wormhole-client/pkg/logger"
)
// SOCKS5Proxy SOCKS5代理客户端
type SOCKS5Proxy struct {
serverAddr string
username string
password string
timeout time.Duration
}
// NewSOCKS5Proxy 创建SOCKS5代理客户端
func NewSOCKS5Proxy(serverAddr, username, password string, timeout time.Duration) *SOCKS5Proxy {
return &SOCKS5Proxy{
serverAddr: serverAddr,
username: username,
password: password,
timeout: timeout,
}
}
// CreateHTTPProxy 创建HTTP代理服务器
func (p *SOCKS5Proxy) CreateHTTPProxy(localPort int) *http.Server {
proxyHandler := &httpProxyHandler{
socks5Proxy: p,
}
server := &http.Server{
Addr: fmt.Sprintf(":%d", localPort),
Handler: proxyHandler,
}
return server
}
// httpProxyHandler HTTP代理处理器
type httpProxyHandler struct {
socks5Proxy *SOCKS5Proxy
}
func (h *httpProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
logger.Info("Processing request: %s %s", r.Method, r.URL.String())
if r.Method == http.MethodConnect {
h.handleHTTPSProxy(w, r)
} else {
h.handleHTTPProxy(w, r)
}
}
// handleHTTPSProxy 处理HTTPS代理请求 (CONNECT方法)
func (h *httpProxyHandler) handleHTTPSProxy(w http.ResponseWriter, r *http.Request) {
destConn, err := h.socks5Proxy.DialTCP(r.Host)
if err != nil {
logger.Error("Failed to connect via SOCKS5: %v", err)
http.Error(w, "Bad Gateway", http.StatusBadGateway)
return
}
defer destConn.Close()
w.WriteHeader(http.StatusOK)
hijacker, ok := w.(http.Hijacker)
if !ok {
logger.Error("Hijacking not supported")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
clientConn, _, err := hijacker.Hijack()
if err != nil {
logger.Error("Failed to hijack connection: %v", err)
return
}
defer clientConn.Close()
// 双向数据转发
go h.copyData(clientConn, destConn)
h.copyData(destConn, clientConn)
}
// handleHTTPProxy 处理HTTP代理请求
func (h *httpProxyHandler) handleHTTPProxy(w http.ResponseWriter, r *http.Request) {
// 通过SOCKS5连接到目标服务器
destConn, err := h.socks5Proxy.DialTCP(r.Host)
if err != nil {
logger.Error("Failed to connect via SOCKS5: %v", err)
http.Error(w, "Bad Gateway", http.StatusBadGateway)
return
}
defer destConn.Close()
// 发送HTTP请求
if err := r.Write(destConn); err != nil {
logger.Error("Failed to write request: %v", err)
http.Error(w, "Bad Gateway", http.StatusBadGateway)
return
}
// 读取响应并返回给客户端
if _, err := io.Copy(w, destConn); err != nil {
logger.Error("Failed to copy response: %v", err)
}
}
// DialTCP 通过SOCKS5连接到目标地址
func (p *SOCKS5Proxy) DialTCP(address string) (net.Conn, error) {
// 连接到SOCKS5代理服务器
conn, err := net.DialTimeout("tcp", p.serverAddr, p.timeout)
if err != nil {
return nil, fmt.Errorf("failed to connect to SOCKS5 server: %v", err)
}
// 执行SOCKS5握手
if err := p.performSOCKS5Handshake(conn, address); err != nil {
conn.Close()
return nil, fmt.Errorf("SOCKS5 handshake failed: %v", err)
}
logger.Debug("Successfully connected to %s via SOCKS5 proxy", address)
return conn, nil
}
// performSOCKS5Handshake 执行SOCKS5握手协议
func (p *SOCKS5Proxy) performSOCKS5Handshake(conn net.Conn, targetAddr string) error {
// 简化的SOCKS5握手实现
// 实际项目中应该完整实现SOCKS5协议
// 发送认证方法选择
authMethods := []byte{0x05, 0x01, 0x02} // 版本5,1个方法,用户名密码认证
if _, err := conn.Write(authMethods); err != nil {
return fmt.Errorf("failed to send auth methods: %v", err)
}
// 读取服务器响应
response := make([]byte, 2)
if _, err := conn.Read(response); err != nil {
return fmt.Errorf("failed to read auth response: %v", err)
}
if response[0] != 0x05 || response[1] != 0x02 {
return fmt.Errorf("unsupported authentication method")
}
// 发送用户名密码
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 {
return fmt.Errorf("failed to send credentials: %v", err)
}
// 读取认证结果
authResult := make([]byte, 2)
if _, err := conn.Read(authResult); err != nil {
return fmt.Errorf("failed to read auth result: %v", err)
}
if authResult[1] != 0x00 {
return fmt.Errorf("authentication failed")
}
// 发送连接请求
host, portStr, err := net.SplitHostPort(targetAddr)
if err != nil {
return fmt.Errorf("invalid target address: %v", err)
}
// 简化的连接请求(实际实现应该支持域名解析)
connectReq := []byte{0x05, 0x01, 0x00, 0x03} // 版本,连接命令,保留字段,域名类型
connectReq = append(connectReq, byte(len(host)))
connectReq = append(connectReq, []byte(host)...)
// 添加端口
portNum := 80 // 默认HTTP端口
if portStr != "" {
// 简化处理:如果端口是443则用443,否则用80
if portStr == "443" {
portNum = 443
}
}
connectReq = append(connectReq, byte(portNum>>8), byte(portNum&0xFF))
if _, err := conn.Write(connectReq); err != nil {
return fmt.Errorf("failed to send connect request: %v", err)
}
// 读取连接响应
connectResp := make([]byte, 10) // 简化的响应读取
if _, err := conn.Read(connectResp); err != nil {
return fmt.Errorf("failed to read connect response: %v", err)
}
if connectResp[1] != 0x00 {
return fmt.Errorf("connection failed, status: %d", connectResp[1])
}
return nil
}
// copyData 数据复制
func (h *httpProxyHandler) copyData(dst, src net.Conn) {
defer dst.Close()
defer src.Close()
io.Copy(dst, src)
}

@ -0,0 +1,224 @@
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 {
return &SystemProxyManager{
originalSettings: make(map[string]string),
}
}
// SetGlobalProxy 设置全局代理
func (s *SystemProxyManager) SetGlobalProxy(httpProxy, httpsProxy, socksProxy string) error {
switch runtime.GOOS {
case "darwin":
return s.setMacOSProxy(httpProxy, httpsProxy, socksProxy)
case "windows":
return s.setWindowsProxy(httpProxy, httpsProxy, socksProxy)
case "linux":
return s.setLinuxProxy(httpProxy, httpsProxy, socksProxy)
default:
return fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
}
}
// RestoreProxy 恢复原始代理设置
func (s *SystemProxyManager) RestoreProxy() error {
switch runtime.GOOS {
case "darwin":
return s.restoreMacOSProxy()
case "windows":
return s.restoreWindowsProxy()
case "linux":
return s.restoreLinuxProxy()
default:
return fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
}
}
// macOS 代理设置
func (s *SystemProxyManager) setMacOSProxy(httpProxy, httpsProxy, socksProxy string) error {
logger.Info("Setting macOS system proxy...")
// 获取网络服务名称
networkService, err := s.getMacOSNetworkService()
if err != nil {
return fmt.Errorf("failed to get network service: %v", err)
}
// 保存原始设置
if err := s.saveMacOSOriginalSettings(networkService); err != nil {
logger.Warn("Failed to save original proxy settings: %v", err)
}
// 设置HTTP代理
if httpProxy != "" {
if err := s.runCommand("networksetup", "-setwebproxy", networkService,
s.parseProxyHost(httpProxy), s.parseProxyPort(httpProxy)); 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)
}
}
// 设置HTTPS代理
if httpsProxy != "" {
if err := s.runCommand("networksetup", "-setsecurewebproxy", networkService,
s.parseProxyHost(httpsProxy), s.parseProxyPort(httpsProxy)); 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)
}
}
// 设置SOCKS代理
if socksProxy != "" {
if err := s.runCommand("networksetup", "-setsocksfirewallproxy", networkService,
s.parseProxyHost(socksProxy), s.parseProxyPort(socksProxy)); 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.Info("macOS system proxy configured successfully")
return nil
}
// 获取macOS网络服务名称
func (s *SystemProxyManager) getMacOSNetworkService() (string, error) {
output, err := exec.Command("networksetup", "-listallnetworkservices").Output()
if err != nil {
return "", err
}
lines := strings.Split(string(output), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line != "" && !strings.HasPrefix(line, "*") && !strings.Contains(line, "An asterisk") {
// 优先选择Wi-Fi,否则选择第一个可用的服务
if strings.Contains(strings.ToLower(line), "wi-fi") || strings.Contains(strings.ToLower(line), "wifi") {
return line, nil
}
}
}
// 如果没找到Wi-Fi,返回第一个可用的服务
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 network service found")
}
// 保存macOS原始代理设置
func (s *SystemProxyManager) saveMacOSOriginalSettings(networkService string) error {
// 保存HTTP代理状态
if output, err := exec.Command("networksetup", "-getwebproxy", networkService).Output(); err == nil {
s.originalSettings["http_proxy"] = string(output)
}
// 保存HTTPS代理状态
if output, err := exec.Command("networksetup", "-getsecurewebproxy", networkService).Output(); err == nil {
s.originalSettings["https_proxy"] = string(output)
}
// 保存SOCKS代理状态
if output, err := exec.Command("networksetup", "-getsocksfirewallproxy", networkService).Output(); err == nil {
s.originalSettings["socks_proxy"] = string(output)
}
s.originalSettings["network_service"] = 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")
}
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
}
// Windows 代理设置(简化实现)
func (s *SystemProxyManager) setWindowsProxy(httpProxy, httpsProxy, socksProxy string) error {
logger.Warn("Windows proxy setting not fully implemented")
return fmt.Errorf("Windows proxy setting not implemented yet")
}
func (s *SystemProxyManager) restoreWindowsProxy() error {
logger.Warn("Windows proxy restoration not fully implemented")
return nil
}
// Linux 代理设置(简化实现)
func (s *SystemProxyManager) setLinuxProxy(httpProxy, httpsProxy, socksProxy string) error {
logger.Warn("Linux proxy setting not fully implemented")
return fmt.Errorf("Linux proxy setting not implemented yet")
}
func (s *SystemProxyManager) restoreLinuxProxy() error {
logger.Warn("Linux proxy restoration not fully implemented")
return 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 err
}
return nil
}
func (s *SystemProxyManager) parseProxyHost(proxy string) string {
// 简单解析,格式: host:port
parts := strings.Split(proxy, ":")
if len(parts) >= 1 {
return parts[0]
}
return proxy
}
func (s *SystemProxyManager) parseProxyPort(proxy string) string {
// 简单解析,格式: host:port
parts := strings.Split(proxy, ":")
if len(parts) >= 2 {
return parts[1]
}
return "8080" // 默认端口
}

@ -0,0 +1,252 @@
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 {
return &DNSProxy{
upstreamDNS: upstreamDNS,
localPort: localPort,
cache: &dnsCache{
entries: make(map[string]*cacheEntry),
},
}
}
// Start 启动DNS代理服务器
func (d *DNSProxy) Start() error {
d.mutex.Lock()
defer d.mutex.Unlock()
if d.running {
return fmt.Errorf("DNS proxy is already running")
}
addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", d.localPort))
if err != nil {
return fmt.Errorf("failed to resolve UDP address: %v", err)
}
d.server, err = net.ListenUDP("udp", addr)
if err != nil {
return fmt.Errorf("failed to start DNS proxy server: %v", err)
}
d.running = true
logger.Info("DNS proxy started on port %d", d.localPort)
// 启动清理缓存的goroutine
go d.cleanupCache()
// 处理DNS请求
go d.handleRequests()
return nil
}
// Stop 停止DNS代理服务器
func (d *DNSProxy) Stop() error {
d.mutex.Lock()
defer d.mutex.Unlock()
if !d.running {
return nil
}
d.running = false
if d.server != nil {
d.server.Close()
}
logger.Info("DNS proxy stopped")
return nil
}
// handleRequests 处理DNS请求
func (d *DNSProxy) handleRequests() {
buffer := make([]byte, 512) // DNS消息最大512字节(UDP)
for d.isRunning() {
n, clientAddr, err := d.server.ReadFromUDP(buffer)
if err != nil {
if d.isRunning() {
logger.Error("Failed to read DNS request: %v", err)
}
continue
}
go d.processRequest(buffer[:n], clientAddr)
}
}
// processRequest 处理单个DNS请求
func (d *DNSProxy) processRequest(request []byte, clientAddr *net.UDPAddr) {
// 简单的DNS请求解析
domain := d.extractDomain(request)
logger.Debug("DNS request for domain: %s", domain)
// 检查缓存
if cachedResponse := d.getFromCache(domain); cachedResponse != nil {
logger.Debug("Serving %s from cache", domain)
d.server.WriteToUDP(cachedResponse, clientAddr)
return
}
// 转发到上游DNS服务器
response, err := d.forwardToUpstream(request)
if err != nil {
logger.Error("Failed to forward DNS request: %v", err)
return
}
// 缓存响应
d.addToCache(domain, response)
// 返回响应给客户端
d.server.WriteToUDP(response, clientAddr)
}
// extractDomain 从DNS请求中提取域名(简化实现)
func (d *DNSProxy) extractDomain(request []byte) string {
if len(request) < 12 {
return ""
}
// 跳过DNS头部(12字节)
offset := 12
var domain strings.Builder
for offset < len(request) {
length := int(request[offset])
if length == 0 {
break
}
offset++
if offset+length > len(request) {
break
}
if domain.Len() > 0 {
domain.WriteByte('.')
}
domain.Write(request[offset : offset+length])
offset += length
}
return domain.String()
}
// forwardToUpstream 转发DNS请求到上游服务器
func (d *DNSProxy) forwardToUpstream(request []byte) ([]byte, error) {
conn, err := net.Dial("udp", d.upstreamDNS)
if err != nil {
return nil, fmt.Errorf("failed to connect to upstream DNS: %v", err)
}
defer conn.Close()
// 设置超时
conn.SetDeadline(time.Now().Add(5 * time.Second))
// 发送请求
if _, err := conn.Write(request); err != nil {
return nil, fmt.Errorf("failed to send DNS request: %v", err)
}
// 读取响应
response := make([]byte, 512)
n, err := conn.Read(response)
if err != nil {
return nil, fmt.Errorf("failed to read DNS response: %v", err)
}
return response[:n], nil
}
// getFromCache 从缓存获取DNS响应
func (d *DNSProxy) getFromCache(domain string) []byte {
d.cache.mutex.RLock()
defer d.cache.mutex.RUnlock()
entry, exists := d.cache.entries[domain]
if !exists || time.Now().After(entry.expiry) {
return nil
}
return entry.response
}
// addToCache 添加DNS响应到缓存
func (d *DNSProxy) addToCache(domain string, response []byte) {
d.cache.mutex.Lock()
defer d.cache.mutex.Unlock()
// 设置缓存过期时间(5分钟)
expiry := time.Now().Add(5 * time.Minute)
d.cache.entries[domain] = &cacheEntry{
response: make([]byte, len(response)),
expiry: expiry,
}
copy(d.cache.entries[domain].response, response)
}
// cleanupCache 清理过期的缓存条目
func (d *DNSProxy) cleanupCache() {
ticker := time.NewTicker(1 * time.Minute)
defer ticker.Stop()
for {
select {
case <-ticker.C:
if !d.isRunning() {
return
}
d.cache.mutex.Lock()
now := time.Now()
for domain, entry := range d.cache.entries {
if now.After(entry.expiry) {
delete(d.cache.entries, domain)
}
}
d.cache.mutex.Unlock()
}
}
}
// isRunning 检查DNS代理是否在运行
func (d *DNSProxy) isRunning() bool {
d.mutex.RLock()
defer d.mutex.RUnlock()
return d.running
}

@ -0,0 +1,121 @@
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() {
defaultLogger = New("INFO")
}
// New 创建新的日志记录器
func New(levelStr string) *Logger {
level := parseLogLevel(levelStr)
logger := log.New(os.Stdout, "", 0)
return &Logger{
level: level,
logger: logger,
}
}
// SetLevel 设置日志级别
func SetLevel(levelStr string) {
defaultLogger.level = parseLogLevel(levelStr)
}
func parseLogLevel(levelStr string) LogLevel {
switch strings.ToUpper(levelStr) {
case "DEBUG":
return DEBUG
case "INFO":
return INFO
case "WARN", "WARNING":
return WARN
case "ERROR":
return ERROR
default:
return INFO
}
}
func (l *Logger) log(level LogLevel, format string, args ...interface{}) {
if level < l.level {
return
}
levelStr := ""
switch level {
case DEBUG:
levelStr = "DEBUG"
case INFO:
levelStr = "INFO"
case WARN:
levelStr = "WARN"
case ERROR:
levelStr = "ERROR"
}
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)
}
// Debug 调试日志
func (l *Logger) Debug(format string, args ...interface{}) {
l.log(DEBUG, format, args...)
}
// Info 信息日志
func (l *Logger) Info(format string, args ...interface{}) {
l.log(INFO, format, args...)
}
// Warn 警告日志
func (l *Logger) Warn(format string, args ...interface{}) {
l.log(WARN, format, args...)
}
// Error 错误日志
func (l *Logger) Error(format string, args ...interface{}) {
l.log(ERROR, format, args...)
}
// 全局日志函数
func Debug(format string, args ...interface{}) {
defaultLogger.Debug(format, args...)
}
func Info(format string, args ...interface{}) {
defaultLogger.Info(format, args...)
}
func Warn(format string, args ...interface{}) {
defaultLogger.Warn(format, args...)
}
func Error(format string, args ...interface{}) {
defaultLogger.Error(format, args...)
}
Loading…
Cancel
Save