From 15a68623ab80bb17f3f38ba9a0b94d23fe98c077 Mon Sep 17 00:00:00 2001 From: ginuerzh Date: Fri, 26 Nov 2021 20:00:23 +0800 Subject: [PATCH] add tcp redirect --- cmd/gost/register.go | 1 + pkg/handler/redirect/handler.go | 114 ++++++++++++++++++++++++++ pkg/handler/redirect/handler_linux.go | 41 +++++++++ pkg/handler/redirect/handler_other.go | 15 ++++ pkg/handler/redirect/metadata.go | 18 ++++ 5 files changed, 189 insertions(+) create mode 100644 pkg/handler/redirect/handler.go create mode 100644 pkg/handler/redirect/handler_linux.go create mode 100644 pkg/handler/redirect/handler_other.go create mode 100644 pkg/handler/redirect/metadata.go diff --git a/cmd/gost/register.go b/cmd/gost/register.go index a0a2f0d..9526f6f 100644 --- a/cmd/gost/register.go +++ b/cmd/gost/register.go @@ -20,6 +20,7 @@ import ( _ "github.com/go-gost/gost/pkg/handler/forward/local" _ "github.com/go-gost/gost/pkg/handler/forward/remote" _ "github.com/go-gost/gost/pkg/handler/http" + _ "github.com/go-gost/gost/pkg/handler/redirect" _ "github.com/go-gost/gost/pkg/handler/relay" _ "github.com/go-gost/gost/pkg/handler/sni" _ "github.com/go-gost/gost/pkg/handler/socks/v4" diff --git a/pkg/handler/redirect/handler.go b/pkg/handler/redirect/handler.go new file mode 100644 index 0000000..f180d38 --- /dev/null +++ b/pkg/handler/redirect/handler.go @@ -0,0 +1,114 @@ +package redirect + +import ( + "context" + "fmt" + "net" + "time" + + "github.com/go-gost/gost/pkg/bypass" + "github.com/go-gost/gost/pkg/chain" + "github.com/go-gost/gost/pkg/handler" + "github.com/go-gost/gost/pkg/logger" + md "github.com/go-gost/gost/pkg/metadata" + "github.com/go-gost/gost/pkg/registry" +) + +func init() { + registry.RegisterHandler("red", NewHandler) + registry.RegisterHandler("redir", NewHandler) + registry.RegisterHandler("redirect", NewHandler) +} + +type redirectHandler struct { + chain *chain.Chain + bypass bypass.Bypass + logger logger.Logger + md metadata +} + +func NewHandler(opts ...handler.Option) handler.Handler { + options := &handler.Options{} + for _, opt := range opts { + opt(options) + } + + return &redirectHandler{ + bypass: options.Bypass, + logger: options.Logger, + } +} + +func (h *redirectHandler) Init(md md.Metadata) (err error) { + return h.parseMetadata(md) +} + +// WithChain implements chain.Chainable interface +func (h *redirectHandler) WithChain(chain *chain.Chain) { + h.chain = chain +} + +func (h *redirectHandler) Handle(ctx context.Context, conn net.Conn) { + start := time.Now() + h.logger = h.logger.WithFields(map[string]interface{}{ + "remote": conn.RemoteAddr().String(), + "local": conn.LocalAddr().String(), + }) + + h.logger.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr()) + defer func() { + h.logger.WithFields(map[string]interface{}{ + "duration": time.Since(start), + }).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr()) + }() + + network := "tcp" + var dstAddr net.Addr + var err error + + if _, ok := conn.(net.PacketConn); ok { + network = "udp" + dstAddr = conn.LocalAddr() + } + + if network == "tcp" { + dstAddr, conn, err = h.getOriginalDstAddr(conn) + if err != nil { + h.logger.Error(err) + return + } + } + defer conn.Close() + + h.logger = h.logger.WithFields(map[string]interface{}{ + "dst": fmt.Sprintf("%s/%s", dstAddr, network), + }) + + h.logger.Infof("%s >> %s", conn.RemoteAddr(), dstAddr) + + if h.bypass != nil && h.bypass.Contains(dstAddr.String()) { + h.logger.Info("bypass: ", dstAddr) + return + } + + r := (&chain.Router{}). + WithChain(h.chain). + WithRetry(h.md.retryCount). + WithLogger(h.logger) + + cc, err := r.Dial(ctx, network, dstAddr.String()) + if err != nil { + h.logger.Error(err) + return + } + defer cc.Close() + + t := time.Now() + h.logger.Infof("%s <-> %s", conn.RemoteAddr(), dstAddr) + handler.Transport(conn, cc) + h.logger. + WithFields(map[string]interface{}{ + "duration": time.Since(t), + }). + Infof("%s >-< %s", conn.RemoteAddr(), dstAddr) +} diff --git a/pkg/handler/redirect/handler_linux.go b/pkg/handler/redirect/handler_linux.go new file mode 100644 index 0000000..925bc80 --- /dev/null +++ b/pkg/handler/redirect/handler_linux.go @@ -0,0 +1,41 @@ +package redirect + +import ( + "errors" + "fmt" + "net" + "syscall" +) + +func (h *redirectHandler) getOriginalDstAddr(conn net.Conn) (addr net.Addr, c net.Conn, err error) { + defer conn.Close() + + tc, ok := conn.(*net.TCPConn) + if !ok { + err = errors.New("wrong connection type, must be TCP") + h.logger.Error(err) + return + } + + fc, err := tc.File() + if err != nil { + return + } + defer fc.Close() + + mreq, err := syscall.GetsockoptIPv6Mreq(int(fc.Fd()), syscall.IPPROTO_IP, 80) + if err != nil { + return + } + + // only ipv4 support + ip := net.IPv4(mreq.Multiaddr[4], mreq.Multiaddr[5], mreq.Multiaddr[6], mreq.Multiaddr[7]) + port := uint16(mreq.Multiaddr[2])<<8 + uint16(mreq.Multiaddr[3]) + addr, err = net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", ip.String(), port)) + if err != nil { + return + } + + c, err = net.FileConn(fc) + return +} diff --git a/pkg/handler/redirect/handler_other.go b/pkg/handler/redirect/handler_other.go new file mode 100644 index 0000000..f576617 --- /dev/null +++ b/pkg/handler/redirect/handler_other.go @@ -0,0 +1,15 @@ +//go:build !linux + +package redirect + +import ( + "errors" + "net" +) + +func (h *redirectHandler) getOriginalDstAddr(conn net.Conn) (addr net.Addr, c net.Conn, err error) { + defer conn.Close() + + err = errors.New("TCP redirect is not available on Windows platform") + return +} diff --git a/pkg/handler/redirect/metadata.go b/pkg/handler/redirect/metadata.go new file mode 100644 index 0000000..7edf818 --- /dev/null +++ b/pkg/handler/redirect/metadata.go @@ -0,0 +1,18 @@ +package redirect + +import ( + md "github.com/go-gost/gost/pkg/metadata" +) + +type metadata struct { + retryCount int +} + +func (h *redirectHandler) parseMetadata(md md.Metadata) (err error) { + const ( + retryCount = "retry" + ) + + h.md.retryCount = md.GetInt(retryCount) + return +}