浏览代码

hysteria: backport upstream patch to support tcp redir

Signed-off-by: Tianling Shen <[email protected]>
Tianling Shen 3 年之前
父节点
当前提交
dfd9a805c4

+ 1 - 1
hysteria/Makefile

@@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=hysteria
 PKG_VERSION:=1.0.4
-PKG_RELEASE:=1
+PKG_RELEASE:=2
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
 PKG_SOURCE_URL:=https://codeload.github.com/HyNetwork/hysteria/tar.gz/v$(PKG_VERSION)?

+ 244 - 0
hysteria/patches/010-feat-TCP-redirect-implementation.patch

@@ -0,0 +1,244 @@
+From 575de280fff4021c613522c477ff8567b782c9c8 Mon Sep 17 00:00:00 2001
+From: Toby <[email protected]>
+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
++}

+ 144 - 0
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 <[email protected]>
+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