From 1c6bc9283ea2e02c4d3bc1eac54d0202adecef83 Mon Sep 17 00:00:00 2001 From: ginuerzh Date: Mon, 30 Jan 2023 21:09:21 +0800 Subject: [PATCH] forward non-HTTP traffic based on host --- admission/wrapper/conn.go | 15 +++++++++++++++ chain/hop.go | 1 - connector/relay/conn.go | 7 +++++++ connector/relay/listener.go | 18 ++++++++++++++---- handler/forward/local/handler.go | 14 +++++++++++++- handler/forward/remote/handler.go | 24 +++++++++++++++++++++--- handler/relay/connect.go | 21 ++++++++++++++++++--- handler/relay/entrypoint.go | 2 +- limiter/conn/wrapper/conn.go | 8 ++++++++ limiter/traffic/wrapper/conn.go | 15 +++++++++++++++ metrics/wrapper/conn.go | 15 +++++++++++++++ 11 files changed, 127 insertions(+), 13 deletions(-) diff --git a/admission/wrapper/conn.go b/admission/wrapper/conn.go index e8a7953..b7fe2f6 100644 --- a/admission/wrapper/conn.go +++ b/admission/wrapper/conn.go @@ -7,6 +7,7 @@ import ( "syscall" "github.com/go-gost/core/admission" + "github.com/go-gost/core/metadata" xnet "github.com/go-gost/x/internal/net" "github.com/go-gost/x/internal/net/udp" ) @@ -48,6 +49,13 @@ func (c *serverConn) SyscallConn() (rc syscall.RawConn, err error) { return } +func (c *serverConn) Metadata() metadata.Metadata { + if md, ok := c.Conn.(metadata.Metadatable); ok { + return md.Metadata() + } + return nil +} + type packetConn struct { net.PacketConn admission admission.Admission @@ -79,6 +87,13 @@ func (c *packetConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } } +func (c *packetConn) Metadata() metadata.Metadata { + if md, ok := c.PacketConn.(metadata.Metadatable); ok { + return md.Metadata() + } + return nil +} + type udpConn struct { net.PacketConn admission admission.Admission diff --git a/chain/hop.go b/chain/hop.go index b9d13c3..ee908d2 100644 --- a/chain/hop.go +++ b/chain/hop.go @@ -97,7 +97,6 @@ func (p *chainHop) Select(ctx context.Context, opts ...chain.SelectOption) *chai if vhost == host || vhost[0] == '.' && strings.HasSuffix(host, vhost[1:]) { filters = append(filters, node) - p.options.logger.Debugf("find node for host: %s -> %s(%s)", host, node.Name, node.Addr) } } if len(filters) == 0 { diff --git a/connector/relay/conn.go b/connector/relay/conn.go index 11b29f9..a2a6171 100644 --- a/connector/relay/conn.go +++ b/connector/relay/conn.go @@ -10,6 +10,7 @@ import ( "net" "sync" + mdata "github.com/go-gost/core/metadata" "github.com/go-gost/relay" ) @@ -121,6 +122,7 @@ type bindConn struct { net.Conn localAddr net.Addr remoteAddr net.Addr + md mdata.Metadata } func (c *bindConn) LocalAddr() net.Addr { @@ -130,3 +132,8 @@ func (c *bindConn) LocalAddr() net.Addr { func (c *bindConn) RemoteAddr() net.Addr { return c.remoteAddr } + +// Metadata implements metadata.Metadatable interface. +func (c *bindConn) Metadata() mdata.Metadata { + return c.md +} diff --git a/connector/relay/listener.go b/connector/relay/listener.go index 432b3ae..f47525f 100644 --- a/connector/relay/listener.go +++ b/connector/relay/listener.go @@ -8,6 +8,7 @@ import ( "github.com/go-gost/core/logger" "github.com/go-gost/relay" "github.com/go-gost/x/internal/util/mux" + mdx "github.com/go-gost/x/metadata" ) type tcpListener struct { @@ -44,11 +45,16 @@ func (p *tcpListener) getPeerConn(conn net.Conn) (net.Conn, error) { return nil, err } - var address string + var address, host string for _, f := range resp.Features { if f.Type() == relay.FeatureAddr { if fa, ok := f.(*relay.AddrFeature); ok { - address = net.JoinHostPort(fa.Host, strconv.Itoa(int(fa.Port))) + v := net.JoinHostPort(fa.Host, strconv.Itoa(int(fa.Port))) + if address != "" { + host = v + } else { + address = v + } } } } @@ -58,11 +64,15 @@ func (p *tcpListener) getPeerConn(conn net.Conn) (net.Conn, error) { return nil, err } - return &bindConn{ + cn := &bindConn{ Conn: conn, localAddr: p.addr, remoteAddr: raddr, - }, nil + } + if host != "" { + cn.md = mdx.NewMetadata(map[string]any{"host": host}) + } + return cn, nil } func (p *tcpListener) Addr() net.Addr { diff --git a/handler/forward/local/handler.go b/handler/forward/local/handler.go index e668874..690d8fb 100644 --- a/handler/forward/local/handler.go +++ b/handler/forward/local/handler.go @@ -3,6 +3,7 @@ package local import ( "bufio" "context" + "crypto/tls" "errors" "fmt" "io" @@ -94,7 +95,7 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand if h.md.sniffing { if network == "tcp" { rw, host, protocol, _ = forward.Sniffing(ctx, conn) - h.options.Logger.Debugf("sniffing: host=%s, protocol=%s", host, protocol) + log.Debugf("sniffing: host=%s, protocol=%s", host, protocol) } } @@ -177,12 +178,14 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, log l ) } if target == nil { + log.Warnf("node for %s not found", req.Host) return resp.Write(rw) } log = log.WithFields(map[string]any{ "dst": target.Addr, }) + log.Debugf("find node for host %s -> %s(%s)", req.Host, target.Name, target.Addr) // log.Debugf("%s >> %s", conn.RemoteAddr(), target.Addr) @@ -199,11 +202,20 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, log l if marker := target.Marker(); marker != nil { marker.Mark() } + log.Warnf("connect to node %s(%s) failed", target.Name, target.Addr) return resp.Write(rw) } if marker := target.Marker(); marker != nil { marker.Reset() } + + if tlsSettings := target.Options().TLS; tlsSettings != nil { + cc = tls.Client(cc, &tls.Config{ + ServerName: tlsSettings.ServerName, + InsecureSkipVerify: !tlsSettings.Secure, + }) + } + connPool.Store(target, cc) log.Debugf("new connection to node %s(%s)", target.Name, target.Addr) diff --git a/handler/forward/remote/handler.go b/handler/forward/remote/handler.go index b13edb8..04f320b 100644 --- a/handler/forward/remote/handler.go +++ b/handler/forward/remote/handler.go @@ -3,6 +3,7 @@ package remote import ( "bufio" "context" + "crypto/tls" "errors" "fmt" "io" @@ -15,7 +16,8 @@ import ( "github.com/go-gost/core/chain" "github.com/go-gost/core/handler" "github.com/go-gost/core/logger" - md "github.com/go-gost/core/metadata" + mdata "github.com/go-gost/core/metadata" + mdutil "github.com/go-gost/core/metadata/util" xnet "github.com/go-gost/x/internal/net" "github.com/go-gost/x/internal/util/forward" "github.com/go-gost/x/registry" @@ -44,7 +46,7 @@ func NewHandler(opts ...handler.Option) handler.Handler { } } -func (h *forwardHandler) Init(md md.Metadata) (err error) { +func (h *forwardHandler) Init(md mdata.Metadata) (err error) { if err = h.parseMetadata(md); err != nil { return } @@ -93,14 +95,19 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand if h.md.sniffing { if network == "tcp" { rw, host, protocol, _ = forward.Sniffing(ctx, conn) + log.Debugf("sniffing: host=%s, protocol=%s", host, protocol) } } - if protocol == forward.ProtoHTTP { h.handleHTTP(ctx, rw, log) return nil } + if md, ok := conn.(mdata.Metadatable); ok { + if v := mdutil.GetString(md.Metadata(), "host"); v != "" { + host = v + } + } var target *chain.Node if h.hop != nil { target = h.hop.Select(ctx, @@ -170,12 +177,14 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, log l ) } if target == nil { + log.Warnf("node for %s not found", req.Host) return resp.Write(rw) } log = log.WithFields(map[string]any{ "dst": target.Addr, }) + log.Debugf("find node for host %s -> %s(%s)", req.Host, target.Name, target.Addr) // log.Debugf("%s >> %s", conn.RemoteAddr(), target.Addr) @@ -192,11 +201,20 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, log l if marker := target.Marker(); marker != nil { marker.Mark() } + log.Warnf("connect to node %s(%s) failed", target.Name, target.Addr) return resp.Write(rw) } if marker := target.Marker(); marker != nil { marker.Reset() } + + if tlsSettings := target.Options().TLS; tlsSettings != nil { + cc = tls.Client(cc, &tls.Config{ + ServerName: tlsSettings.ServerName, + InsecureSkipVerify: !tlsSettings.Secure, + }) + } + connPool.Store(target, cc) log.Debugf("new connection to node %s(%s)", target.Name, target.Addr) diff --git a/handler/relay/connect.go b/handler/relay/connect.go index 5d8eb53..fe2ac0c 100644 --- a/handler/relay/connect.go +++ b/handler/relay/connect.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "net" + "strconv" "time" "github.com/go-gost/core/logger" @@ -110,7 +111,7 @@ func (h *relayHandler) handleConnectTunnel(ctx context.Context, conn net.Conn, n Status: relay.StatusOK, } - host, _, _ := net.SplitHostPort(address) + host, sp, _ := net.SplitHostPort(address) if h.options.Bypass != nil && h.options.Bypass.Contains(address) { log.Debug("bypass: ", address) @@ -157,12 +158,26 @@ func (h *relayHandler) handleConnectTunnel(ctx context.Context, conn net.Conn, n conn = rc } - af := &relay.AddrFeature{} + var features []relay.Feature + af := &relay.AddrFeature{} // visitor address af.ParseFrom(conn.RemoteAddr().String()) + features = append(features, af) + + if host != "" { + port, _ := strconv.Atoi(sp) + // target host + af = &relay.AddrFeature{ + AType: relay.AddrDomain, + Host: host, + Port: uint16(port), + } + features = append(features, af) + } + resp = relay.Response{ Version: relay.Version1, Status: relay.StatusOK, - Features: []relay.Feature{af}, + Features: features, } resp.WriteTo(cc) diff --git a/handler/relay/entrypoint.go b/handler/relay/entrypoint.go index a8e6e31..6d4409e 100644 --- a/handler/relay/entrypoint.go +++ b/handler/relay/entrypoint.go @@ -120,7 +120,7 @@ func (h *epHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.H tunnelID = parseTunnelID(h.ingress.Get(host)) } if tunnelID.IsPrivate() { - err := fmt.Errorf("tunnel %s is private", tunnelID) + err := fmt.Errorf("access denied: tunnel %s is private", tunnelID) log.Error(err) return err } diff --git a/limiter/conn/wrapper/conn.go b/limiter/conn/wrapper/conn.go index c519a80..4395148 100644 --- a/limiter/conn/wrapper/conn.go +++ b/limiter/conn/wrapper/conn.go @@ -6,6 +6,7 @@ import ( "syscall" limiter "github.com/go-gost/core/limiter/conn" + "github.com/go-gost/core/metadata" ) var ( @@ -41,3 +42,10 @@ func (c *serverConn) Close() error { c.limiter.Allow(-1) return c.Conn.Close() } + +func (c *serverConn) Metadata() metadata.Metadata { + if md, ok := c.Conn.(metadata.Metadatable); ok { + return md.Metadata() + } + return nil +} diff --git a/limiter/traffic/wrapper/conn.go b/limiter/traffic/wrapper/conn.go index 3842299..144d130 100644 --- a/limiter/traffic/wrapper/conn.go +++ b/limiter/traffic/wrapper/conn.go @@ -10,6 +10,7 @@ import ( "time" limiter "github.com/go-gost/core/limiter/traffic" + "github.com/go-gost/core/metadata" xnet "github.com/go-gost/x/internal/net" "github.com/go-gost/x/internal/net/udp" "github.com/patrickmn/go-cache" @@ -118,6 +119,13 @@ func (c *serverConn) SyscallConn() (rc syscall.RawConn, err error) { return } +func (c *serverConn) Metadata() metadata.Metadata { + if md, ok := c.Conn.(metadata.Metadatable); ok { + return md.Metadata() + } + return nil +} + type packetConn struct { net.PacketConn limiter limiter.TrafficLimiter @@ -217,6 +225,13 @@ func (c *packetConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { return c.PacketConn.WriteTo(p, addr) } +func (c *packetConn) Metadata() metadata.Metadata { + if md, ok := c.PacketConn.(metadata.Metadatable); ok { + return md.Metadata() + } + return nil +} + type udpConn struct { net.PacketConn limiter limiter.TrafficLimiter diff --git a/metrics/wrapper/conn.go b/metrics/wrapper/conn.go index dd9bdc7..3cd4fe7 100644 --- a/metrics/wrapper/conn.go +++ b/metrics/wrapper/conn.go @@ -6,6 +6,7 @@ import ( "net" "syscall" + "github.com/go-gost/core/metadata" "github.com/go-gost/core/metrics" xnet "github.com/go-gost/x/internal/net" "github.com/go-gost/x/internal/net/udp" @@ -65,6 +66,13 @@ func (c *serverConn) SyscallConn() (rc syscall.RawConn, err error) { return } +func (c *serverConn) Metadata() metadata.Metadata { + if md, ok := c.Conn.(metadata.Metadatable); ok { + return md.Metadata() + } + return nil +} + type packetConn struct { net.PacketConn service string @@ -104,6 +112,13 @@ func (c *packetConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { return } +func (c *packetConn) Metadata() metadata.Metadata { + if md, ok := c.PacketConn.(metadata.Metadatable); ok { + return md.Metadata() + } + return nil +} + type udpConn struct { net.PacketConn service string