From 295f763bf684ed83a8cbcfe4dcf056b0ec4e585b Mon Sep 17 00:00:00 2001 From: Tianling Shen Date: Wed, 8 Jun 2022 02:28:05 +0800 Subject: [PATCH] hysteria: backport upstream patch to support tcp redir Signed-off-by: Tianling Shen --- ...010-feat-TCP-redirect-implementation.patch | 244 ++++++++++++++++++ .../020-fix-build-failure-on-linux-386.patch | 144 +++++++++++ 2 files changed, 388 insertions(+) create mode 100644 hysteria/patches/010-feat-TCP-redirect-implementation.patch create mode 100644 hysteria/patches/020-fix-build-failure-on-linux-386.patch diff --git a/hysteria/patches/010-feat-TCP-redirect-implementation.patch b/hysteria/patches/010-feat-TCP-redirect-implementation.patch new file mode 100644 index 000000000..8ddb9ce25 --- /dev/null +++ b/hysteria/patches/010-feat-TCP-redirect-implementation.patch @@ -0,0 +1,244 @@ +From 575de280fff4021c613522c477ff8567b782c9c8 Mon Sep 17 00:00:00 2001 +From: Toby +Date: Mon, 6 Jun 2022 18:09:34 -0700 +Subject: [PATCH] feat: TCP redirect implementation + +--- + cmd/client.go | 33 +++++++++++ + cmd/config.go | 10 +++- + pkg/redirect/tcp_linux.go | 119 ++++++++++++++++++++++++++++++++++++++ + pkg/redirect/tcp_stub.go | 23 ++++++++ + 4 files changed, 184 insertions(+), 1 deletion(-) + create mode 100644 pkg/redirect/tcp_linux.go + create mode 100644 pkg/redirect/tcp_stub.go + +--- a/cmd/client.go ++++ b/cmd/client.go +@@ -6,6 +6,7 @@ import ( + "errors" + "github.com/oschwald/geoip2-golang" + "github.com/tobyxdd/hysteria/pkg/pmtud_fix" ++ "github.com/tobyxdd/hysteria/pkg/redirect" + "github.com/yosuke-furukawa/json5/encoding/json5" + "io" + "io/ioutil" +@@ -434,6 +435,38 @@ func client(config *clientConfig) { + errChan <- rl.ListenAndServe() + }() + } ++ ++ if len(config.TCPRedirect.Listen) > 0 { ++ go func() { ++ rl, err := redirect.NewTCPRedirect(client, config.TCPRedirect.Listen, ++ time.Duration(config.TCPRedirect.Timeout)*time.Second, ++ func(addr, reqAddr net.Addr) { ++ logrus.WithFields(logrus.Fields{ ++ "src": addr.String(), ++ "dst": reqAddr.String(), ++ }).Debug("TCP Redirect request") ++ }, ++ func(addr, reqAddr net.Addr, err error) { ++ if err != io.EOF { ++ logrus.WithFields(logrus.Fields{ ++ "error": err, ++ "src": addr.String(), ++ "dst": reqAddr.String(), ++ }).Info("TCP Redirect error") ++ } else { ++ logrus.WithFields(logrus.Fields{ ++ "src": addr.String(), ++ "dst": reqAddr.String(), ++ }).Debug("TCP Redirect EOF") ++ } ++ }) ++ if err != nil { ++ logrus.WithField("error", err).Fatal("Failed to initialize TCP Redirect") ++ } ++ logrus.WithField("addr", config.TCPRedirect.Listen).Info("TCP Redirect up and running") ++ errChan <- rl.ListenAndServe() ++ }() ++ } + + err := <-errChan + logrus.WithField("error", err).Fatal("Client shutdown") +--- a/cmd/config.go ++++ b/cmd/config.go +@@ -175,6 +175,10 @@ type clientConfig struct { + Listen string `json:"listen"` + Timeout int `json:"timeout"` + } `json:"tproxy_udp"` ++ TCPRedirect struct { ++ Listen string `json:"listen"` ++ Timeout int `json:"timeout"` ++ } `json:"redirect_tcp"` + ACL string `json:"acl"` + MMDB string `json:"mmdb"` + Obfs string `json:"obfs"` +@@ -216,7 +220,8 @@ func (c *clientConfig) Check() error { + if len(c.SOCKS5.Listen) == 0 && len(c.HTTP.Listen) == 0 && len(c.TUN.Name) == 0 && + len(c.TCPRelay.Listen) == 0 && len(c.UDPRelay.Listen) == 0 && + len(c.TCPRelays) == 0 && len(c.UDPRelays) == 0 && +- len(c.TCPTProxy.Listen) == 0 && len(c.UDPTProxy.Listen) == 0 { ++ len(c.TCPTProxy.Listen) == 0 && len(c.UDPTProxy.Listen) == 0 && ++ len(c.TCPRedirect.Listen) == 0 { + return errors.New("please enable at least one mode") + } + if c.SOCKS5.Timeout != 0 && c.SOCKS5.Timeout <= 4 { +@@ -256,6 +261,9 @@ func (c *clientConfig) Check() error { + if c.UDPTProxy.Timeout != 0 && c.UDPTProxy.Timeout <= 4 { + return errors.New("invalid UDP TProxy timeout") + } ++ if c.TCPRedirect.Timeout != 0 && c.TCPRedirect.Timeout <= 4 { ++ return errors.New("invalid TCP Redirect timeout") ++ } + if len(c.Server) == 0 { + return errors.New("no server address") + } +--- /dev/null ++++ b/pkg/redirect/tcp_linux.go +@@ -0,0 +1,119 @@ ++package redirect ++ ++import ( ++ "encoding/binary" ++ "errors" ++ "github.com/tobyxdd/hysteria/pkg/core" ++ "github.com/tobyxdd/hysteria/pkg/utils" ++ "net" ++ "syscall" ++ "time" ++ "unsafe" ++) ++ ++const ( ++ SO_ORIGINAL_DST = 80 ++ IP6T_SO_ORIGINAL_DST = 80 ++) ++ ++type TCPRedirect struct { ++ HyClient *core.Client ++ ListenAddr *net.TCPAddr ++ Timeout time.Duration ++ ++ ConnFunc func(addr, reqAddr net.Addr) ++ ErrorFunc func(addr, reqAddr net.Addr, err error) ++} ++ ++func NewTCPRedirect(hyClient *core.Client, listen string, timeout time.Duration, ++ connFunc func(addr, reqAddr net.Addr), ++ errorFunc func(addr, reqAddr net.Addr, err error)) (*TCPRedirect, error) { ++ tAddr, err := net.ResolveTCPAddr("tcp", listen) ++ if err != nil { ++ return nil, err ++ } ++ r := &TCPRedirect{ ++ HyClient: hyClient, ++ ListenAddr: tAddr, ++ Timeout: timeout, ++ ConnFunc: connFunc, ++ ErrorFunc: errorFunc, ++ } ++ return r, nil ++} ++ ++func (r *TCPRedirect) ListenAndServe() error { ++ listener, err := net.ListenTCP("tcp", r.ListenAddr) ++ if err != nil { ++ return err ++ } ++ defer listener.Close() ++ for { ++ c, err := listener.Accept() ++ if err != nil { ++ return err ++ } ++ go func() { ++ defer c.Close() ++ dest, err := getDestAddr(c.(*net.TCPConn)) ++ if err != nil || dest.IP.IsLoopback() { ++ // Silently drop the connection if we failed to get the destination address, ++ // or if it's a loopback address (not a redirected connection). ++ return ++ } ++ r.ConnFunc(c.RemoteAddr(), dest) ++ rc, err := r.HyClient.DialTCP(dest.String()) ++ if err != nil { ++ r.ErrorFunc(c.RemoteAddr(), dest, err) ++ return ++ } ++ defer rc.Close() ++ err = utils.PipePairWithTimeout(c, rc, r.Timeout) ++ r.ErrorFunc(c.RemoteAddr(), dest, err) ++ }() ++ } ++} ++ ++type sockAddr struct { ++ family uint16 ++ port [2]byte // big endian regardless of host byte order ++ data [24]byte // check sockaddr_in or sockaddr_in6 for more information ++} ++ ++func getDestAddr(conn *net.TCPConn) (*net.TCPAddr, error) { ++ rc, err := conn.SyscallConn() ++ if err != nil { ++ return nil, err ++ } ++ var addr sockAddr ++ addrSize := uint32(unsafe.Sizeof(addr)) ++ var err2 error ++ err = rc.Control(func(fd uintptr) { ++ // try IPv6 first ++ _, _, err := syscall.Syscall6(syscall.SYS_GETSOCKOPT, fd, syscall.SOL_IPV6, IP6T_SO_ORIGINAL_DST, ++ uintptr(unsafe.Pointer(&addr)), uintptr(unsafe.Pointer(&addrSize)), 0) ++ if err != 0 { ++ // try IPv4 ++ _, _, err = syscall.Syscall6(syscall.SYS_GETSOCKOPT, fd, syscall.SOL_IP, SO_ORIGINAL_DST, ++ uintptr(unsafe.Pointer(&addr)), uintptr(unsafe.Pointer(&addrSize)), 0) ++ if err != 0 { ++ // failed ++ err2 = err ++ } ++ } ++ }) ++ if err != nil { ++ return nil, err ++ } ++ if err2 != nil { ++ return nil, err2 ++ } ++ switch addr.family { ++ case syscall.AF_INET: ++ return &net.TCPAddr{IP: addr.data[:4], Port: int(binary.BigEndian.Uint16(addr.port[:]))}, nil ++ case syscall.AF_INET6: ++ return &net.TCPAddr{IP: addr.data[4:20], Port: int(binary.BigEndian.Uint16(addr.port[:]))}, nil ++ default: ++ return nil, errors.New("unknown address family") ++ } ++} +--- /dev/null ++++ b/pkg/redirect/tcp_stub.go +@@ -0,0 +1,23 @@ ++//go:build !linux ++// +build !linux ++ ++package redirect ++ ++import ( ++ "errors" ++ "github.com/tobyxdd/hysteria/pkg/core" ++ "net" ++ "time" ++) ++ ++type TCPRedirect struct{} ++ ++func NewTCPRedirect(hyClient *core.Client, listen string, timeout time.Duration, ++ connFunc func(addr, reqAddr net.Addr), ++ errorFunc func(addr, reqAddr net.Addr, err error)) (*TCPRedirect, error) { ++ return nil, errors.New("not supported on the current system") ++} ++ ++func (r *TCPRedirect) ListenAndServe() error { ++ return nil ++} diff --git a/hysteria/patches/020-fix-build-failure-on-linux-386.patch b/hysteria/patches/020-fix-build-failure-on-linux-386.patch new file mode 100644 index 000000000..5b5caa3b1 --- /dev/null +++ b/hysteria/patches/020-fix-build-failure-on-linux-386.patch @@ -0,0 +1,144 @@ +From 8a64099a9633f5537c41d4e12ea44870fe5d2e38 Mon Sep 17 00:00:00 2001 +From: Toby +Date: Tue, 7 Jun 2022 12:44:38 -0700 +Subject: [PATCH] fix: build failure on linux 386 + +--- + pkg/redirect/origdst_linux.go | 38 +++++++++++++++++++++++++++++++ + pkg/redirect/origdst_linux_386.go | 36 +++++++++++++++++++++++++++++ + pkg/redirect/tcp_linux.go | 28 ++--------------------- + 3 files changed, 76 insertions(+), 26 deletions(-) + create mode 100644 pkg/redirect/origdst_linux.go + create mode 100644 pkg/redirect/origdst_linux_386.go + +--- /dev/null ++++ b/pkg/redirect/origdst_linux.go +@@ -0,0 +1,38 @@ ++//go:build !386 ++// +build !386 ++ ++package redirect ++ ++import ( ++ "syscall" ++ "unsafe" ++) ++ ++const ( ++ SO_ORIGINAL_DST = 80 ++ IP6T_SO_ORIGINAL_DST = 80 ++) ++ ++type sockAddr struct { ++ family uint16 ++ port [2]byte // big endian regardless of host byte order ++ data [24]byte // check sockaddr_in or sockaddr_in6 for more information ++} ++ ++func getOrigDst(fd uintptr) (*sockAddr, error) { ++ var addr sockAddr ++ addrSize := uint32(unsafe.Sizeof(addr)) ++ // try IPv6 first ++ _, _, err := syscall.Syscall6(syscall.SYS_GETSOCKOPT, fd, syscall.SOL_IPV6, IP6T_SO_ORIGINAL_DST, ++ uintptr(unsafe.Pointer(&addr)), uintptr(unsafe.Pointer(&addrSize)), 0) ++ if err != 0 { ++ // try IPv4 ++ _, _, err = syscall.Syscall6(syscall.SYS_GETSOCKOPT, fd, syscall.SOL_IP, SO_ORIGINAL_DST, ++ uintptr(unsafe.Pointer(&addr)), uintptr(unsafe.Pointer(&addrSize)), 0) ++ if err != 0 { ++ // failed ++ return nil, err ++ } ++ } ++ return &addr, nil ++} +--- /dev/null ++++ b/pkg/redirect/origdst_linux_386.go +@@ -0,0 +1,36 @@ ++package redirect ++ ++import ( ++ "syscall" ++ "unsafe" ++) ++ ++const ( ++ SYS_GETSOCKOPT = 15 ++ SO_ORIGINAL_DST = 80 ++ IP6T_SO_ORIGINAL_DST = 80 ++) ++ ++type sockAddr struct { ++ family uint16 ++ port [2]byte // big endian regardless of host byte order ++ data [24]byte // check sockaddr_in or sockaddr_in6 for more information ++} ++ ++func getOrigDst(fd uintptr) (*sockAddr, error) { ++ var addr sockAddr ++ addrSize := uint32(unsafe.Sizeof(addr)) ++ // try IPv6 first ++ _, _, err := syscall.Syscall6(syscall.SYS_SOCKETCALL, SYS_GETSOCKOPT, fd, syscall.SOL_IPV6, IP6T_SO_ORIGINAL_DST, ++ uintptr(unsafe.Pointer(&addr)), uintptr(unsafe.Pointer(&addrSize))) ++ if err != 0 { ++ // try IPv4 ++ _, _, err = syscall.Syscall6(syscall.SYS_SOCKETCALL, SYS_GETSOCKOPT, fd, syscall.SOL_IP, SO_ORIGINAL_DST, ++ uintptr(unsafe.Pointer(&addr)), uintptr(unsafe.Pointer(&addrSize))) ++ if err != 0 { ++ // failed ++ return nil, err ++ } ++ } ++ return &addr, nil ++} +--- a/pkg/redirect/tcp_linux.go ++++ b/pkg/redirect/tcp_linux.go +@@ -8,12 +8,6 @@ import ( + "net" + "syscall" + "time" +- "unsafe" +-) +- +-const ( +- SO_ORIGINAL_DST = 80 +- IP6T_SO_ORIGINAL_DST = 80 + ) + + type TCPRedirect struct { +@@ -74,33 +68,15 @@ func (r *TCPRedirect) ListenAndServe() e + } + } + +-type sockAddr struct { +- family uint16 +- port [2]byte // big endian regardless of host byte order +- data [24]byte // check sockaddr_in or sockaddr_in6 for more information +-} +- + func getDestAddr(conn *net.TCPConn) (*net.TCPAddr, error) { + rc, err := conn.SyscallConn() + if err != nil { + return nil, err + } +- var addr sockAddr +- addrSize := uint32(unsafe.Sizeof(addr)) ++ var addr *sockAddr + var err2 error + err = rc.Control(func(fd uintptr) { +- // try IPv6 first +- _, _, err := syscall.Syscall6(syscall.SYS_GETSOCKOPT, fd, syscall.SOL_IPV6, IP6T_SO_ORIGINAL_DST, +- uintptr(unsafe.Pointer(&addr)), uintptr(unsafe.Pointer(&addrSize)), 0) +- if err != 0 { +- // try IPv4 +- _, _, err = syscall.Syscall6(syscall.SYS_GETSOCKOPT, fd, syscall.SOL_IP, SO_ORIGINAL_DST, +- uintptr(unsafe.Pointer(&addr)), uintptr(unsafe.Pointer(&addrSize)), 0) +- if err != 0 { +- // failed +- err2 = err +- } +- } ++ addr, err2 = getOrigDst(fd) + }) + if err != nil { + return nil, err