From e8a63b28e2467dfa8dee89dcf322ffaea884f7ce Mon Sep 17 00:00:00 2001 From: iuu <2167162990@qq.com> Date: Fri, 10 Jan 2025 14:31:56 +0800 Subject: [PATCH] 1 --- .gitignore | 3 +- direct.txt => direct.txt- | 3 + pkg/proxy/directlist.go | 94 ----------------- pkg/proxy/proxylist.go | 215 ++++++++++++++++++++++++++++++++++++++ pkg/proxy/server.go | 132 +++++++++++++---------- proxy.txt | 35 +++++++ 6 files changed, 329 insertions(+), 153 deletions(-) rename direct.txt => direct.txt- (99%) delete mode 100644 pkg/proxy/directlist.go create mode 100644 pkg/proxy/proxylist.go create mode 100644 proxy.txt diff --git a/.gitignore b/.gitignore index b2f38b4..8048ff2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -proxy.log \ No newline at end of file +proxy.log +proxy-2025-01-10T06-26-50.064.log \ No newline at end of file diff --git a/direct.txt b/direct.txt- similarity index 99% rename from direct.txt rename to direct.txt- index cbf02fd..3f551a1 100644 --- a/direct.txt +++ b/direct.txt- @@ -4,6 +4,9 @@ # 自定义域名 *.19year.cn + + + # 常用国内网站 *.baidu.com *.qq.com diff --git a/pkg/proxy/directlist.go b/pkg/proxy/directlist.go deleted file mode 100644 index 03ac55f..0000000 --- a/pkg/proxy/directlist.go +++ /dev/null @@ -1,94 +0,0 @@ -package proxy - -import ( - "bufio" - "os" - "path/filepath" - "strings" - "sync" - - "s5/pkg/logger" -) - -// DirectList 直连域名管理器 -type DirectList struct { - patterns []string // 域名匹配模式 - mu sync.RWMutex // 读写锁 -} - -// NewDirectList 创建新的直连域名管理器 -func NewDirectList(filename string) *DirectList { - dl := &DirectList{} - if err := dl.Load(filename); err != nil { - log := logger.Get() - log.Errorf("[DIRECT] 加载直连域名列表失败: %v", err) - } - return dl -} - -// Load 加载直连域名列表 -func (dl *DirectList) Load(filename string) error { - log := logger.Get() - log.Debugf("[DIRECT] 开始加载直连域名列表...") - - // 获取绝对路径 - absPath, err := filepath.Abs(filename) - if err != nil { - return err - } - - file, err := os.Open(absPath) - if err != nil { - return err - } - defer file.Close() - - var patterns []string - scanner := bufio.NewScanner(file) - for scanner.Scan() { - pattern := strings.TrimSpace(scanner.Text()) - if pattern == "" || strings.HasPrefix(pattern, "#") { - continue - } - patterns = append(patterns, pattern) - } - - dl.mu.Lock() - dl.patterns = patterns - dl.mu.Unlock() - - log.Infof("[DIRECT] 直连域名列表加载完成,共 %d 条规则", len(patterns)) - //for _, pattern := range patterns { - //log.Debugf("[DIRECT] - %s", pattern) - //} - - return scanner.Err() -} - -// Match 检查域名是否匹配直连列表 -func (dl *DirectList) Match(domain string) bool { - dl.mu.RLock() - defer dl.mu.RUnlock() - - log := logger.Get() - log.Debugf("[DIRECT] 检查域名: %s", domain) - - domain = strings.ToLower(domain) - for _, pattern := range dl.patterns { - if matchDomain(domain, pattern) { - log.Debugf("[DIRECT] - 匹配规则: %s", pattern) - return true - } - } - - return false -} - -// matchDomain 检查域名是否匹配模式 -func matchDomain(domain, pattern string) bool { - if pattern[0] == '*' { - suffix := pattern[1:] - return strings.HasSuffix(domain, suffix) - } - return domain == pattern -} diff --git a/pkg/proxy/proxylist.go b/pkg/proxy/proxylist.go new file mode 100644 index 0000000..f0b8002 --- /dev/null +++ b/pkg/proxy/proxylist.go @@ -0,0 +1,215 @@ +package proxy + +import ( + "bufio" + "net" + "os" + "path/filepath" + "strings" + "sync" + + "s5/pkg/logger" +) + +// ProxyList 直连域名管理器 +type ProxyList struct { + patterns []string // 域名匹配模式 + mu sync.RWMutex // 读写锁 +} + +// NewProxyList 创建新的直连域名管理器 +func NewProxyList(filename string) *ProxyList { + dl := &ProxyList{} + if err := dl.Load(filename); err != nil { + log := logger.Get() + log.Errorf("[PROXY] 加载直连域名列表失败: %v", err) + } + return dl +} + +// Load 加载直连域名列表 +func (dl *ProxyList) Load(filename string) error { + log := logger.Get() + log.Debugf("[PROXY] =====================================================") + log.Debugf("[PROXY] 开始加载代理域名列表...") + log.Debugf("[PROXY] 配置文件: %s", filename) + + // 获取绝对路径 + absPath, err := filepath.Abs(filename) + if err != nil { + log.Errorf("[PROXY] 解析文件路径失败: %v", err) + log.Debugf("[PROXY] =====================================================") + return err + } + log.Debugf("[PROXY] 绝对路径: %s", absPath) + + // 检查文件是否存在 + if _, err := os.Stat(absPath); os.IsNotExist(err) { + log.Errorf("[PROXY] 文件不存在: %s", absPath) + log.Debugf("[PROXY] 将创建默认配置文件") + + // 创建默认配置文件 + if err := dl.createDefaultConfig(absPath); err != nil { + log.Errorf("[PROXY] 创建默认配置文件失败: %v", err) + log.Debugf("[PROXY] =====================================================") + return err + } + } + + file, err := os.Open(absPath) + if err != nil { + log.Errorf("[PROXY] 打开文件失败: %v", err) + log.Debugf("[PROXY] =====================================================") + return err + } + defer file.Close() + + var patterns []string + var skipped, added int + scanner := bufio.NewScanner(file) + lineNum := 0 + + log.Debugf("[PROXY] 开始解析规则:") + for scanner.Scan() { + lineNum++ + line := scanner.Text() + pattern := strings.TrimSpace(line) + + if pattern == "" { + log.Debugf("[PROXY] 行 %d: 空行,已跳过", lineNum) + skipped++ + continue + } + if strings.HasPrefix(pattern, "#") { + log.Debugf("[PROXY] 行 %d: 注释行,已跳过: %s", lineNum, line) + skipped++ + continue + } + + // 验证规则格式 + if strings.HasPrefix(pattern, "*.") { + log.Debugf("[PROXY] 行 %d: 添加通配符规则: %s", lineNum, pattern) + } else if strings.Contains(pattern, "*") { + log.Warnf("[PROXY] 行 %d: 不支持的通配符位置,规则可能无法正常工作: %s", lineNum, pattern) + } else { + log.Debugf("[PROXY] 行 %d: 添加精确匹配规则: %s", lineNum, pattern) + } + + patterns = append(patterns, pattern) + added++ + } + + if err := scanner.Err(); err != nil { + log.Errorf("[PROXY] 读取文件失败: %v", err) + log.Debugf("[PROXY] =====================================================") + return err + } + + dl.mu.Lock() + dl.patterns = patterns + dl.mu.Unlock() + + log.Debugf("[PROXY] 规则统计:") + log.Debugf("[PROXY] - 总行数: %d", lineNum) + log.Debugf("[PROXY] - 跳过行数: %d", skipped) + log.Debugf("[PROXY] - 有效规则: %d", added) + log.Infof("[PROXY] 代理域名列表加载完成") + log.Debugf("[PROXY] =====================================================") + + return nil +} + +// createDefaultConfig 创建默认配置文件 +func (dl *ProxyList) createDefaultConfig(path string) error { + defaultConfig := `# 代理域名列表 +# 每行一个域名,支持通配符 * +# 以 # 开头的行为注释 + +# 需要代理的域名 +*.google.com +*.youtube.com +*.facebook.com +*.twitter.com +*.github.com +*.githubusercontent.com +*.cloudflare.com +*.wikipedia.org +*.medium.com +*.reddit.com + +# 国外服务 +*.netflix.com +*.amazon.com +*.microsoft.com +*.apple.com +*.dropbox.com +*.telegram.org +*.whatsapp.com +*.instagram.com +*.twitch.tv +*.spotify.com + +# 开发相关 +*.docker.com +*.npmjs.com +*.pypi.org +*.rubygems.org +*.maven.org` + + return os.WriteFile(path, []byte(defaultConfig), 0644) +} + +// Match 检查域名是否匹配直连列表 +func (dl *ProxyList) Match(domain string) bool { + dl.mu.RLock() + defer dl.mu.RUnlock() + + log := logger.Get() + log.Debugf("[PROXY] =====================================================") + log.Debugf("[PROXY] 开始检查域名: %s", domain) + + // 处理特殊情况 + if domain == "" { + log.Warnf("[PROXY] 域名为空") + log.Debugf("[PROXY] =====================================================") + return false + } + + domain = strings.ToLower(domain) + log.Debugf("[PROXY] 规则匹配:") + log.Debugf("[PROXY] - 原始域名: %s", domain) + log.Debugf("[PROXY] - 处理后域名: %s", domain) + + // 检查是否是IP地址 + if ip := net.ParseIP(domain); ip != nil { + log.Debugf("[PROXY] - 域名是IP地址: %s", ip) + log.Debugf("[PROXY] =====================================================") + return false + } + + // 遍历所有规则进行匹配 + for i, pattern := range dl.patterns { + log.Debugf("[PROXY] 检查规则 #%d: %s", i+1, pattern) + if matchDomain(domain, pattern) { + log.Debugf("[PROXY] - 匹配成功: %s -> %s", domain, pattern) + log.Debugf("[PROXY] =====================================================") + return true + } + } + + log.Debugf("[PROXY] - 未匹配任何规则") + log.Debugf("[PROXY] =====================================================") + return false +} + +// matchDomain 检查域名是否匹配模式 +func matchDomain(domain, pattern string) bool { + if pattern[0] == '*' { + suffix := pattern[1:] + matched := strings.HasSuffix(domain, suffix) + if matched { + return true + } + } + return domain == pattern +} diff --git a/pkg/proxy/server.go b/pkg/proxy/server.go index 0c117dd..2801b5a 100644 --- a/pkg/proxy/server.go +++ b/pkg/proxy/server.go @@ -3,6 +3,7 @@ package proxy import ( "context" + "encoding/base64" "encoding/binary" "errors" "fmt" @@ -13,7 +14,6 @@ import ( "sync" "sync/atomic" "time" - "encoding/base64" "s5/pkg/config" "s5/pkg/logger" @@ -28,14 +28,14 @@ type ServerConfig struct { // Server 代理服务器实例 type Server struct { - config ServerConfig // 服务器配置 - socks5Listener net.Listener // SOCKS5 监听器 - httpServer *http.Server // HTTP 服务器 - done chan struct{} // 关闭信号 - activeConns sync.WaitGroup // 活动连接计数 - upstreamDialer *UpstreamDialer // 上游代理拨号器 - shutdownTimeout time.Duration // 关闭超时时间 - directList *DirectList // 直连域名管理器 + config ServerConfig // 服务器配置 + socks5Listener net.Listener // SOCKS5 监听器 + httpServer *http.Server // HTTP 服务器 + done chan struct{} // 关闭信号 + activeConns sync.WaitGroup // 活动连接计数 + upstreamDialer *UpstreamDialer // 上游代理拨号器 + shutdownTimeout time.Duration // 关闭超时时间 + directList *ProxyList // 直连域名管理器 } // NewServer 创建新的代理服务器实例 @@ -52,11 +52,11 @@ func NewServer(config ServerConfig, upstreamConfig config.UpstreamConfig) *Serve } return &Server{ - config: config, - done: make(chan struct{}), - upstreamDialer: upstreamDialer, + config: config, + done: make(chan struct{}), + upstreamDialer: upstreamDialer, shutdownTimeout: 30 * time.Second, - directList: NewDirectList("direct.txt"), + directList: NewProxyList("proxy.txt"), } } @@ -193,7 +193,7 @@ func (s *Server) handleSocks5Connection(client net.Conn) { log := logger.Get() log.Debugf("[SOCKS5] =====================================================") log.Debugf("[SOCKS5] 收到新的SOCKS5连接") - + clientAddr := client.RemoteAddr().String() startTime := time.Now() upBytes := &atomic.Int64{} @@ -212,7 +212,7 @@ func (s *Server) handleSocks5Connection(client net.Conn) { log.Infof("[SOCKS5] - 连接时长: %v", duration.Round(time.Millisecond)) log.Infof("[SOCKS5] - 上行流量: %s", formatBytes(upBytes.Load())) log.Infof("[SOCKS5] - 下行流量: %s", formatBytes(downBytes.Load())) - log.Infof("[SOCKS5] - 总流量: %s", formatBytes(upBytes.Load() + downBytes.Load())) + log.Infof("[SOCKS5] - 总流量: %s", formatBytes(upBytes.Load()+downBytes.Load())) log.Infof("[SOCKS5] - 平均速度: %s/s", formatBytes(int64(float64(upBytes.Load()+downBytes.Load())/duration.Seconds()))) log.Debugf("[SOCKS5] =====================================================") }() @@ -278,7 +278,7 @@ func (s *Server) handleHTTP(w http.ResponseWriter, r *http.Request) { s.config.HttpAddr, s.upstreamDialer.config.Server, r.Host) - + clientAddr := r.RemoteAddr startTime := time.Now() upBytes := &atomic.Int64{} @@ -297,7 +297,7 @@ func (s *Server) handleHTTP(w http.ResponseWriter, r *http.Request) { log.Debugf("[HTTP] - Transfer-Encoding: %v", r.TransferEncoding) log.Debugf("[HTTP] - Close: %v", r.Close) log.Debugf("[HTTP] - TLS: %v", r.TLS != nil) - + // 打印所有请求头 log.Debugf("[HTTP] 请求头:") for k, vs := range r.Header { @@ -354,7 +354,7 @@ func (s *Server) handleHTTP(w http.ResponseWriter, r *http.Request) { log.Infof("[HTTP] - 连接时长: %v", duration.Round(time.Millisecond)) log.Infof("[HTTP] - 上行流量: %s", formatBytes(upBytes.Load())) log.Infof("[HTTP] - 下行流量: %s", formatBytes(downBytes.Load())) - log.Infof("[HTTP] - 总流量: %s", formatBytes(upBytes.Load() + downBytes.Load())) + log.Infof("[HTTP] - 总流量: %s", formatBytes(upBytes.Load()+downBytes.Load())) log.Debugf("[HTTP] =====================================================") }() @@ -429,43 +429,43 @@ func (s *Server) handleSocks5Handshake(client net.Conn) error { log := logger.Get() log.Debugf("[SOCKS5] =====================================================") log.Debugf("[SOCKS5] 开始握手阶段") - + // 读取客户端支持的认证方法 buf := make([]byte, 2) if _, err := io.ReadFull(client, buf); err != nil { log.Debugf("[SOCKS5] 读取认证方法失败: %v", err) return fmt.Errorf("读取认证方法失败: %v", err) } - + log.Debugf("[SOCKS5] 收到握手请求:") log.Debugf("[SOCKS5] - 版本号: SOCKS%d", buf[0]) log.Debugf("[SOCKS5] - 认证方法数量: %d", buf[1]) - + // 读取认证方法列表 methods := make([]byte, buf[1]) if _, err := io.ReadFull(client, methods); err != nil { log.Debugf("[SOCKS5] 读取认证方法列表失败: %v", err) return fmt.Errorf("读取认证方法列表失败: %v", err) } - + log.Debugf("[SOCKS5] 支持的认证方法:") for _, method := range methods { log.Debugf("[SOCKS5] - 0x%02x (%s)", method, socks5AuthMethodToString(method)) } - + // 选择无认证方法 response := []byte{0x05, 0x00} if _, err := client.Write(response); err != nil { log.Debugf("[SOCKS5] 发送认证响应失败: %v", err) return fmt.Errorf("发送认证响应失败: %v", err) } - + log.Debugf("[SOCKS5] 发送握手响应:") log.Debugf("[SOCKS5] - 版本号: SOCKS5") log.Debugf("[SOCKS5] - 选择的认证方法: 0x00 (NO AUTHENTICATION REQUIRED)") log.Debugf("[SOCKS5] 握手完成") log.Debugf("[SOCKS5] =====================================================") - + return nil } @@ -478,20 +478,20 @@ func (s *Server) handleSocks5Request(client net.Conn) (net.Conn, string, error) client.RemoteAddr(), s.config.Socks5Addr, s.upstreamDialer.config.Server) - + // 读取请求头 buf := make([]byte, 4) if _, err := io.ReadFull(client, buf); err != nil { log.Debugf("[SOCKS5] 读取请求头失败: %v", err) return nil, "", fmt.Errorf("读取请求头失败: %v", err) } - + log.Debugf("[SOCKS5] 收到请求头:") log.Debugf("[SOCKS5] - 版本号: SOCKS%d", buf[0]) log.Debugf("[SOCKS5] - 命令: %s (0x%02x)", socks5CmdToString(buf[1]), buf[1]) log.Debugf("[SOCKS5] - 保留字节: 0x%02x", buf[2]) log.Debugf("[SOCKS5] - 地址类型: %s (0x%02x)", socks5AddrTypeToString(buf[3]), buf[3]) - + // 读取目标地址 var addr string switch buf[3] { @@ -503,7 +503,7 @@ func (s *Server) handleSocks5Request(client net.Conn) (net.Conn, string, error) } addr = net.IP(ipv4).String() log.Debugf("[SOCKS5] - IPv4地址: %s", addr) - + case 0x03: // 域名 domainLen := make([]byte, 1) if _, err := io.ReadFull(client, domainLen); err != nil { @@ -518,7 +518,7 @@ func (s *Server) handleSocks5Request(client net.Conn) (net.Conn, string, error) addr = string(domain) log.Debugf("[SOCKS5] - 域名长度: %d", domainLen[0]) log.Debugf("[SOCKS5] - 域名: %s", addr) - + case 0x04: // IPv6 ipv6 := make([]byte, 16) if _, err := io.ReadFull(client, ipv6); err != nil { @@ -528,7 +528,7 @@ func (s *Server) handleSocks5Request(client net.Conn) (net.Conn, string, error) addr = net.IP(ipv6).String() log.Debugf("[SOCKS5] - IPv6地址: %s", addr) } - + // 读取端口 portBuf := make([]byte, 2) if _, err := io.ReadFull(client, portBuf); err != nil { @@ -544,7 +544,7 @@ func (s *Server) handleSocks5Request(client net.Conn) (net.Conn, string, error) targetAddr) log.Debugf("[SOCKS5] - 端口: %d", port) log.Debugf("[SOCKS5] - 完整目标地址: %s", targetAddr) - + // 连接目标服务器 log.Debugf("[SOCKS5] 开始连接目标服务器...") target, err := s.dialTarget(targetAddr) @@ -556,15 +556,15 @@ func (s *Server) handleSocks5Request(client net.Conn) (net.Conn, string, error) client.Write(resp) return nil, "", fmt.Errorf("连接目标服务器失败: %v", err) } - + // 发送成功响应 resp := []byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} log.Debugf("[SOCKS5] 发送成功响应: %x", resp) client.Write(resp) - + log.Debugf("[SOCKS5] 请求处理完成") log.Debugf("[SOCKS5] =====================================================") - + return target, targetAddr, nil } @@ -617,7 +617,7 @@ func (s *Server) dialTarget(addr string) (net.Conn, error) { log := logger.Get() log.Debugf("[PROXY] =====================================================") log.Debugf("[PROXY] 开始处理连接请求: %s", addr) - + // 提取域名和端口 host := addr port := "" @@ -630,19 +630,8 @@ func (s *Server) dialTarget(addr string) (net.Conn, error) { if ip := net.ParseIP(host); ip != nil { log.Debugf("[PROXY] 目标是IP地址: %s", host) log.Debugf("[PROXY] - 端口: %s", port) - } else { - log.Debugf("[PROXY] 目标是域名: %s", host) - log.Debugf("[PROXY] - 端口: %s", port) - } - - // 检查是否是直连域名 - useDirect := s.directList.Match(host) - log.Debugf("[PROXY] 连接方式判断:") - log.Debugf("[PROXY] - 是否直连域名: %v", useDirect) - - if useDirect { - // 使用直接连接 - log.Debugf("[PROXY] 使用直接连接") + // IP地址默认直连 + log.Debugf("[PROXY] IP地址默认使用直连") conn, err := net.Dial("tcp", addr) if err != nil { log.Errorf("[PROXY] 直接连接失败: %v", err) @@ -657,22 +646,49 @@ func (s *Server) dialTarget(addr string) (net.Conn, error) { return conn, nil } - // 使用代理连接 - log.Debugf("[PROXY] 使用代理连接:") - log.Debugf("[PROXY] - 上游代理: %s", s.upstreamDialer.config.Server) - log.Debugf("[PROXY] - 代理类型: %s", s.upstreamDialer.config.Type) - - conn, err := s.upstreamDialer.Dial("tcp", addr) + // 检查是否在代理列表中 + useProxy := s.directList.Match(host) + log.Debugf("[PROXY] 域名检查:") + log.Debugf("[PROXY] - 目标地址: %s", addr) + log.Debugf("[PROXY] - 域名: %s", host) + log.Debugf("[PROXY] - 端口: %s", port) + log.Debugf("[PROXY] - 是否在代理列表: %v", useProxy) + + if useProxy { + // 使用代理连接 + log.Debugf("[PROXY] 使用代理连接:") + log.Debugf("[PROXY] - 上游代理: %s", s.upstreamDialer.config.Server) + log.Debugf("[PROXY] - 代理类型: %s", s.upstreamDialer.config.Type) + conn, err := s.upstreamDialer.Dial("tcp", addr) + if err != nil { + log.Errorf("[PROXY] 代理连接失败: %v", err) + log.Debugf("[PROXY] =====================================================") + return nil, err + } + log.Debugf("[PROXY] 代理连接成功") + log.Debugf("[PROXY] 连接详情:") + log.Debugf("[PROXY] - 本地地址: %s", conn.LocalAddr()) + log.Debugf("[PROXY] - 远程地址: %s", conn.RemoteAddr()) + log.Debugf("[PROXY] - 连接方式: 代理") + log.Debugf("[PROXY] - 判断依据: 域名在代理列表中") + log.Debugf("[PROXY] =====================================================") + return conn, nil + } + + // 不在代理列表中的域名直连 + log.Debugf("[PROXY] 使用直接连接") + conn, err := net.Dial("tcp", addr) if err != nil { - log.Errorf("[PROXY] 代理连接失败: %v", err) + log.Errorf("[PROXY] 直接连接失败: %v", err) log.Debugf("[PROXY] =====================================================") return nil, err } - - log.Debugf("[PROXY] 代理连接成功") + log.Debugf("[PROXY] 直接连接成功") log.Debugf("[PROXY] 连接详情:") log.Debugf("[PROXY] - 本地地址: %s", conn.LocalAddr()) log.Debugf("[PROXY] - 远程地址: %s", conn.RemoteAddr()) + log.Debugf("[PROXY] - 连接方式: 直连") + log.Debugf("[PROXY] - 判断依据: 域名不在代理列表中") log.Debugf("[PROXY] =====================================================") return conn, nil diff --git a/proxy.txt b/proxy.txt new file mode 100644 index 0000000..e44af95 --- /dev/null +++ b/proxy.txt @@ -0,0 +1,35 @@ +# 代理域名列表 +# 每行一个域名,支持通配符 * +# 以 # 开头的行为注释 + +# 需要代理的域名 +*.google.com +*.youtube.com +*.facebook.com +*.twitter.com +*.github.com +*.githubusercontent.com +*.cloudflare.com +*.wikipedia.org +*.medium.com +*.reddit.com +*.googlevideo.com + +# 国外服务 +*.netflix.com +*.amazon.com +*.microsoft.com +*.apple.com +*.dropbox.com +*.telegram.org +*.whatsapp.com +*.instagram.com +*.twitch.tv +*.spotify.com + +# 开发相关 +*.docker.com +*.npmjs.com +*.pypi.org +*.rubygems.org +*.maven.org \ No newline at end of file