add sniffer for forwarding handler

This commit is contained in:
ginuerzh
2024-10-14 21:18:38 +08:00
parent 27242d0b66
commit 0ffed81601
10 changed files with 1220 additions and 1091 deletions
+88 -68
View File
@@ -23,6 +23,7 @@ import (
"github.com/go-gost/core/recorder"
dissector "github.com/go-gost/tls-dissector"
xbypass "github.com/go-gost/x/bypass"
ctxvalue "github.com/go-gost/x/ctx"
xio "github.com/go-gost/x/internal/io"
xnet "github.com/go-gost/x/internal/net"
xhttp "github.com/go-gost/x/internal/net/http"
@@ -97,14 +98,11 @@ type Sniffer struct {
}
func (h *Sniffer) HandleHTTP(ctx context.Context, conn net.Conn, opts ...HandleOption) error {
var o HandleOptions
var ho HandleOptions
for _, opt := range opts {
opt(&o)
opt(&ho)
}
ro := o.RecorderObject
log := o.Log
pStats := stats.Stats{}
conn = stats_wrapper.WrapConn(conn, &pStats)
@@ -113,11 +111,31 @@ func (h *Sniffer) HandleHTTP(ctx context.Context, conn net.Conn, opts ...HandleO
if err != nil {
return err
}
log := ho.Log
if log.IsLevelEnabled(logger.TraceLevel) {
dump, _ := httputil.DumpRequest(req, false)
log.Trace(string(dump))
}
ro := ho.RecorderObject
if clientIP := xhttp.GetClientIP(req); clientIP != nil {
ro.ClientIP = clientIP.String()
}
clientAddr := ro.RemoteAddr
if ro.ClientIP != "" {
if _, port, _ := net.SplitHostPort(ro.RemoteAddr); port != "" {
clientAddr = net.JoinHostPort(ro.ClientIP, port)
}
}
ctx = ctxvalue.ContextWithClientAddr(ctx, ctxvalue.ClientAddr(clientAddr))
// http/2
if req.Method == "PRI" && len(req.Header) == 0 && req.URL.Path == "*" && req.Proto == "HTTP/2.0" {
return h.serveH2(ctx, xnet.NewReadWriteConn(br, conn, conn), &ho)
}
host := req.Host
if host != "" {
if _, _, err := net.SplitHostPort(host); err != nil {
@@ -129,59 +147,12 @@ func (h *Sniffer) HandleHTTP(ctx context.Context, conn net.Conn, opts ...HandleO
"host": host,
})
if o.Bypass != nil && o.Bypass.Contains(ctx, "tcp", host) {
if ho.Bypass != nil && ho.Bypass.Contains(ctx, "tcp", host) {
return xbypass.ErrBypass
}
}
// http/2
if req.Method == "PRI" && len(req.Header) == 0 && req.URL.Path == "*" && req.Proto == "HTTP/2.0" {
const expectedBody = "SM\r\n\r\n"
buf := make([]byte, len(expectedBody))
n, err := io.ReadFull(br, buf)
if err != nil {
return fmt.Errorf("h2: error reading client preface: %s", err)
}
if string(buf[:n]) != expectedBody {
return errors.New("h2: invalid client preface")
}
ro.Time = time.Time{}
tr := &http2.Transport{
DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
if dial := o.DialTLS; dial != nil {
return dial(ctx, network, addr, cfg)
}
cc, err := (&net.Dialer{}).DialContext(ctx, network, addr)
if err != nil {
return nil, err
}
cc = tls.Client(cc, cfg)
return cc, nil
},
}
defer tr.CloseIdleConnections()
h2s := &http2.Server{}
h2s.ServeConn(xnet.NewReadWriteConn(br, conn, conn), &http2.ServeConnOpts{
Context: ctx,
SawClientPreface: true,
Handler: &h2Handler{
transport: tr,
recorder: h.Recorder,
recorderOptions: h.RecorderOptions,
recorderObject: ro,
log: log,
},
})
return nil
}
dial := o.Dial
dial := ho.Dial
if dial == nil {
dial = (&net.Dialer{}).DialContext
}
@@ -220,6 +191,54 @@ func (h *Sniffer) HandleHTTP(ctx context.Context, conn net.Conn, opts ...HandleO
}
}
func (h *Sniffer) serveH2(ctx context.Context, conn net.Conn, ho *HandleOptions) error {
const expectedBody = "SM\r\n\r\n"
buf := make([]byte, len(expectedBody))
n, err := io.ReadFull(conn, buf)
if err != nil {
return fmt.Errorf("h2: error reading client preface: %s", err)
}
if string(buf[:n]) != expectedBody {
return errors.New("h2: invalid client preface")
}
ro := ho.RecorderObject
log := ho.Log
ro.Time = time.Time{}
tr := &http2.Transport{
DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
if dial := ho.DialTLS; dial != nil {
return dial(ctx, network, addr, cfg)
}
cc, err := (&net.Dialer{}).DialContext(ctx, network, addr)
if err != nil {
return nil, err
}
cc = tls.Client(cc, cfg)
return cc, nil
},
}
defer tr.CloseIdleConnections()
(&http2.Server{}).ServeConn(conn, &http2.ServeConnOpts{
Context: ctx,
SawClientPreface: true,
Handler: &h2Handler{
transport: tr,
recorder: h.Recorder,
recorderOptions: h.RecorderOptions,
recorderObject: ro,
log: log,
},
})
return nil
}
func (h *Sniffer) httpRoundTrip(ctx context.Context, rw, cc io.ReadWriter, req *http.Request, ro *xrecorder.HandlerRecorderObject, pStats *stats.Stats, log logger.Logger) (close bool, err error) {
close = true
@@ -245,9 +264,6 @@ func (h *Sniffer) httpRoundTrip(ctx context.Context, rw, cc io.ReadWriter, req *
}).Infof("%s >-< %s", ro.RemoteAddr, req.Host)
}()
if clientIP := xhttp.GetClientIP(req); clientIP != nil {
ro.ClientIP = clientIP.String()
}
ro.HTTP = &xrecorder.HTTPRecorderObject{
Host: req.Host,
Proto: req.Proto,
@@ -345,9 +361,9 @@ func (h *Sniffer) httpRoundTrip(ctx context.Context, rw, cc io.ReadWriter, req *
}
func (h *Sniffer) HandleTLS(ctx context.Context, conn net.Conn, opts ...HandleOption) error {
var o HandleOptions
var ho HandleOptions
for _, opt := range opts {
opt(&o)
opt(&ho)
}
buf := new(bytes.Buffer)
@@ -356,7 +372,9 @@ func (h *Sniffer) HandleTLS(ctx context.Context, conn net.Conn, opts ...HandleOp
return err
}
ro := o.RecorderObject
log := ho.Log
ro := ho.RecorderObject
ro.TLS = &xrecorder.TLSRecorderObject{
ServerName: clientHello.ServerName,
ClientHello: hex.EncodeToString(buf.Bytes()),
@@ -365,6 +383,8 @@ func (h *Sniffer) HandleTLS(ctx context.Context, conn net.Conn, opts ...HandleOp
ro.TLS.Proto = clientHello.SupportedProtos[0]
}
ctx = ctxvalue.ContextWithClientAddr(ctx, ctxvalue.ClientAddr(ro.RemoteAddr))
host := clientHello.ServerName
if host != "" {
if _, _, err := net.SplitHostPort(host); err != nil {
@@ -372,12 +392,12 @@ func (h *Sniffer) HandleTLS(ctx context.Context, conn net.Conn, opts ...HandleOp
}
ro.Host = host
if o.Bypass != nil && o.Bypass.Contains(ctx, "tcp", host) {
if ho.Bypass != nil && ho.Bypass.Contains(ctx, "tcp", host) {
return xbypass.ErrBypass
}
}
dial := o.Dial
dial := ho.Dial
if dial == nil {
dial = (&net.Dialer{}).DialContext
}
@@ -393,7 +413,7 @@ func (h *Sniffer) HandleTLS(ctx context.Context, conn net.Conn, opts ...HandleOp
host = ro.Host
}
if h.MitmBypass == nil || !h.MitmBypass.Contains(ctx, "tcp", host) {
return h.terminateTLS(ctx, xnet.NewReadWriteConn(io.MultiReader(buf, conn), conn, conn), cc, clientHello, &o)
return h.terminateTLS(ctx, xnet.NewReadWriteConn(io.MultiReader(buf, conn), conn, conn), cc, clientHello, &ho)
}
}
@@ -424,18 +444,18 @@ func (h *Sniffer) HandleTLS(ctx context.Context, conn net.Conn, opts ...HandleOp
return err
}
o.Log.Infof("%s <-> %s", ro.RemoteAddr, ro.Host)
log.Infof("%s <-> %s", ro.RemoteAddr, ro.Host)
xnet.Transport(conn, cc)
o.Log.WithFields(map[string]any{
log.WithFields(map[string]any{
"duration": time.Since(ro.Time),
}).Infof("%s >-< %s", ro.RemoteAddr, ro.Host)
return err
}
func (h *Sniffer) terminateTLS(ctx context.Context, conn, cc net.Conn, clientHello *dissector.ClientHelloInfo, o *HandleOptions) error {
ro := o.RecorderObject
log := o.Log
func (h *Sniffer) terminateTLS(ctx context.Context, conn, cc net.Conn, clientHello *dissector.ClientHelloInfo, ho *HandleOptions) error {
ro := ho.RecorderObject
log := ho.Log
nextProtos := clientHello.SupportedProtos
if h.NegotiatedProtocol != "" {