add route for recorder
This commit is contained in:
+6
-5
@@ -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))
|
route = r.options.Chain.Route(ctx, network, ipAddr, chain.WithHostRouteOption(address))
|
||||||
}
|
}
|
||||||
|
|
||||||
if log.IsLevelEnabled(logger.DebugLevel) {
|
buf := ctxvalue.BufferFromContext(ctx)
|
||||||
buf := bytes.Buffer{}
|
if buf == nil {
|
||||||
|
buf = &bytes.Buffer{}
|
||||||
|
}
|
||||||
for _, node := range routePath(route) {
|
for _, node := range routePath(route) {
|
||||||
fmt.Fprintf(&buf, "%s@%s > ", node.Name, node.Addr)
|
fmt.Fprintf(buf, "%s@%s > ", node.Name, node.Addr)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(&buf, "%s", ipAddr)
|
fmt.Fprintf(buf, "%s", ipAddr)
|
||||||
log.Debugf("route(retry=%d) %s", i, buf.String())
|
log.Debugf("route(retry=%d) %s", i, buf.String())
|
||||||
}
|
|
||||||
|
|
||||||
if route == nil {
|
if route == nil {
|
||||||
route = DefaultRoute
|
route = DefaultRoute
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ type TLSOptions struct {
|
|||||||
MinVersion string `yaml:"minVersion,omitempty" json:"minVersion,omitempty"`
|
MinVersion string `yaml:"minVersion,omitempty" json:"minVersion,omitempty"`
|
||||||
MaxVersion string `yaml:"maxVersion,omitempty" json:"maxVersion,omitempty"`
|
MaxVersion string `yaml:"maxVersion,omitempty" json:"maxVersion,omitempty"`
|
||||||
CipherSuites []string `yaml:"cipherSuites,omitempty" json:"cipherSuites,omitempty"`
|
CipherSuites []string `yaml:"cipherSuites,omitempty" json:"cipherSuites,omitempty"`
|
||||||
|
ALPN []string `yaml:"alpn,omitempty" json:"alpn,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PluginConfig struct {
|
type PluginConfig struct {
|
||||||
|
|||||||
@@ -220,6 +220,7 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
|
|||||||
tlsCfg.Options.MinVersion = o.MinVersion
|
tlsCfg.Options.MinVersion = o.MinVersion
|
||||||
tlsCfg.Options.MaxVersion = o.MaxVersion
|
tlsCfg.Options.MaxVersion = o.MaxVersion
|
||||||
tlsCfg.Options.CipherSuites = o.CipherSuites
|
tlsCfg.Options.CipherSuites = o.CipherSuites
|
||||||
|
tlsCfg.Options.ALPN = o.ALPN
|
||||||
}
|
}
|
||||||
opts = append(opts, chain.TLSNodeOption(tlsCfg))
|
opts = append(opts, chain.TLSNodeOption(tlsCfg))
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-1
@@ -1,6 +1,9 @@
|
|||||||
package ctx
|
package ctx
|
||||||
|
|
||||||
import "context"
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
// clientAddrKey saves the client address.
|
// clientAddrKey saves the client address.
|
||||||
type clientAddrKey struct{}
|
type clientAddrKey struct{}
|
||||||
@@ -74,3 +77,18 @@ func ClientIDFromContext(ctx context.Context) ClientID {
|
|||||||
v, _ := ctx.Value(keyClientID).(ClientID)
|
v, _ := ctx.Value(keyClientID).(ClientID)
|
||||||
return v
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ require (
|
|||||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
|
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
|
||||||
github.com/gin-contrib/cors v1.6.0
|
github.com/gin-contrib/cors v1.6.0
|
||||||
github.com/gin-gonic/gin v1.9.1
|
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/gosocks4 v0.0.1
|
||||||
github.com/go-gost/gosocks5 v0.4.2
|
github.com/go-gost/gosocks5 v0.4.2
|
||||||
github.com/go-gost/plugin v0.1.1
|
github.com/go-gost/plugin v0.1.1
|
||||||
github.com/go-gost/relay v0.5.0
|
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/go-redis/redis/v8 v8.11.5
|
||||||
github.com/gobwas/glob v0.2.3
|
github.com/gobwas/glob v0.2.3
|
||||||
github.com/golang/snappy v0.0.4
|
github.com/golang/snappy v0.0.4
|
||||||
|
|||||||
@@ -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-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 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
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.5 h1:Rbj+xq+q2RkxviUErjwyT8wbqCa5WeV+UelyIs99dFI=
|
||||||
github.com/go-gost/core v0.1.4/go.mod h1:WGI43jOka7FAsSAwi/fSMaqxdR+E339ycb4NBGlFr6A=
|
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 h1:+k1sec8HlELuQV7rWftIkmy8UijzUt2I6t+iMPlGB2s=
|
||||||
github.com/go-gost/gosocks4 v0.0.1/go.mod h1:3B6L47HbU/qugDg4JnoFPHgJXE43Inz8Bah1QaN9qCc=
|
github.com/go-gost/gosocks4 v0.0.1/go.mod h1:3B6L47HbU/qugDg4JnoFPHgJXE43Inz8Bah1QaN9qCc=
|
||||||
github.com/go-gost/gosocks5 v0.4.2 h1:IianxHTkACPqCwiOAT3MHoMdSUl+SEPSRu1ikawC1Pc=
|
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/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 h1:JG1tgy/KWiVXS0ukuVXvbM0kbYuJTWxYpJ5JwzsCf/c=
|
||||||
github.com/go-gost/relay v0.5.0/go.mod h1:lcX+23LCQ3khIeASBo+tJ/WbwXFO32/N5YN6ucuYTG8=
|
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.1 h1:2zUOTPzCQAUQ54Rpy0UEi3JPMQSYsIFSeFeKrzmkCoU=
|
||||||
github.com/go-gost/tls-dissector v0.1.0/go.mod h1:/9QfdewqmHdaE362Hv5nDaSWLx3pCmtD870d6GaquXs=
|
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 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"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())
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,7 +196,9 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
|
|||||||
|
|
||||||
log.Debugf("%s >> %s", conn.RemoteAddr(), addr)
|
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 {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
// TODO: the router itself may be failed due to the failed node in the router,
|
// 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
|
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 {
|
if err != nil {
|
||||||
// TODO: the router itself may be failed due to the failed node in the router,
|
// TODO: the router itself may be failed due to the failed node in the router,
|
||||||
// the dead marker may be a wrong operation.
|
// 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,
|
MinVersion: tlsSettings.Options.MinVersion,
|
||||||
MaxVersion: tlsSettings.Options.MaxVersion,
|
MaxVersion: tlsSettings.Options.MaxVersion,
|
||||||
CipherSuites: tlsSettings.Options.CipherSuites,
|
CipherSuites: tlsSettings.Options.CipherSuites,
|
||||||
|
ALPN: tlsSettings.Options.ALPN,
|
||||||
})
|
})
|
||||||
cc = tls.Client(cc, cfg)
|
cc = tls.Client(cc, cfg)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,7 +195,9 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
|
|||||||
|
|
||||||
log.Debugf("%s >> %s", conn.RemoteAddr(), target.Addr)
|
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 {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
// TODO: the router itself may be failed due to the failed node in the router,
|
// 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 {
|
if _, _, err := net.SplitHostPort(host); err != nil {
|
||||||
host = net.JoinHostPort(strings.Trim(host, "[]"), "80")
|
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)
|
log.Debugf("bypass: %s %s", host, req.RequestURI)
|
||||||
resp.StatusCode = http.StatusForbidden
|
resp.StatusCode = http.StatusForbidden
|
||||||
resp.Write(rw)
|
resp.Write(rw)
|
||||||
@@ -350,7 +353,9 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser,
|
|||||||
bodyRewrites = httpSettings.RewriteBody
|
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 {
|
if err != nil {
|
||||||
// TODO: the router itself may be failed due to the failed node in the router,
|
// TODO: the router itself may be failed due to the failed node in the router,
|
||||||
// the dead marker may be a wrong operation.
|
// 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,
|
MinVersion: tlsSettings.Options.MinVersion,
|
||||||
MaxVersion: tlsSettings.Options.MaxVersion,
|
MaxVersion: tlsSettings.Options.MaxVersion,
|
||||||
CipherSuites: tlsSettings.Options.CipherSuites,
|
CipherSuites: tlsSettings.Options.CipherSuites,
|
||||||
|
ALPN: tlsSettings.Options.ALPN,
|
||||||
})
|
})
|
||||||
cc = tls.Client(cc, cfg)
|
cc = tls.Client(cc, cfg)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package http
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/binary"
|
"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})
|
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 {
|
if err != nil {
|
||||||
resp.StatusCode = http.StatusServiceUnavailable
|
resp.StatusCode = http.StatusServiceUnavailable
|
||||||
|
|
||||||
|
|||||||
@@ -243,7 +243,9 @@ func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
|
|||||||
ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: addr})
|
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 {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
resp.StatusCode = http.StatusServiceUnavailable
|
resp.StatusCode = http.StatusServiceUnavailable
|
||||||
|
|||||||
+218
-60
@@ -6,6 +6,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"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()),
|
"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.sniffing {
|
||||||
if h.md.sniffingTimeout > 0 {
|
if h.md.sniffingTimeout > 0 {
|
||||||
conn.SetReadDeadline(time.Now().Add(h.md.sniffingTimeout))
|
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 {
|
if h.md.sniffingTimeout > 0 {
|
||||||
conn.SetReadDeadline(time.Time{})
|
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])
|
tlsVersion := binary.BigEndian.Uint16(hdr[1:3])
|
||||||
if err == nil &&
|
if err == nil &&
|
||||||
hdr[0] == dissector.Handshake &&
|
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
|
// try to sniff HTTP traffic
|
||||||
if isHTTP(string(hdr[:])) {
|
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)
|
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)
|
log.Debug("bypass: ", dstAddr)
|
||||||
return xbypass.ErrBypass
|
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 {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return err
|
return err
|
||||||
@@ -182,27 +190,13 @@ func (h *redirectHandler) Handle(ctx context.Context, conn net.Conn, opts ...han
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *redirectHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, raddr, dstAddr net.Addr, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
|
func (h *redirectHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser, raddr, dstAddr net.Addr, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
|
||||||
req, err := http.ReadRequest(bufio.NewReader(rw))
|
br := bufio.NewReader(rw)
|
||||||
|
req, err := http.ReadRequest(br)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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) {
|
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||||
dump, _ := httputil.DumpRequest(req, false)
|
dump, _ := httputil.DumpRequest(req, false)
|
||||||
log.Trace(string(dump))
|
log.Trace(string(dump))
|
||||||
@@ -217,12 +211,15 @@ func (h *redirectHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, radd
|
|||||||
"host": host,
|
"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)
|
log.Debugf("bypass: %s %s", host, req.RequestURI)
|
||||||
return xbypass.ErrBypass
|
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 {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
if !h.md.sniffingFallback {
|
if !h.md.sniffingFallback {
|
||||||
@@ -231,7 +228,9 @@ func (h *redirectHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, radd
|
|||||||
}
|
}
|
||||||
|
|
||||||
if cc == nil {
|
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 {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return err
|
return err
|
||||||
@@ -239,14 +238,138 @@ func (h *redirectHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, radd
|
|||||||
}
|
}
|
||||||
defer cc.Close()
|
defer cc.Close()
|
||||||
|
|
||||||
t := time.Now()
|
if req.Header.Get("Upgrade") == "websocket" {
|
||||||
log.Infof("%s <-> %s", raddr, host)
|
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() {
|
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{
|
log.WithFields(map[string]any{
|
||||||
"duration": time.Since(t),
|
"duration": time.Since(start),
|
||||||
}).Infof("%s >-< %s", raddr, host)
|
}).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
|
var reqBody *xhttp.Body
|
||||||
if opts := h.recorder.Options; opts != nil && opts.HTTPBody {
|
if opts := h.recorder.Options; opts != nil && opts.HTTPBody {
|
||||||
if req.Body != nil {
|
if req.Body != nil {
|
||||||
@@ -258,8 +381,8 @@ func (h *redirectHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, radd
|
|||||||
req.Body = reqBody
|
req.Body = reqBody
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := req.Write(cc); err != nil {
|
|
||||||
log.Error(err)
|
if err = req.Write(cc); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,44 +391,68 @@ func (h *redirectHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, radd
|
|||||||
ro.HTTP.Request.ContentLength = reqBody.Length()
|
ro.HTTP.Request.ContentLength = reqBody.Length()
|
||||||
}
|
}
|
||||||
|
|
||||||
br := bufio.NewReader(cc)
|
go func() {
|
||||||
resp, err := http.ReadResponse(br, req)
|
var err error
|
||||||
if err != nil {
|
var res *http.Response
|
||||||
log.Error(err)
|
var respBody *xhttp.Body
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
ro.HTTP.Response.Header = resp.Header
|
defer func() {
|
||||||
ro.HTTP.StatusCode = resp.StatusCode
|
ro.Duration = time.Since(start)
|
||||||
ro.HTTP.Response.ContentLength = resp.ContentLength
|
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)
|
||||||
|
}()
|
||||||
|
|
||||||
|
res, err = http.ReadResponse(bufio.NewReader(cc), req)
|
||||||
|
if err != nil {
|
||||||
|
rw.Close()
|
||||||
|
log.Errorf("read response: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||||
dump, _ := httputil.DumpResponse(resp, false)
|
dump, _ := httputil.DumpResponse(res, false)
|
||||||
log.Trace(string(dump))
|
log.Trace(string(dump))
|
||||||
}
|
}
|
||||||
|
|
||||||
var respBody *xhttp.Body
|
if res.Close {
|
||||||
|
defer rw.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
if opts := h.recorder.Options; opts != nil && opts.HTTPBody {
|
||||||
maxSize := opts.MaxBodySize
|
maxSize := opts.MaxBodySize
|
||||||
if maxSize <= 0 {
|
if maxSize <= 0 {
|
||||||
maxSize = defaultBodySize
|
maxSize = defaultBodySize
|
||||||
}
|
}
|
||||||
respBody = xhttp.NewBody(resp.Body, maxSize)
|
respBody = xhttp.NewBody(res.Body, maxSize)
|
||||||
resp.Body = respBody
|
res.Body = respBody
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := resp.Write(rw); err != nil {
|
if err = res.Write(rw); err != nil {
|
||||||
log.Error(err)
|
rw.Close()
|
||||||
return err
|
log.Errorf("write response: %v", err)
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
if respBody != nil {
|
|
||||||
ro.HTTP.Response.Body = respBody.Content()
|
|
||||||
ro.HTTP.Response.ContentLength = resp.ContentLength
|
|
||||||
}
|
|
||||||
|
|
||||||
netpkg.Transport(rw, xio.NewReadWriter(br, cc))
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -319,6 +466,7 @@ func (h *redirectHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, rad
|
|||||||
}
|
}
|
||||||
ro.TLS = &xrecorder.TLSRecorderObject{
|
ro.TLS = &xrecorder.TLSRecorderObject{
|
||||||
ServerName: clientHello.ServerName,
|
ServerName: clientHello.ServerName,
|
||||||
|
ClientHello: hex.EncodeToString(buf.Bytes()),
|
||||||
}
|
}
|
||||||
if len(clientHello.SupportedProtos) > 0 {
|
if len(clientHello.SupportedProtos) > 0 {
|
||||||
ro.TLS.Proto = 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
|
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)
|
log.Debug("bypass: ", host)
|
||||||
return xbypass.ErrBypass
|
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 {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
|
|
||||||
@@ -356,7 +507,9 @@ func (h *redirectHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, rad
|
|||||||
}
|
}
|
||||||
|
|
||||||
if cc == nil {
|
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 {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return err
|
return err
|
||||||
@@ -368,7 +521,8 @@ func (h *redirectHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, rad
|
|||||||
return err
|
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.CipherSuite = tls_util.CipherSuite(serverHello.CipherSuite).String()
|
||||||
ro.TLS.CompressionMethod = serverHello.CompressionMethod
|
ro.TLS.CompressionMethod = serverHello.CompressionMethod
|
||||||
if serverHello.Proto != "" {
|
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 {
|
if _, err := buf.WriteTo(rw); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -390,7 +548,7 @@ func (h *redirectHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, rad
|
|||||||
"duration": time.Since(t),
|
"duration": time.Since(t),
|
||||||
}).Infof("%s >-< %s", raddr, host)
|
}).Infof("%s >-< %s", raddr, host)
|
||||||
|
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *redirectHandler) checkRateLimit(addr net.Addr) bool {
|
func (h *redirectHandler) checkRateLimit(addr net.Addr) bool {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package redirect
|
package redirect
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
@@ -100,12 +101,15 @@ func (h *redirectHandler) Handle(ctx context.Context, conn net.Conn, opts ...han
|
|||||||
|
|
||||||
log.Debugf("%s >> %s", conn.RemoteAddr(), dstAddr)
|
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)
|
log.Debug("bypass: ", dstAddr)
|
||||||
return xbypass.ErrBypass
|
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 {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package relay
|
package relay
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -18,9 +19,10 @@ import (
|
|||||||
serial "github.com/go-gost/x/internal/util/serial"
|
serial "github.com/go-gost/x/internal/util/serial"
|
||||||
traffic_wrapper "github.com/go-gost/x/limiter/traffic/wrapper"
|
traffic_wrapper "github.com/go-gost/x/limiter/traffic/wrapper"
|
||||||
stats_wrapper "github.com/go-gost/x/observer/stats/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 network == "unix" || network == "serial" {
|
||||||
if host, _, _ := net.SplitHostPort(address); host != "" {
|
if host, _, _ := net.SplitHostPort(address); host != "" {
|
||||||
address = host
|
address = host
|
||||||
@@ -67,7 +69,9 @@ func (h *relayHandler) handleConnect(ctx context.Context, conn net.Conn, network
|
|||||||
case "serial":
|
case "serial":
|
||||||
cc, err = serial.OpenPort(serial.ParseConfigFromAddr(address))
|
cc, err = serial.OpenPort(serial.ParseConfigFromAddr(address))
|
||||||
default:
|
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 {
|
if err != nil {
|
||||||
resp.Status = relay.StatusNetworkUnreachable
|
resp.Status = relay.StatusNetworkUnreachable
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ func (h *relayHandler) Handle(ctx context.Context, conn net.Conn, opts ...handle
|
|||||||
|
|
||||||
switch req.Cmd & relay.CmdMask {
|
switch req.Cmd & relay.CmdMask {
|
||||||
case 0, relay.CmdConnect:
|
case 0, relay.CmdConnect:
|
||||||
return h.handleConnect(ctx, conn, network, address, log)
|
return h.handleConnect(ctx, conn, network, address, ro, log)
|
||||||
case relay.CmdBind:
|
case relay.CmdBind:
|
||||||
return h.handleBind(ctx, conn, network, address, log)
|
return h.handleBind(ctx, conn, network, address, log)
|
||||||
default:
|
default:
|
||||||
|
|||||||
+16
-5
@@ -7,6 +7,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
"io"
|
"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})
|
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 {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return err
|
return err
|
||||||
@@ -247,7 +250,7 @@ func (h *sniHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, raddr net
|
|||||||
|
|
||||||
if respBody != nil {
|
if respBody != nil {
|
||||||
ro.HTTP.Response.Body = respBody.Content()
|
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))
|
netpkg.Transport(rw, xio.NewReadWriter(br, cc))
|
||||||
@@ -269,6 +272,7 @@ func (h *sniHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, raddr ne
|
|||||||
}
|
}
|
||||||
ro.TLS = &xrecorder.TLSRecorderObject{
|
ro.TLS = &xrecorder.TLSRecorderObject{
|
||||||
ServerName: clientHello.ServerName,
|
ServerName: clientHello.ServerName,
|
||||||
|
ClientHello: hex.EncodeToString(buf.Bytes()),
|
||||||
}
|
}
|
||||||
if len(clientHello.SupportedProtos) > 0 {
|
if len(clientHello.SupportedProtos) > 0 {
|
||||||
ro.TLS.Proto = 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})
|
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 {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return err
|
return err
|
||||||
@@ -306,7 +312,8 @@ func (h *sniHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, raddr ne
|
|||||||
return err
|
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.CipherSuite = tls_util.CipherSuite(serverHello.CipherSuite).String()
|
||||||
ro.TLS.CompressionMethod = serverHello.CompressionMethod
|
ro.TLS.CompressionMethod = serverHello.CompressionMethod
|
||||||
if serverHello.Proto != "" {
|
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 {
|
if _, err := buf.WriteTo(rw); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -328,7 +339,7 @@ func (h *sniHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, raddr ne
|
|||||||
"duration": time.Since(t),
|
"duration": time.Since(t),
|
||||||
}).Infof("%s >-< %s", raddr, host)
|
}).Infof("%s >-< %s", raddr, host)
|
||||||
|
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *sniHandler) decodeHost(r io.Reader) (host string, err error) {
|
func (h *sniHandler) decodeHost(r io.Reader) (host string, err error) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package v4
|
package v4
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
@@ -148,7 +149,7 @@ func (h *socks4Handler) Handle(ctx context.Context, conn net.Conn, opts ...handl
|
|||||||
|
|
||||||
switch req.Cmd {
|
switch req.Cmd {
|
||||||
case gosocks4.CmdConnect:
|
case gosocks4.CmdConnect:
|
||||||
return h.handleConnect(ctx, conn, req, log)
|
return h.handleConnect(ctx, conn, req, ro, log)
|
||||||
case gosocks4.CmdBind:
|
case gosocks4.CmdBind:
|
||||||
return h.handleBind(ctx, conn, req)
|
return h.handleBind(ctx, conn, req)
|
||||||
default:
|
default:
|
||||||
@@ -165,7 +166,7 @@ func (h *socks4Handler) Close() error {
|
|||||||
return nil
|
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()
|
addr := req.Addr.String()
|
||||||
|
|
||||||
log = log.WithFields(map[string]any{
|
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})
|
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 {
|
if err != nil {
|
||||||
resp := gosocks4.NewReply(gosocks4.Failed, nil)
|
resp := gosocks4.NewReply(gosocks4.Failed, nil)
|
||||||
log.Trace(resp)
|
log.Trace(resp)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package v5
|
package v5
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
@@ -14,9 +15,10 @@ import (
|
|||||||
netpkg "github.com/go-gost/x/internal/net"
|
netpkg "github.com/go-gost/x/internal/net"
|
||||||
traffic_wrapper "github.com/go-gost/x/limiter/traffic/wrapper"
|
traffic_wrapper "github.com/go-gost/x/limiter/traffic/wrapper"
|
||||||
stats_wrapper "github.com/go-gost/x/observer/stats/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{
|
log = log.WithFields(map[string]any{
|
||||||
"dst": fmt.Sprintf("%s/%s", address, network),
|
"dst": fmt.Sprintf("%s/%s", address, network),
|
||||||
"cmd": "connect",
|
"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})
|
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 {
|
if err != nil {
|
||||||
resp := gosocks5.NewReply(gosocks5.NetUnreachable, nil)
|
resp := gosocks5.NewReply(gosocks5.NetUnreachable, nil)
|
||||||
log.Trace(resp)
|
log.Trace(resp)
|
||||||
|
|||||||
@@ -148,17 +148,17 @@ func (h *socks5Handler) Handle(ctx context.Context, conn net.Conn, opts ...handl
|
|||||||
|
|
||||||
switch req.Cmd {
|
switch req.Cmd {
|
||||||
case gosocks5.CmdConnect:
|
case gosocks5.CmdConnect:
|
||||||
return h.handleConnect(ctx, conn, "tcp", address, log)
|
return h.handleConnect(ctx, conn, "tcp", address, ro, log)
|
||||||
case gosocks5.CmdBind:
|
case gosocks5.CmdBind:
|
||||||
return h.handleBind(ctx, conn, "tcp", address, log)
|
return h.handleBind(ctx, conn, "tcp", address, log)
|
||||||
case socks.CmdMuxBind:
|
case socks.CmdMuxBind:
|
||||||
return h.handleMuxBind(ctx, conn, "tcp", address, log)
|
return h.handleMuxBind(ctx, conn, "tcp", address, log)
|
||||||
case gosocks5.CmdUdp:
|
case gosocks5.CmdUdp:
|
||||||
ro.Network = "udp"
|
ro.Network = "udp"
|
||||||
return h.handleUDP(ctx, conn, log)
|
return h.handleUDP(ctx, conn, ro, log)
|
||||||
case socks.CmdUDPTun:
|
case socks.CmdUDPTun:
|
||||||
ro.Network = "udp"
|
ro.Network = "udp"
|
||||||
return h.handleUDPTun(ctx, conn, "udp", address, log)
|
return h.handleUDPTun(ctx, conn, "udp", address, ro, log)
|
||||||
default:
|
default:
|
||||||
err = ErrUnknownCmd
|
err = ErrUnknownCmd
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package v5
|
package v5
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -16,9 +17,10 @@ import (
|
|||||||
"github.com/go-gost/x/internal/net/udp"
|
"github.com/go-gost/x/internal/net/udp"
|
||||||
"github.com/go-gost/x/internal/util/socks"
|
"github.com/go-gost/x/internal/util/socks"
|
||||||
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
|
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{
|
log = log.WithFields(map[string]any{
|
||||||
"cmd": "udp",
|
"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())
|
log.Debugf("bind on %s OK", cc.LocalAddr())
|
||||||
|
|
||||||
// obtain a udp connection
|
// 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 {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package v5
|
package v5
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
@@ -14,9 +15,10 @@ import (
|
|||||||
"github.com/go-gost/x/internal/net/udp"
|
"github.com/go-gost/x/internal/net/udp"
|
||||||
"github.com/go-gost/x/internal/util/socks"
|
"github.com/go-gost/x/internal/util/socks"
|
||||||
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
|
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{
|
log = log.WithFields(map[string]any{
|
||||||
"cmd": "udp-tun",
|
"cmd": "udp-tun",
|
||||||
})
|
})
|
||||||
@@ -37,7 +39,9 @@ func (h *socks5Handler) handleUDPTun(ctx context.Context, conn net.Conn, network
|
|||||||
}
|
}
|
||||||
|
|
||||||
// obtain a udp connection
|
// 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 {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package ss
|
package ss
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"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()})
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package ss
|
package ss
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
@@ -120,7 +121,9 @@ func (h *ssuHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.
|
|||||||
}
|
}
|
||||||
|
|
||||||
// obtain a udp connection
|
// 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 {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package ssh
|
package ssh
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
@@ -132,7 +133,9 @@ func (h *forwardHandler) handleDirectForward(ctx context.Context, conn *sshd_uti
|
|||||||
return xbypass.ErrBypass
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.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) {
|
func loadCA(caFile string) (cp *x509.CertPool, err error) {
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ type TLSRecorderObject struct {
|
|||||||
CompressionMethod uint8 `json:"compressionMethod"`
|
CompressionMethod uint8 `json:"compressionMethod"`
|
||||||
Proto string `json:"proto"`
|
Proto string `json:"proto"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
|
ClientHello string `json:"clientHello"`
|
||||||
|
ServerHello string `json:"serverHello"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DNSRecorderObject struct {
|
type DNSRecorderObject struct {
|
||||||
@@ -68,6 +70,7 @@ type HandlerRecorderObject struct {
|
|||||||
HTTP *HTTPRecorderObject `json:"http,omitempty"`
|
HTTP *HTTPRecorderObject `json:"http,omitempty"`
|
||||||
TLS *TLSRecorderObject `json:"tls,omitempty"`
|
TLS *TLSRecorderObject `json:"tls,omitempty"`
|
||||||
DNS *DNSRecorderObject `json:"dns,omitempty"`
|
DNS *DNSRecorderObject `json:"dns,omitempty"`
|
||||||
|
Route string `json:"route,omitempty"`
|
||||||
Err string `json:"err,omitempty"`
|
Err string `json:"err,omitempty"`
|
||||||
Duration time.Duration `json:"duration"`
|
Duration time.Duration `json:"duration"`
|
||||||
Time time.Time `json:"time"`
|
Time time.Time `json:"time"`
|
||||||
|
|||||||
Reference in New Issue
Block a user