add handler recorder
This commit is contained in:
+71
-21
@@ -15,8 +15,12 @@ import (
|
||||
"github.com/go-gost/core/hosts"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/core/recorder"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
xhop "github.com/go-gost/x/hop"
|
||||
resolver_util "github.com/go-gost/x/internal/util/resolver"
|
||||
rate_limiter "github.com/go-gost/x/limiter/rate"
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
"github.com/go-gost/x/registry"
|
||||
"github.com/go-gost/x/resolver/exchanger"
|
||||
"github.com/miekg/dns"
|
||||
@@ -37,6 +41,7 @@ type dnsHandler struct {
|
||||
hostMapper hosts.HostMapper
|
||||
md metadata
|
||||
options handler.Options
|
||||
recorder recorder.RecorderObject
|
||||
}
|
||||
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
@@ -108,6 +113,13 @@ func (h *dnsHandler) Init(md md.Metadata) (err error) {
|
||||
h.exchangers["default"] = ex
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
if ro.Record == xrecorder.RecorderServiceHandler {
|
||||
h.recorder = ro
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -116,24 +128,44 @@ func (h *dnsHandler) Forward(hop hop.Hop) {
|
||||
h.hop = hop
|
||||
}
|
||||
|
||||
func (h *dnsHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
|
||||
func (h *dnsHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) (err error) {
|
||||
defer conn.Close()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
ro := &xrecorder.HandlerRecorderObject{
|
||||
Service: h.options.Service,
|
||||
Network: conn.LocalAddr().Network(),
|
||||
RemoteAddr: conn.RemoteAddr().String(),
|
||||
LocalAddr: conn.LocalAddr().String(),
|
||||
Time: start,
|
||||
SID: string(ctxvalue.SidFromContext(ctx)),
|
||||
}
|
||||
|
||||
log := h.options.Logger.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
})
|
||||
|
||||
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
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.Warnf("recorder: %v", err)
|
||||
}
|
||||
|
||||
log.WithFields(map[string]any{
|
||||
"duration": time.Since(start),
|
||||
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
|
||||
}()
|
||||
|
||||
if !h.checkRateLimit(conn.RemoteAddr()) {
|
||||
return nil
|
||||
return rate_limiter.ErrRateLimit
|
||||
}
|
||||
|
||||
b := bufpool.Get(h.md.bufferSize)
|
||||
@@ -145,7 +177,7 @@ func (h *dnsHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.
|
||||
return err
|
||||
}
|
||||
|
||||
reply, err := h.request(ctx, b[:n], log)
|
||||
reply, err := h.request(ctx, b[:n], ro, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -170,7 +202,7 @@ func (h *dnsHandler) checkRateLimit(addr net.Addr) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (h *dnsHandler) request(ctx context.Context, msg []byte, log logger.Logger) ([]byte, error) {
|
||||
func (h *dnsHandler) request(ctx context.Context, msg []byte, ro *xrecorder.HandlerRecorderObject, log logger.Logger) ([]byte, error) {
|
||||
mq := dns.Msg{}
|
||||
if err := mq.Unpack(msg); err != nil {
|
||||
log.Error(err)
|
||||
@@ -181,6 +213,14 @@ func (h *dnsHandler) request(ctx context.Context, msg []byte, log logger.Logger)
|
||||
return nil, errors.New("msg: empty question")
|
||||
}
|
||||
|
||||
ro.DNS = &xrecorder.DNSRecorderObject{
|
||||
ID: int(mq.Id),
|
||||
Name: mq.Question[0].Name,
|
||||
Class: dns.Class(mq.Question[0].Qclass).String(),
|
||||
Type: dns.Type(mq.Question[0].Qtype).String(),
|
||||
Question: mq.String(),
|
||||
}
|
||||
|
||||
resolver_util.AddSubnetOpt(&mq, h.md.clientIP)
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
@@ -188,13 +228,15 @@ func (h *dnsHandler) request(ctx context.Context, msg []byte, log logger.Logger)
|
||||
}
|
||||
|
||||
var mr *dns.Msg
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
defer func() {
|
||||
if mr != nil {
|
||||
defer func() {
|
||||
if mr != nil {
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
log.Trace(mr.String())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
ro.DNS.Answer = mr.String()
|
||||
}
|
||||
}()
|
||||
|
||||
if h.options.Bypass != nil && mq.Question[0].Qclass == dns.ClassINET {
|
||||
if h.options.Bypass.Contains(context.Background(), "udp", strings.Trim(mq.Question[0].Name, ".")) {
|
||||
@@ -214,10 +256,12 @@ func (h *dnsHandler) request(ctx context.Context, msg []byte, log logger.Logger)
|
||||
// only cache for single question message.
|
||||
if len(mq.Question) == 1 {
|
||||
var ttl time.Duration
|
||||
mr, ttl = h.cache.Load(resolver_util.NewCacheKey(&mq.Question[0]))
|
||||
mr, ttl = h.cache.Load(ctx, resolver_util.NewCacheKey(&mq.Question[0]))
|
||||
if mr != nil {
|
||||
mr.Id = mq.Id
|
||||
if int32(ttl.Seconds()) > 0 {
|
||||
ro.DNS.Cached = true
|
||||
|
||||
log.Debugf("message %d (cached): %s", mq.Id, mq.Question[0].String())
|
||||
b := bufpool.Get(h.md.bufferSize)
|
||||
return mr.PackBuffer(b)
|
||||
@@ -225,6 +269,12 @@ func (h *dnsHandler) request(ctx context.Context, msg []byte, log logger.Logger)
|
||||
}
|
||||
}
|
||||
|
||||
ex := h.selectExchanger(ctx, strings.Trim(mq.Question[0].Name, "."))
|
||||
if ex == nil {
|
||||
return nil, fmt.Errorf("exchange not found for %s", mq.Question[0].Name)
|
||||
}
|
||||
ro.Host = ex.String()
|
||||
|
||||
if mr != nil && h.md.async {
|
||||
b := bufpool.Get(h.md.bufferSize)
|
||||
reply, err := mr.PackBuffer(b)
|
||||
@@ -234,15 +284,21 @@ func (h *dnsHandler) request(ctx context.Context, msg []byte, log logger.Logger)
|
||||
h.cache.RefreshTTL(resolver_util.NewCacheKey(&mq.Question[0]))
|
||||
|
||||
log.Debugf("exchange message %d (async): %s", mq.Id, mq.Question[0].String())
|
||||
go h.exchange(ctx, &mq)
|
||||
go h.exchange(ctx, ex, &mq)
|
||||
return reply, nil
|
||||
}
|
||||
|
||||
log.Debugf("exchange message %d: %s", mq.Id, mq.Question[0].String())
|
||||
return h.exchange(ctx, &mq)
|
||||
mr, err := h.exchange(ctx, ex, &mq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b := bufpool.Get(h.md.bufferSize)
|
||||
return mr.PackBuffer(b)
|
||||
}
|
||||
|
||||
func (h *dnsHandler) exchange(ctx context.Context, mq *dns.Msg) ([]byte, error) {
|
||||
func (h *dnsHandler) exchange(ctx context.Context, ex exchanger.Exchanger, mq *dns.Msg) (*dns.Msg, error) {
|
||||
b := bufpool.Get(h.md.bufferSize)
|
||||
defer bufpool.Put(b)
|
||||
|
||||
@@ -251,12 +307,6 @@ func (h *dnsHandler) exchange(ctx context.Context, mq *dns.Msg) ([]byte, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ex := h.selectExchanger(ctx, strings.Trim(mq.Question[0].Name, "."))
|
||||
if ex == nil {
|
||||
err = fmt.Errorf("exchange not found for %s", mq.Question[0].Name)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reply, err := ex.Exchange(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -268,10 +318,10 @@ func (h *dnsHandler) exchange(ctx context.Context, mq *dns.Msg) ([]byte, error)
|
||||
}
|
||||
if len(mq.Question) == 1 {
|
||||
key := resolver_util.NewCacheKey(&mq.Question[0])
|
||||
h.cache.Store(key, mr, h.md.ttl)
|
||||
h.cache.Store(ctx, key, mr, h.md.ttl)
|
||||
}
|
||||
|
||||
return reply, nil
|
||||
return mr, nil
|
||||
}
|
||||
|
||||
// lookup host mapper
|
||||
|
||||
@@ -21,12 +21,16 @@ import (
|
||||
"github.com/go-gost/core/hop"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/core/recorder"
|
||||
xbypass "github.com/go-gost/x/bypass"
|
||||
"github.com/go-gost/x/config"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
xio "github.com/go-gost/x/internal/io"
|
||||
xnet "github.com/go-gost/x/internal/net"
|
||||
"github.com/go-gost/x/internal/util/forward"
|
||||
tls_util "github.com/go-gost/x/internal/util/tls"
|
||||
rate_limiter "github.com/go-gost/x/limiter/rate"
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
@@ -37,9 +41,10 @@ func init() {
|
||||
}
|
||||
|
||||
type forwardHandler struct {
|
||||
hop hop.Hop
|
||||
md metadata
|
||||
options handler.Options
|
||||
hop hop.Hop
|
||||
md metadata
|
||||
options handler.Options
|
||||
recorder recorder.RecorderObject
|
||||
}
|
||||
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
@@ -58,6 +63,13 @@ func (h *forwardHandler) Init(md md.Metadata) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
if ro.Record == xrecorder.RecorderServiceHandler {
|
||||
h.recorder = ro
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -66,24 +78,42 @@ func (h *forwardHandler) Forward(hop hop.Hop) {
|
||||
h.hop = hop
|
||||
}
|
||||
|
||||
func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
|
||||
func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) (err error) {
|
||||
defer conn.Close()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
ro := &xrecorder.HandlerRecorderObject{
|
||||
Service: h.options.Service,
|
||||
RemoteAddr: conn.RemoteAddr().String(),
|
||||
LocalAddr: conn.LocalAddr().String(),
|
||||
Time: start,
|
||||
SID: string(ctxvalue.SidFromContext(ctx)),
|
||||
}
|
||||
|
||||
log := h.options.Logger.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
})
|
||||
|
||||
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
defer func() {
|
||||
if !ro.Time.IsZero() {
|
||||
if err != nil {
|
||||
ro.Err = err.Error()
|
||||
}
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Record(ctx, h.recorder.Recorder)
|
||||
}
|
||||
|
||||
log.WithFields(map[string]any{
|
||||
"duration": time.Since(start),
|
||||
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
}()
|
||||
|
||||
if !h.checkRateLimit(conn.RemoteAddr()) {
|
||||
return nil
|
||||
return rate_limiter.ErrRateLimit
|
||||
}
|
||||
|
||||
network := "tcp"
|
||||
@@ -106,7 +136,11 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
|
||||
}
|
||||
|
||||
if protocol == forward.ProtoHTTP {
|
||||
h.handleHTTP(ctx, xio.NewReadWriteCloser(rw, rw, conn), conn.RemoteAddr(), log)
|
||||
ro2 := &xrecorder.HandlerRecorderObject{}
|
||||
*ro2 = *ro
|
||||
ro.Time = time.Time{}
|
||||
|
||||
h.handleHTTP(ctx, xio.NewReadWriteCloser(rw, rw, conn), conn.RemoteAddr(), ro2, log)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -144,6 +178,9 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
|
||||
}
|
||||
}
|
||||
|
||||
ro.Network = network
|
||||
ro.Host = addr
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
"host": host,
|
||||
"node": target.Name,
|
||||
@@ -177,7 +214,7 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser, remoteAddr net.Addr, log logger.Logger) (err error) {
|
||||
func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser, remoteAddr net.Addr, ro *xrecorder.HandlerRecorderObject, log logger.Logger) (err error) {
|
||||
br := bufio.NewReader(rw)
|
||||
|
||||
for {
|
||||
@@ -202,6 +239,27 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser,
|
||||
log.Trace(string(dump))
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
ro.Time = start
|
||||
ro.HTTP = &xrecorder.HTTPRecorderObject{
|
||||
Host: req.Host,
|
||||
Proto: req.Proto,
|
||||
Scheme: req.URL.Scheme,
|
||||
Method: req.Method,
|
||||
URI: req.RequestURI,
|
||||
RequestHeader: req.Header.Clone(),
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
ro.HTTP.StatusCode = resp.StatusCode
|
||||
ro.HTTP.ResponseHeader = resp.Header
|
||||
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Err = err.Error()
|
||||
ro.Record(ctx, h.recorder.Recorder)
|
||||
}
|
||||
}()
|
||||
|
||||
host := req.Host
|
||||
if _, _, err := net.SplitHostPort(host); err != nil {
|
||||
host = net.JoinHostPort(strings.Trim(host, "[]"), "80")
|
||||
@@ -209,7 +267,8 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser,
|
||||
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
|
||||
return resp.Write(rw)
|
||||
resp.Write(rw)
|
||||
return xbypass.ErrBypass
|
||||
}
|
||||
|
||||
if addr := getRealClientAddr(req, remoteAddr); addr != remoteAddr {
|
||||
@@ -236,6 +295,8 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser,
|
||||
return resp.Write(rw)
|
||||
}
|
||||
|
||||
ro.Host = target.Addr
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
"host": req.Host,
|
||||
"node": target.Name,
|
||||
@@ -305,14 +366,15 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser,
|
||||
cc = tls.Client(cc, cfg)
|
||||
}
|
||||
|
||||
if err := req.Write(cc); err != nil {
|
||||
if err = req.Write(cc); err != nil {
|
||||
cc.Close()
|
||||
log.Warnf("send request to node %s(%s): %v", target.Name, target.Addr, err)
|
||||
return resp.Write(rw)
|
||||
resp.Write(rw)
|
||||
return err
|
||||
}
|
||||
|
||||
if req.Header.Get("Upgrade") == "websocket" {
|
||||
err := xnet.Transport(cc, xio.NewReadWriter(br, rw))
|
||||
err = xnet.Transport(cc, xio.NewReadWriter(br, rw))
|
||||
if err == nil {
|
||||
err = io.EOF
|
||||
}
|
||||
@@ -322,12 +384,28 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser,
|
||||
go func() {
|
||||
defer cc.Close()
|
||||
|
||||
res, err := http.ReadResponse(bufio.NewReader(cc), req)
|
||||
var err error
|
||||
var res *http.Response
|
||||
|
||||
defer func() {
|
||||
ro.Duration = time.Since(start)
|
||||
if err != nil {
|
||||
ro.Err = err.Error()
|
||||
}
|
||||
if res != nil && ro.HTTP != nil {
|
||||
ro.HTTP.ResponseHeader = res.Header
|
||||
ro.HTTP.StatusCode = res.StatusCode
|
||||
}
|
||||
ro.Record(ctx, h.recorder.Recorder)
|
||||
}()
|
||||
|
||||
res, err = http.ReadResponse(bufio.NewReader(cc), req)
|
||||
if err != nil {
|
||||
log.Warnf("read response from node %s(%s): %v", target.Name, target.Addr, err)
|
||||
resp.Write(rw)
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
dump, _ := httputil.DumpResponse(res, false)
|
||||
@@ -338,7 +416,7 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser,
|
||||
defer rw.Close()
|
||||
}
|
||||
|
||||
if err := h.rewriteBody(res, bodyRewrites...); err != nil {
|
||||
if err = h.rewriteBody(res, bodyRewrites...); err != nil {
|
||||
rw.Close()
|
||||
log.Errorf("rewrite body: %v", err)
|
||||
return
|
||||
|
||||
@@ -22,6 +22,8 @@ import (
|
||||
"github.com/go-gost/core/logger"
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/core/metadata/util"
|
||||
"github.com/go-gost/core/recorder"
|
||||
xbypass "github.com/go-gost/x/bypass"
|
||||
"github.com/go-gost/x/config"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
xio "github.com/go-gost/x/internal/io"
|
||||
@@ -29,6 +31,8 @@ import (
|
||||
"github.com/go-gost/x/internal/net/proxyproto"
|
||||
"github.com/go-gost/x/internal/util/forward"
|
||||
tls_util "github.com/go-gost/x/internal/util/tls"
|
||||
rate_limiter "github.com/go-gost/x/limiter/rate"
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
@@ -38,9 +42,10 @@ func init() {
|
||||
}
|
||||
|
||||
type forwardHandler struct {
|
||||
hop hop.Hop
|
||||
md metadata
|
||||
options handler.Options
|
||||
hop hop.Hop
|
||||
md metadata
|
||||
options handler.Options
|
||||
recorder recorder.RecorderObject
|
||||
}
|
||||
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
@@ -59,6 +64,13 @@ func (h *forwardHandler) Init(md mdata.Metadata) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
if ro.Record == xrecorder.RecorderServiceHandler {
|
||||
h.recorder = ro
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -67,24 +79,42 @@ func (h *forwardHandler) Forward(hop hop.Hop) {
|
||||
h.hop = hop
|
||||
}
|
||||
|
||||
func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
|
||||
func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) (err error) {
|
||||
defer conn.Close()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
ro := &xrecorder.HandlerRecorderObject{
|
||||
Service: h.options.Service,
|
||||
RemoteAddr: conn.RemoteAddr().String(),
|
||||
LocalAddr: conn.LocalAddr().String(),
|
||||
Time: start,
|
||||
SID: string(ctxvalue.SidFromContext(ctx)),
|
||||
}
|
||||
|
||||
log := h.options.Logger.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
})
|
||||
|
||||
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
defer func() {
|
||||
if !ro.Time.IsZero() {
|
||||
if err != nil {
|
||||
ro.Err = err.Error()
|
||||
}
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Record(ctx, h.recorder.Recorder)
|
||||
}
|
||||
|
||||
log.WithFields(map[string]any{
|
||||
"duration": time.Since(start),
|
||||
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
}()
|
||||
|
||||
if !h.checkRateLimit(conn.RemoteAddr()) {
|
||||
return nil
|
||||
return rate_limiter.ErrRateLimit
|
||||
}
|
||||
|
||||
network := "tcp"
|
||||
@@ -108,7 +138,11 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
|
||||
}
|
||||
}
|
||||
if protocol == forward.ProtoHTTP {
|
||||
h.handleHTTP(ctx, xio.NewReadWriteCloser(rw, rw, conn), conn.RemoteAddr(), localAddr, log)
|
||||
ro2 := &xrecorder.HandlerRecorderObject{}
|
||||
*ro2 = *ro
|
||||
ro.Time = time.Time{}
|
||||
|
||||
h.handleHTTP(ctx, xio.NewReadWriteCloser(rw, rw, conn), conn.RemoteAddr(), localAddr, ro2, log)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -143,6 +177,9 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
|
||||
}
|
||||
}
|
||||
|
||||
ro.Network = network
|
||||
ro.Host = target.Addr
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
"host": host,
|
||||
"node": target.Name,
|
||||
@@ -178,7 +215,7 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser, remoteAddr net.Addr, localAddr net.Addr, log logger.Logger) (err error) {
|
||||
func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser, remoteAddr net.Addr, localAddr net.Addr, ro *xrecorder.HandlerRecorderObject, log logger.Logger) (err error) {
|
||||
br := bufio.NewReader(rw)
|
||||
|
||||
for {
|
||||
@@ -202,6 +239,27 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser,
|
||||
log.Trace(string(dump))
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
ro.Time = start
|
||||
ro.HTTP = &xrecorder.HTTPRecorderObject{
|
||||
Host: req.Host,
|
||||
Proto: req.Proto,
|
||||
Scheme: req.URL.Scheme,
|
||||
Method: req.Method,
|
||||
URI: req.RequestURI,
|
||||
RequestHeader: req.Header.Clone(),
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
ro.HTTP.StatusCode = resp.StatusCode
|
||||
ro.HTTP.ResponseHeader = resp.Header
|
||||
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Err = err.Error()
|
||||
ro.Record(ctx, h.recorder.Recorder)
|
||||
}
|
||||
}()
|
||||
|
||||
host := req.Host
|
||||
if _, _, err := net.SplitHostPort(host); err != nil {
|
||||
host = net.JoinHostPort(strings.Trim(host, "[]"), "80")
|
||||
@@ -209,7 +267,8 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser,
|
||||
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
|
||||
return resp.Write(rw)
|
||||
resp.Write(rw)
|
||||
return xbypass.ErrBypass
|
||||
}
|
||||
|
||||
if addr := getRealClientAddr(req, remoteAddr); addr != remoteAddr {
|
||||
@@ -236,6 +295,8 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser,
|
||||
return resp.Write(rw)
|
||||
}
|
||||
|
||||
ro.Host = target.Addr
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
"host": req.Host,
|
||||
"node": target.Name,
|
||||
@@ -306,10 +367,11 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser,
|
||||
|
||||
cc = proxyproto.WrapClientConn(h.md.proxyProtocol, remoteAddr, localAddr, cc)
|
||||
|
||||
if err := req.Write(cc); err != nil {
|
||||
if err = req.Write(cc); err != nil {
|
||||
cc.Close()
|
||||
log.Warnf("send request to node %s(%s): %v", target.Name, target.Addr, err)
|
||||
return resp.Write(rw)
|
||||
resp.Write(rw)
|
||||
return err
|
||||
}
|
||||
|
||||
if req.Header.Get("Upgrade") == "websocket" {
|
||||
@@ -323,12 +385,28 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser,
|
||||
go func() {
|
||||
defer cc.Close()
|
||||
|
||||
res, err := http.ReadResponse(bufio.NewReader(cc), req)
|
||||
var err error
|
||||
var res *http.Response
|
||||
|
||||
defer func() {
|
||||
ro.Duration = time.Since(start)
|
||||
if err != nil {
|
||||
ro.Err = err.Error()
|
||||
}
|
||||
if res != nil && ro.HTTP != nil {
|
||||
ro.HTTP.ResponseHeader = res.Header
|
||||
ro.HTTP.StatusCode = res.StatusCode
|
||||
}
|
||||
ro.Record(ctx, h.recorder.Recorder)
|
||||
}()
|
||||
|
||||
res, err = http.ReadResponse(bufio.NewReader(cc), req)
|
||||
if err != nil {
|
||||
log.Warnf("read response from node %s(%s): %v", target.Name, target.Addr, err)
|
||||
resp.Write(rw)
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
dump, _ := httputil.DumpResponse(res, false)
|
||||
@@ -339,7 +417,7 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriteCloser,
|
||||
defer rw.Close()
|
||||
}
|
||||
|
||||
if err := h.rewriteBody(res, bodyRewrites...); err != nil {
|
||||
if err = h.rewriteBody(res, bodyRewrites...); err != nil {
|
||||
rw.Close()
|
||||
log.Errorf("rewrite body: %v", err)
|
||||
return
|
||||
|
||||
+104
-23
@@ -24,13 +24,17 @@ import (
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/core/observer/stats"
|
||||
"github.com/go-gost/core/recorder"
|
||||
xbypass "github.com/go-gost/x/bypass"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
xio "github.com/go-gost/x/internal/io"
|
||||
netpkg "github.com/go-gost/x/internal/net"
|
||||
limiter_util "github.com/go-gost/x/internal/util/limiter"
|
||||
stats_util "github.com/go-gost/x/internal/util/stats"
|
||||
rate_limiter "github.com/go-gost/x/limiter/rate"
|
||||
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"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
@@ -39,11 +43,12 @@ func init() {
|
||||
}
|
||||
|
||||
type httpHandler struct {
|
||||
md metadata
|
||||
options handler.Options
|
||||
stats *stats_util.HandlerStats
|
||||
limiter traffic.TrafficLimiter
|
||||
cancel context.CancelFunc
|
||||
md metadata
|
||||
options handler.Options
|
||||
stats *stats_util.HandlerStats
|
||||
limiter traffic.TrafficLimiter
|
||||
cancel context.CancelFunc
|
||||
recorder recorder.RecorderObject
|
||||
}
|
||||
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
@@ -74,15 +79,29 @@ func (h *httpHandler) Init(md md.Metadata) error {
|
||||
h.limiter = limiter_util.NewCachedTrafficLimiter(limiter, 30*time.Second, 60*time.Second)
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
if ro.Record == xrecorder.RecorderServiceHandler {
|
||||
h.recorder = ro
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *httpHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
|
||||
func (h *httpHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) (err error) {
|
||||
defer conn.Close()
|
||||
|
||||
// ctx = sx.ContextWithHash(ctx, &sx.Hash{})
|
||||
|
||||
start := time.Now()
|
||||
|
||||
ro := &xrecorder.HandlerRecorderObject{
|
||||
Service: h.options.Service,
|
||||
RemoteAddr: conn.RemoteAddr().String(),
|
||||
LocalAddr: conn.LocalAddr().String(),
|
||||
Time: start,
|
||||
SID: string(ctxvalue.SidFromContext(ctx)),
|
||||
}
|
||||
|
||||
log := h.options.Logger.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
@@ -90,13 +109,21 @@ func (h *httpHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler
|
||||
})
|
||||
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
defer func() {
|
||||
if !ro.Time.IsZero() {
|
||||
if err != nil {
|
||||
ro.Err = err.Error()
|
||||
}
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Record(ctx, h.recorder.Recorder)
|
||||
}
|
||||
|
||||
log.WithFields(map[string]any{
|
||||
"duration": time.Since(start),
|
||||
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
}()
|
||||
|
||||
if !h.checkRateLimit(conn.RemoteAddr()) {
|
||||
return nil
|
||||
return rate_limiter.ErrRateLimit
|
||||
}
|
||||
|
||||
req, err := http.ReadRequest(bufio.NewReader(conn))
|
||||
@@ -106,7 +133,7 @@ func (h *httpHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler
|
||||
}
|
||||
defer req.Body.Close()
|
||||
|
||||
return h.handleRequest(ctx, conn, req, log)
|
||||
return h.handleRequest(ctx, conn, req, ro, log)
|
||||
}
|
||||
|
||||
func (h *httpHandler) Close() error {
|
||||
@@ -116,7 +143,7 @@ func (h *httpHandler) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *http.Request, log logger.Logger) error {
|
||||
func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *http.Request, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
|
||||
if !req.URL.IsAbs() && govalidator.IsDNSName(req.Host) {
|
||||
req.URL.Scheme = "http"
|
||||
}
|
||||
@@ -125,6 +152,7 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
|
||||
if network != "udp" {
|
||||
network = "tcp"
|
||||
}
|
||||
ro.Network = network
|
||||
|
||||
// Try to get the actual host.
|
||||
// Compatible with GOST 2.x.
|
||||
@@ -133,15 +161,13 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
|
||||
req.Host = h
|
||||
}
|
||||
}
|
||||
req.Header.Del("Gost-Target")
|
||||
|
||||
if v := req.Header.Get("X-Gost-Target"); v != "" {
|
||||
if h, err := h.decodeServerName(v); err == nil {
|
||||
req.Host = h
|
||||
}
|
||||
}
|
||||
req.Header.Del("X-Gost-Target")
|
||||
|
||||
ro.Host = req.Host
|
||||
addr := req.Host
|
||||
if _, port, _ := net.SplitHostPort(addr); port == "" {
|
||||
addr = net.JoinHostPort(strings.Trim(addr, "[]"), "80")
|
||||
@@ -150,8 +176,10 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
|
||||
fields := map[string]any{
|
||||
"dst": addr,
|
||||
}
|
||||
|
||||
if u, _, _ := h.basicProxyAuth(req.Header.Get("Proxy-Authorization")); u != "" {
|
||||
fields["user"] = u
|
||||
ro.Client = u
|
||||
}
|
||||
log = log.WithFields(fields)
|
||||
|
||||
@@ -170,14 +198,26 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
|
||||
if resp.Header == nil {
|
||||
resp.Header = http.Header{}
|
||||
}
|
||||
|
||||
if resp.Header.Get("Proxy-Agent") == "" {
|
||||
resp.Header.Set("Proxy-Agent", h.md.proxyAgent)
|
||||
}
|
||||
|
||||
ro.HTTP = &xrecorder.HTTPRecorderObject{
|
||||
Host: req.Host,
|
||||
Proto: req.Proto,
|
||||
Scheme: req.URL.Scheme,
|
||||
Method: req.Method,
|
||||
URI: req.RequestURI,
|
||||
RequestHeader: req.Header.Clone(),
|
||||
}
|
||||
defer func() {
|
||||
ro.HTTP.StatusCode = resp.StatusCode
|
||||
ro.HTTP.ResponseHeader = resp.Header
|
||||
}()
|
||||
|
||||
clientID, ok := h.authenticate(ctx, conn, req, resp, log)
|
||||
if !ok {
|
||||
return nil
|
||||
return errors.New("authenication failed")
|
||||
}
|
||||
ctx = ctxvalue.ContextWithClientID(ctx, ctxvalue.ClientID(clientID))
|
||||
|
||||
@@ -189,8 +229,8 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
|
||||
log.Trace(string(dump))
|
||||
}
|
||||
log.Debug("bypass: ", addr)
|
||||
|
||||
return resp.Write(conn)
|
||||
resp.Write(conn)
|
||||
return xbypass.ErrBypass
|
||||
}
|
||||
|
||||
if network == "udp" {
|
||||
@@ -209,8 +249,6 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
|
||||
return resp.Write(conn)
|
||||
}
|
||||
|
||||
req.Header.Del("Proxy-Authorization")
|
||||
|
||||
switch h.md.hash {
|
||||
case "host":
|
||||
ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: addr})
|
||||
@@ -249,7 +287,11 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
|
||||
}
|
||||
|
||||
if req.Method != http.MethodConnect {
|
||||
return h.handleProxy(xio.NewReadWriteCloser(rw, rw, conn), cc, req, log)
|
||||
ro2 := &xrecorder.HandlerRecorderObject{}
|
||||
*ro2 = *ro
|
||||
ro.Time = time.Time{}
|
||||
|
||||
return h.handleProxy(ctx, xio.NewReadWriteCloser(rw, rw, conn), cc, req, ro2, log)
|
||||
}
|
||||
|
||||
resp.StatusCode = http.StatusOK
|
||||
@@ -274,7 +316,7 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *httpHandler) handleProxy(rw io.ReadWriteCloser, cc io.ReadWriter, req *http.Request, log logger.Logger) (err error) {
|
||||
func (h *httpHandler) handleProxy(ctx context.Context, rw io.ReadWriteCloser, cc io.ReadWriter, req *http.Request, ro *xrecorder.HandlerRecorderObject, log logger.Logger) (err error) {
|
||||
roundTrip := func(req *http.Request) error {
|
||||
if req == nil {
|
||||
return nil
|
||||
@@ -287,6 +329,21 @@ func (h *httpHandler) handleProxy(rw io.ReadWriteCloser, cc io.ReadWriter, req *
|
||||
StatusCode: http.StatusServiceUnavailable,
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
ro.Time = start
|
||||
|
||||
if ro.HTTP != nil {
|
||||
ro.HTTP = &xrecorder.HTTPRecorderObject{
|
||||
Host: req.Host,
|
||||
Proto: req.Proto,
|
||||
Scheme: req.URL.Scheme,
|
||||
Method: req.Method,
|
||||
URI: req.RequestURI,
|
||||
RequestHeader: req.Header.Clone(),
|
||||
StatusCode: resp.StatusCode,
|
||||
}
|
||||
}
|
||||
|
||||
// HTTP/1.0
|
||||
if req.ProtoMajor == 1 && req.ProtoMinor == 0 {
|
||||
if strings.ToLower(req.Header.Get("Connection")) == "keep-alive" {
|
||||
@@ -296,20 +353,44 @@ func (h *httpHandler) handleProxy(rw io.ReadWriteCloser, cc io.ReadWriter, req *
|
||||
}
|
||||
}
|
||||
|
||||
req.Header.Del("Proxy-Authorization")
|
||||
req.Header.Del("Proxy-Connection")
|
||||
req.Header.Del("Gost-Target")
|
||||
req.Header.Del("X-Gost-Target")
|
||||
|
||||
if err = req.Write(cc); err != nil {
|
||||
resp.Write(rw)
|
||||
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Err = err.Error()
|
||||
ro.Record(ctx, h.recorder.Recorder)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
res, err := http.ReadResponse(bufio.NewReader(cc), req)
|
||||
var err error
|
||||
var res *http.Response
|
||||
|
||||
defer func() {
|
||||
ro.Duration = time.Since(start)
|
||||
if err != nil {
|
||||
ro.Err = err.Error()
|
||||
}
|
||||
if res != nil && ro.HTTP != nil {
|
||||
ro.HTTP.ResponseHeader = res.Header
|
||||
ro.HTTP.StatusCode = res.StatusCode
|
||||
}
|
||||
ro.Record(ctx, h.recorder.Recorder)
|
||||
}()
|
||||
|
||||
res, err = http.ReadResponse(bufio.NewReader(cc), req)
|
||||
if err != nil {
|
||||
h.options.Logger.Errorf("read response: %v", err)
|
||||
resp.Write(rw)
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
dump, _ := httputil.DumpResponse(res, false)
|
||||
|
||||
+67
-17
@@ -24,13 +24,17 @@ import (
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/core/observer/stats"
|
||||
"github.com/go-gost/core/recorder"
|
||||
xbypass "github.com/go-gost/x/bypass"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
xio "github.com/go-gost/x/internal/io"
|
||||
netpkg "github.com/go-gost/x/internal/net"
|
||||
limiter_util "github.com/go-gost/x/internal/util/limiter"
|
||||
stats_util "github.com/go-gost/x/internal/util/stats"
|
||||
rate_limiter "github.com/go-gost/x/limiter/rate"
|
||||
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"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
@@ -39,11 +43,12 @@ func init() {
|
||||
}
|
||||
|
||||
type http2Handler struct {
|
||||
md metadata
|
||||
options handler.Options
|
||||
stats *stats_util.HandlerStats
|
||||
limiter traffic.TrafficLimiter
|
||||
cancel context.CancelFunc
|
||||
md metadata
|
||||
options handler.Options
|
||||
stats *stats_util.HandlerStats
|
||||
limiter traffic.TrafficLimiter
|
||||
cancel context.CancelFunc
|
||||
recorder recorder.RecorderObject
|
||||
}
|
||||
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
@@ -74,31 +79,55 @@ func (h *http2Handler) Init(md md.Metadata) error {
|
||||
h.limiter = limiter_util.NewCachedTrafficLimiter(limiter, 30*time.Second, 60*time.Second)
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
if ro.Record == xrecorder.RecorderServiceHandler {
|
||||
h.recorder = ro
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *http2Handler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
|
||||
func (h *http2Handler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) (err error) {
|
||||
defer conn.Close()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
ro := &xrecorder.HandlerRecorderObject{
|
||||
Service: h.options.Service,
|
||||
RemoteAddr: conn.RemoteAddr().String(),
|
||||
LocalAddr: conn.LocalAddr().String(),
|
||||
Network: "tcp",
|
||||
Time: start,
|
||||
SID: string(ctxvalue.SidFromContext(ctx)),
|
||||
}
|
||||
|
||||
log := h.options.Logger.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
})
|
||||
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
ro.Err = err.Error()
|
||||
}
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Record(ctx, h.recorder.Recorder)
|
||||
|
||||
log.WithFields(map[string]any{
|
||||
"duration": time.Since(start),
|
||||
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
}()
|
||||
|
||||
if !h.checkRateLimit(conn.RemoteAddr()) {
|
||||
return nil
|
||||
return rate_limiter.ErrRateLimit
|
||||
}
|
||||
|
||||
v, ok := conn.(md.Metadatable)
|
||||
if !ok || v == nil {
|
||||
err := errors.New("wrong connection type")
|
||||
err = errors.New("wrong connection type")
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
@@ -107,6 +136,7 @@ func (h *http2Handler) Handle(ctx context.Context, conn net.Conn, opts ...handle
|
||||
return h.roundTrip(ctx,
|
||||
md.Get("w").(http.ResponseWriter),
|
||||
md.Get("r").(*http.Request),
|
||||
ro,
|
||||
log,
|
||||
)
|
||||
}
|
||||
@@ -121,7 +151,7 @@ func (h *http2Handler) Close() error {
|
||||
// NOTE: there is an issue (golang/go#43989) will cause the client hangs
|
||||
// when server returns an non-200 status code,
|
||||
// May be fixed in go1.18.
|
||||
func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req *http.Request, log logger.Logger) error {
|
||||
func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req *http.Request, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
|
||||
// Try to get the actual host.
|
||||
// Compatible with GOST 2.x.
|
||||
if v := req.Header.Get("Gost-Target"); v != "" {
|
||||
@@ -129,15 +159,13 @@ func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
|
||||
req.Host = h
|
||||
}
|
||||
}
|
||||
req.Header.Del("Gost-Target")
|
||||
|
||||
if v := req.Header.Get("X-Gost-Target"); v != "" {
|
||||
if h, err := h.decodeServerName(v); err == nil {
|
||||
req.Host = h
|
||||
}
|
||||
}
|
||||
req.Header.Del("X-Gost-Target")
|
||||
|
||||
ro.Host = req.Host
|
||||
addr := req.Host
|
||||
if _, port, _ := net.SplitHostPort(addr); port == "" {
|
||||
addr = net.JoinHostPort(strings.Trim(addr, "[]"), "80")
|
||||
@@ -148,6 +176,7 @@ func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
|
||||
}
|
||||
if u, _, _ := h.basicProxyAuth(req.Header.Get("Proxy-Authorization")); u != "" {
|
||||
fields["user"] = u
|
||||
ro.Client = u
|
||||
}
|
||||
log = log.WithFields(fields)
|
||||
|
||||
@@ -164,25 +193,41 @@ func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
|
||||
resp := &http.Response{
|
||||
ProtoMajor: 2,
|
||||
ProtoMinor: 0,
|
||||
Header: http.Header{},
|
||||
Header: w.Header(),
|
||||
Body: io.NopCloser(bytes.NewReader([]byte{})),
|
||||
}
|
||||
|
||||
ro.HTTP = &xrecorder.HTTPRecorderObject{
|
||||
Host: req.Host,
|
||||
Proto: req.Proto,
|
||||
Scheme: req.URL.Scheme,
|
||||
Method: req.Method,
|
||||
URI: req.RequestURI,
|
||||
RequestHeader: req.Header.Clone(),
|
||||
}
|
||||
defer func() {
|
||||
ro.HTTP.StatusCode = resp.StatusCode
|
||||
ro.HTTP.ResponseHeader = resp.Header
|
||||
}()
|
||||
|
||||
clientID, ok := h.authenticate(ctx, w, req, resp, log)
|
||||
if !ok {
|
||||
return nil
|
||||
return errors.New("authenication failed")
|
||||
}
|
||||
ctx = ctxvalue.ContextWithClientID(ctx, ctxvalue.ClientID(clientID))
|
||||
|
||||
if h.options.Bypass != nil && h.options.Bypass.Contains(ctx, "tcp", addr) {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
resp.StatusCode = http.StatusForbidden
|
||||
w.WriteHeader(resp.StatusCode)
|
||||
log.Debug("bypass: ", addr)
|
||||
return nil
|
||||
return xbypass.ErrBypass
|
||||
}
|
||||
|
||||
// delete the proxy related headers.
|
||||
req.Header.Del("Proxy-Authorization")
|
||||
req.Header.Del("Proxy-Connection")
|
||||
req.Header.Del("Gost-Target")
|
||||
req.Header.Del("X-Gost-Target")
|
||||
|
||||
switch h.md.hash {
|
||||
case "host":
|
||||
@@ -192,12 +237,14 @@ func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
|
||||
cc, err := h.options.Router.Dial(ctx, "tcp", addr)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
resp.StatusCode = http.StatusServiceUnavailable
|
||||
w.WriteHeader(resp.StatusCode)
|
||||
return err
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
if req.Method == http.MethodConnect {
|
||||
resp.StatusCode = http.StatusOK
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if fw, ok := w.(http.Flusher); ok {
|
||||
fw.Flush()
|
||||
@@ -209,6 +256,7 @@ func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
|
||||
conn, _, err := hj.Hijack()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
resp.StatusCode = http.StatusInternalServerError
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return err
|
||||
}
|
||||
@@ -253,6 +301,8 @@ func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
|
||||
}
|
||||
|
||||
// TODO: forward request
|
||||
resp.StatusCode = http.StatusBadRequest
|
||||
w.WriteHeader(resp.StatusCode)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ func (h *http3Handler) Handle(ctx context.Context, conn net.Conn, opts ...handle
|
||||
log := h.options.Logger.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
})
|
||||
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
defer func() {
|
||||
|
||||
@@ -18,9 +18,14 @@ import (
|
||||
"github.com/go-gost/core/handler"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"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"
|
||||
netpkg "github.com/go-gost/x/internal/net"
|
||||
rate_limiter "github.com/go-gost/x/limiter/rate"
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
@@ -31,8 +36,9 @@ func init() {
|
||||
}
|
||||
|
||||
type redirectHandler struct {
|
||||
md metadata
|
||||
options handler.Options
|
||||
md metadata
|
||||
options handler.Options
|
||||
recorder recorder.RecorderObject
|
||||
}
|
||||
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
@@ -51,6 +57,13 @@ func (h *redirectHandler) Init(md md.Metadata) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
if ro.Record == xrecorder.RecorderServiceHandler {
|
||||
h.recorder = ro
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -58,20 +71,39 @@ func (h *redirectHandler) Handle(ctx context.Context, conn net.Conn, opts ...han
|
||||
defer conn.Close()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
ro := &xrecorder.HandlerRecorderObject{
|
||||
Service: h.options.Service,
|
||||
Network: "tcp",
|
||||
RemoteAddr: conn.RemoteAddr().String(),
|
||||
LocalAddr: conn.LocalAddr().String(),
|
||||
Time: start,
|
||||
SID: string(ctxvalue.SidFromContext(ctx)),
|
||||
}
|
||||
|
||||
log := h.options.Logger.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
})
|
||||
|
||||
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
defer func() {
|
||||
if !ro.Time.IsZero() {
|
||||
if err != nil {
|
||||
ro.Err = err.Error()
|
||||
}
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Record(ctx, h.recorder.Recorder)
|
||||
}
|
||||
|
||||
log.WithFields(map[string]any{
|
||||
"duration": time.Since(start),
|
||||
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
}()
|
||||
|
||||
if !h.checkRateLimit(conn.RemoteAddr()) {
|
||||
return nil
|
||||
return rate_limiter.ErrRateLimit
|
||||
}
|
||||
|
||||
var dstAddr net.Addr
|
||||
@@ -86,6 +118,8 @@ func (h *redirectHandler) Handle(ctx context.Context, conn net.Conn, opts ...han
|
||||
}
|
||||
}
|
||||
|
||||
ro.Host = dstAddr.String()
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
"dst": fmt.Sprintf("%s/%s", dstAddr, dstAddr.Network()),
|
||||
})
|
||||
@@ -111,7 +145,7 @@ 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, log)
|
||||
return h.handleHTTP(ctx, rw, conn.RemoteAddr(), dstAddr, ro, log)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +153,7 @@ func (h *redirectHandler) Handle(ctx context.Context, conn net.Conn, opts ...han
|
||||
|
||||
if h.options.Bypass != nil && h.options.Bypass.Contains(ctx, dstAddr.Network(), dstAddr.String()) {
|
||||
log.Debug("bypass: ", dstAddr)
|
||||
return nil
|
||||
return xbypass.ErrBypass
|
||||
}
|
||||
|
||||
cc, err := h.options.Router.Dial(ctx, dstAddr.Network(), dstAddr.String())
|
||||
@@ -139,17 +173,27 @@ 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, log logger.Logger) error {
|
||||
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))
|
||||
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,
|
||||
RequestHeader: req.Header,
|
||||
}
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
dump, _ := httputil.DumpRequest(req, false)
|
||||
log.Trace(string(dump))
|
||||
}
|
||||
|
||||
ro.Host = req.Host
|
||||
host := req.Host
|
||||
if _, _, err := net.SplitHostPort(host); err != nil {
|
||||
host = net.JoinHostPort(strings.Trim(host, "[]"), "80")
|
||||
@@ -160,12 +204,15 @@ func (h *redirectHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, radd
|
||||
|
||||
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 nil
|
||||
return xbypass.ErrBypass
|
||||
}
|
||||
|
||||
cc, err := h.options.Router.Dial(ctx, "tcp", host)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
if !h.md.sniffingFallback {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if cc == nil {
|
||||
@@ -190,23 +237,28 @@ func (h *redirectHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, radd
|
||||
return err
|
||||
}
|
||||
|
||||
var rw2 io.ReadWriter = cc
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
var buf bytes.Buffer
|
||||
resp, err := http.ReadResponse(bufio.NewReader(io.TeeReader(cc, &buf)), req)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
br := bufio.NewReader(cc)
|
||||
resp, err := http.ReadResponse(br, req)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
ro.HTTP.StatusCode = resp.StatusCode
|
||||
ro.HTTP.ResponseHeader = resp.Header
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
dump, _ := httputil.DumpResponse(resp, false)
|
||||
log.Trace(string(dump))
|
||||
|
||||
rw2 = xio.NewReadWriter(io.MultiReader(&buf, cc), cc)
|
||||
}
|
||||
|
||||
netpkg.Transport(rw, rw2)
|
||||
if err := resp.Write(rw); err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
netpkg.Transport(rw, xio.NewReadWriter(br, cc))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,7 +8,12 @@ import (
|
||||
|
||||
"github.com/go-gost/core/handler"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/core/recorder"
|
||||
xbypass "github.com/go-gost/x/bypass"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
netpkg "github.com/go-gost/x/internal/net"
|
||||
rate_limiter "github.com/go-gost/x/limiter/rate"
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
@@ -17,8 +22,9 @@ func init() {
|
||||
}
|
||||
|
||||
type redirectHandler struct {
|
||||
md metadata
|
||||
options handler.Options
|
||||
md metadata
|
||||
options handler.Options
|
||||
recorder recorder.RecorderObject
|
||||
}
|
||||
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
@@ -37,30 +43,55 @@ func (h *redirectHandler) Init(md md.Metadata) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
if ro.Record == xrecorder.RecorderServiceHandler {
|
||||
h.recorder = ro
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (h *redirectHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
|
||||
func (h *redirectHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) (err error) {
|
||||
defer conn.Close()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
ro := &xrecorder.HandlerRecorderObject{
|
||||
Service: h.options.Service,
|
||||
RemoteAddr: conn.RemoteAddr().String(),
|
||||
LocalAddr: conn.LocalAddr().String(),
|
||||
Time: start,
|
||||
SID: string(ctxvalue.SidFromContext(ctx)),
|
||||
}
|
||||
|
||||
log := h.options.Logger.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
})
|
||||
|
||||
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
ro.Err = err.Error()
|
||||
}
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Record(ctx, h.recorder.Recorder)
|
||||
|
||||
log.WithFields(map[string]any{
|
||||
"duration": time.Since(start),
|
||||
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
}()
|
||||
|
||||
if !h.checkRateLimit(conn.RemoteAddr()) {
|
||||
return nil
|
||||
return rate_limiter.ErrRateLimit
|
||||
}
|
||||
|
||||
dstAddr := conn.LocalAddr()
|
||||
ro.Network = dstAddr.Network()
|
||||
ro.Host = dstAddr.String()
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
"dst": fmt.Sprintf("%s/%s", dstAddr, dstAddr.Network()),
|
||||
@@ -70,7 +101,7 @@ func (h *redirectHandler) Handle(ctx context.Context, conn net.Conn, opts ...han
|
||||
|
||||
if h.options.Bypass != nil && h.options.Bypass.Contains(ctx, dstAddr.Network(), dstAddr.String()) {
|
||||
log.Debug("bypass: ", dstAddr)
|
||||
return nil
|
||||
return xbypass.ErrBypass
|
||||
}
|
||||
|
||||
cc, err := h.options.Router.Dial(ctx, dstAddr.Network(), dstAddr.String())
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/core/observer/stats"
|
||||
"github.com/go-gost/relay"
|
||||
xbypass "github.com/go-gost/x/bypass"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
xnet "github.com/go-gost/x/internal/net"
|
||||
serial "github.com/go-gost/x/internal/util/serial"
|
||||
@@ -49,8 +50,8 @@ func (h *relayHandler) handleConnect(ctx context.Context, conn net.Conn, network
|
||||
if h.options.Bypass != nil && h.options.Bypass.Contains(ctx, network, address) {
|
||||
log.Debug("bypass: ", address)
|
||||
resp.Status = relay.StatusForbidden
|
||||
_, err = resp.WriteTo(conn)
|
||||
return
|
||||
resp.WriteTo(conn)
|
||||
return xbypass.ErrBypass
|
||||
}
|
||||
|
||||
switch h.md.hash {
|
||||
|
||||
+37
-14
@@ -11,10 +11,13 @@ import (
|
||||
"github.com/go-gost/core/hop"
|
||||
"github.com/go-gost/core/limiter/traffic"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/core/recorder"
|
||||
"github.com/go-gost/relay"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
limiter_util "github.com/go-gost/x/internal/util/limiter"
|
||||
stats_util "github.com/go-gost/x/internal/util/stats"
|
||||
rate_limiter "github.com/go-gost/x/limiter/rate"
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
@@ -22,7 +25,6 @@ var (
|
||||
ErrBadVersion = errors.New("relay: bad version")
|
||||
ErrUnknownCmd = errors.New("relay: unknown command")
|
||||
ErrUnauthorized = errors.New("relay: unauthorized")
|
||||
ErrRateLimit = errors.New("relay: rate limiting exceeded")
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -30,12 +32,13 @@ func init() {
|
||||
}
|
||||
|
||||
type relayHandler struct {
|
||||
hop hop.Hop
|
||||
md metadata
|
||||
options handler.Options
|
||||
stats *stats_util.HandlerStats
|
||||
limiter traffic.TrafficLimiter
|
||||
cancel context.CancelFunc
|
||||
hop hop.Hop
|
||||
md metadata
|
||||
options handler.Options
|
||||
stats *stats_util.HandlerStats
|
||||
limiter traffic.TrafficLimiter
|
||||
cancel context.CancelFunc
|
||||
recorder recorder.RecorderObject
|
||||
}
|
||||
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
@@ -66,6 +69,13 @@ func (h *relayHandler) Init(md md.Metadata) (err error) {
|
||||
h.limiter = limiter_util.NewCachedTrafficLimiter(limiter, 30*time.Second, 60*time.Second)
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
if ro.Record == xrecorder.RecorderServiceHandler {
|
||||
h.recorder = ro
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -75,25 +85,40 @@ func (h *relayHandler) Forward(hop hop.Hop) {
|
||||
}
|
||||
|
||||
func (h *relayHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) (err error) {
|
||||
defer conn.Close()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
ro := &xrecorder.HandlerRecorderObject{
|
||||
Service: h.options.Service,
|
||||
RemoteAddr: conn.RemoteAddr().String(),
|
||||
LocalAddr: conn.LocalAddr().String(),
|
||||
Time: start,
|
||||
SID: string(ctxvalue.SidFromContext(ctx)),
|
||||
}
|
||||
|
||||
log := h.options.Logger.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
})
|
||||
|
||||
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
ro.Err = err.Error()
|
||||
}
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Record(ctx, h.recorder.Recorder)
|
||||
|
||||
log.WithFields(map[string]any{
|
||||
"duration": time.Since(start),
|
||||
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
}()
|
||||
|
||||
if !h.checkRateLimit(conn.RemoteAddr()) {
|
||||
return ErrRateLimit
|
||||
return rate_limiter.ErrRateLimit
|
||||
}
|
||||
|
||||
if h.md.readTimeout > 0 {
|
||||
@@ -139,6 +164,7 @@ func (h *relayHandler) Handle(ctx context.Context, conn net.Conn, opts ...handle
|
||||
}
|
||||
|
||||
if user != "" {
|
||||
ro.Client = user
|
||||
log = log.WithFields(map[string]any{"user": user})
|
||||
}
|
||||
|
||||
@@ -156,21 +182,18 @@ func (h *relayHandler) Handle(ctx context.Context, conn net.Conn, opts ...handle
|
||||
if (req.Cmd & relay.FUDP) == relay.FUDP {
|
||||
network = "udp"
|
||||
}
|
||||
ro.Network = network
|
||||
ro.Host = address
|
||||
|
||||
if h.hop != nil {
|
||||
defer conn.Close()
|
||||
// forward mode
|
||||
return h.handleForward(ctx, conn, network, log)
|
||||
}
|
||||
|
||||
switch req.Cmd & relay.CmdMask {
|
||||
case 0, relay.CmdConnect:
|
||||
defer conn.Close()
|
||||
|
||||
return h.handleConnect(ctx, conn, network, address, log)
|
||||
case relay.CmdBind:
|
||||
defer conn.Close()
|
||||
|
||||
return h.handleBind(ctx, conn, network, address, log)
|
||||
default:
|
||||
resp.Status = relay.StatusBadRequest
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/core/recorder"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
xnet "github.com/go-gost/x/internal/net"
|
||||
serial "github.com/go-gost/x/internal/util/serial"
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
@@ -46,12 +47,10 @@ func (h *serialHandler) Init(md md.Metadata) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
if opts := h.options.Router.Options(); opts != nil {
|
||||
for _, ro := range opts.Recorders {
|
||||
if ro.Record == xrecorder.RecorderServiceHandlerSerial {
|
||||
h.recorder = ro
|
||||
break
|
||||
}
|
||||
for _, ro := range h.options.Recorders {
|
||||
if ro.Record == xrecorder.RecorderServiceHandlerSerial {
|
||||
h.recorder = ro
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +70,7 @@ func (h *serialHandler) Handle(ctx context.Context, conn net.Conn, opts ...handl
|
||||
log = log.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
})
|
||||
|
||||
conn = &recorderConn{
|
||||
|
||||
+70
-22
@@ -20,10 +20,14 @@ import (
|
||||
"github.com/go-gost/core/handler"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"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"
|
||||
netpkg "github.com/go-gost/x/internal/net"
|
||||
rate_limiter "github.com/go-gost/x/limiter/rate"
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
@@ -32,8 +36,9 @@ func init() {
|
||||
}
|
||||
|
||||
type sniHandler struct {
|
||||
md metadata
|
||||
options handler.Options
|
||||
md metadata
|
||||
options handler.Options
|
||||
recorder recorder.RecorderObject
|
||||
}
|
||||
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
@@ -54,27 +59,51 @@ func (h *sniHandler) Init(md md.Metadata) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
if ro.Record == xrecorder.RecorderServiceHandler {
|
||||
h.recorder = ro
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *sniHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
|
||||
func (h *sniHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) (err error) {
|
||||
defer conn.Close()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
ro := &xrecorder.HandlerRecorderObject{
|
||||
Service: h.options.Service,
|
||||
RemoteAddr: conn.RemoteAddr().String(),
|
||||
LocalAddr: conn.LocalAddr().String(),
|
||||
Network: "tcp",
|
||||
Time: start,
|
||||
SID: string(ctxvalue.SidFromContext(ctx)),
|
||||
}
|
||||
|
||||
log := h.options.Logger.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
})
|
||||
|
||||
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
ro.Err = err.Error()
|
||||
}
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Record(ctx, h.recorder.Recorder)
|
||||
|
||||
log.WithFields(map[string]any{
|
||||
"duration": time.Since(start),
|
||||
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
}()
|
||||
|
||||
if !h.checkRateLimit(conn.RemoteAddr()) {
|
||||
return nil
|
||||
return rate_limiter.ErrRateLimit
|
||||
}
|
||||
|
||||
var hdr [dissector.RecordHeaderLen]byte
|
||||
@@ -88,17 +117,26 @@ func (h *sniHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.
|
||||
tlsVersion := binary.BigEndian.Uint16(hdr[1:3])
|
||||
if hdr[0] == dissector.Handshake &&
|
||||
(tlsVersion >= tls.VersionTLS10 && tlsVersion <= tls.VersionTLS13) {
|
||||
return h.handleHTTPS(ctx, rw, conn.RemoteAddr(), log)
|
||||
return h.handleHTTPS(ctx, rw, conn.RemoteAddr(), ro, log)
|
||||
}
|
||||
return h.handleHTTP(ctx, rw, conn.RemoteAddr(), log)
|
||||
return h.handleHTTP(ctx, rw, conn.RemoteAddr(), ro, log)
|
||||
}
|
||||
|
||||
func (h *sniHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, raddr net.Addr, log logger.Logger) error {
|
||||
func (h *sniHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, raddr net.Addr, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
|
||||
req, err := http.ReadRequest(bufio.NewReader(rw))
|
||||
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,
|
||||
RequestHeader: req.Header,
|
||||
}
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
dump, _ := httputil.DumpRequest(req, false)
|
||||
log.Trace(string(dump))
|
||||
@@ -108,13 +146,16 @@ func (h *sniHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, raddr net
|
||||
if _, _, err := net.SplitHostPort(host); err != nil {
|
||||
host = net.JoinHostPort(strings.Trim(host, "[]"), "80")
|
||||
}
|
||||
|
||||
ro.Host = req.Host
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
"host": host,
|
||||
})
|
||||
|
||||
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 nil
|
||||
return xbypass.ErrBypass
|
||||
}
|
||||
|
||||
switch h.md.hash {
|
||||
@@ -142,28 +183,33 @@ func (h *sniHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, raddr net
|
||||
return err
|
||||
}
|
||||
|
||||
var rw2 io.ReadWriter = cc
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
var buf bytes.Buffer
|
||||
resp, err := http.ReadResponse(bufio.NewReader(io.TeeReader(cc, &buf)), req)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
br := bufio.NewReader(cc)
|
||||
resp, err := http.ReadResponse(br, req)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
ro.HTTP.StatusCode = resp.StatusCode
|
||||
ro.HTTP.ResponseHeader = resp.Header
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
dump, _ := httputil.DumpResponse(resp, false)
|
||||
log.Trace(string(dump))
|
||||
|
||||
rw2 = xio.NewReadWriter(io.MultiReader(&buf, cc), cc)
|
||||
}
|
||||
|
||||
netpkg.Transport(rw, rw2)
|
||||
if err := resp.Write(rw); err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
netpkg.Transport(rw, xio.NewReadWriter(br, cc))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *sniHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, raddr net.Addr, log logger.Logger) error {
|
||||
func (h *sniHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, raddr net.Addr, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
|
||||
buf := new(bytes.Buffer)
|
||||
host, err := h.decodeHost(io.TeeReader(rw, buf))
|
||||
if err != nil {
|
||||
@@ -175,6 +221,8 @@ func (h *sniHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, raddr ne
|
||||
host = net.JoinHostPort(strings.Trim(host, "[]"), "443")
|
||||
}
|
||||
|
||||
ro.Host = host
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
"dst": host,
|
||||
})
|
||||
@@ -182,7 +230,7 @@ func (h *sniHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, raddr ne
|
||||
|
||||
if h.options.Bypass != nil && h.options.Bypass.Contains(ctx, "tcp", host) {
|
||||
log.Debug("bypass: ", host)
|
||||
return nil
|
||||
return xbypass.ErrBypass
|
||||
}
|
||||
|
||||
switch h.md.hash {
|
||||
|
||||
@@ -12,13 +12,16 @@ import (
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/core/observer/stats"
|
||||
"github.com/go-gost/core/recorder"
|
||||
"github.com/go-gost/gosocks4"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
netpkg "github.com/go-gost/x/internal/net"
|
||||
limiter_util "github.com/go-gost/x/internal/util/limiter"
|
||||
stats_util "github.com/go-gost/x/internal/util/stats"
|
||||
rate_limiter "github.com/go-gost/x/limiter/rate"
|
||||
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"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
@@ -33,11 +36,12 @@ func init() {
|
||||
}
|
||||
|
||||
type socks4Handler struct {
|
||||
md metadata
|
||||
options handler.Options
|
||||
stats *stats_util.HandlerStats
|
||||
limiter traffic.TrafficLimiter
|
||||
cancel context.CancelFunc
|
||||
md metadata
|
||||
options handler.Options
|
||||
stats *stats_util.HandlerStats
|
||||
limiter traffic.TrafficLimiter
|
||||
cancel context.CancelFunc
|
||||
recorder recorder.RecorderObject
|
||||
}
|
||||
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
@@ -68,28 +72,51 @@ func (h *socks4Handler) Init(md md.Metadata) (err error) {
|
||||
h.limiter = limiter_util.NewCachedTrafficLimiter(limiter, 30*time.Second, 60*time.Second)
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
if ro.Record == xrecorder.RecorderServiceHandler {
|
||||
h.recorder = ro
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *socks4Handler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
|
||||
func (h *socks4Handler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) (err error) {
|
||||
defer conn.Close()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
ro := &xrecorder.HandlerRecorderObject{
|
||||
Service: h.options.Service,
|
||||
Network: "tcp",
|
||||
RemoteAddr: conn.RemoteAddr().String(),
|
||||
LocalAddr: conn.LocalAddr().String(),
|
||||
Time: start,
|
||||
SID: string(ctxvalue.SidFromContext(ctx)),
|
||||
}
|
||||
|
||||
log := h.options.Logger.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
})
|
||||
|
||||
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
ro.Err = err.Error()
|
||||
}
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Record(ctx, h.recorder.Recorder)
|
||||
|
||||
log.WithFields(map[string]any{
|
||||
"duration": time.Since(start),
|
||||
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
}()
|
||||
|
||||
if !h.checkRateLimit(conn.RemoteAddr()) {
|
||||
return nil
|
||||
return rate_limiter.ErrRateLimit
|
||||
}
|
||||
|
||||
if h.md.readTimeout > 0 {
|
||||
@@ -101,6 +128,8 @@ func (h *socks4Handler) Handle(ctx context.Context, conn net.Conn, opts ...handl
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
ro.Host = req.Addr.String()
|
||||
log.Trace(req)
|
||||
|
||||
conn.SetReadDeadline(time.Time{})
|
||||
|
||||
@@ -9,11 +9,14 @@ import (
|
||||
"github.com/go-gost/core/handler"
|
||||
"github.com/go-gost/core/limiter/traffic"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/core/recorder"
|
||||
"github.com/go-gost/gosocks5"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
limiter_util "github.com/go-gost/x/internal/util/limiter"
|
||||
"github.com/go-gost/x/internal/util/socks"
|
||||
stats_util "github.com/go-gost/x/internal/util/stats"
|
||||
rate_limiter "github.com/go-gost/x/limiter/rate"
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
@@ -33,6 +36,7 @@ type socks5Handler struct {
|
||||
stats *stats_util.HandlerStats
|
||||
limiter traffic.TrafficLimiter
|
||||
cancel context.CancelFunc
|
||||
recorder recorder.RecorderObject
|
||||
}
|
||||
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
@@ -70,28 +74,51 @@ func (h *socks5Handler) Init(md md.Metadata) (err error) {
|
||||
h.limiter = limiter_util.NewCachedTrafficLimiter(limiter, 30*time.Second, 60*time.Second)
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
if ro.Record == xrecorder.RecorderServiceHandler {
|
||||
h.recorder = ro
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (h *socks5Handler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
|
||||
func (h *socks5Handler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) (err error) {
|
||||
defer conn.Close()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
ro := &xrecorder.HandlerRecorderObject{
|
||||
Service: h.options.Service,
|
||||
Network: "tcp",
|
||||
RemoteAddr: conn.RemoteAddr().String(),
|
||||
LocalAddr: conn.LocalAddr().String(),
|
||||
Time: start,
|
||||
SID: string(ctxvalue.SidFromContext(ctx)),
|
||||
}
|
||||
|
||||
log := h.options.Logger.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
})
|
||||
|
||||
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
ro.Err = err.Error()
|
||||
}
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Record(ctx, h.recorder.Recorder)
|
||||
|
||||
log.WithFields(map[string]any{
|
||||
"duration": time.Since(start),
|
||||
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
}()
|
||||
|
||||
if !h.checkRateLimit(conn.RemoteAddr()) {
|
||||
return nil
|
||||
return rate_limiter.ErrRateLimit
|
||||
}
|
||||
|
||||
if h.md.readTimeout > 0 {
|
||||
@@ -109,12 +136,14 @@ func (h *socks5Handler) Handle(ctx context.Context, conn net.Conn, opts ...handl
|
||||
if clientID := sc.ID(); clientID != "" {
|
||||
ctx = ctxvalue.ContextWithClientID(ctx, ctxvalue.ClientID(clientID))
|
||||
log = log.WithFields(map[string]any{"user": clientID})
|
||||
ro.Client = clientID
|
||||
}
|
||||
|
||||
conn = sc
|
||||
conn.SetReadDeadline(time.Time{})
|
||||
|
||||
address := req.Addr.String()
|
||||
ro.Host = address
|
||||
|
||||
switch req.Cmd {
|
||||
case gosocks5.CmdConnect:
|
||||
@@ -124,8 +153,10 @@ func (h *socks5Handler) Handle(ctx context.Context, conn net.Conn, opts ...handl
|
||||
case socks.CmdMuxBind:
|
||||
return h.handleMuxBind(ctx, conn, "tcp", address, log)
|
||||
case gosocks5.CmdUdp:
|
||||
ro.Network = "udp"
|
||||
return h.handleUDP(ctx, conn, log)
|
||||
case socks.CmdUDPTun:
|
||||
ro.Network = "udp"
|
||||
return h.handleUDPTun(ctx, conn, "udp", address, log)
|
||||
default:
|
||||
err = ErrUnknownCmd
|
||||
|
||||
+36
-5
@@ -8,10 +8,13 @@ import (
|
||||
|
||||
"github.com/go-gost/core/handler"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/core/recorder"
|
||||
"github.com/go-gost/gosocks5"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
netpkg "github.com/go-gost/x/internal/net"
|
||||
"github.com/go-gost/x/internal/util/ss"
|
||||
rate_limiter "github.com/go-gost/x/limiter/rate"
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
"github.com/go-gost/x/registry"
|
||||
"github.com/shadowsocks/go-shadowsocks2/core"
|
||||
)
|
||||
@@ -21,9 +24,10 @@ func init() {
|
||||
}
|
||||
|
||||
type ssHandler struct {
|
||||
cipher core.Cipher
|
||||
md metadata
|
||||
options handler.Options
|
||||
cipher core.Cipher
|
||||
md metadata
|
||||
options handler.Options
|
||||
recorder recorder.RecorderObject
|
||||
}
|
||||
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
@@ -50,27 +54,53 @@ func (h *ssHandler) Init(md md.Metadata) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
if ro.Record == xrecorder.RecorderServiceHandler {
|
||||
h.recorder = ro
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (h *ssHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
|
||||
func (h *ssHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) (err error) {
|
||||
defer conn.Close()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
ro := &xrecorder.HandlerRecorderObject{
|
||||
Service: h.options.Service,
|
||||
Network: "tcp",
|
||||
RemoteAddr: conn.RemoteAddr().String(),
|
||||
LocalAddr: conn.LocalAddr().String(),
|
||||
Time: start,
|
||||
SID: string(ctxvalue.SidFromContext(ctx)),
|
||||
}
|
||||
|
||||
log := h.options.Logger.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
})
|
||||
|
||||
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
defer func() {
|
||||
if !ro.Time.IsZero() {
|
||||
if err != nil {
|
||||
ro.Err = err.Error()
|
||||
}
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Record(ctx, h.recorder.Recorder)
|
||||
}
|
||||
|
||||
log.WithFields(map[string]any{
|
||||
"duration": time.Since(start),
|
||||
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
}()
|
||||
|
||||
if !h.checkRateLimit(conn.RemoteAddr()) {
|
||||
return nil
|
||||
return rate_limiter.ErrRateLimit
|
||||
}
|
||||
|
||||
if h.cipher != nil {
|
||||
@@ -87,6 +117,7 @@ func (h *ssHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.H
|
||||
io.Copy(io.Discard, conn)
|
||||
return err
|
||||
}
|
||||
ro.Host = addr.String()
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
"dst": addr.String(),
|
||||
|
||||
@@ -10,8 +10,12 @@ import (
|
||||
"github.com/go-gost/core/handler"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/core/recorder"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
"github.com/go-gost/x/internal/util/relay"
|
||||
"github.com/go-gost/x/internal/util/ss"
|
||||
rate_limiter "github.com/go-gost/x/limiter/rate"
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
"github.com/go-gost/x/registry"
|
||||
"github.com/shadowsocks/go-shadowsocks2/core"
|
||||
)
|
||||
@@ -21,9 +25,10 @@ func init() {
|
||||
}
|
||||
|
||||
type ssuHandler struct {
|
||||
cipher core.Cipher
|
||||
md metadata
|
||||
options handler.Options
|
||||
cipher core.Cipher
|
||||
md metadata
|
||||
options handler.Options
|
||||
recorder recorder.RecorderObject
|
||||
}
|
||||
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
@@ -51,27 +56,51 @@ func (h *ssuHandler) Init(md md.Metadata) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
if ro.Record == xrecorder.RecorderServiceHandler {
|
||||
h.recorder = ro
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (h *ssuHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
|
||||
func (h *ssuHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) (err error) {
|
||||
defer conn.Close()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
ro := &xrecorder.HandlerRecorderObject{
|
||||
Service: h.options.Service,
|
||||
Network: "udp",
|
||||
RemoteAddr: conn.RemoteAddr().String(),
|
||||
LocalAddr: conn.LocalAddr().String(),
|
||||
Time: start,
|
||||
SID: string(ctxvalue.SidFromContext(ctx)),
|
||||
}
|
||||
|
||||
log := h.options.Logger.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
})
|
||||
|
||||
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
ro.Err = err.Error()
|
||||
}
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Record(ctx, h.recorder.Recorder)
|
||||
|
||||
log.WithFields(map[string]any{
|
||||
"duration": time.Since(start),
|
||||
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
}()
|
||||
|
||||
if !h.checkRateLimit(conn.RemoteAddr()) {
|
||||
return nil
|
||||
return rate_limiter.ErrRateLimit
|
||||
}
|
||||
|
||||
pc, ok := conn.(net.PacketConn)
|
||||
@@ -106,14 +135,14 @@ func (h *ssuHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.
|
||||
|
||||
t := time.Now()
|
||||
log.Infof("%s <-> %s", conn.LocalAddr(), cc.LocalAddr())
|
||||
h.relayPacket(pc, cc, log)
|
||||
h.relayPacket(ctx, pc, cc, ro, log)
|
||||
log.WithFields(map[string]any{"duration": time.Since(t)}).
|
||||
Infof("%s >-< %s", conn.LocalAddr(), cc.LocalAddr())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *ssuHandler) relayPacket(pc1, pc2 net.PacketConn, log logger.Logger) (err error) {
|
||||
func (h *ssuHandler) relayPacket(ctx context.Context, pc1, pc2 net.PacketConn, ro *xrecorder.HandlerRecorderObject, log logger.Logger) (err error) {
|
||||
bufSize := h.md.bufferSize
|
||||
errc := make(chan error, 2)
|
||||
|
||||
@@ -128,7 +157,11 @@ func (h *ssuHandler) relayPacket(pc1, pc2 net.PacketConn, log logger.Logger) (er
|
||||
return err
|
||||
}
|
||||
|
||||
if h.options.Bypass != nil && h.options.Bypass.Contains(context.Background(), addr.Network(), addr.String()) {
|
||||
if ro.Host == "" {
|
||||
ro.Host = addr.String()
|
||||
}
|
||||
|
||||
if h.options.Bypass != nil && h.options.Bypass.Contains(ctx, addr.Network(), addr.String()) {
|
||||
log.Warn("bypass: ", addr)
|
||||
return nil
|
||||
}
|
||||
@@ -160,7 +193,7 @@ func (h *ssuHandler) relayPacket(pc1, pc2 net.PacketConn, log logger.Logger) (er
|
||||
return err
|
||||
}
|
||||
|
||||
if h.options.Bypass != nil && h.options.Bypass.Contains(context.Background(), raddr.Network(), raddr.String()) {
|
||||
if h.options.Bypass != nil && h.options.Bypass.Contains(ctx, raddr.Network(), raddr.String()) {
|
||||
log.Warn("bypass: ", raddr)
|
||||
return nil
|
||||
}
|
||||
|
||||
+51
-9
@@ -12,8 +12,13 @@ import (
|
||||
"github.com/go-gost/core/handler"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/core/recorder"
|
||||
xbypass "github.com/go-gost/x/bypass"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
netpkg "github.com/go-gost/x/internal/net"
|
||||
sshd_util "github.com/go-gost/x/internal/util/sshd"
|
||||
rate_limiter "github.com/go-gost/x/limiter/rate"
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
"github.com/go-gost/x/registry"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
@@ -28,8 +33,9 @@ func init() {
|
||||
}
|
||||
|
||||
type forwardHandler struct {
|
||||
md metadata
|
||||
options handler.Options
|
||||
md metadata
|
||||
options handler.Options
|
||||
recorder recorder.RecorderObject
|
||||
}
|
||||
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
@@ -48,26 +54,60 @@ func (h *forwardHandler) Init(md md.Metadata) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
if ro.Record == xrecorder.RecorderServiceHandler {
|
||||
h.recorder = ro
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
|
||||
func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) (err error) {
|
||||
defer conn.Close()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
ro := &xrecorder.HandlerRecorderObject{
|
||||
Service: h.options.Service,
|
||||
Network: "tcp",
|
||||
RemoteAddr: conn.RemoteAddr().String(),
|
||||
LocalAddr: conn.LocalAddr().String(),
|
||||
Time: start,
|
||||
SID: string(ctxvalue.SidFromContext(ctx)),
|
||||
}
|
||||
|
||||
log := h.options.Logger.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
})
|
||||
|
||||
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
defer func() {
|
||||
if !ro.Time.IsZero() {
|
||||
if err != nil {
|
||||
ro.Err = err.Error()
|
||||
}
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Record(ctx, h.recorder.Recorder)
|
||||
}
|
||||
|
||||
log.WithFields(map[string]any{
|
||||
"duration": time.Since(start),
|
||||
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
}()
|
||||
|
||||
if !h.checkRateLimit(conn.RemoteAddr()) {
|
||||
return nil
|
||||
return rate_limiter.ErrRateLimit
|
||||
}
|
||||
|
||||
switch cc := conn.(type) {
|
||||
case *sshd_util.DirectForwardConn:
|
||||
return h.handleDirectForward(ctx, cc, log)
|
||||
return h.handleDirectForward(ctx, cc, ro, log)
|
||||
case *sshd_util.RemoteForwardConn:
|
||||
return h.handleRemoteForward(ctx, cc, log)
|
||||
return h.handleRemoteForward(ctx, cc, ro, log)
|
||||
default:
|
||||
err := errors.New("sshd: wrong connection type")
|
||||
log.Error(err)
|
||||
@@ -75,9 +115,10 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
|
||||
}
|
||||
}
|
||||
|
||||
func (h *forwardHandler) handleDirectForward(ctx context.Context, conn *sshd_util.DirectForwardConn, log logger.Logger) error {
|
||||
func (h *forwardHandler) handleDirectForward(ctx context.Context, conn *sshd_util.DirectForwardConn, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
|
||||
targetAddr := conn.DstAddr()
|
||||
|
||||
ro.Host = targetAddr
|
||||
log = log.WithFields(map[string]any{
|
||||
"dst": fmt.Sprintf("%s/%s", targetAddr, "tcp"),
|
||||
"cmd": "connect",
|
||||
@@ -87,7 +128,7 @@ func (h *forwardHandler) handleDirectForward(ctx context.Context, conn *sshd_uti
|
||||
|
||||
if h.options.Bypass != nil && h.options.Bypass.Contains(ctx, "tcp", targetAddr) {
|
||||
log.Debugf("bypass %s", targetAddr)
|
||||
return nil
|
||||
return xbypass.ErrBypass
|
||||
}
|
||||
|
||||
cc, err := h.options.Router.Dial(ctx, "tcp", targetAddr)
|
||||
@@ -106,7 +147,7 @@ func (h *forwardHandler) handleDirectForward(ctx context.Context, conn *sshd_uti
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *forwardHandler) handleRemoteForward(ctx context.Context, conn *sshd_util.RemoteForwardConn, log logger.Logger) error {
|
||||
func (h *forwardHandler) handleRemoteForward(ctx context.Context, conn *sshd_util.RemoteForwardConn, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
|
||||
req := conn.Request()
|
||||
|
||||
t := tcpipForward{}
|
||||
@@ -117,6 +158,7 @@ func (h *forwardHandler) handleRemoteForward(ctx context.Context, conn *sshd_uti
|
||||
|
||||
network := "tcp"
|
||||
addr := net.JoinHostPort(t.Host, strconv.Itoa(int(t.Port)))
|
||||
ro.Host = addr
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
"dst": fmt.Sprintf("%s/%s", addr, network),
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/go-gost/x/registry"
|
||||
"github.com/shadowsocks/go-shadowsocks2/core"
|
||||
"github.com/shadowsocks/go-shadowsocks2/shadowaead"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
"github.com/songgao/water/waterutil"
|
||||
)
|
||||
|
||||
@@ -87,6 +88,7 @@ func (h *tapHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.
|
||||
log = log.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
})
|
||||
|
||||
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
tun_util "github.com/go-gost/x/internal/util/tun"
|
||||
"github.com/go-gost/x/registry"
|
||||
"github.com/songgao/water/waterutil"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -74,6 +75,7 @@ func (h *tunHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.
|
||||
log = log.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
})
|
||||
|
||||
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/go-gost/core/listener"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/core/recorder"
|
||||
"github.com/go-gost/core/sd"
|
||||
"github.com/go-gost/relay"
|
||||
admission "github.com/go-gost/x/admission/wrapper"
|
||||
@@ -25,14 +26,17 @@ import (
|
||||
"github.com/go-gost/x/internal/net/proxyproto"
|
||||
climiter "github.com/go-gost/x/limiter/conn/wrapper"
|
||||
metrics "github.com/go-gost/x/metrics/wrapper"
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
)
|
||||
|
||||
type entrypoint struct {
|
||||
node string
|
||||
pool *ConnectorPool
|
||||
ingress ingress.Ingress
|
||||
sd sd.SD
|
||||
log logger.Logger
|
||||
node string
|
||||
service string
|
||||
pool *ConnectorPool
|
||||
ingress ingress.Ingress
|
||||
sd sd.SD
|
||||
log logger.Logger
|
||||
recorder recorder.RecorderObject
|
||||
}
|
||||
|
||||
func (ep *entrypoint) handle(ctx context.Context, conn net.Conn) error {
|
||||
@@ -77,6 +81,40 @@ func (ep *entrypoint) handle(ctx context.Context, conn net.Conn) error {
|
||||
return err
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
ro := &xrecorder.HandlerRecorderObject{
|
||||
Node: ep.node,
|
||||
Service: ep.service,
|
||||
RemoteAddr: conn.RemoteAddr().String(),
|
||||
LocalAddr: conn.LocalAddr().String(),
|
||||
Network: "tcp",
|
||||
Host: req.Host,
|
||||
Time: start,
|
||||
HTTP: &xrecorder.HTTPRecorderObject{
|
||||
Host: req.Host,
|
||||
Method: req.Method,
|
||||
Proto: req.Proto,
|
||||
Scheme: req.URL.Scheme,
|
||||
URI: req.RequestURI,
|
||||
RequestHeader: req.Header,
|
||||
},
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
d := time.Since(start)
|
||||
log.WithFields(map[string]any{
|
||||
"duration": d,
|
||||
}).Debugf("%s >-< %s", conn.RemoteAddr(), req.Host)
|
||||
|
||||
ro.HTTP.StatusCode = resp.StatusCode
|
||||
ro.HTTP.ResponseHeader = resp.Header
|
||||
|
||||
ro.Duration = d
|
||||
ro.Err = err.Error()
|
||||
ro.Record(ctx, ep.recorder.Recorder)
|
||||
}
|
||||
}()
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
dump, _ := httputil.DumpRequest(req, false)
|
||||
log.Trace(string(dump))
|
||||
@@ -92,16 +130,21 @@ func (ep *entrypoint) handle(ctx context.Context, conn net.Conn) error {
|
||||
}
|
||||
}
|
||||
if tunnelID.IsZero() {
|
||||
err := fmt.Errorf("no route to host %s", req.Host)
|
||||
err = fmt.Errorf("no route to host %s", req.Host)
|
||||
log.Error(err)
|
||||
resp.StatusCode = http.StatusBadGateway
|
||||
return resp.Write(conn)
|
||||
resp.Write(conn)
|
||||
return err
|
||||
}
|
||||
|
||||
ro.Client = tunnelID.String()
|
||||
|
||||
if tunnelID.IsPrivate() {
|
||||
err := fmt.Errorf("access denied: tunnel %s is private for host %s", tunnelID, req.Host)
|
||||
err = fmt.Errorf("access denied: tunnel %s is private for host %s", tunnelID, req.Host)
|
||||
log.Error(err)
|
||||
resp.StatusCode = http.StatusBadGateway
|
||||
return resp.Write(conn)
|
||||
resp.Write(conn)
|
||||
return err
|
||||
}
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
@@ -116,6 +159,7 @@ func (ep *entrypoint) handle(ctx context.Context, conn net.Conn) error {
|
||||
})
|
||||
remoteAddr = addr
|
||||
}
|
||||
ro.RemoteAddr = remoteAddr.String()
|
||||
|
||||
d := &Dialer{
|
||||
node: ep.node,
|
||||
@@ -165,14 +209,15 @@ func (ep *entrypoint) handle(ctx context.Context, conn net.Conn) error {
|
||||
}
|
||||
}
|
||||
|
||||
if err := req.Write(c); err != nil {
|
||||
if err = req.Write(c); err != nil {
|
||||
c.Close()
|
||||
log.Errorf("send request: %v", err)
|
||||
return resp.Write(conn)
|
||||
resp.Write(conn)
|
||||
return err
|
||||
}
|
||||
|
||||
if req.Header.Get("Upgrade") == "websocket" {
|
||||
err := xnet.Transport(c, xio.NewReadWriter(br, conn))
|
||||
err = xnet.Transport(c, xio.NewReadWriter(br, conn))
|
||||
if err == nil {
|
||||
err = io.EOF
|
||||
}
|
||||
@@ -182,21 +227,35 @@ func (ep *entrypoint) handle(ctx context.Context, conn net.Conn) error {
|
||||
go func() {
|
||||
defer c.Close()
|
||||
|
||||
t := time.Now()
|
||||
log.Debugf("%s <-> %s", remoteAddr, host)
|
||||
|
||||
var err error
|
||||
var res *http.Response
|
||||
|
||||
defer func() {
|
||||
d := time.Since(start)
|
||||
log.WithFields(map[string]any{
|
||||
"duration": time.Since(t),
|
||||
"duration": d,
|
||||
}).Debugf("%s >-< %s", remoteAddr, host)
|
||||
|
||||
ro.Duration = d
|
||||
if err != nil {
|
||||
ro.Err = err.Error()
|
||||
}
|
||||
if res != nil && ro.HTTP != nil {
|
||||
ro.HTTP.ResponseHeader = res.Header
|
||||
ro.HTTP.StatusCode = res.StatusCode
|
||||
}
|
||||
ro.Record(ctx, ep.recorder.Recorder)
|
||||
}()
|
||||
|
||||
res, err := http.ReadResponse(bufio.NewReader(c), req)
|
||||
res, err = http.ReadResponse(bufio.NewReader(c), req)
|
||||
if err != nil {
|
||||
log.Errorf("read response: %v", err)
|
||||
resp.Write(conn)
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
dump, _ := httputil.DumpResponse(res, false)
|
||||
|
||||
+21
-23
@@ -13,13 +13,13 @@ import (
|
||||
"github.com/go-gost/core/listener"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/core/recorder"
|
||||
"github.com/go-gost/core/service"
|
||||
"github.com/go-gost/relay"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
xnet "github.com/go-gost/x/internal/net"
|
||||
limiter_util "github.com/go-gost/x/internal/util/limiter"
|
||||
stats_util "github.com/go-gost/x/internal/util/stats"
|
||||
rate_limiter "github.com/go-gost/x/limiter/rate"
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
"github.com/go-gost/x/registry"
|
||||
xservice "github.com/go-gost/x/service"
|
||||
@@ -32,7 +32,6 @@ var (
|
||||
ErrTunnelID = errors.New("invalid tunnel ID")
|
||||
ErrTunnelNotAvailable = errors.New("tunnel not available")
|
||||
ErrUnauthorized = errors.New("unauthorized")
|
||||
ErrRateLimit = errors.New("rate limiting exceeded")
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -40,17 +39,16 @@ func init() {
|
||||
}
|
||||
|
||||
type tunnelHandler struct {
|
||||
id string
|
||||
options handler.Options
|
||||
pool *ConnectorPool
|
||||
recorder recorder.Recorder
|
||||
epSvc service.Service
|
||||
ep *entrypoint
|
||||
md metadata
|
||||
log logger.Logger
|
||||
stats *stats_util.HandlerStats
|
||||
limiter traffic.TrafficLimiter
|
||||
cancel context.CancelFunc
|
||||
id string
|
||||
options handler.Options
|
||||
pool *ConnectorPool
|
||||
epSvc service.Service
|
||||
ep *entrypoint
|
||||
md metadata
|
||||
log logger.Logger
|
||||
stats *stats_util.HandlerStats
|
||||
limiter traffic.TrafficLimiter
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
@@ -79,19 +77,11 @@ func (h *tunnelHandler) Init(md md.Metadata) (err error) {
|
||||
"node": h.id,
|
||||
})
|
||||
|
||||
if opts := h.options.Router.Options(); opts != nil {
|
||||
for _, ro := range opts.Recorders {
|
||||
if ro.Record == xrecorder.RecorderServiceHandlerTunnel {
|
||||
h.recorder = ro.Recorder
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h.pool = NewConnectorPool(h.id, h.md.sd)
|
||||
|
||||
h.ep = &entrypoint{
|
||||
node: h.id,
|
||||
service: h.options.Service,
|
||||
pool: h.pool,
|
||||
ingress: h.md.ingress,
|
||||
sd: h.md.sd,
|
||||
@@ -103,6 +93,13 @@ func (h *tunnelHandler) Init(md md.Metadata) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
if ro.Record == xrecorder.RecorderServiceHandler {
|
||||
h.ep.recorder = ro
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
h.cancel = cancel
|
||||
|
||||
@@ -174,6 +171,7 @@ func (h *tunnelHandler) Handle(ctx context.Context, conn net.Conn, opts ...handl
|
||||
log := h.log.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
})
|
||||
|
||||
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
@@ -188,7 +186,7 @@ func (h *tunnelHandler) Handle(ctx context.Context, conn net.Conn, opts ...handl
|
||||
}()
|
||||
|
||||
if !h.checkRateLimit(conn.RemoteAddr()) {
|
||||
return ErrRateLimit
|
||||
return rate_limiter.ErrRateLimit
|
||||
}
|
||||
|
||||
if h.md.readTimeout > 0 {
|
||||
|
||||
+43
-7
@@ -12,7 +12,10 @@ import (
|
||||
"github.com/go-gost/core/hop"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/core/recorder"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
xnet "github.com/go-gost/x/internal/net"
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
@@ -21,9 +24,10 @@ func init() {
|
||||
}
|
||||
|
||||
type unixHandler struct {
|
||||
hop hop.Hop
|
||||
md metadata
|
||||
options handler.Options
|
||||
hop hop.Hop
|
||||
md metadata
|
||||
options handler.Options
|
||||
recorder recorder.RecorderObject
|
||||
}
|
||||
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
@@ -42,6 +46,13 @@ func (h *unixHandler) Init(md md.Metadata) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
for _, ro := range h.options.Recorders {
|
||||
if ro.Record == xrecorder.RecorderServiceHandler {
|
||||
h.recorder = ro
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -50,20 +61,43 @@ func (h *unixHandler) Forward(hop hop.Hop) {
|
||||
h.hop = hop
|
||||
}
|
||||
|
||||
func (h *unixHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
|
||||
func (h *unixHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) (err error) {
|
||||
defer conn.Close()
|
||||
|
||||
log := h.options.Logger
|
||||
start := time.Now()
|
||||
|
||||
log = log.WithFields(map[string]any{
|
||||
ro := &xrecorder.HandlerRecorderObject{
|
||||
Service: h.options.Service,
|
||||
Network: "unix",
|
||||
RemoteAddr: conn.RemoteAddr().String(),
|
||||
LocalAddr: conn.LocalAddr().String(),
|
||||
Time: start,
|
||||
SID: string(ctxvalue.SidFromContext(ctx)),
|
||||
}
|
||||
|
||||
log := h.options.Logger.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
})
|
||||
|
||||
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
ro.Err = err.Error()
|
||||
}
|
||||
ro.Duration = time.Since(start)
|
||||
ro.Record(ctx, h.recorder.Recorder)
|
||||
|
||||
log.WithFields(map[string]any{
|
||||
"duration": time.Since(start),
|
||||
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
}()
|
||||
|
||||
if h.hop != nil {
|
||||
target := h.hop.Select(ctx)
|
||||
if target == nil {
|
||||
err := errors.New("target not available")
|
||||
err = errors.New("target not available")
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
@@ -71,6 +105,8 @@ func (h *unixHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler
|
||||
"node": target.Name,
|
||||
"dst": target.Addr,
|
||||
})
|
||||
ro.Host = target.Addr
|
||||
|
||||
return h.forwardUnix(ctx, conn, target, log)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user