This commit is contained in:
2025-01-10 14:31:56 +08:00
parent 312e30c87b
commit e8a63b28e2
6 changed files with 329 additions and 153 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
proxy.log
proxy-2025-01-10T06-26-50.064.log

View File

@@ -4,6 +4,9 @@
# 自定义域名
*.19year.cn
# 常用国内网站
*.baidu.com
*.qq.com

View File

@@ -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
}

215
pkg/proxy/proxylist.go Normal file
View File

@@ -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
}

View File

@@ -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"
@@ -35,7 +35,7 @@ type Server struct {
activeConns sync.WaitGroup // 活动连接计数
upstreamDialer *UpstreamDialer // 上游代理拨号器
shutdownTimeout time.Duration // 关闭超时时间
directList *DirectList // 直连域名管理器
directList *ProxyList // 直连域名管理器
}
// NewServer 创建新的代理服务器实例
@@ -56,7 +56,7 @@ func NewServer(config ServerConfig, upstreamConfig config.UpstreamConfig) *Serve
done: make(chan struct{}),
upstreamDialer: upstreamDialer,
shutdownTimeout: 30 * time.Second,
directList: NewDirectList("direct.txt"),
directList: NewProxyList("proxy.txt"),
}
}
@@ -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] =====================================================")
}()
@@ -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] =====================================================")
}()
@@ -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
}
// 检查是否在代理列表中
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.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

35
proxy.txt Normal file
View File

@@ -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