add route for recorder

This commit is contained in:
ginuerzh
2024-09-24 20:20:15 +08:00
parent dd179bc951
commit e22aa91571
26 changed files with 364 additions and 117 deletions
+8 -7
View File
@@ -108,14 +108,15 @@ func (r *Router) dial(ctx context.Context, network, address string, log logger.L
route = r.options.Chain.Route(ctx, network, ipAddr, chain.WithHostRouteOption(address))
}
if log.IsLevelEnabled(logger.DebugLevel) {
buf := bytes.Buffer{}
for _, node := range routePath(route) {
fmt.Fprintf(&buf, "%s@%s > ", node.Name, node.Addr)
}
fmt.Fprintf(&buf, "%s", ipAddr)
log.Debugf("route(retry=%d) %s", i, buf.String())
buf := ctxvalue.BufferFromContext(ctx)
if buf == nil {
buf = &bytes.Buffer{}
}
for _, node := range routePath(route) {
fmt.Fprintf(buf, "%s@%s > ", node.Name, node.Addr)
}
fmt.Fprintf(buf, "%s", ipAddr)
log.Debugf("route(retry=%d) %s", i, buf.String())
if route == nil {
route = DefaultRoute
+1
View File
@@ -121,6 +121,7 @@ type TLSOptions struct {
MinVersion string `yaml:"minVersion,omitempty" json:"minVersion,omitempty"`
MaxVersion string `yaml:"maxVersion,omitempty" json:"maxVersion,omitempty"`
CipherSuites []string `yaml:"cipherSuites,omitempty" json:"cipherSuites,omitempty"`
ALPN []string `yaml:"alpn,omitempty" json:"alpn,omitempty"`
}
type PluginConfig struct {
+1
View File
@@ -220,6 +220,7 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
tlsCfg.Options.MinVersion = o.MinVersion
tlsCfg.Options.MaxVersion = o.MaxVersion
tlsCfg.Options.CipherSuites = o.CipherSuites
tlsCfg.Options.ALPN = o.ALPN
}
opts = append(opts, chain.TLSNodeOption(tlsCfg))
}
+19 -1
View File
@@ -1,6 +1,9 @@
package ctx
import "context"
import (
"bytes"
"context"
)
// clientAddrKey saves the client address.
type clientAddrKey struct{}
@@ -74,3 +77,18 @@ func ClientIDFromContext(ctx context.Context) ClientID {
v, _ := ctx.Value(keyClientID).(ClientID)
return v
}
type bufferKey struct{}
var (
keyBuffer = &bufferKey{}
)
func ContextWithBuffer(ctx context.Context, buffer *bytes.Buffer) context.Context {
return context.WithValue(ctx, keyBuffer, buffer)
}
func BufferFromContext(ctx context.Context) *bytes.Buffer {
v, _ := ctx.Value(keyBuffer).(*bytes.Buffer)
return v
}
+2 -2
View File
@@ -9,12 +9,12 @@ require (
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
github.com/gin-contrib/cors v1.6.0
github.com/gin-gonic/gin v1.9.1
github.com/go-gost/core v0.1.4
github.com/go-gost/core v0.1.5
github.com/go-gost/gosocks4 v0.0.1
github.com/go-gost/gosocks5 v0.4.2
github.com/go-gost/plugin v0.1.1
github.com/go-gost/relay v0.5.0
github.com/go-gost/tls-dissector v0.1.0
github.com/go-gost/tls-dissector v0.1.1
github.com/go-redis/redis/v8 v8.11.5
github.com/gobwas/glob v0.2.3
github.com/golang/snappy v0.0.4
+4 -4
View File
@@ -53,8 +53,8 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-gost/core v0.1.4 h1:uP1r6kpi39NaFtKo6guCjMSvgJl8eTWW3Lb/8+4YDdo=
github.com/go-gost/core v0.1.4/go.mod h1:WGI43jOka7FAsSAwi/fSMaqxdR+E339ycb4NBGlFr6A=
github.com/go-gost/core v0.1.5 h1:Rbj+xq+q2RkxviUErjwyT8wbqCa5WeV+UelyIs99dFI=
github.com/go-gost/core v0.1.5/go.mod h1:WGI43jOka7FAsSAwi/fSMaqxdR+E339ycb4NBGlFr6A=
github.com/go-gost/gosocks4 v0.0.1 h1:+k1sec8HlELuQV7rWftIkmy8UijzUt2I6t+iMPlGB2s=
github.com/go-gost/gosocks4 v0.0.1/go.mod h1:3B6L47HbU/qugDg4JnoFPHgJXE43Inz8Bah1QaN9qCc=
github.com/go-gost/gosocks5 v0.4.2 h1:IianxHTkACPqCwiOAT3MHoMdSUl+SEPSRu1ikawC1Pc=
@@ -63,8 +63,8 @@ github.com/go-gost/plugin v0.1.1 h1:LNoc/Rqwb3ceGhhxwhpjf1SeeYBGe80MJ9E4lpM3qak=
github.com/go-gost/plugin v0.1.1/go.mod h1:oN23l+yGDCIP9G3KnDl/I/0zVGOobZUDCB2Z5yYYXts=
github.com/go-gost/relay v0.5.0 h1:JG1tgy/KWiVXS0ukuVXvbM0kbYuJTWxYpJ5JwzsCf/c=
github.com/go-gost/relay v0.5.0/go.mod h1:lcX+23LCQ3khIeASBo+tJ/WbwXFO32/N5YN6ucuYTG8=
github.com/go-gost/tls-dissector v0.1.0 h1:hpgzC/Qt9MMGGie+hLeBAZ379OG72sSc1TRiG7+rahI=
github.com/go-gost/tls-dissector v0.1.0/go.mod h1:/9QfdewqmHdaE362Hv5nDaSWLx3pCmtD870d6GaquXs=
github.com/go-gost/tls-dissector v0.1.1 h1:2zUOTPzCQAUQ54Rpy0UEi3JPMQSYsIFSeFeKrzmkCoU=
github.com/go-gost/tls-dissector v0.1.1/go.mod h1:/9QfdewqmHdaE362Hv5nDaSWLx3pCmtD870d6GaquXs=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
+5 -1
View File
@@ -1,6 +1,7 @@
package dns
import (
"bytes"
"context"
"errors"
"fmt"
@@ -290,7 +291,10 @@ func (h *dnsHandler) request(ctx context.Context, msg []byte, ro *xrecorder.Hand
}
log.Debugf("exchange message %d: %s", mq.Id, mq.Question[0].String())
mr, err := h.exchange(ctx, ex, &mq)
var buf bytes.Buffer
mr, err := h.exchange(ctxvalue.ContextWithBuffer(ctx, &buf), ex, &mq)
ro.Route = buf.String()
if err != nil {
return nil, err
}
+7 -2
View File
@@ -196,7 +196,9 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
log.Debugf("%s >> %s", conn.RemoteAddr(), addr)
cc, err := h.options.Router.Dial(ctx, network, addr)
var buf bytes.Buffer
cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), network, addr)
ro.Route = buf.String()
if err != nil {
log.Error(err)
// TODO: the router itself may be failed due to the failed node in the router,
@@ -351,7 +353,9 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser,
bodyRewrites = httpSettings.RewriteBody
}
cc, err = h.options.Router.Dial(ctx, "tcp", target.Addr)
var buf bytes.Buffer
cc, err = h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), "tcp", target.Addr)
ro.Route = buf.String()
if err != nil {
// TODO: the router itself may be failed due to the failed node in the router,
// the dead marker may be a wrong operation.
@@ -376,6 +380,7 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser,
MinVersion: tlsSettings.Options.MinVersion,
MaxVersion: tlsSettings.Options.MaxVersion,
CipherSuites: tlsSettings.Options.CipherSuites,
ALPN: tlsSettings.Options.ALPN,
})
cc = tls.Client(cc, cfg)
}
+9 -3
View File
@@ -195,7 +195,9 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
log.Debugf("%s >> %s", conn.RemoteAddr(), target.Addr)
cc, err := h.options.Router.Dial(ctx, network, target.Addr)
var buf bytes.Buffer
cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), network, target.Addr)
ro.Route = buf.String()
if err != nil {
log.Error(err)
// TODO: the router itself may be failed due to the failed node in the router,
@@ -278,7 +280,8 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser,
if _, _, err := net.SplitHostPort(host); err != nil {
host = net.JoinHostPort(strings.Trim(host, "[]"), "80")
}
if bp := h.options.Bypass; bp != nil && bp.Contains(ctx, "tcp", host, bypass.WithPathOption(req.RequestURI)) {
if bp := h.options.Bypass; bp != nil &&
bp.Contains(ctx, "tcp", host, bypass.WithPathOption(req.RequestURI)) {
log.Debugf("bypass: %s %s", host, req.RequestURI)
resp.StatusCode = http.StatusForbidden
resp.Write(rw)
@@ -350,7 +353,9 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser,
bodyRewrites = httpSettings.RewriteBody
}
cc, err = h.options.Router.Dial(ctx, "tcp", target.Addr)
var buf bytes.Buffer
cc, err = h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), "tcp", target.Addr)
ro.Route = buf.String()
if err != nil {
// TODO: the router itself may be failed due to the failed node in the router,
// the dead marker may be a wrong operation.
@@ -375,6 +380,7 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser,
MinVersion: tlsSettings.Options.MinVersion,
MaxVersion: tlsSettings.Options.MaxVersion,
CipherSuites: tlsSettings.Options.CipherSuites,
ALPN: tlsSettings.Options.ALPN,
})
cc = tls.Client(cc, cfg)
}
+4 -1
View File
@@ -2,6 +2,7 @@ package http
import (
"bufio"
"bytes"
"context"
"encoding/base64"
"encoding/binary"
@@ -267,7 +268,9 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: addr})
}
cc, err := h.options.Router.Dial(ctx, network, addr)
var buf bytes.Buffer
cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), network, addr)
ro.Route = buf.String()
if err != nil {
resp.StatusCode = http.StatusServiceUnavailable
+3 -1
View File
@@ -243,7 +243,9 @@ func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: addr})
}
cc, err := h.options.Router.Dial(ctx, "tcp", addr)
var buf bytes.Buffer
cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), "tcp", addr)
ro.Route = buf.String()
if err != nil {
log.Error(err)
resp.StatusCode = http.StatusServiceUnavailable
+227 -69
View File
@@ -6,6 +6,8 @@ import (
"context"
"crypto/tls"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"io"
"net"
@@ -133,7 +135,7 @@ func (h *redirectHandler) Handle(ctx context.Context, conn net.Conn, opts ...han
"dst": fmt.Sprintf("%s/%s", dstAddr, dstAddr.Network()),
})
var rw io.ReadWriter = conn
var rw io.ReadWriteCloser = conn
if h.md.sniffing {
if h.md.sniffingTimeout > 0 {
conn.SetReadDeadline(time.Now().Add(h.md.sniffingTimeout))
@@ -144,7 +146,7 @@ func (h *redirectHandler) Handle(ctx context.Context, conn net.Conn, opts ...han
if h.md.sniffingTimeout > 0 {
conn.SetReadDeadline(time.Time{})
}
rw = xio.NewReadWriter(io.MultiReader(bytes.NewReader(hdr[:n]), rw), rw)
rw = xio.NewReadWriteCloser(io.MultiReader(bytes.NewReader(hdr[:n]), rw), rw, rw)
tlsVersion := binary.BigEndian.Uint16(hdr[1:3])
if err == nil &&
hdr[0] == dissector.Handshake &&
@@ -154,18 +156,24 @@ func (h *redirectHandler) Handle(ctx context.Context, conn net.Conn, opts ...han
// try to sniff HTTP traffic
if isHTTP(string(hdr[:])) {
return h.handleHTTP(ctx, rw, conn.RemoteAddr(), dstAddr, ro, log)
ro2 := &xrecorder.HandlerRecorderObject{}
*ro2 = *ro
ro.Time = time.Time{}
return h.handleHTTP(ctx, rw, conn.RemoteAddr(), dstAddr, ro2, log)
}
}
log.Debugf("%s >> %s", conn.RemoteAddr(), dstAddr)
if h.options.Bypass != nil && h.options.Bypass.Contains(ctx, dstAddr.Network(), dstAddr.String()) {
if h.options.Bypass != nil &&
h.options.Bypass.Contains(ctx, dstAddr.Network(), dstAddr.String()) {
log.Debug("bypass: ", dstAddr)
return xbypass.ErrBypass
}
cc, err := h.options.Router.Dial(ctx, dstAddr.Network(), dstAddr.String())
var buf bytes.Buffer
cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), dstAddr.Network(), dstAddr.String())
ro.Route = buf.String()
if err != nil {
log.Error(err)
return err
@@ -182,27 +190,13 @@ func (h *redirectHandler) Handle(ctx context.Context, conn net.Conn, opts ...han
return nil
}
func (h *redirectHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, raddr, dstAddr net.Addr, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
req, err := http.ReadRequest(bufio.NewReader(rw))
func (h *redirectHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser, raddr, dstAddr net.Addr, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
br := bufio.NewReader(rw)
req, err := http.ReadRequest(br)
if err != nil {
return err
}
ro.HTTP = &xrecorder.HTTPRecorderObject{
Host: req.Host,
Proto: req.Proto,
Scheme: req.URL.Scheme,
Method: req.Method,
URI: req.RequestURI,
Request: xrecorder.HTTPRequestRecorderObject{
ContentLength: req.ContentLength,
Header: req.Header,
},
}
if clientIP := xhttp.GetClientIP(req); clientIP != nil {
ro.ClientIP = clientIP.String()
}
if log.IsLevelEnabled(logger.TraceLevel) {
dump, _ := httputil.DumpRequest(req, false)
log.Trace(string(dump))
@@ -217,12 +211,15 @@ func (h *redirectHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, radd
"host": host,
})
if h.options.Bypass != nil && h.options.Bypass.Contains(ctx, "tcp", host, bypass.WithPathOption(req.RequestURI)) {
if h.options.Bypass != nil &&
h.options.Bypass.Contains(ctx, "tcp", host, bypass.WithPathOption(req.RequestURI)) {
log.Debugf("bypass: %s %s", host, req.RequestURI)
return xbypass.ErrBypass
}
cc, err := h.options.Router.Dial(ctx, "tcp", host)
var buf bytes.Buffer
cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), "tcp", host)
ro.Route = buf.String()
if err != nil {
log.Error(err)
if !h.md.sniffingFallback {
@@ -231,7 +228,9 @@ func (h *redirectHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, radd
}
if cc == nil {
cc, err = h.options.Router.Dial(ctx, "tcp", dstAddr.String())
var buf bytes.Buffer
cc, err = h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), "tcp", dstAddr.String())
ro.Route = buf.String()
if err != nil {
log.Error(err)
return err
@@ -239,14 +238,138 @@ func (h *redirectHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, radd
}
defer cc.Close()
t := time.Now()
log.Infof("%s <-> %s", raddr, host)
if req.Header.Get("Upgrade") == "websocket" {
return h.handleWebsocket(ctx, raddr, rw, cc, req, ro, log)
}
if err := h.httpRoundTrip(ctx, raddr, rw, cc, req, ro, log); err != nil {
return err
}
for {
req, err := http.ReadRequest(br)
if err != nil {
if errors.Is(err, io.EOF) || errors.Is(err, net.ErrClosed) {
return nil
}
return err
}
if log.IsLevelEnabled(logger.TraceLevel) {
dump, _ := httputil.DumpRequest(req, false)
log.Trace(string(dump))
}
if err = h.httpRoundTrip(ctx, raddr, rw, cc, req, ro, log); err != nil {
return err
}
}
}
func (h *redirectHandler) handleWebsocket(ctx context.Context, raddr net.Addr, rw io.ReadWriteCloser, cc io.ReadWriter, req *http.Request, ro *xrecorder.HandlerRecorderObject, log logger.Logger) (err error) {
start := time.Now()
ro.Time = start
log.Infof("%s <-> %s", raddr, req.Host)
defer func() {
ro.Duration = time.Since(start)
if err != nil {
ro.Err = err.Error()
}
if err := ro.Record(ctx, h.recorder.Recorder); err != nil {
log.Errorf("record: %v", err)
}
log.WithFields(map[string]any{
"duration": time.Since(t),
}).Infof("%s >-< %s", raddr, host)
"duration": time.Since(start),
}).Infof("%s >-< %s", raddr, req.Host)
}()
ro.HTTP = &xrecorder.HTTPRecorderObject{
Host: req.Host,
Proto: req.Proto,
Scheme: req.URL.Scheme,
Method: req.Method,
URI: req.RequestURI,
Request: xrecorder.HTTPRequestRecorderObject{
ContentLength: req.ContentLength,
Header: req.Header.Clone(),
},
}
if err = req.Write(cc); err != nil {
return
}
res, err := http.ReadResponse(bufio.NewReader(cc), req)
if err != nil {
return
}
defer res.Body.Close()
ro.HTTP.StatusCode = res.StatusCode
ro.HTTP.Response.Header = res.Header
ro.HTTP.Response.ContentLength = res.ContentLength
if log.IsLevelEnabled(logger.TraceLevel) {
dump, _ := httputil.DumpResponse(res, false)
log.Trace(string(dump))
}
if err = res.Write(rw); err != nil {
return
}
netpkg.Transport(rw, cc)
return
}
func (h *redirectHandler) httpRoundTrip(ctx context.Context, raddr net.Addr, rw io.ReadWriteCloser, cc io.ReadWriter, req *http.Request, ro *xrecorder.HandlerRecorderObject, log logger.Logger) (err error) {
if req == nil {
return nil
}
start := time.Now()
ro.Time = start
log.Infof("%s <-> %s", raddr, req.Host)
defer func() {
if err != nil {
ro.Duration = time.Since(start)
ro.Err = err.Error()
if err := ro.Record(ctx, h.recorder.Recorder); err != nil {
log.Errorf("record: %v", err)
}
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >-< %s", raddr, req.Host)
}
}()
ro.HTTP = &xrecorder.HTTPRecorderObject{
Host: req.Host,
Proto: req.Proto,
Scheme: req.URL.Scheme,
Method: req.Method,
URI: req.RequestURI,
Request: xrecorder.HTTPRequestRecorderObject{
ContentLength: req.ContentLength,
Header: req.Header.Clone(),
},
}
// HTTP/1.0
if req.ProtoMajor == 1 && req.ProtoMinor == 0 {
if strings.ToLower(req.Header.Get("Connection")) == "keep-alive" {
req.Header.Del("Connection")
} else {
req.Header.Set("Connection", "close")
}
}
var reqBody *xhttp.Body
if opts := h.recorder.Options; opts != nil && opts.HTTPBody {
if req.Body != nil {
@@ -258,8 +381,8 @@ func (h *redirectHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, radd
req.Body = reqBody
}
}
if err := req.Write(cc); err != nil {
log.Error(err)
if err = req.Write(cc); err != nil {
return err
}
@@ -268,44 +391,68 @@ func (h *redirectHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, radd
ro.HTTP.Request.ContentLength = reqBody.Length()
}
br := bufio.NewReader(cc)
resp, err := http.ReadResponse(br, req)
if err != nil {
log.Error(err)
return err
}
defer resp.Body.Close()
go func() {
var err error
var res *http.Response
var respBody *xhttp.Body
ro.HTTP.Response.Header = resp.Header
ro.HTTP.StatusCode = resp.StatusCode
ro.HTTP.Response.ContentLength = resp.ContentLength
defer func() {
ro.Duration = time.Since(start)
if err != nil {
ro.Err = err.Error()
}
if res != nil {
ro.HTTP.StatusCode = res.StatusCode
ro.HTTP.Response.Header = res.Header
ro.HTTP.Response.ContentLength = res.ContentLength
if respBody != nil {
ro.HTTP.Response.Body = respBody.Content()
ro.HTTP.Response.ContentLength = respBody.Length()
}
}
ro.Record(ctx, h.recorder.Recorder)
}()
if log.IsLevelEnabled(logger.TraceLevel) {
dump, _ := httputil.DumpResponse(resp, false)
log.Trace(string(dump))
}
var respBody *xhttp.Body
if opts := h.recorder.Options; opts != nil && opts.HTTPBody {
maxSize := opts.MaxBodySize
if maxSize <= 0 {
maxSize = defaultBodySize
res, err = http.ReadResponse(bufio.NewReader(cc), req)
if err != nil {
rw.Close()
log.Errorf("read response: %v", err)
return
}
respBody = xhttp.NewBody(resp.Body, maxSize)
resp.Body = respBody
}
defer res.Body.Close()
if err := resp.Write(rw); err != nil {
log.Error(err)
return err
}
if log.IsLevelEnabled(logger.TraceLevel) {
dump, _ := httputil.DumpResponse(res, false)
log.Trace(string(dump))
}
if respBody != nil {
ro.HTTP.Response.Body = respBody.Content()
ro.HTTP.Response.ContentLength = resp.ContentLength
}
if res.Close {
defer rw.Close()
}
netpkg.Transport(rw, xio.NewReadWriter(br, cc))
// HTTP/1.0
if req.ProtoMajor == 1 && req.ProtoMinor == 0 {
if !res.Close {
res.Header.Set("Connection", "keep-alive")
}
res.ProtoMajor = req.ProtoMajor
res.ProtoMinor = req.ProtoMinor
}
if opts := h.recorder.Options; opts != nil && opts.HTTPBody {
maxSize := opts.MaxBodySize
if maxSize <= 0 {
maxSize = defaultBodySize
}
respBody = xhttp.NewBody(res.Body, maxSize)
res.Body = respBody
}
if err = res.Write(rw); err != nil {
rw.Close()
log.Errorf("write response: %v", err)
}
}()
return nil
}
@@ -318,7 +465,8 @@ func (h *redirectHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, rad
return err
}
ro.TLS = &xrecorder.TLSRecorderObject{
ServerName: clientHello.ServerName,
ServerName: clientHello.ServerName,
ClientHello: hex.EncodeToString(buf.Bytes()),
}
if len(clientHello.SupportedProtos) > 0 {
ro.TLS.Proto = clientHello.SupportedProtos[0]
@@ -340,12 +488,15 @@ func (h *redirectHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, rad
})
ro.Host = host
if h.options.Bypass != nil && h.options.Bypass.Contains(ctx, "tcp", host) {
if h.options.Bypass != nil &&
h.options.Bypass.Contains(ctx, "tcp", host) {
log.Debug("bypass: ", host)
return xbypass.ErrBypass
}
cc, err = h.options.Router.Dial(ctx, "tcp", host)
var routeBuf bytes.Buffer
cc, err = h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &routeBuf), "tcp", host)
ro.Route = routeBuf.String()
if err != nil {
log.Error(err)
@@ -356,7 +507,9 @@ func (h *redirectHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, rad
}
if cc == nil {
cc, err = h.options.Router.Dial(ctx, "tcp", dstAddr.String())
var routeBuf bytes.Buffer
cc, err = h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &routeBuf), "tcp", dstAddr.String())
ro.Route = routeBuf.String()
if err != nil {
log.Error(err)
return err
@@ -368,7 +521,8 @@ func (h *redirectHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, rad
return err
}
if serverHello, _ := dissector.ParseServerHello(io.TeeReader(cc, buf)); serverHello != nil {
serverHello, err := dissector.ParseServerHello(io.TeeReader(cc, buf))
if serverHello != nil {
ro.TLS.CipherSuite = tls_util.CipherSuite(serverHello.CipherSuite).String()
ro.TLS.CompressionMethod = serverHello.CompressionMethod
if serverHello.Proto != "" {
@@ -379,6 +533,10 @@ func (h *redirectHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, rad
}
}
if buf.Len() > 0 {
ro.TLS.ServerHello = hex.EncodeToString(buf.Bytes())
}
if _, err := buf.WriteTo(rw); err != nil {
return err
}
@@ -390,7 +548,7 @@ func (h *redirectHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, rad
"duration": time.Since(t),
}).Infof("%s >-< %s", raddr, host)
return nil
return err
}
func (h *redirectHandler) checkRateLimit(addr net.Addr) bool {
+6 -2
View File
@@ -1,6 +1,7 @@
package redirect
import (
"bytes"
"context"
"fmt"
"net"
@@ -100,12 +101,15 @@ func (h *redirectHandler) Handle(ctx context.Context, conn net.Conn, opts ...han
log.Debugf("%s >> %s", conn.RemoteAddr(), dstAddr)
if h.options.Bypass != nil && h.options.Bypass.Contains(ctx, dstAddr.Network(), dstAddr.String()) {
if h.options.Bypass != nil &&
h.options.Bypass.Contains(ctx, dstAddr.Network(), dstAddr.String()) {
log.Debug("bypass: ", dstAddr)
return xbypass.ErrBypass
}
cc, err := h.options.Router.Dial(ctx, dstAddr.Network(), dstAddr.String())
var buf bytes.Buffer
cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), dstAddr.Network(), dstAddr.String())
ro.Route = buf.String()
if err != nil {
log.Error(err)
return err
+6 -2
View File
@@ -1,6 +1,7 @@
package relay
import (
"bytes"
"context"
"errors"
"fmt"
@@ -18,9 +19,10 @@ import (
serial "github.com/go-gost/x/internal/util/serial"
traffic_wrapper "github.com/go-gost/x/limiter/traffic/wrapper"
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
xrecorder "github.com/go-gost/x/recorder"
)
func (h *relayHandler) handleConnect(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) (err error) {
func (h *relayHandler) handleConnect(ctx context.Context, conn net.Conn, network, address string, ro *xrecorder.HandlerRecorderObject, log logger.Logger) (err error) {
if network == "unix" || network == "serial" {
if host, _, _ := net.SplitHostPort(address); host != "" {
address = host
@@ -67,7 +69,9 @@ func (h *relayHandler) handleConnect(ctx context.Context, conn net.Conn, network
case "serial":
cc, err = serial.OpenPort(serial.ParseConfigFromAddr(address))
default:
cc, err = h.options.Router.Dial(ctx, network, address)
var buf bytes.Buffer
cc, err = h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), network, address)
ro.Route = buf.String()
}
if err != nil {
resp.Status = relay.StatusNetworkUnreachable
+1 -1
View File
@@ -193,7 +193,7 @@ func (h *relayHandler) Handle(ctx context.Context, conn net.Conn, opts ...handle
switch req.Cmd & relay.CmdMask {
case 0, relay.CmdConnect:
return h.handleConnect(ctx, conn, network, address, log)
return h.handleConnect(ctx, conn, network, address, ro, log)
case relay.CmdBind:
return h.handleBind(ctx, conn, network, address, log)
default:
+17 -6
View File
@@ -7,6 +7,7 @@ import (
"crypto/tls"
"encoding/base64"
"encoding/binary"
"encoding/hex"
"errors"
"hash/crc32"
"io"
@@ -176,7 +177,9 @@ func (h *sniHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, raddr net
ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: host})
}
cc, err := h.options.Router.Dial(ctx, "tcp", host)
var buf bytes.Buffer
cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), "tcp", host)
ro.Route = buf.String()
if err != nil {
log.Error(err)
return err
@@ -247,7 +250,7 @@ func (h *sniHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, raddr net
if respBody != nil {
ro.HTTP.Response.Body = respBody.Content()
ro.HTTP.Response.ContentLength = resp.ContentLength
ro.HTTP.Response.ContentLength = respBody.Length()
}
netpkg.Transport(rw, xio.NewReadWriter(br, cc))
@@ -268,7 +271,8 @@ func (h *sniHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, raddr ne
return err
}
ro.TLS = &xrecorder.TLSRecorderObject{
ServerName: clientHello.ServerName,
ServerName: clientHello.ServerName,
ClientHello: hex.EncodeToString(buf.Bytes()),
}
if len(clientHello.SupportedProtos) > 0 {
ro.TLS.Proto = clientHello.SupportedProtos[0]
@@ -295,7 +299,9 @@ func (h *sniHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, raddr ne
ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: host})
}
cc, err := h.options.Router.Dial(ctx, "tcp", host)
var routeBuf bytes.Buffer
cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &routeBuf), "tcp", host)
ro.Route = routeBuf.String()
if err != nil {
log.Error(err)
return err
@@ -306,7 +312,8 @@ func (h *sniHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, raddr ne
return err
}
if serverHello, _ := dissector.ParseServerHello(io.TeeReader(cc, buf)); serverHello != nil {
serverHello, err := dissector.ParseServerHello(io.TeeReader(cc, buf))
if serverHello != nil {
ro.TLS.CipherSuite = tls_util.CipherSuite(serverHello.CipherSuite).String()
ro.TLS.CompressionMethod = serverHello.CompressionMethod
if serverHello.Proto != "" {
@@ -317,6 +324,10 @@ func (h *sniHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, raddr ne
}
}
if buf.Len() > 0 {
ro.TLS.ServerHello = hex.EncodeToString(buf.Bytes())
}
if _, err := buf.WriteTo(rw); err != nil {
return err
}
@@ -328,7 +339,7 @@ func (h *sniHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, raddr ne
"duration": time.Since(t),
}).Infof("%s >-< %s", raddr, host)
return nil
return err
}
func (h *sniHandler) decodeHost(r io.Reader) (host string, err error) {
+6 -3
View File
@@ -1,6 +1,7 @@
package v4
import (
"bytes"
"context"
"errors"
"net"
@@ -148,7 +149,7 @@ func (h *socks4Handler) Handle(ctx context.Context, conn net.Conn, opts ...handl
switch req.Cmd {
case gosocks4.CmdConnect:
return h.handleConnect(ctx, conn, req, log)
return h.handleConnect(ctx, conn, req, ro, log)
case gosocks4.CmdBind:
return h.handleBind(ctx, conn, req)
default:
@@ -165,7 +166,7 @@ func (h *socks4Handler) Close() error {
return nil
}
func (h *socks4Handler) handleConnect(ctx context.Context, conn net.Conn, req *gosocks4.Request, log logger.Logger) error {
func (h *socks4Handler) handleConnect(ctx context.Context, conn net.Conn, req *gosocks4.Request, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
addr := req.Addr.String()
log = log.WithFields(map[string]any{
@@ -185,7 +186,9 @@ func (h *socks4Handler) handleConnect(ctx context.Context, conn net.Conn, req *g
ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: addr})
}
cc, err := h.options.Router.Dial(ctx, "tcp", addr)
var buf bytes.Buffer
cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), "tcp", addr)
ro.Route = buf.String()
if err != nil {
resp := gosocks4.NewReply(gosocks4.Failed, nil)
log.Trace(resp)
+6 -2
View File
@@ -1,6 +1,7 @@
package v5
import (
"bytes"
"context"
"fmt"
"net"
@@ -14,9 +15,10 @@ import (
netpkg "github.com/go-gost/x/internal/net"
traffic_wrapper "github.com/go-gost/x/limiter/traffic/wrapper"
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
xrecorder "github.com/go-gost/x/recorder"
)
func (h *socks5Handler) handleConnect(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) error {
func (h *socks5Handler) handleConnect(ctx context.Context, conn net.Conn, network, address string, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
log = log.WithFields(map[string]any{
"dst": fmt.Sprintf("%s/%s", address, network),
"cmd": "connect",
@@ -35,7 +37,9 @@ func (h *socks5Handler) handleConnect(ctx context.Context, conn net.Conn, networ
ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: address})
}
cc, err := h.options.Router.Dial(ctx, network, address)
var buf bytes.Buffer
cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), network, address)
ro.Route = buf.String()
if err != nil {
resp := gosocks5.NewReply(gosocks5.NetUnreachable, nil)
log.Trace(resp)
+3 -3
View File
@@ -148,17 +148,17 @@ func (h *socks5Handler) Handle(ctx context.Context, conn net.Conn, opts ...handl
switch req.Cmd {
case gosocks5.CmdConnect:
return h.handleConnect(ctx, conn, "tcp", address, log)
return h.handleConnect(ctx, conn, "tcp", address, ro, log)
case gosocks5.CmdBind:
return h.handleBind(ctx, conn, "tcp", address, log)
case socks.CmdMuxBind:
return h.handleMuxBind(ctx, conn, "tcp", address, log)
case gosocks5.CmdUdp:
ro.Network = "udp"
return h.handleUDP(ctx, conn, log)
return h.handleUDP(ctx, conn, ro, log)
case socks.CmdUDPTun:
ro.Network = "udp"
return h.handleUDPTun(ctx, conn, "udp", address, log)
return h.handleUDPTun(ctx, conn, "udp", address, ro, log)
default:
err = ErrUnknownCmd
log.Error(err)
+6 -2
View File
@@ -1,6 +1,7 @@
package v5
import (
"bytes"
"context"
"errors"
"fmt"
@@ -16,9 +17,10 @@ import (
"github.com/go-gost/x/internal/net/udp"
"github.com/go-gost/x/internal/util/socks"
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
xrecorder "github.com/go-gost/x/recorder"
)
func (h *socks5Handler) handleUDP(ctx context.Context, conn net.Conn, log logger.Logger) error {
func (h *socks5Handler) handleUDP(ctx context.Context, conn net.Conn, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
log = log.WithFields(map[string]any{
"cmd": "udp",
})
@@ -59,7 +61,9 @@ func (h *socks5Handler) handleUDP(ctx context.Context, conn net.Conn, log logger
log.Debugf("bind on %s OK", cc.LocalAddr())
// obtain a udp connection
c, err := h.options.Router.Dial(ctx, "udp", "") // UDP association
var buf bytes.Buffer
c, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), "udp", "") // UDP association
ro.Route = buf.String()
if err != nil {
log.Error(err)
return err
+6 -2
View File
@@ -1,6 +1,7 @@
package v5
import (
"bytes"
"context"
"errors"
"net"
@@ -14,9 +15,10 @@ import (
"github.com/go-gost/x/internal/net/udp"
"github.com/go-gost/x/internal/util/socks"
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
xrecorder "github.com/go-gost/x/recorder"
)
func (h *socks5Handler) handleUDPTun(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) error {
func (h *socks5Handler) handleUDPTun(ctx context.Context, conn net.Conn, network, address string, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
log = log.WithFields(map[string]any{
"cmd": "udp-tun",
})
@@ -37,7 +39,9 @@ func (h *socks5Handler) handleUDPTun(ctx context.Context, conn net.Conn, network
}
// obtain a udp connection
c, err := h.options.Router.Dial(ctx, "udp", "") // UDP association
var buf bytes.Buffer
c, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), "udp", "") // UDP association
ro.Route = buf.String()
if err != nil {
log.Error(err)
return err
+4 -1
View File
@@ -1,6 +1,7 @@
package ss
import (
"bytes"
"context"
"io"
"net"
@@ -136,7 +137,9 @@ func (h *ssHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.H
ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: addr.String()})
}
cc, err := h.options.Router.Dial(ctx, "tcp", addr.String())
var buf bytes.Buffer
cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), "tcp", addr.String())
ro.Route = buf.String()
if err != nil {
return err
}
+4 -1
View File
@@ -1,6 +1,7 @@
package ss
import (
"bytes"
"context"
"errors"
"net"
@@ -120,7 +121,9 @@ func (h *ssuHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.
}
// obtain a udp connection
c, err := h.options.Router.Dial(ctx, "udp", "") // UDP association
var buf bytes.Buffer
c, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), "udp", "") // UDP association
ro.Route = buf.String()
if err != nil {
log.Error(err)
return err
+4 -1
View File
@@ -1,6 +1,7 @@
package ssh
import (
"bytes"
"context"
"encoding/binary"
"errors"
@@ -132,7 +133,9 @@ func (h *forwardHandler) handleDirectForward(ctx context.Context, conn *sshd_uti
return xbypass.ErrBypass
}
cc, err := h.options.Router.Dial(ctx, "tcp", targetAddr)
var buf bytes.Buffer
cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), "tcp", targetAddr)
ro.Route = buf.String()
if err != nil {
return err
}
+2
View File
@@ -291,6 +291,8 @@ func SetTLSOptions(cfg *tls.Config, opts *config.TLSOptions) {
cfg.CipherSuites = append(cfg.CipherSuites, tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256)
}
}
cfg.NextProtos = opts.ALPN
}
func loadCA(caFile string) (cp *x509.CertPool, err error) {
+3
View File
@@ -44,6 +44,8 @@ type TLSRecorderObject struct {
CompressionMethod uint8 `json:"compressionMethod"`
Proto string `json:"proto"`
Version string `json:"version"`
ClientHello string `json:"clientHello"`
ServerHello string `json:"serverHello"`
}
type DNSRecorderObject struct {
@@ -68,6 +70,7 @@ type HandlerRecorderObject struct {
HTTP *HTTPRecorderObject `json:"http,omitempty"`
TLS *TLSRecorderObject `json:"tls,omitempty"`
DNS *DNSRecorderObject `json:"dns,omitempty"`
Route string `json:"route,omitempty"`
Err string `json:"err,omitempty"`
Duration time.Duration `json:"duration"`
Time time.Time `json:"time"`