mirror of
https://github.com/cedar2025/hysteria.git
synced 2025-07-06 11:19:51 +00:00
commit
078f860174
@ -245,6 +245,15 @@ hysteria_traffic_uplink_bytes_total{auth="aGFja2VyISE="} 37452
|
|||||||
"cert": "/home/ubuntu/my_cert.crt", // Cert file (HTTPS proxy)
|
"cert": "/home/ubuntu/my_cert.crt", // Cert file (HTTPS proxy)
|
||||||
"key": "/home/ubuntu/my_key.crt" // Key file (HTTPS proxy)
|
"key": "/home/ubuntu/my_key.crt" // Key file (HTTPS proxy)
|
||||||
},
|
},
|
||||||
|
"tun": {
|
||||||
|
"name": "tun-hy", // TUN interface name
|
||||||
|
"timeout": 300, // Timeout in seconds
|
||||||
|
"address": "192.0.2.2", // TUN interface address, not applicable for Linux
|
||||||
|
"gateway": "192.0.2.1", // TUN interface gateway, not applicable for Linux
|
||||||
|
"mask": "255.255.255.252", // TUN interface mask, not applicable for Linux
|
||||||
|
"dns": [ "8.8.8.8", "8.8.4.4" ], // TUN interface DNS, only applicable for Windows
|
||||||
|
"persist": false // Persist TUN interface after exit, only applicable for Linux
|
||||||
|
},
|
||||||
"relay_tcp": {
|
"relay_tcp": {
|
||||||
"listen": "127.0.0.1:2222", // TCP relay listen address
|
"listen": "127.0.0.1:2222", // TCP relay listen address
|
||||||
"remote": "123.123.123.123:22", // TCP relay remote address
|
"remote": "123.123.123.123:22", // TCP relay remote address
|
||||||
|
@ -232,6 +232,15 @@ hysteria_traffic_uplink_bytes_total{auth="aGFja2VyISE="} 37452
|
|||||||
"cert": "/home/ubuntu/my_cert.crt", // 证书 (变为 HTTPS 代理)
|
"cert": "/home/ubuntu/my_cert.crt", // 证书 (变为 HTTPS 代理)
|
||||||
"key": "/home/ubuntu/my_key.crt" // 证书密钥 (变为 HTTPS 代理)
|
"key": "/home/ubuntu/my_key.crt" // 证书密钥 (变为 HTTPS 代理)
|
||||||
},
|
},
|
||||||
|
"tun": {
|
||||||
|
"name": "tun-hy", // TUN 接口名称
|
||||||
|
"timeout": 300, // 超时秒数
|
||||||
|
"address": "192.0.2.2", // TUN 接口地址(不适用于 Linux)
|
||||||
|
"gateway": "192.0.2.1", // TUN 接口网关(不适用于 Linux)
|
||||||
|
"mask": "255.255.255.252", // TUN 接口子网掩码(不适用于 Linux)
|
||||||
|
"dns": [ "8.8.8.8", "8.8.4.4" ], // TUN 接口 DNS 服务器(仅适用于 Windows)
|
||||||
|
"persist": false // 在程序退出之后保留接口(仅适用于 Linux)
|
||||||
|
},
|
||||||
"relay_tcp": {
|
"relay_tcp": {
|
||||||
"listen": "127.0.0.1:2222", // TCP 转发监听地址
|
"listen": "127.0.0.1:2222", // TCP 转发监听地址
|
||||||
"remote": "123.123.123.123:22", // TCP 转发目标地址
|
"remote": "123.123.123.123:22", // TCP 转发目标地址
|
||||||
|
@ -15,10 +15,12 @@ import (
|
|||||||
"github.com/tobyxdd/hysteria/pkg/socks5"
|
"github.com/tobyxdd/hysteria/pkg/socks5"
|
||||||
"github.com/tobyxdd/hysteria/pkg/tproxy"
|
"github.com/tobyxdd/hysteria/pkg/tproxy"
|
||||||
"github.com/tobyxdd/hysteria/pkg/transport"
|
"github.com/tobyxdd/hysteria/pkg/transport"
|
||||||
|
"github.com/tobyxdd/hysteria/pkg/tun"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -189,6 +191,56 @@ func client(config *clientConfig) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(config.TUN.Name) != 0 {
|
||||||
|
go func() {
|
||||||
|
timeout := time.Duration(config.TUN.Timeout) * time.Second
|
||||||
|
if timeout == 0 {
|
||||||
|
timeout = 300 * time.Second
|
||||||
|
}
|
||||||
|
tunServer, err := tun.NewServer(client, transport.DefaultTransport,
|
||||||
|
time.Duration(config.TUN.Timeout)*time.Second,
|
||||||
|
config.TUN.Name, config.TUN.Address, config.TUN.Gateway, config.TUN.Mask, config.TUN.DNS, config.TUN.Persist)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithField("error", err).Fatal("Failed to initialize TUN server")
|
||||||
|
}
|
||||||
|
tunServer.ACLEngine = aclEngine
|
||||||
|
tunServer.RequestFunc = func(addr net.Addr, reqAddr string, action acl.Action, arg string) {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"action": actionToString(action, arg),
|
||||||
|
"src": addr.String(),
|
||||||
|
"dst": reqAddr,
|
||||||
|
}).Debugf("TUN %s request", strings.ToUpper(addr.Network()))
|
||||||
|
}
|
||||||
|
tunServer.ErrorFunc = func(addr net.Addr, reqAddr string, err error) {
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"src": addr.String(),
|
||||||
|
"dst": reqAddr,
|
||||||
|
}).Debugf("TUN %s EOF", strings.ToUpper(addr.Network()))
|
||||||
|
} else if err == core.ErrClosed && strings.HasPrefix(addr.Network(), "udp") {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"src": addr.String(),
|
||||||
|
"dst": reqAddr,
|
||||||
|
}).Debugf("TUN %s closed for timeout", strings.ToUpper(addr.Network()))
|
||||||
|
} else if err.Error() == "deadline exceeded" && strings.HasPrefix(addr.Network(), "tcp") {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"src": addr.String(),
|
||||||
|
"dst": reqAddr,
|
||||||
|
}).Debugf("TUN %s closed for timeout", strings.ToUpper(addr.Network()))
|
||||||
|
} else {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"error": err,
|
||||||
|
"src": addr.String(),
|
||||||
|
"dst": reqAddr,
|
||||||
|
}).Infof("TUN %s error", strings.ToUpper(addr.Network()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errChan <- tunServer.ListenAndServe()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
if len(config.TCPRelay.Listen) > 0 {
|
if len(config.TCPRelay.Listen) > 0 {
|
||||||
go func() {
|
go func() {
|
||||||
rl, err := relay.NewTCPRelay(client, transport.DefaultTransport,
|
rl, err := relay.NewTCPRelay(client, transport.DefaultTransport,
|
||||||
|
@ -88,6 +88,15 @@ type clientConfig struct {
|
|||||||
Cert string `json:"cert"`
|
Cert string `json:"cert"`
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
} `json:"http"`
|
} `json:"http"`
|
||||||
|
TUN struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Timeout int `json:"timeout"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
Gateway string `json:"gateway"`
|
||||||
|
Mask string `json:"mask"`
|
||||||
|
DNS []string `json:"dns"`
|
||||||
|
Persist bool `json:"persist"`
|
||||||
|
} `json:"tun"`
|
||||||
TCPRelay struct {
|
TCPRelay struct {
|
||||||
Listen string `json:"listen"`
|
Listen string `json:"listen"`
|
||||||
Remote string `json:"remote"`
|
Remote string `json:"remote"`
|
||||||
@ -118,10 +127,10 @@ type clientConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *clientConfig) Check() error {
|
func (c *clientConfig) Check() error {
|
||||||
if len(c.SOCKS5.Listen) == 0 && len(c.HTTP.Listen) == 0 &&
|
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.TCPRelay.Listen) == 0 && len(c.UDPRelay.Listen) == 0 &&
|
||||||
len(c.TCPTProxy.Listen) == 0 && len(c.UDPTProxy.Listen) == 0 {
|
len(c.TCPTProxy.Listen) == 0 && len(c.UDPTProxy.Listen) == 0 {
|
||||||
return errors.New("no SOCKS5, HTTP, relay or TProxy listen address")
|
return errors.New("please enable at least one mode")
|
||||||
}
|
}
|
||||||
if len(c.TCPRelay.Listen) > 0 && len(c.TCPRelay.Remote) == 0 {
|
if len(c.TCPRelay.Listen) > 0 && len(c.TCPRelay.Remote) == 0 {
|
||||||
return errors.New("no TCP relay remote address")
|
return errors.New("no TCP relay remote address")
|
||||||
@ -135,6 +144,9 @@ func (c *clientConfig) Check() error {
|
|||||||
if c.HTTP.Timeout != 0 && c.HTTP.Timeout <= 4 {
|
if c.HTTP.Timeout != 0 && c.HTTP.Timeout <= 4 {
|
||||||
return errors.New("invalid HTTP timeout")
|
return errors.New("invalid HTTP timeout")
|
||||||
}
|
}
|
||||||
|
if c.TUN.Timeout != 0 && c.TUN.Timeout < 4 {
|
||||||
|
return errors.New("invalid TUN timeout")
|
||||||
|
}
|
||||||
if c.TCPRelay.Timeout != 0 && c.TCPRelay.Timeout <= 4 {
|
if c.TCPRelay.Timeout != 0 && c.TCPRelay.Timeout <= 4 {
|
||||||
return errors.New("invalid TCP relay timeout")
|
return errors.New("invalid TCP relay timeout")
|
||||||
}
|
}
|
||||||
|
1
go.mod
1
go.mod
@ -9,6 +9,7 @@ require (
|
|||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||||
github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e
|
github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e
|
||||||
github.com/elazarl/goproxy/ext v0.0.0-20210110162100-a92cc753f88e
|
github.com/elazarl/goproxy/ext v0.0.0-20210110162100-a92cc753f88e
|
||||||
|
github.com/eycorsican/go-tun2socks v1.16.11
|
||||||
github.com/hashicorp/golang-lru v0.5.4
|
github.com/hashicorp/golang-lru v0.5.4
|
||||||
github.com/lucas-clemente/quic-go v0.20.1
|
github.com/lucas-clemente/quic-go v0.20.1
|
||||||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
|
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
|
||||||
|
5
go.sum
5
go.sum
@ -79,6 +79,8 @@ github.com/elazarl/goproxy/ext v0.0.0-20210110162100-a92cc753f88e/go.mod h1:gNh8
|
|||||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/eycorsican/go-tun2socks v1.16.11 h1:+hJDNgisrYaGEqoSxhdikMgMJ4Ilfwm/IZDrWRrbaH8=
|
||||||
|
github.com/eycorsican/go-tun2socks v1.16.11/go.mod h1:wgB2BFT8ZaPKyKOQ/5dljMG/YIow+AIXyq4KBwJ5sGQ=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
||||||
@ -363,6 +365,8 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
|
|||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
|
github.com/songgao/water v0.0.0-20190725173103-fd331bda3f4b h1:+y4hCMc/WKsDbAPsOQZgBSaSZ26uh2afyaWeVg/3s/c=
|
||||||
|
github.com/songgao/water v0.0.0-20190725173103-fd331bda3f4b/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=
|
||||||
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||||
@ -462,6 +466,7 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
|
||||||
|
70
pkg/tun/server.go
Normal file
70
pkg/tun/server.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
tun2socks "github.com/eycorsican/go-tun2socks/core"
|
||||||
|
"github.com/eycorsican/go-tun2socks/tun"
|
||||||
|
"github.com/tobyxdd/hysteria/pkg/acl"
|
||||||
|
"github.com/tobyxdd/hysteria/pkg/core"
|
||||||
|
"github.com/tobyxdd/hysteria/pkg/transport"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
HyClient *core.Client
|
||||||
|
Timeout time.Duration
|
||||||
|
TunDev io.ReadWriteCloser
|
||||||
|
Transport transport.Transport
|
||||||
|
ACLEngine *acl.Engine
|
||||||
|
|
||||||
|
RequestFunc func(addr net.Addr, reqAddr string, action acl.Action, arg string)
|
||||||
|
ErrorFunc func(addr net.Addr, reqAddr string, err error)
|
||||||
|
|
||||||
|
udpConnMap map[tun2socks.UDPConn]*udpConnInfo
|
||||||
|
udpConnMapLock sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
MTU = 1500
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewServerWithTunDev(hyClient *core.Client, transport transport.Transport,
|
||||||
|
timeout time.Duration,
|
||||||
|
tunDev io.ReadWriteCloser) (*Server, error) {
|
||||||
|
s := &Server{
|
||||||
|
HyClient: hyClient,
|
||||||
|
Transport: transport,
|
||||||
|
Timeout: timeout,
|
||||||
|
TunDev: tunDev,
|
||||||
|
udpConnMap: make(map[tun2socks.UDPConn]*udpConnInfo),
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer(hyClient *core.Client, transport transport.Transport,
|
||||||
|
timeout time.Duration,
|
||||||
|
name, address, gateway, mask string, dnsServers []string, persist bool) (*Server, error) {
|
||||||
|
tunDev, err := tun.OpenTunDevice(name, address, gateway, mask, dnsServers, persist)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewServerWithTunDev(hyClient, transport, timeout, tunDev)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ListenAndServe() error {
|
||||||
|
lwipWriter := tun2socks.NewLWIPStack().(io.Writer)
|
||||||
|
|
||||||
|
tun2socks.RegisterTCPConnHandler(s)
|
||||||
|
tun2socks.RegisterUDPConnHandler(s)
|
||||||
|
tun2socks.RegisterOutputFn(func(data []byte) (int, error) {
|
||||||
|
return s.TunDev.Write(data)
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := io.CopyBuffer(lwipWriter, s.TunDev, make([]byte, MTU))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
80
pkg/tun/tcp.go
Normal file
80
pkg/tun/tcp.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
tun2socks "github.com/eycorsican/go-tun2socks/core"
|
||||||
|
"github.com/tobyxdd/hysteria/pkg/acl"
|
||||||
|
"github.com/tobyxdd/hysteria/pkg/utils"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) Handle(conn net.Conn, target *net.TCPAddr) error {
|
||||||
|
action, arg := acl.ActionProxy, ""
|
||||||
|
var resErr error
|
||||||
|
if s.ACLEngine != nil {
|
||||||
|
action, arg, _, resErr = s.ACLEngine.ResolveAndMatch(target.IP.String())
|
||||||
|
}
|
||||||
|
if s.RequestFunc != nil {
|
||||||
|
s.RequestFunc(conn.LocalAddr(), target.String(), action, arg)
|
||||||
|
}
|
||||||
|
var closeErr error
|
||||||
|
defer func() {
|
||||||
|
if s.ErrorFunc != nil && closeErr != nil {
|
||||||
|
s.ErrorFunc(conn.LocalAddr(), target.String(), closeErr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
switch action {
|
||||||
|
case acl.ActionDirect:
|
||||||
|
if resErr != nil {
|
||||||
|
closeErr = resErr
|
||||||
|
return resErr
|
||||||
|
}
|
||||||
|
rc, err := s.Transport.LocalDialTCP(nil, target)
|
||||||
|
if err != nil {
|
||||||
|
closeErr = err
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go s.relayTCP(conn, rc)
|
||||||
|
return nil
|
||||||
|
case acl.ActionProxy:
|
||||||
|
rc, err := s.HyClient.DialTCP(target.String())
|
||||||
|
if err != nil {
|
||||||
|
closeErr = err
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go s.relayTCP(conn, rc)
|
||||||
|
return nil
|
||||||
|
case acl.ActionBlock:
|
||||||
|
closeErr = errors.New("blocked in ACL")
|
||||||
|
// caller will abort the connection when err != nil
|
||||||
|
return closeErr
|
||||||
|
case acl.ActionHijack:
|
||||||
|
rc, err := s.Transport.LocalDial("tcp", net.JoinHostPort(arg, strconv.Itoa(target.Port)))
|
||||||
|
if err != nil {
|
||||||
|
closeErr = err
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go s.relayTCP(conn, rc)
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
closeErr = fmt.Errorf("unknown action %d", action)
|
||||||
|
// caller will abort the connection when err != nil
|
||||||
|
return closeErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) relayTCP(clientConn, relayConn net.Conn) {
|
||||||
|
closeErr := utils.PipePairWithTimeout(relayConn, clientConn, s.Timeout)
|
||||||
|
if s.ErrorFunc != nil {
|
||||||
|
s.ErrorFunc(clientConn.LocalAddr(), relayConn.RemoteAddr().String(), closeErr)
|
||||||
|
}
|
||||||
|
relayConn.Close()
|
||||||
|
clientConn.Close()
|
||||||
|
if closeErr != nil && closeErr.Error() == "deadline exceeded" {
|
||||||
|
if clientConn, ok := clientConn.(tun2socks.TCPConn); ok {
|
||||||
|
clientConn.Abort()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
180
pkg/tun/udp.go
Normal file
180
pkg/tun/udp.go
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
tun2socks "github.com/eycorsican/go-tun2socks/core"
|
||||||
|
"github.com/tobyxdd/hysteria/pkg/acl"
|
||||||
|
"github.com/tobyxdd/hysteria/pkg/core"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const udpBufferSize = 65535
|
||||||
|
|
||||||
|
type udpConnInfo struct {
|
||||||
|
hyConn core.UDPConn
|
||||||
|
target string
|
||||||
|
expire atomic.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) fetchUDPInput(conn tun2socks.UDPConn, ci *udpConnInfo) {
|
||||||
|
defer func() {
|
||||||
|
s.closeUDPConn(conn)
|
||||||
|
}()
|
||||||
|
|
||||||
|
if s.Timeout > 0 {
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
life := ci.expire.Load().(time.Time).Sub(time.Now())
|
||||||
|
if life < 0 {
|
||||||
|
s.closeUDPConn(conn)
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
time.Sleep(life)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for {
|
||||||
|
var bs []byte
|
||||||
|
var from string
|
||||||
|
bs, from, err = ci.hyConn.ReadFrom()
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
ci.expire.Store(time.Now().Add(s.Timeout))
|
||||||
|
udpAddr, _ := net.ResolveUDPAddr("udp", from)
|
||||||
|
_, err = conn.WriteFrom(bs, udpAddr)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.ErrorFunc != nil {
|
||||||
|
s.ErrorFunc(conn.LocalAddr(), ci.target, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Connect(conn tun2socks.UDPConn, target *net.UDPAddr) error {
|
||||||
|
action, arg := acl.ActionProxy, ""
|
||||||
|
var resErr error
|
||||||
|
if s.ACLEngine != nil {
|
||||||
|
action, arg, _, resErr = s.ACLEngine.ResolveAndMatch(target.IP.String())
|
||||||
|
}
|
||||||
|
if s.RequestFunc != nil {
|
||||||
|
s.RequestFunc(conn.LocalAddr(), target.String(), action, arg)
|
||||||
|
}
|
||||||
|
var hyConn core.UDPConn
|
||||||
|
var closeErr error
|
||||||
|
defer func() {
|
||||||
|
if s.ErrorFunc != nil && closeErr != nil {
|
||||||
|
s.ErrorFunc(conn.LocalAddr(), target.String(), closeErr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
switch action {
|
||||||
|
case acl.ActionDirect:
|
||||||
|
if resErr != nil {
|
||||||
|
closeErr = resErr
|
||||||
|
return resErr
|
||||||
|
}
|
||||||
|
var relayConn net.Conn
|
||||||
|
relayConn, closeErr = s.Transport.LocalDial("udp", target.String())
|
||||||
|
if closeErr != nil {
|
||||||
|
return closeErr
|
||||||
|
}
|
||||||
|
hyConn = &delegatedUDPConn{
|
||||||
|
underlayConn: relayConn,
|
||||||
|
delegatedRemoteAddr: target.String(),
|
||||||
|
}
|
||||||
|
case acl.ActionProxy:
|
||||||
|
hyConn, closeErr = s.HyClient.DialUDP()
|
||||||
|
if closeErr != nil {
|
||||||
|
return closeErr
|
||||||
|
}
|
||||||
|
case acl.ActionBlock:
|
||||||
|
closeErr = errors.New("blocked in ACL")
|
||||||
|
return closeErr
|
||||||
|
case acl.ActionHijack:
|
||||||
|
hijackAddr := net.JoinHostPort(arg, strconv.Itoa(target.Port))
|
||||||
|
var relayConn net.Conn
|
||||||
|
relayConn, closeErr = s.Transport.LocalDial("udp", hijackAddr)
|
||||||
|
if closeErr != nil {
|
||||||
|
return closeErr
|
||||||
|
}
|
||||||
|
hyConn = &delegatedUDPConn{
|
||||||
|
underlayConn: relayConn,
|
||||||
|
delegatedRemoteAddr: target.String(),
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
closeErr = fmt.Errorf("unknown action %d", action)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ci := udpConnInfo{
|
||||||
|
hyConn: hyConn,
|
||||||
|
target: net.JoinHostPort(target.IP.String(), strconv.Itoa(target.Port)),
|
||||||
|
}
|
||||||
|
ci.expire.Store(time.Now().Add(s.Timeout))
|
||||||
|
s.udpConnMapLock.Lock()
|
||||||
|
s.udpConnMap[conn] = &ci
|
||||||
|
s.udpConnMapLock.Unlock()
|
||||||
|
go s.fetchUDPInput(conn, &ci)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ReceiveTo(conn tun2socks.UDPConn, data []byte, addr *net.UDPAddr) error {
|
||||||
|
s.udpConnMapLock.RLock()
|
||||||
|
ci, ok := s.udpConnMap[conn]
|
||||||
|
s.udpConnMapLock.RUnlock()
|
||||||
|
if !ok {
|
||||||
|
err := errors.New("previous connection closed for timeout")
|
||||||
|
s.ErrorFunc(conn.LocalAddr(), addr.String(), err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ci.expire.Store(time.Now().Add(s.Timeout))
|
||||||
|
_ = ci.hyConn.WriteTo(data, addr.String())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) closeUDPConn(conn tun2socks.UDPConn) {
|
||||||
|
conn.Close()
|
||||||
|
s.udpConnMapLock.Lock()
|
||||||
|
defer s.udpConnMapLock.Unlock()
|
||||||
|
if c, ok := s.udpConnMap[conn]; ok {
|
||||||
|
c.hyConn.Close()
|
||||||
|
delete(s.udpConnMap, conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type delegatedUDPConn struct {
|
||||||
|
underlayConn net.Conn
|
||||||
|
delegatedRemoteAddr string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *delegatedUDPConn) ReadFrom() (bs []byte, addr string, err error) {
|
||||||
|
buf := make([]byte, udpBufferSize)
|
||||||
|
n, err := c.underlayConn.Read(buf)
|
||||||
|
if n > 0 {
|
||||||
|
bs = append(bs, buf[0:n]...)
|
||||||
|
}
|
||||||
|
if err != nil || err == io.EOF {
|
||||||
|
addr = c.delegatedRemoteAddr
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *delegatedUDPConn) WriteTo(bs []byte, addr string) error {
|
||||||
|
_, err := io.Copy(c.underlayConn, bytes.NewReader(bs))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *delegatedUDPConn) Close() error {
|
||||||
|
return c.underlayConn.Close()
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user