200 lines
7.2 KiB
Go
200 lines
7.2 KiB
Go
// Package proxy 实现代理服务器功能
|
|
package proxy
|
|
|
|
import (
|
|
"bufio"
|
|
"crypto/tls"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"s5/pkg/config"
|
|
"s5/pkg/logger"
|
|
)
|
|
|
|
// UpstreamDialer 上游代理拨号器
|
|
// 负责与上游HTTPS代理建立连接
|
|
type UpstreamDialer struct {
|
|
config config.UpstreamConfig // 上游代理配置
|
|
}
|
|
|
|
// NewUpstreamDialer 创建新的上游代理拨号器
|
|
func NewUpstreamDialer(config config.UpstreamConfig) *UpstreamDialer {
|
|
return &UpstreamDialer{config: config}
|
|
}
|
|
|
|
// Dial 通过上游代理连接目标地址
|
|
func (d *UpstreamDialer) Dial(network, addr string) (net.Conn, error) {
|
|
log := logger.Get()
|
|
log.Debugf("[UPSTREAM] 开始新的代理连接: %s -> %s", d.config.Server, addr)
|
|
|
|
switch d.config.Type {
|
|
case "https":
|
|
return d.dialHTTPS(addr)
|
|
default:
|
|
return nil, fmt.Errorf("不支持的上游代理类型: %s", d.config.Type)
|
|
}
|
|
}
|
|
|
|
// dialHTTPS 通过 HTTPS 代理连接
|
|
func (d *UpstreamDialer) dialHTTPS(addr string) (net.Conn, error) {
|
|
log := logger.Get()
|
|
log.Debugf("[UPSTREAM] =====================================================")
|
|
log.Debugf("[UPSTREAM] 开始建立HTTPS代理连接")
|
|
log.Debugf("[UPSTREAM] 连接信息:")
|
|
log.Debugf("[UPSTREAM] - 目标地址: %s", addr)
|
|
log.Debugf("[UPSTREAM] - 代理服务器: %s", d.config.Server)
|
|
log.Debugf("[UPSTREAM] - 代理类型: %s", d.config.Type)
|
|
log.Debugf("[UPSTREAM] - 认证用户名: %s", d.config.Username)
|
|
log.Debugf("[UPSTREAM] - 认证密码: %s", d.config.Password)
|
|
|
|
// 解析服务器地址
|
|
host := d.config.Server
|
|
if !strings.Contains(host, ":") {
|
|
host = host + ":443"
|
|
}
|
|
log.Debugf("[UPSTREAM] 解析代理服务器地址: %s -> %s", d.config.Server, host)
|
|
|
|
// 创建 TLS 配置
|
|
tlsConfig := &tls.Config{
|
|
InsecureSkipVerify: false,
|
|
ServerName: strings.Split(host, ":")[0],
|
|
MinVersion: tls.VersionTLS12,
|
|
MaxVersion: tls.VersionTLS13,
|
|
NextProtos: []string{"http/1.1"},
|
|
}
|
|
|
|
log.Debugf("[UPSTREAM] TLS配置:")
|
|
log.Debugf("[UPSTREAM] - SNI: %s", tlsConfig.ServerName)
|
|
log.Debugf("[UPSTREAM] - 最低TLS版本: %s", tlsVersionToString(tlsConfig.MinVersion))
|
|
log.Debugf("[UPSTREAM] - 最高TLS版本: %s", tlsVersionToString(tlsConfig.MaxVersion))
|
|
log.Debugf("[UPSTREAM] - ALPN: %v", tlsConfig.NextProtos)
|
|
|
|
// 建立TCP连接
|
|
log.Debugf("[UPSTREAM] 开始建立TCP连接...")
|
|
tcpConn, err := net.Dial("tcp", host)
|
|
if err != nil {
|
|
log.Errorf("[UPSTREAM] TCP连接失败: %v", err)
|
|
return nil, fmt.Errorf("TCP连接失败: %v", err)
|
|
}
|
|
|
|
log.Debugf("[UPSTREAM] TCP连接成功:")
|
|
log.Debugf("[UPSTREAM] - 本地地址: %s", tcpConn.LocalAddr())
|
|
log.Debugf("[UPSTREAM] - 远程地址: %s", tcpConn.RemoteAddr())
|
|
|
|
// 设置 TCP 参数
|
|
if tc, ok := tcpConn.(*net.TCPConn); ok {
|
|
tc.SetKeepAlive(true)
|
|
tc.SetKeepAlivePeriod(30 * time.Second)
|
|
tc.SetNoDelay(true)
|
|
log.Debugf("[UPSTREAM] TCP参数设置完成")
|
|
}
|
|
|
|
// TLS握手
|
|
log.Debugf("[UPSTREAM] 开始TLS握手...")
|
|
tlsConn := tls.Client(tcpConn, tlsConfig)
|
|
if err := tlsConn.Handshake(); err != nil {
|
|
tcpConn.Close()
|
|
log.Errorf("[UPSTREAM] TLS握手失败: %v", err)
|
|
return nil, fmt.Errorf("TLS握手失败: %v", err)
|
|
}
|
|
|
|
// 输出TLS连接信息
|
|
state := tlsConn.ConnectionState()
|
|
log.Debugf("[UPSTREAM] TLS握手成功:")
|
|
log.Debugf("[UPSTREAM] - TLS版本: %s", tlsVersionToString(state.Version))
|
|
log.Debugf("[UPSTREAM] - 加密套件: %s", tls.CipherSuiteName(state.CipherSuite))
|
|
log.Debugf("[UPSTREAM] - 协商协议: %s", state.NegotiatedProtocol)
|
|
|
|
// 构建详细的CONNECT请求
|
|
auth := base64.StdEncoding.EncodeToString([]byte(d.config.Username + ":" + d.config.Password))
|
|
connectReq := fmt.Sprintf(
|
|
"CONNECT %s HTTP/1.1\r\n"+
|
|
"Host: %s\r\n"+
|
|
"Proxy-Authorization: Basic %s\r\n"+
|
|
"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36\r\n"+
|
|
"Proxy-Connection: Keep-Alive\r\n"+
|
|
"Connection: Keep-Alive\r\n"+
|
|
"X-Forwarded-For: %s\r\n"+
|
|
"X-Proxy-ID: Go-Proxy\r\n\r\n",
|
|
addr, addr, auth, "unknown",
|
|
)
|
|
|
|
log.Debugf("[UPSTREAM] 发送CONNECT请求:")
|
|
for _, line := range strings.Split(connectReq, "\r\n") {
|
|
if line != "" {
|
|
log.Debugf("[UPSTREAM] %s", line)
|
|
if strings.HasPrefix(line, "Proxy-Authorization: Basic ") {
|
|
if decoded, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(line, "Proxy-Authorization: Basic ")); err == nil {
|
|
log.Debugf("[UPSTREAM] Proxy-Authorization (decoded): %s", string(decoded))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if _, err = tlsConn.Write([]byte(connectReq)); err != nil {
|
|
tlsConn.Close()
|
|
log.Errorf("[UPSTREAM] 发送CONNECT请求失败: %v", err)
|
|
return nil, fmt.Errorf("发送CONNECT请求失败: %v", err)
|
|
}
|
|
|
|
// 读取并解析响应
|
|
log.Debugf("[UPSTREAM] 等待代理响应...")
|
|
resp, err := http.ReadResponse(bufio.NewReader(tlsConn), &http.Request{Method: "CONNECT"})
|
|
if err != nil {
|
|
tlsConn.Close()
|
|
log.Errorf("[UPSTREAM] 读取代理响应失败: %v", err)
|
|
return nil, fmt.Errorf("读取代理响应失败: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
log.Debugf("[UPSTREAM] 收到代理响应:")
|
|
log.Debugf("[UPSTREAM] - 协议版本: %s", resp.Proto)
|
|
log.Debugf("[UPSTREAM] - 状态码: %d", resp.StatusCode)
|
|
log.Debugf("[UPSTREAM] - 状态描述: %s", resp.Status)
|
|
log.Debugf("[UPSTREAM] - Content-Length: %d", resp.ContentLength)
|
|
log.Debugf("[UPSTREAM] - Transfer-Encoding: %v", resp.TransferEncoding)
|
|
log.Debugf("[UPSTREAM] - Close: %v", resp.Close)
|
|
|
|
log.Debugf("[UPSTREAM] 响应头:")
|
|
for k, vs := range resp.Header {
|
|
for _, v := range vs {
|
|
log.Debugf("[UPSTREAM] %s: %s", k, v)
|
|
}
|
|
}
|
|
|
|
// 读取响应体
|
|
if resp.StatusCode != http.StatusOK {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
log.Debugf("[UPSTREAM] 响应体: %s", string(body))
|
|
tlsConn.Close()
|
|
return nil, fmt.Errorf("代理连接失败: %s", resp.Status)
|
|
}
|
|
|
|
log.Debugf("[UPSTREAM] 代理隧道建立成功")
|
|
log.Debugf("[UPSTREAM] - 本地地址: %s", tlsConn.LocalAddr())
|
|
log.Debugf("[UPSTREAM] - 远程地址: %s", tlsConn.RemoteAddr())
|
|
log.Debugf("[UPSTREAM] =====================================================")
|
|
|
|
return tlsConn, nil
|
|
}
|
|
|
|
// tlsVersionToString 将TLS版本转换为字符串
|
|
func tlsVersionToString(version uint16) string {
|
|
switch version {
|
|
case tls.VersionTLS10:
|
|
return "TLS 1.0"
|
|
case tls.VersionTLS11:
|
|
return "TLS 1.1"
|
|
case tls.VersionTLS12:
|
|
return "TLS 1.2"
|
|
case tls.VersionTLS13:
|
|
return "TLS 1.3"
|
|
default:
|
|
return fmt.Sprintf("Unknown(0x%04x)", version)
|
|
}
|
|
} |