From cc0b74c5c219bf1cea3e573372c58368a81e7d66 Mon Sep 17 00:00:00 2001 From: luorijun Date: Mon, 3 Mar 2025 10:15:48 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=20HTTP=20=E5=A4=84=E7=90=86?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=8C=E4=BC=98=E5=8C=96=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E5=92=8C=E8=AE=A4=E8=AF=81=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/fwd/dispatcher/dispatch.go | 20 ++-- server/fwd/http/http.go | 146 ++++++++++++++++++++++++++---- server/server.go | 3 +- 3 files changed, 144 insertions(+), 25 deletions(-) diff --git a/server/fwd/dispatcher/dispatch.go b/server/fwd/dispatcher/dispatch.go index 6c7e06c..5d5274e 100644 --- a/server/fwd/dispatcher/dispatch.go +++ b/server/fwd/dispatcher/dispatch.go @@ -6,6 +6,7 @@ import ( "net" "proxy-server/pkg/utils" "proxy-server/server/fwd/core" + "proxy-server/server/fwd/http" "proxy-server/server/fwd/socks" "strconv" "time" @@ -98,9 +99,16 @@ func (s *Server) acceptHttp(ls net.Listener) error { } go func() { - err := s.processHttp(conn) + user, err := http.Process(s.ctx, conn) if err != nil { slog.Error("dispatcher http process error", "err", err) + utils.Close(conn) + return + } + select { + case <-s.ctx.Done(): + utils.Close(user) + case s.Conn <- user: } }() } @@ -121,7 +129,7 @@ func (s *Server) acceptSocks(ls net.Listener) error { } go func() { - conn, err := socks.Process(s.ctx, conn) + user, err := socks.Process(s.ctx, conn) if err != nil { slog.Error("处理 socks 连接失败", "err", err) utils.Close(conn) @@ -129,16 +137,12 @@ func (s *Server) acceptSocks(ls net.Listener) error { } select { case <-s.ctx.Done(): - utils.Close(conn) - case s.Conn <- conn: + utils.Close(user) + case s.Conn <- user: } }() } } -func (s *Server) processHttp(conn net.Conn) error { - return nil -} - type Conn struct { } diff --git a/server/fwd/http/http.go b/server/fwd/http/http.go index 733c7f0..1f8d8a6 100644 --- a/server/fwd/http/http.go +++ b/server/fwd/http/http.go @@ -3,8 +3,11 @@ package http import ( "bufio" "context" + "encoding/base64" + "io" "net" "net/textproto" + "net/url" "proxy-server/server/fwd/core" "strings" @@ -12,8 +15,14 @@ import ( ) type Request struct { - auth *core.AuthContext - dest *core.FwdAddr + conn net.Conn + reader *bufio.Reader + method string + uri string + proto string + headers *textproto.MIMEHeader + auth *core.AuthContext + dest *core.FwdAddr } func Process(ctx context.Context, conn net.Conn) (*core.Conn, error) { @@ -25,38 +34,143 @@ func Process(ctx context.Context, conn net.Conn) (*core.Conn, error) { if err != nil { return nil, err } - parts := strings.Split(line, " ") + parts := strings.Fields(line) if len(parts) != 3 { - return nil, errors.New("invalid http request") + return nil, errors.New("无效的 http 请求") } - var req Request + // 请求头 + headers, err := textReader.ReadMIMEHeader() + if err != nil { + return nil, errors.Wrap(err, "解析请求头失败") + } + + // 验证账号 + authInfo := headers.Get("Proxy-Authorization") + var auth *core.AuthContext + if authInfo == "" { + auth, err = core.CheckIp(conn) + if err != nil { + return nil, errors.Wrap(err, "验证账号失败") + } + } else { + authParts := strings.Split(authInfo, " ") + if len(authParts) != 2 { + return nil, errors.New("无效的 Proxy-Authorization") + } + if authParts[0] != "Basic" { + return nil, errors.New("不支持的认证方式") + } + authBytes, err := base64.URLEncoding.DecodeString(authParts[1]) + if err != nil { + return nil, errors.Wrap(err, "解码认证信息失败") + } + authPair := strings.Split(string(authBytes), ":") + auth, err = core.CheckPass(conn, authPair[0], authPair[1]) + } + + // 获取 Host + host := headers.Get("Host") + if host == "" { + return nil, errors.New("无效的 Host") + } + if !strings.Contains(host, ":") { + host += ":80" + } + addr, err := net.ResolveTCPAddr("tcp", host) + if err != nil { + return nil, errors.Wrap(err, "解析 Host 失败") + } + + request := &Request{ + conn: conn, + reader: reader, + method: parts[0], + uri: parts[1], + proto: parts[2], + headers: &headers, + dest: &core.FwdAddr{ + IP: addr.IP, + Port: addr.Port, + Domain: host, + }, + auth: auth, + } + + var user *core.Conn if parts[0] == "CONNECT" { - req, err = processHttps(ctx, textReader) + user, err = processHttps(ctx, request) if err != nil { return nil, err } } else { - req, err = processHttp(ctx, textReader) + user, err = processHttp(ctx, request) if err != nil { return nil, err } } + return user, nil +} + +func processHttps(ctx context.Context, req *Request) (*core.Conn, error) { + + // 响应 CONNECT + _, err := req.conn.Write([]byte("HTTP/1.1 200 Connection Established\r\n\r\n")) + if err != nil { + return nil, errors.Wrap(err, "响应 CONNECT 失败") + } + return &core.Conn{ - Conn: conn, - Reader: reader, - Tag: conn.RemoteAddr().String() + "_" + conn.LocalAddr().String(), - Protocol: "http", + Conn: req.conn, + Reader: req.reader, + Tag: req.conn.RemoteAddr().String() + "_" + req.conn.LocalAddr().String(), + Protocol: "https", Dest: req.dest, Auth: req.auth, }, nil } -func processHttps(ctx context.Context, reader *textproto.Reader) (Request, error) { - panic("not implemented") -} +func processHttp(ctx context.Context, req *Request) (*core.Conn, error) { -func processHttp(ctx context.Context, reader *textproto.Reader) (Request, error) { - panic("not implemented") + // 修改请求头 + rawUrl, err := url.Parse(req.uri) + if err != nil { + return nil, errors.Wrap(err, "解析请求地址失败") + } + rawUrl.Scheme = "" + rawUrl.Host = "" + req.uri = rawUrl.String() + req.headers.Del("Proxy-Authorization") + + // 构造请求 + sb := strings.Builder{} + + sb.WriteString(req.method) + sb.WriteString(" ") + sb.WriteString(req.uri) + sb.WriteString(" ") + sb.WriteString(req.proto) + sb.WriteString("\r\n") + + for k, v := range *req.headers { + sb.WriteString(k) + sb.WriteString(": ") + sb.WriteString(strings.Join(v, ",")) + sb.WriteString("\r\n") + } + + sb.WriteString("\r\n") + + mReader := io.MultiReader(strings.NewReader(sb.String()), req.conn) + newReader := bufio.NewReader(mReader) + + return &core.Conn{ + Conn: req.conn, + Reader: newReader, + Tag: req.conn.RemoteAddr().String() + "_" + req.conn.LocalAddr().String(), + Protocol: "http", + Dest: req.dest, + Auth: req.auth, + }, nil } diff --git a/server/server.go b/server/server.go index 1ed0965..a0bf6b9 100644 --- a/server/server.go +++ b/server/server.go @@ -80,7 +80,8 @@ func Start() { func initLog() { writer := colorable.NewColorable(os.Stdout) logger := slog.New(tint.NewHandler(writer, &tint.Options{ - Level: slog.LevelDebug, + Level: slog.LevelDebug, + TimeFormat: time.RFC3339, ReplaceAttr: func(_ []string, attr slog.Attr) slog.Attr { err, ok := attr.Value.Any().(error) if !ok {