From bb99579bb92cb6c9c8a56c52fd5d9c4bff2b216a Mon Sep 17 00:00:00 2001 From: WoaShieShei <158140161+WoaShieShei@users.noreply.github.com> Date: Thu, 1 Feb 2024 03:48:58 +0000 Subject: [PATCH 001/161] test: correct TestStringToBps after `6d6a26b` (#928) --- app/internal/utils/bpsconv_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/internal/utils/bpsconv_test.go b/app/internal/utils/bpsconv_test.go index 2de4d3d..3226560 100644 --- a/app/internal/utils/bpsconv_test.go +++ b/app/internal/utils/bpsconv_test.go @@ -13,12 +13,12 @@ func TestStringToBps(t *testing.T) { wantErr bool }{ {"bps", args{"800 bps"}, 100, false}, - {"kbps", args{"800 kbps"}, 102400, false}, - {"mbps", args{"800 mbps"}, 104857600, false}, - {"gbps", args{"800 gbps"}, 107374182400, false}, - {"tbps", args{"800 tbps"}, 109951162777600, false}, - {"mbps simp", args{"100m"}, 13107200, false}, - {"gbps simp upper", args{"2G"}, 268435456, false}, + {"kbps", args{"800 kbps"}, 100_000, false}, + {"mbps", args{"800 mbps"}, 100_000_000, false}, + {"gbps", args{"800 gbps"}, 100_000_000_000, false}, + {"tbps", args{"800 tbps"}, 100_000_000_000_000, false}, + {"mbps simp", args{"100m"}, 12_500_000, false}, + {"gbps simp upper", args{"2G"}, 250_000_000, false}, {"invalid 1", args{"damn"}, 0, true}, {"invalid 2", args{"6444"}, 0, true}, {"invalid 3", args{"5.4 mbps"}, 0, true}, From f0d59ebee1d8a1bb89c03d772ceca9f36305f91b Mon Sep 17 00:00:00 2001 From: Haruue Icymoon Date: Sat, 3 Feb 2024 20:57:11 +0800 Subject: [PATCH 002/161] chore: human-readable outbounds socks5 error msg ref: #932 --- extras/outbounds/ob_socks5.go | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/extras/outbounds/ob_socks5.go b/extras/outbounds/ob_socks5.go index 21bc6b6..d6d7add 100644 --- a/extras/outbounds/ob_socks5.go +++ b/extras/outbounds/ob_socks5.go @@ -31,7 +31,31 @@ type errSOCKS5RequestFailed struct { } func (e errSOCKS5RequestFailed) Error() string { - return fmt.Sprintf("SOCKS5 request failed: %d", e.Rep) + var msg string + // RFC 1928 + switch e.Rep { + case 0x00: + msg = "succeeded" + case 0x01: + msg = "general SOCKS server failure" + case 0x02: + msg = "connection not allowed by ruleset" + case 0x03: + msg = "Network unreachable" + case 0x04: + msg = "Host unreachable" + case 0x05: + msg = "Connection refused" + case 0x06: + msg = "TTL expired" + case 0x07: + msg = "Command not supported" + case 0x08: + msg = "Address type not supported" + default: + msg = "undefined" + } + return fmt.Sprintf("SOCKS5 request failed: %s (%d)", msg, e.Rep) } // socks5Outbound is a PluggableOutbound that connects to the target using From e22aa0630b61053a013967b05be75dd3856cea12 Mon Sep 17 00:00:00 2001 From: Haruue Icymoon Date: Wed, 21 Feb 2024 16:21:49 +0800 Subject: [PATCH 003/161] fix: geo db load fail after download error now geo db are downloaded to a temp file, have a integrity check by a loading test, and then moved to where it should be. fix: #944 --- app/internal/utils/geoloader.go | 40 +++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/app/internal/utils/geoloader.go b/app/internal/utils/geoloader.go index 56cb205..0517731 100644 --- a/app/internal/utils/geoloader.go +++ b/app/internal/utils/geoloader.go @@ -1,6 +1,7 @@ package utils import ( + "fmt" "io" "net/http" "os" @@ -15,6 +16,7 @@ const ( geoipURL = "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geoip.dat" geositeFilename = "geosite.dat" geositeURL = "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat" + geoDlTmpPattern = ".hysteria-geoloader.dlpart.*" geoDefaultUpdateInterval = 7 * 24 * time.Hour // 7 days ) @@ -49,7 +51,7 @@ func (l *GeoLoader) shouldDownload(filename string) bool { } } -func (l *GeoLoader) download(filename, url string) error { +func (l *GeoLoader) downloadAndCheck(filename, url string, checkFunc func(filename string) error) error { l.DownloadFunc(filename, url) resp, err := http.Get(url) @@ -59,16 +61,34 @@ func (l *GeoLoader) download(filename, url string) error { } defer resp.Body.Close() - f, err := os.Create(filename) + f, err := os.CreateTemp(".", geoDlTmpPattern) if err != nil { l.DownloadErrFunc(err) return err } - defer f.Close() + defer os.Remove(f.Name()) _, err = io.Copy(f, resp.Body) - l.DownloadErrFunc(err) - return err + if err != nil { + f.Close() + l.DownloadErrFunc(err) + return err + } + f.Close() + + err = checkFunc(f.Name()) + if err != nil { + l.DownloadErrFunc(fmt.Errorf("integrity check failed: %w", err)) + return err + } + + err = os.Rename(f.Name(), filename) + if err != nil { + l.DownloadErrFunc(fmt.Errorf("rename failed: %w", err)) + return err + } + + return nil } func (l *GeoLoader) LoadGeoIP() (map[string]*v2geo.GeoIP, error) { @@ -82,7 +102,10 @@ func (l *GeoLoader) LoadGeoIP() (map[string]*v2geo.GeoIP, error) { filename = geoipFilename } if autoDL && l.shouldDownload(filename) { - err := l.download(filename, geoipURL) + err := l.downloadAndCheck(filename, geoipURL, func(filename string) error { + _, err := v2geo.LoadGeoIP(filename) + return err + }) if err != nil { return nil, err } @@ -106,7 +129,10 @@ func (l *GeoLoader) LoadGeoSite() (map[string]*v2geo.GeoSite, error) { filename = geositeFilename } if autoDL && l.shouldDownload(filename) { - err := l.download(filename, geositeURL) + err := l.downloadAndCheck(filename, geositeURL, func(filename string) error { + _, err := v2geo.LoadGeoSite(filename) + return err + }) if err != nil { return nil, err } From 6dea0adb19be7ecf4fd3edfbcc9e8288bcc7fb30 Mon Sep 17 00:00:00 2001 From: Haruue Icymoon Date: Wed, 21 Feb 2024 17:22:45 +0800 Subject: [PATCH 004/161] feat: re-download geo db when autoDL && load fail --- app/internal/utils/geoloader.go | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/app/internal/utils/geoloader.go b/app/internal/utils/geoloader.go index 0517731..328be68 100644 --- a/app/internal/utils/geoloader.go +++ b/app/internal/utils/geoloader.go @@ -43,6 +43,10 @@ func (l *GeoLoader) shouldDownload(filename string) bool { if os.IsNotExist(err) { return true } + if info.Size() == 0 { + // empty files are loadable by v2geo, but we consider it broken + return true + } dt := time.Now().Sub(info.ModTime()) if l.UpdateInterval == 0 { return dt > geoDefaultUpdateInterval @@ -101,7 +105,15 @@ func (l *GeoLoader) LoadGeoIP() (map[string]*v2geo.GeoIP, error) { autoDL = true filename = geoipFilename } - if autoDL && l.shouldDownload(filename) { + if autoDL { + if !l.shouldDownload(filename) { + m, err := v2geo.LoadGeoIP(filename) + if err == nil { + l.geoipMap = m + return m, nil + } + // file is broken, download it again + } err := l.downloadAndCheck(filename, geoipURL, func(filename string) error { _, err := v2geo.LoadGeoIP(filename) return err @@ -128,7 +140,15 @@ func (l *GeoLoader) LoadGeoSite() (map[string]*v2geo.GeoSite, error) { autoDL = true filename = geositeFilename } - if autoDL && l.shouldDownload(filename) { + if autoDL { + if !l.shouldDownload(filename) { + m, err := v2geo.LoadGeoSite(filename) + if err == nil { + l.geositeMap = m + return m, nil + } + // file is broken, download it again + } err := l.downloadAndCheck(filename, geositeURL, func(filename string) error { _, err := v2geo.LoadGeoSite(filename) return err From 842b0ab3f7de28f52bf7548c8f29ff5df5304185 Mon Sep 17 00:00:00 2001 From: Haruue Icymoon Date: Thu, 22 Feb 2024 01:32:10 +0800 Subject: [PATCH 005/161] feat: load previous download when download fail --- app/internal/utils/geoloader.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/internal/utils/geoloader.go b/app/internal/utils/geoloader.go index 328be68..fa9775b 100644 --- a/app/internal/utils/geoloader.go +++ b/app/internal/utils/geoloader.go @@ -119,7 +119,10 @@ func (l *GeoLoader) LoadGeoIP() (map[string]*v2geo.GeoIP, error) { return err }) if err != nil { - return nil, err + // as long as the previous download exists, fallback to it + if _, serr := os.Stat(filename); os.IsNotExist(serr) { + return nil, err + } } } m, err := v2geo.LoadGeoIP(filename) @@ -154,7 +157,10 @@ func (l *GeoLoader) LoadGeoSite() (map[string]*v2geo.GeoSite, error) { return err }) if err != nil { - return nil, err + // as long as the previous download exists, fallback to it + if _, serr := os.Stat(filename); os.IsNotExist(serr) { + return nil, err + } } } m, err := v2geo.LoadGeoSite(filename) From 1ac9d4956bd16bbd59157da8e4532b2db6104ef9 Mon Sep 17 00:00:00 2001 From: Haruue Date: Tue, 27 Feb 2024 20:51:59 +0800 Subject: [PATCH 006/161] feat: allow set ListenUDP impl for udphop conn Third-party clients can use this to set options on created sockets. e.g. calling VpnService.protect() on Android. --- app/cmd/client.go | 2 +- extras/transport/udphop/conn.go | 37 ++++++++++++++++++++------------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/app/cmd/client.go b/app/cmd/client.go index b20facb..9a785d2 100644 --- a/app/cmd/client.go +++ b/app/cmd/client.go @@ -179,7 +179,7 @@ func (c *clientConfig) fillConnFactory(hyConfig *client.Config) error { if hyConfig.ServerAddr.Network() == "udphop" { hopAddr := hyConfig.ServerAddr.(*udphop.UDPHopAddr) newFunc = func(addr net.Addr) (net.PacketConn, error) { - return udphop.NewUDPHopPacketConn(hopAddr, c.Transport.UDP.HopInterval) + return udphop.NewUDPHopPacketConn(hopAddr, c.Transport.UDP.HopInterval, nil) } } else { newFunc = func(addr net.Addr) (net.PacketConn, error) { diff --git a/extras/transport/udphop/conn.go b/extras/transport/udphop/conn.go index ccb0b38..f20c583 100644 --- a/extras/transport/udphop/conn.go +++ b/extras/transport/udphop/conn.go @@ -17,9 +17,10 @@ const ( ) type udpHopPacketConn struct { - Addr net.Addr - Addrs []net.Addr - HopInterval time.Duration + Addr net.Addr + Addrs []net.Addr + HopInterval time.Duration + ListenUDPFunc ListenUDPFunc connMutex sync.RWMutex prevConn net.PacketConn @@ -43,29 +44,37 @@ type udpPacket struct { Err error } -func NewUDPHopPacketConn(addr *UDPHopAddr, hopInterval time.Duration) (net.PacketConn, error) { +type ListenUDPFunc func() (net.PacketConn, error) + +func NewUDPHopPacketConn(addr *UDPHopAddr, hopInterval time.Duration, listenUDPFunc ListenUDPFunc) (net.PacketConn, error) { if hopInterval == 0 { hopInterval = defaultHopInterval } else if hopInterval < 5*time.Second { return nil, errors.New("hop interval must be at least 5 seconds") } + if listenUDPFunc == nil { + listenUDPFunc = func() (net.PacketConn, error) { + return net.ListenUDP("udp", nil) + } + } addrs, err := addr.addrs() if err != nil { return nil, err } - curConn, err := net.ListenUDP("udp", nil) + curConn, err := listenUDPFunc() if err != nil { return nil, err } hConn := &udpHopPacketConn{ - Addr: addr, - Addrs: addrs, - HopInterval: hopInterval, - prevConn: nil, - currentConn: curConn, - addrIndex: rand.Intn(len(addrs)), - recvQueue: make(chan *udpPacket, packetQueueSize), - closeChan: make(chan struct{}), + Addr: addr, + Addrs: addrs, + HopInterval: hopInterval, + ListenUDPFunc: listenUDPFunc, + prevConn: nil, + currentConn: curConn, + addrIndex: rand.Intn(len(addrs)), + recvQueue: make(chan *udpPacket, packetQueueSize), + closeChan: make(chan struct{}), bufPool: sync.Pool{ New: func() interface{} { return make([]byte, udpBufferSize) @@ -121,7 +130,7 @@ func (u *udpHopPacketConn) hop() { if u.closed { return } - newConn, err := net.ListenUDP("udp", nil) + newConn, err := u.ListenUDPFunc() if err != nil { // Could be temporary, just skip this hop return From 0c2b0234fa435c35aef7ec5f5352431e34b45daf Mon Sep 17 00:00:00 2001 From: Toby Date: Thu, 29 Feb 2024 16:38:42 -0800 Subject: [PATCH 007/161] fix: FreeBSD IPv4-mapped IPv6 listening addr fix --- app/go.mod | 2 +- app/go.sum | 4 ++-- core/go.mod | 2 +- core/go.sum | 4 ++-- extras/go.mod | 2 +- extras/go.sum | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/go.mod b/app/go.mod index 51f43b1..57e7c76 100644 --- a/app/go.mod +++ b/app/go.mod @@ -17,7 +17,7 @@ require ( ) require ( - github.com/apernet/quic-go v0.41.1-0.20240122005439-5bf4609c416f // indirect + github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d // indirect github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect diff --git a/app/go.sum b/app/go.sum index 239e55d..aedcb4c 100644 --- a/app/go.sum +++ b/app/go.sum @@ -40,8 +40,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f h1:uVh0qpEslrWjgzx9vOcyCqsOY3c9kofDZ1n+qaw35ZY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f/go.mod h1:xkkq9D4ygcldQQhKS/w9CadiCKwCngU7K9E3DaKahpM= -github.com/apernet/quic-go v0.41.1-0.20240122005439-5bf4609c416f h1:4jBGc3SlgQT8YFqHhfnK7EVFVY292CxagfNqfPiQZhY= -github.com/apernet/quic-go v0.41.1-0.20240122005439-5bf4609c416f/go.mod h1:4GInxO6ypy63J2NaO5rQx1wRp6K8YHI6zqLG+VswU6I= +github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d h1:K1DMSNtPcaZ/lihYmOHnjThNfUX7cD6SNuVRFnVLVmI= +github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d/go.mod h1:4GInxO6ypy63J2NaO5rQx1wRp6K8YHI6zqLG+VswU6I= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= diff --git a/core/go.mod b/core/go.mod index 392e6bb..3268576 100644 --- a/core/go.mod +++ b/core/go.mod @@ -3,7 +3,7 @@ module github.com/apernet/hysteria/core go 1.21 require ( - github.com/apernet/quic-go v0.41.1-0.20240122005439-5bf4609c416f + github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d github.com/stretchr/testify v1.8.4 go.uber.org/goleak v1.2.1 golang.org/x/exp v0.0.0-20221205204356-47842c84f3db diff --git a/core/go.sum b/core/go.sum index a44e749..fa8c8b8 100644 --- a/core/go.sum +++ b/core/go.sum @@ -1,5 +1,5 @@ -github.com/apernet/quic-go v0.41.1-0.20240122005439-5bf4609c416f h1:4jBGc3SlgQT8YFqHhfnK7EVFVY292CxagfNqfPiQZhY= -github.com/apernet/quic-go v0.41.1-0.20240122005439-5bf4609c416f/go.mod h1:4GInxO6ypy63J2NaO5rQx1wRp6K8YHI6zqLG+VswU6I= +github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d h1:K1DMSNtPcaZ/lihYmOHnjThNfUX7cD6SNuVRFnVLVmI= +github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d/go.mod h1:4GInxO6ypy63J2NaO5rQx1wRp6K8YHI6zqLG+VswU6I= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= diff --git a/extras/go.mod b/extras/go.mod index e22bf77..57a25e0 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -15,7 +15,7 @@ require ( ) require ( - github.com/apernet/quic-go v0.41.1-0.20240122005439-5bf4609c416f // indirect + github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect diff --git a/extras/go.sum b/extras/go.sum index b6d2e23..a02e3a8 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -1,5 +1,5 @@ -github.com/apernet/quic-go v0.41.1-0.20240122005439-5bf4609c416f h1:4jBGc3SlgQT8YFqHhfnK7EVFVY292CxagfNqfPiQZhY= -github.com/apernet/quic-go v0.41.1-0.20240122005439-5bf4609c416f/go.mod h1:4GInxO6ypy63J2NaO5rQx1wRp6K8YHI6zqLG+VswU6I= +github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d h1:K1DMSNtPcaZ/lihYmOHnjThNfUX7cD6SNuVRFnVLVmI= +github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d/go.mod h1:4GInxO6ypy63J2NaO5rQx1wRp6K8YHI6zqLG+VswU6I= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= From a0bd58063ba5e31a95c20587157449c25a937c22 Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 9 Mar 2024 20:38:30 -0800 Subject: [PATCH 008/161] feat: built-in speed test client & server --- app/cmd/server.go | 6 + app/cmd/server_test.go | 1 + app/cmd/server_test.yaml | 2 + app/cmd/speedtest.go | 150 +++++++ extras/outbounds/speedtest.go | 36 ++ extras/outbounds/speedtest/client.go | 125 ++++++ extras/outbounds/speedtest/protocol.go | 152 +++++++ extras/outbounds/speedtest/protocol_test.go | 446 ++++++++++++++++++++ extras/outbounds/speedtest/server.go | 99 +++++ 9 files changed, 1017 insertions(+) create mode 100644 app/cmd/speedtest.go create mode 100644 extras/outbounds/speedtest.go create mode 100644 extras/outbounds/speedtest/client.go create mode 100644 extras/outbounds/speedtest/protocol.go create mode 100644 extras/outbounds/speedtest/protocol_test.go create mode 100644 extras/outbounds/speedtest/server.go diff --git a/app/cmd/server.go b/app/cmd/server.go index 3f4bd1e..0f289dc 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -52,6 +52,7 @@ type serverConfig struct { QUIC serverConfigQUIC `mapstructure:"quic"` Bandwidth serverConfigBandwidth `mapstructure:"bandwidth"` IgnoreClientBandwidth bool `mapstructure:"ignoreClientBandwidth"` + SpeedTest bool `mapstructure:"speedTest"` DisableUDP bool `mapstructure:"disableUDP"` UDPIdleTimeout time.Duration `mapstructure:"udpIdleTimeout"` Auth serverConfigAuth `mapstructure:"auth"` @@ -528,6 +529,11 @@ func (c *serverConfig) fillOutboundConfig(hyConfig *server.Config) error { return configError{Field: "resolver.type", Err: errors.New("unsupported resolver type")} } + // Speed test + if c.SpeedTest { + uOb = outbounds.NewSpeedtestHandler(uOb) + } + hyConfig.Outbound = &outbounds.PluggableOutboundAdapter{PluggableOutbound: uOb} return nil } diff --git a/app/cmd/server_test.go b/app/cmd/server_test.go index 1c4d2f6..935a998 100644 --- a/app/cmd/server_test.go +++ b/app/cmd/server_test.go @@ -56,6 +56,7 @@ func TestServerConfig(t *testing.T) { Down: "100 mbps", }, IgnoreClientBandwidth: true, + SpeedTest: true, DisableUDP: true, UDPIdleTimeout: 120 * time.Second, Auth: serverConfigAuth{ diff --git a/app/cmd/server_test.yaml b/app/cmd/server_test.yaml index 47d3b19..1ab5d5f 100644 --- a/app/cmd/server_test.yaml +++ b/app/cmd/server_test.yaml @@ -36,6 +36,8 @@ bandwidth: ignoreClientBandwidth: true +speedTest: true + disableUDP: true udpIdleTimeout: 120s diff --git a/app/cmd/speedtest.go b/app/cmd/speedtest.go new file mode 100644 index 0000000..67cf71f --- /dev/null +++ b/app/cmd/speedtest.go @@ -0,0 +1,150 @@ +package cmd + +import ( + "fmt" + "time" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + "go.uber.org/zap" + + "github.com/apernet/hysteria/core/client" + "github.com/apernet/hysteria/extras/outbounds" + "github.com/apernet/hysteria/extras/outbounds/speedtest" +) + +var ( + skipDownload bool + skipUpload bool + dataSize uint32 + useBytes bool + + speedtestAddr = fmt.Sprintf("%s:%d", outbounds.SpeedtestDest, 0) +) + +// speedtestCmd represents the speedtest command +var speedtestCmd = &cobra.Command{ + Use: "speedtest", + Short: "Speed test mode", + Long: "Perform a speed test through the proxy server. The server must have speed test support enabled.", + Run: runSpeedtest, +} + +func init() { + initSpeedtestFlags() + rootCmd.AddCommand(speedtestCmd) +} + +func initSpeedtestFlags() { + speedtestCmd.Flags().BoolVar(&skipDownload, "skip-download", false, "Skip download test") + speedtestCmd.Flags().BoolVar(&skipUpload, "skip-upload", false, "Skip upload test") + speedtestCmd.Flags().Uint32Var(&dataSize, "data-size", 1024*1024*100, "Data size for download and upload tests") + speedtestCmd.Flags().BoolVar(&useBytes, "use-bytes", false, "Use bytes per second instead of bits per second") +} + +func runSpeedtest(cmd *cobra.Command, args []string) { + logger.Info("speed test mode") + + if err := viper.ReadInConfig(); err != nil { + logger.Fatal("failed to read client config", zap.Error(err)) + } + var config clientConfig + if err := viper.Unmarshal(&config); err != nil { + logger.Fatal("failed to parse client config", zap.Error(err)) + } + hyConfig, err := config.Config() + if err != nil { + logger.Fatal("failed to load client config", zap.Error(err)) + } + + c, info, err := client.NewClient(hyConfig) + if err != nil { + logger.Fatal("failed to initialize client", zap.Error(err)) + } + defer c.Close() + logger.Info("connected to server", + zap.Bool("udpEnabled", info.UDPEnabled), + zap.Uint64("tx", info.Tx)) + + if !skipDownload { + runDownloadTest(c) + } + if !skipUpload { + runUploadTest(c) + } +} + +func runDownloadTest(c client.Client) { + logger.Info("performing download test") + downConn, err := c.TCP(speedtestAddr) + if err != nil { + logger.Fatal("failed to connect", zap.Error(err)) + } + defer downConn.Close() + + downClient := &speedtest.Client{Conn: downConn} + currentTotal := uint32(0) + err = downClient.Download(dataSize, func(d time.Duration, b uint32, done bool) { + if !done { + currentTotal += b + logger.Info("downloading", + zap.Uint32("bytes", b), + zap.String("progress", fmt.Sprintf("%.2f%%", float64(currentTotal)/float64(dataSize)*100)), + zap.String("speed", formatSpeed(b, d, useBytes))) + } else { + logger.Info("download complete", + zap.Uint32("bytes", b), + zap.String("speed", formatSpeed(b, d, useBytes))) + } + }) + if err != nil { + logger.Fatal("download test failed", zap.Error(err)) + } + logger.Info("download test complete") +} + +func runUploadTest(c client.Client) { + logger.Info("performing upload test") + upConn, err := c.TCP(speedtestAddr) + if err != nil { + logger.Fatal("failed to connect", zap.Error(err)) + } + defer upConn.Close() + + upClient := &speedtest.Client{Conn: upConn} + currentTotal := uint32(0) + err = upClient.Upload(dataSize, func(d time.Duration, b uint32, done bool) { + if !done { + currentTotal += b + logger.Info("uploading", + zap.Uint32("bytes", b), + zap.String("progress", fmt.Sprintf("%.2f%%", float64(currentTotal)/float64(dataSize)*100)), + zap.String("speed", formatSpeed(b, d, useBytes))) + } else { + logger.Info("upload complete", + zap.Uint32("bytes", b), + zap.String("speed", formatSpeed(b, d, useBytes))) + } + }) + if err != nil { + logger.Fatal("upload test failed", zap.Error(err)) + } + logger.Info("upload test complete") +} + +func formatSpeed(bytes uint32, duration time.Duration, useBytes bool) string { + speed := float64(bytes) / duration.Seconds() + var units []string + if useBytes { + units = []string{"B/s", "KB/s", "MB/s", "GB/s"} + } else { + units = []string{"bps", "Kbps", "Mbps", "Gbps"} + speed *= 8 + } + unitIndex := 0 + for speed > 1024 && unitIndex < len(units)-1 { + speed /= 1024 + unitIndex++ + } + return fmt.Sprintf("%.2f %s", speed, units[unitIndex]) +} diff --git a/extras/outbounds/speedtest.go b/extras/outbounds/speedtest.go new file mode 100644 index 0000000..9e5ab43 --- /dev/null +++ b/extras/outbounds/speedtest.go @@ -0,0 +1,36 @@ +package outbounds + +import ( + "net" + + "github.com/apernet/hysteria/extras/outbounds/speedtest" +) + +const ( + SpeedtestDest = "_SpeedTest" +) + +// speedtestHandler is a PluggableOutbound that handles speed test requests. +// It's used to intercept speed test requests and return a pseudo connection that +// implements the speed test protocol. +type speedtestHandler struct { + Next PluggableOutbound +} + +func NewSpeedtestHandler(next PluggableOutbound) PluggableOutbound { + return &speedtestHandler{ + Next: next, + } +} + +func (s *speedtestHandler) TCP(reqAddr *AddrEx) (net.Conn, error) { + if reqAddr.Host == SpeedtestDest { + return speedtest.NewServerConn(), nil + } else { + return s.Next.TCP(reqAddr) + } +} + +func (s *speedtestHandler) UDP(reqAddr *AddrEx) (UDPConn, error) { + return s.Next.UDP(reqAddr) +} diff --git a/extras/outbounds/speedtest/client.go b/extras/outbounds/speedtest/client.go new file mode 100644 index 0000000..ea4c5a6 --- /dev/null +++ b/extras/outbounds/speedtest/client.go @@ -0,0 +1,125 @@ +package speedtest + +import ( + "fmt" + "io" + "net" + "sync/atomic" + "time" +) + +type Client struct { + Conn net.Conn +} + +// Download requests the server to send l bytes of data. +// The callback function cb is called every second with the time since the last call, +// and the number of bytes received in that time. +func (c *Client) Download(l uint32, cb func(time.Duration, uint32, bool)) error { + err := writeDownloadRequest(c.Conn, l) + if err != nil { + return err + } + ok, msg, err := readDownloadResponse(c.Conn) + if err != nil { + return err + } + if !ok { + return fmt.Errorf("server rejected download request: %s", msg) + } + var counter uint32 + stopChan := make(chan struct{}) + defer close(stopChan) + // Call the callback function every second, + // with the time since the last call and the number of bytes received in that time. + go func() { + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + t := time.Now() + for { + select { + case <-stopChan: + return + case <-ticker.C: + cb(time.Since(t), atomic.SwapUint32(&counter, 0), false) + t = time.Now() + } + } + }() + buf := make([]byte, chunkSize) + startTime := time.Now() + remaining := l + for remaining > 0 { + n := remaining + if n > chunkSize { + n = chunkSize + } + rn, err := c.Conn.Read(buf[:n]) + remaining -= uint32(rn) + atomic.AddUint32(&counter, uint32(rn)) + if err != nil && !(remaining == 0 && err == io.EOF) { + return err + } + } + // One last call to the callback function to report the total time and bytes received. + cb(time.Since(startTime), l, true) + return nil +} + +// Upload requests the server to receive l bytes of data. +// The callback function cb is called every second with the time since the last call, +// and the number of bytes sent in that time. +func (c *Client) Upload(l uint32, cb func(time.Duration, uint32, bool)) error { + err := writeUploadRequest(c.Conn, l) + if err != nil { + return err + } + ok, msg, err := readUploadResponse(c.Conn) + if err != nil { + return err + } + if !ok { + return fmt.Errorf("server rejected upload request: %s", msg) + } + var counter uint32 + stopChan := make(chan struct{}) + defer close(stopChan) + // Call the callback function every second, + // with the time since the last call and the number of bytes sent in that time. + go func() { + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + t := time.Now() + for { + select { + case <-stopChan: + return + case <-ticker.C: + cb(time.Since(t), atomic.SwapUint32(&counter, 0), false) + t = time.Now() + } + } + }() + buf := make([]byte, chunkSize) + remaining := l + for remaining > 0 { + n := remaining + if n > chunkSize { + n = chunkSize + } + _, err := c.Conn.Write(buf[:n]) + if err != nil { + return err + } + remaining -= n + atomic.AddUint32(&counter, n) + } + // Now we should receive the upload summary from the server. + elapsed, received, err := readUploadSummary(c.Conn) + if err != nil { + return err + } + // One last call to the callback function to report the total time and bytes sent. + cb(elapsed, received, true) + return nil +} diff --git a/extras/outbounds/speedtest/protocol.go b/extras/outbounds/speedtest/protocol.go new file mode 100644 index 0000000..8d1adb8 --- /dev/null +++ b/extras/outbounds/speedtest/protocol.go @@ -0,0 +1,152 @@ +package speedtest + +import ( + "encoding/binary" + "io" + "time" +) + +const ( + typeDownload = 0x1 + typeUpload = 0x2 +) + +// DownloadRequest format: +// 0x1 (byte) +// Request data length (uint32 BE) + +func readDownloadRequest(r io.Reader) (uint32, error) { + var l uint32 + err := binary.Read(r, binary.BigEndian, &l) + return l, err +} + +func writeDownloadRequest(w io.Writer, l uint32) error { + buf := make([]byte, 5) + buf[0] = typeDownload + binary.BigEndian.PutUint32(buf[1:], l) + _, err := w.Write(buf) + return err +} + +// DownloadResponse format: +// Status (byte, 0=ok, 1=error) +// Message length (uint16 BE) +// Message (bytes) + +func readDownloadResponse(r io.Reader) (bool, string, error) { + var status [1]byte + if _, err := io.ReadFull(r, status[:]); err != nil { + return false, "", err + } + var msgLen uint16 + if err := binary.Read(r, binary.BigEndian, &msgLen); err != nil { + return false, "", err + } + // No message is fine + if msgLen == 0 { + return status[0] == 0, "", nil + } + msgBuf := make([]byte, msgLen) + _, err := io.ReadFull(r, msgBuf) + if err != nil { + return false, "", err + } + return status[0] == 0, string(msgBuf), nil +} + +func writeDownloadResponse(w io.Writer, ok bool, msg string) error { + sz := 1 + 2 + len(msg) + buf := make([]byte, sz) + if ok { + buf[0] = 0 + } else { + buf[0] = 1 + } + binary.BigEndian.PutUint16(buf[1:], uint16(len(msg))) + copy(buf[3:], msg) + _, err := w.Write(buf) + return err +} + +// UploadRequest format: +// 0x2 (byte) +// Upload data length (uint32 BE) + +func readUploadRequest(r io.Reader) (uint32, error) { + var l uint32 + err := binary.Read(r, binary.BigEndian, &l) + return l, err +} + +func writeUploadRequest(w io.Writer, l uint32) error { + buf := make([]byte, 5) + buf[0] = typeUpload + binary.BigEndian.PutUint32(buf[1:], l) + _, err := w.Write(buf) + return err +} + +// UploadResponse format: +// Status (byte, 0=ok, 1=error) +// Message length (uint16 BE) +// Message (bytes) + +func readUploadResponse(r io.Reader) (bool, string, error) { + var status [1]byte + if _, err := io.ReadFull(r, status[:]); err != nil { + return false, "", err + } + var msgLen uint16 + if err := binary.Read(r, binary.BigEndian, &msgLen); err != nil { + return false, "", err + } + // No message is fine + if msgLen == 0 { + return status[0] == 0, "", nil + } + msgBuf := make([]byte, msgLen) + _, err := io.ReadFull(r, msgBuf) + if err != nil { + return false, "", err + } + return status[0] == 0, string(msgBuf), nil +} + +func writeUploadResponse(w io.Writer, ok bool, msg string) error { + sz := 1 + 2 + len(msg) + buf := make([]byte, sz) + if ok { + buf[0] = 0 + } else { + buf[0] = 1 + } + binary.BigEndian.PutUint16(buf[1:], uint16(len(msg))) + copy(buf[3:], msg) + _, err := w.Write(buf) + return err +} + +// UploadSummary format: +// Duration (in milliseconds, uint32 BE) +// Received data length (uint32 BE) + +func readUploadSummary(r io.Reader) (time.Duration, uint32, error) { + var duration uint32 + if err := binary.Read(r, binary.BigEndian, &duration); err != nil { + return 0, 0, err + } + var l uint32 + if err := binary.Read(r, binary.BigEndian, &l); err != nil { + return 0, 0, err + } + return time.Duration(duration) * time.Millisecond, l, nil +} + +func writeUploadSummary(w io.Writer, duration time.Duration, l uint32) error { + buf := make([]byte, 8) + binary.BigEndian.PutUint32(buf, uint32(duration/time.Millisecond)) + binary.BigEndian.PutUint32(buf[4:], l) + _, err := w.Write(buf) + return err +} diff --git a/extras/outbounds/speedtest/protocol_test.go b/extras/outbounds/speedtest/protocol_test.go new file mode 100644 index 0000000..1ad23a4 --- /dev/null +++ b/extras/outbounds/speedtest/protocol_test.go @@ -0,0 +1,446 @@ +package speedtest + +import ( + "bytes" + "testing" + "time" +) + +func TestReadDownloadRequest(t *testing.T) { + tests := []struct { + name string + data []byte + want uint32 + wantErr bool + }{ + { + name: "normal", + data: []byte{0x0, 0x1, 0xBD, 0xC2}, + want: 114114, + wantErr: false, + }, + { + name: "normal zero", + data: []byte{0x0, 0x0, 0x0, 0x0}, + want: 0, + wantErr: false, + }, + { + name: "incomplete", + data: []byte{0x0, 0x1, 0x2}, + want: 0, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := bytes.NewReader(tt.data) + got, err := readDownloadRequest(r) + if (err != nil) != tt.wantErr { + t.Errorf("readDownloadRequest() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("readDownloadRequest() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestWriteDownloadRequest(t *testing.T) { + tests := []struct { + name string + l uint32 + wantW string + wantErr bool + }{ + { + name: "normal", + l: 78909912, + wantW: "\x01\x04\xB4\x11\xD8", + wantErr: false, + }, + { + name: "normal zero", + l: 0, + wantW: "\x01\x00\x00\x00\x00", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + w := &bytes.Buffer{} + err := writeDownloadRequest(w, tt.l) + if (err != nil) != tt.wantErr { + t.Errorf("writeDownloadRequest() error = %v, wantErr %v", err, tt.wantErr) + return + } + if gotW := w.String(); gotW != tt.wantW { + t.Errorf("writeDownloadRequest() gotW = %v, want %v", gotW, tt.wantW) + } + }) + } +} + +func TestReadDownloadResponse(t *testing.T) { + tests := []struct { + name string + data []byte + want bool + want1 string + wantErr bool + }{ + { + name: "normal ok", + data: []byte{0x0, 0x0, 0x2, 0x41, 0x42}, + want: true, + want1: "AB", + wantErr: false, + }, + { + name: "normal ok no message", + data: []byte{0x0, 0x0, 0x0, 0x0}, + want: true, + want1: "", + wantErr: false, + }, + { + name: "normal error", + data: []byte{0x1, 0x0, 0x2, 0x43, 0x44}, + want: false, + want1: "CD", + wantErr: false, + }, + { + name: "incomplete", + data: []byte{0x0, 0x99, 0x99, 0x45, 0x46, 0x47}, + want: false, + want1: "", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := bytes.NewReader(tt.data) + got, got1, err := readDownloadResponse(r) + if (err != nil) != tt.wantErr { + t.Errorf("readDownloadResponse() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("readDownloadResponse() got = %v, want %v", got, tt.want) + } + if got1 != tt.want1 { + t.Errorf("readDownloadResponse() got1 = %v, want %v", got1, tt.want1) + } + }) + } +} + +func TestWriteDownloadResponse(t *testing.T) { + type args struct { + ok bool + msg string + } + tests := []struct { + name string + args args + wantW string + wantErr bool + }{ + { + name: "normal ok", + args: args{ok: true, msg: "wahaha"}, + wantW: "\x00\x00\x06wahaha", + wantErr: false, + }, + { + name: "normal error", + args: args{ok: false, msg: "bullbull"}, + wantW: "\x01\x00\x08bullbull", + wantErr: false, + }, + { + name: "empty ok", + args: args{ok: true, msg: ""}, + wantW: "\x00\x00\x00", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + w := &bytes.Buffer{} + err := writeDownloadResponse(w, tt.args.ok, tt.args.msg) + if (err != nil) != tt.wantErr { + t.Errorf("writeDownloadResponse() error = %v, wantErr %v", err, tt.wantErr) + return + } + if gotW := w.String(); gotW != tt.wantW { + t.Errorf("writeDownloadResponse() gotW = %v, want %v", gotW, tt.wantW) + } + }) + } +} + +func TestReadUploadRequest(t *testing.T) { + tests := []struct { + name string + data []byte + want uint32 + wantErr bool + }{ + { + name: "normal", + data: []byte{0x0, 0x0, 0x26, 0xEE}, + want: 9966, + wantErr: false, + }, + { + name: "normal zero", + data: []byte{0x0, 0x0, 0x0, 0x0}, + want: 0, + wantErr: false, + }, + { + name: "incomplete", + data: []byte{0x1}, + want: 0, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := bytes.NewReader(tt.data) + got, err := readUploadRequest(r) + if (err != nil) != tt.wantErr { + t.Errorf("readUploadRequest() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("readUploadRequest() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestWriteUploadRequest(t *testing.T) { + tests := []struct { + name string + l uint32 + wantW string + wantErr bool + }{ + { + name: "normal", + l: 2291758882, + wantW: "\x02\x88\x99\x77\x22", + wantErr: false, + }, + { + name: "normal zero", + l: 0, + wantW: "\x02\x00\x00\x00\x00", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + w := &bytes.Buffer{} + err := writeUploadRequest(w, tt.l) + if (err != nil) != tt.wantErr { + t.Errorf("writeUploadRequest() error = %v, wantErr %v", err, tt.wantErr) + return + } + if gotW := w.String(); gotW != tt.wantW { + t.Errorf("writeUploadRequest() gotW = %v, want %v", gotW, tt.wantW) + } + }) + } +} + +func TestReadUploadResponse(t *testing.T) { + tests := []struct { + name string + data []byte + want bool + want1 string + wantErr bool + }{ + { + name: "normal ok", + data: []byte{0x0, 0x0, 0x2, 0x41, 0x42}, + want: true, + want1: "AB", + wantErr: false, + }, + { + name: "normal ok no message", + data: []byte{0x0, 0x0, 0x0}, + want: true, + want1: "", + wantErr: false, + }, + { + name: "normal error", + data: []byte{0x1, 0x0, 0x2, 0x43, 0x44}, + want: false, + want1: "CD", + wantErr: false, + }, + { + name: "incomplete", + data: []byte{0x0, 0x99, 0x99, 0x45, 0x46, 0x47}, + want: false, + want1: "", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := bytes.NewReader(tt.data) + got, got1, err := readUploadResponse(r) + if (err != nil) != tt.wantErr { + t.Errorf("readUploadResponse() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("readUploadResponse() got = %v, want %v", got, tt.want) + } + if got1 != tt.want1 { + t.Errorf("readUploadResponse() got1 = %v, want %v", got1, tt.want1) + } + }) + } +} + +func TestWriteUploadResponse(t *testing.T) { + type args struct { + ok bool + msg string + } + tests := []struct { + name string + args args + wantW string + wantErr bool + }{ + { + name: "normal ok", + args: args{ok: true, msg: "lul"}, + wantW: "\x00\x00\x03lul", + wantErr: false, + }, + { + name: "normal error", + args: args{ok: false, msg: "notforu"}, + wantW: "\x01\x00\x07notforu", + wantErr: false, + }, + { + name: "empty ok", + args: args{ok: true, msg: ""}, + wantW: "\x00\x00\x00", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + w := &bytes.Buffer{} + err := writeUploadResponse(w, tt.args.ok, tt.args.msg) + if (err != nil) != tt.wantErr { + t.Errorf("writeUploadResponse() error = %v, wantErr %v", err, tt.wantErr) + return + } + if gotW := w.String(); gotW != tt.wantW { + t.Errorf("writeUploadResponse() gotW = %v, want %v", gotW, tt.wantW) + } + }) + } +} + +func TestReadUploadSummary(t *testing.T) { + tests := []struct { + name string + data []byte + want time.Duration + want1 uint32 + wantErr bool + }{ + { + name: "normal", + data: []byte{0x0, 0x0, 0x14, 0x6E, 0x0, 0x26, 0x25, 0xA0}, + want: 5230 * time.Millisecond, + want1: 2500000, + wantErr: false, + }, + { + name: "zero", + data: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + want: 0, + want1: 0, + wantErr: false, + }, + { + name: "incomplete", + data: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + want: 0, + want1: 0, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := bytes.NewReader(tt.data) + got, got1, err := readUploadSummary(r) + if (err != nil) != tt.wantErr { + t.Errorf("readUploadSummary() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("readUploadSummary() got = %v, want %v", got, tt.want) + } + if got1 != tt.want1 { + t.Errorf("readUploadSummary() got1 = %v, want %v", got1, tt.want1) + } + }) + } +} + +func TestWriteUploadSummary(t *testing.T) { + type args struct { + duration time.Duration + l uint32 + } + tests := []struct { + name string + args args + wantW string + wantErr bool + }{ + { + name: "normal", + args: args{duration: 5230 * time.Millisecond, l: 2500000}, + wantW: "\x00\x00\x14\x6E\x00\x26\x25\xA0", + wantErr: false, + }, + { + name: "zero", + args: args{duration: 0, l: 0}, + wantW: "\x00\x00\x00\x00\x00\x00\x00\x00", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + w := &bytes.Buffer{} + err := writeUploadSummary(w, tt.args.duration, tt.args.l) + if (err != nil) != tt.wantErr { + t.Errorf("writeUploadSummary() error = %v, wantErr %v", err, tt.wantErr) + return + } + if gotW := w.String(); gotW != tt.wantW { + t.Errorf("writeUploadSummary() gotW = %v, want %v", gotW, tt.wantW) + } + }) + } +} diff --git a/extras/outbounds/speedtest/server.go b/extras/outbounds/speedtest/server.go new file mode 100644 index 0000000..d280b57 --- /dev/null +++ b/extras/outbounds/speedtest/server.go @@ -0,0 +1,99 @@ +package speedtest + +import ( + "crypto/rand" + "fmt" + "io" + "net" + "time" +) + +const ( + chunkSize = 64 * 1024 +) + +// NewServerConn creates a new "pseudo" connection that implements the speed test protocol. +// It's called "pseudo" because it's not a real TCP connection - everything is done in memory. +func NewServerConn() net.Conn { + rConn, iConn := net.Pipe() // return conn & internal conn + // Start the server logic + go server(iConn) + return rConn +} + +func server(conn net.Conn) error { + defer conn.Close() + // First byte determines the request type + var typ [1]byte + if _, err := io.ReadFull(conn, typ[:]); err != nil { + return err + } + switch typ[0] { + case typeDownload: + return handleDownload(conn) + case typeUpload: + return handleUpload(conn) + default: + return fmt.Errorf("unknown request type: %d", typ[0]) + } +} + +// handleDownload reads the download request and sends the requested amount of data. +func handleDownload(conn net.Conn) error { + l, err := readDownloadRequest(conn) + if err != nil { + return err + } + err = writeDownloadResponse(conn, true, "OK") + if err != nil { + return err + } + buf := make([]byte, chunkSize) + // Fill the buffer with random data. + // For now, we only do it once and repeat the same data for performance reasons. + _, err = rand.Read(buf) + if err != nil { + return err + } + remaining := l + for remaining > 0 { + n := remaining + if n > chunkSize { + n = chunkSize + } + _, err := conn.Write(buf[:n]) + if err != nil { + return err + } + remaining -= n + } + return nil +} + +// handleUpload reads the upload request, reads & discards the requested amount of data, +// and sends the upload summary. +func handleUpload(conn net.Conn) error { + l, err := readUploadRequest(conn) + if err != nil { + return err + } + err = writeUploadResponse(conn, true, "OK") + if err != nil { + return err + } + buf := make([]byte, chunkSize) + startTime := time.Now() + remaining := l + for remaining > 0 { + n := remaining + if n > chunkSize { + n = chunkSize + } + rn, err := conn.Read(buf[:n]) + remaining -= uint32(rn) + if err != nil && !(remaining == 0 && err == io.EOF) { + return err + } + } + return writeUploadSummary(conn, time.Since(startTime), l) +} From e99ac076daade9a89d400abe736cf7dfdf59c101 Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 9 Mar 2024 21:25:49 -0800 Subject: [PATCH 009/161] chore: "server may not support speed test" hint when it's a dial error --- app/cmd/speedtest.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/app/cmd/speedtest.go b/app/cmd/speedtest.go index 67cf71f..86bdbdb 100644 --- a/app/cmd/speedtest.go +++ b/app/cmd/speedtest.go @@ -1,6 +1,7 @@ package cmd import ( + "errors" "fmt" "time" @@ -9,6 +10,7 @@ import ( "go.uber.org/zap" "github.com/apernet/hysteria/core/client" + hyErrors "github.com/apernet/hysteria/core/errors" "github.com/apernet/hysteria/extras/outbounds" "github.com/apernet/hysteria/extras/outbounds/speedtest" ) @@ -78,7 +80,11 @@ func runDownloadTest(c client.Client) { logger.Info("performing download test") downConn, err := c.TCP(speedtestAddr) if err != nil { - logger.Fatal("failed to connect", zap.Error(err)) + if errors.As(err, &hyErrors.DialError{}) { + logger.Fatal("failed to connect (server may not support speed test)", zap.Error(err)) + } else { + logger.Fatal("failed to connect", zap.Error(err)) + } } defer downConn.Close() @@ -107,7 +113,11 @@ func runUploadTest(c client.Client) { logger.Info("performing upload test") upConn, err := c.TCP(speedtestAddr) if err != nil { - logger.Fatal("failed to connect", zap.Error(err)) + if errors.As(err, &hyErrors.DialError{}) { + logger.Fatal("failed to connect (server may not support speed test)", zap.Error(err)) + } else { + logger.Fatal("failed to connect", zap.Error(err)) + } } defer upConn.Close() From d82d76743f9c0d2beb3aaf8c41ae1361b917b111 Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 9 Mar 2024 21:39:30 -0800 Subject: [PATCH 010/161] chore: use @ instead of _ for speed test dest --- extras/outbounds/speedtest.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/outbounds/speedtest.go b/extras/outbounds/speedtest.go index 9e5ab43..b788424 100644 --- a/extras/outbounds/speedtest.go +++ b/extras/outbounds/speedtest.go @@ -7,7 +7,7 @@ import ( ) const ( - SpeedtestDest = "_SpeedTest" + SpeedtestDest = "@SpeedTest" ) // speedtestHandler is a PluggableOutbound that handles speed test requests. From 9c51995cc43860128734621f576359931cfef0d3 Mon Sep 17 00:00:00 2001 From: Haruue Date: Sun, 10 Mar 2024 22:41:26 +0800 Subject: [PATCH 011/161] fix: cwnd undersize in extremely-low rtt scenarios Prevent the congestion window from falling below the size of single packet in scenarios with extremely low RTT, which previously led to transmission stalls. --- core/internal/congestion/brutal/brutal.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/internal/congestion/brutal/brutal.go b/core/internal/congestion/brutal/brutal.go index a9eabe8..4edd45d 100644 --- a/core/internal/congestion/brutal/brutal.go +++ b/core/internal/congestion/brutal/brutal.go @@ -69,7 +69,7 @@ func (b *BrutalSender) HasPacingBudget(now time.Time) bool { } func (b *BrutalSender) CanSend(bytesInFlight congestion.ByteCount) bool { - return bytesInFlight < b.GetCongestionWindow() + return bytesInFlight <= b.GetCongestionWindow() } func (b *BrutalSender) GetCongestionWindow() congestion.ByteCount { @@ -77,7 +77,11 @@ func (b *BrutalSender) GetCongestionWindow() congestion.ByteCount { if rtt <= 0 { return 10240 } - return congestion.ByteCount(float64(b.bps) * rtt.Seconds() * congestionWindowMultiplier / b.ackRate) + cwnd := congestion.ByteCount(float64(b.bps) * rtt.Seconds() * congestionWindowMultiplier / b.ackRate) + if cwnd < b.maxDatagramSize { + cwnd = b.maxDatagramSize + } + return cwnd } func (b *BrutalSender) OnPacketSent(sentTime time.Time, bytesInFlight congestion.ByteCount, From 78aa85d35c9de79bc52d9bf55b77a876cb537b3a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 06:13:44 +0000 Subject: [PATCH 012/161] chore(deps): bump softprops/action-gh-release from 1 to 2 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 1 to 2. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v1...v2) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6134883..55e2aba 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,7 +50,7 @@ jobs: done - name: Upload GitHub - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: files: build/* From ee056deaadb4a3400eeb63bcfcb5d6437a8f210c Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:35:12 +0800 Subject: [PATCH 013/161] support range format ProtoPort --- extras/outbounds/acl/compile.go | 42 ++++++++++++++++ extras/outbounds/acl/compile_test.go | 73 ++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/extras/outbounds/acl/compile.go b/extras/outbounds/acl/compile.go index 40d0069..cd5c844 100644 --- a/extras/outbounds/acl/compile.go +++ b/extras/outbounds/acl/compile.go @@ -107,6 +107,11 @@ type GeoLoader interface { func Compile[O Outbound](rules []TextRule, outbounds map[string]O, cacheSize int, geoLoader GeoLoader, ) (CompiledRuleSet[O], error) { + for _, rule := range rules { + if extra, rangeLen := splitPortRangeRules(&rule); rangeLen > 0 { + rules = append(rules, extra...) + } + } compiledRules := make([]compiledRule[O], len(rules)) for i, rule := range rules { outbound, ok := outbounds[strings.ToLower(rule.Outbound)] @@ -282,3 +287,40 @@ func parseGeoSiteName(s string) (string, []string) { } return base, attrs } + +func splitPortRangeRules(rule *TextRule) ([]TextRule, int) { + protoPort := strings.ToLower(rule.ProtoPort) + if protoPort == "" || protoPort == "*" || protoPort == "*/*" { + return nil, 0 + } + parts := strings.SplitN(protoPort, "/", 2) + if len(parts) != 2 { + return nil, 0 + } + ports := strings.SplitN(strings.TrimSpace(parts[1]), "-", 2) + if len(ports) != 2 { + return nil, 0 + } + minPorts, err := strconv.Atoi(ports[0]) + if err != nil { + return nil, 0 + } + maxPorts, err := strconv.Atoi(ports[1]) + if err != nil { + return nil, 0 + } + + portLength := maxPorts - minPorts + if portLength <= 0 || minPorts == 0 { + return nil, 0 + } + + // port range: minPort < port <= MaxPort + extraRules := make([]TextRule, portLength) + for i := range extraRules { + extraRules[i] = *rule + extraRules[i].ProtoPort = fmt.Sprintf("%s/%d", parts[0], minPorts+i+1) + } + rule.ProtoPort = fmt.Sprintf("%s/%d", parts[0], minPorts) + return extraRules, portLength +} diff --git a/extras/outbounds/acl/compile_test.go b/extras/outbounds/acl/compile_test.go index 772f8b6..87245b7 100644 --- a/extras/outbounds/acl/compile_test.go +++ b/extras/outbounds/acl/compile_test.go @@ -303,3 +303,76 @@ func Test_parseGeoSiteName(t *testing.T) { }) } } + +func Test_splitPortRangeRules(t *testing.T) { + rules := []TextRule{ + { + Outbound: "ob1", + Address: "1.2.3.4", + ProtoPort: "tcp/1-1024", + HijackAddress: "", + }, + { + Outbound: "ob1", + Address: "1.2.3.4", + ProtoPort: "udp/1-1024", + HijackAddress: "", + }, + { + Outbound: "ob1", + Address: "1.2.3.4", + ProtoPort: "*/1-1024", + HijackAddress: "", + }, + { + Outbound: "ob1", + Address: "1.2.3.4", + ProtoPort: "tcp/0-222", + HijackAddress: "", + }, + { + Outbound: "ob1", + Address: "1.2.3.4", + ProtoPort: "tcp/1024", + HijackAddress: "", + }, + { + Outbound: "ob1", + Address: "1.2.3.4", + ProtoPort: "tcp/-1-9", + HijackAddress: "", + }, + { + Outbound: "ob1", + Address: "1.2.3.4", + ProtoPort: "tcp/6881-6889", + HijackAddress: "", + }, + } + _, rangeLen0 := splitPortRangeRules(&rules[0]) + assert.Equal(t, 1023, rangeLen0) + + _, rangeLen1 := splitPortRangeRules(&rules[1]) + assert.Equal(t, 1023, rangeLen1) + + _, rangeLen2 := splitPortRangeRules(&rules[2]) + assert.Equal(t, 1023, rangeLen2) + + _, rangeLen3 := splitPortRangeRules(&rules[3]) + assert.Equal(t, 0, rangeLen3) + + _, rangeLen4 := splitPortRangeRules(&rules[4]) + assert.Equal(t, 0, rangeLen4) + + _, rangeLen5 := splitPortRangeRules(&rules[5]) + assert.Equal(t, 0, rangeLen5) + + rangeRule, _ := splitPortRangeRules(&rules[6]) + for _, rule := range rangeRule { + assert.Equal(t, "ob1", rule.Outbound) + assert.Equal(t, "1.2.3.4", rule.Address) + t.Log(rule.ProtoPort) + assert.Equal(t, "", rule.HijackAddress) + } + assert.Equal(t, "tcp/6881", rules[6].ProtoPort) +} From 321681444068ce10d044903b6c9f0a2519cb1982 Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:54:30 +0800 Subject: [PATCH 014/161] remove useless code --- extras/outbounds/acl/compile.go | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/extras/outbounds/acl/compile.go b/extras/outbounds/acl/compile.go index cd5c844..ebf5207 100644 --- a/extras/outbounds/acl/compile.go +++ b/extras/outbounds/acl/compile.go @@ -108,7 +108,7 @@ func Compile[O Outbound](rules []TextRule, outbounds map[string]O, cacheSize int, geoLoader GeoLoader, ) (CompiledRuleSet[O], error) { for _, rule := range rules { - if extra, rangeLen := splitPortRangeRules(&rule); rangeLen > 0 { + if extra := splitPortRangeRules(&rule); extra != nil { rules = append(rules, extra...) } } @@ -288,31 +288,38 @@ func parseGeoSiteName(s string) (string, []string) { return base, attrs } -func splitPortRangeRules(rule *TextRule) ([]TextRule, int) { +// splitPortRangeRules splits a rule containing a port range and divides it into multiple rules, each specifying a single port. +// +// If protoPort has a port range, such as "tcp/80-90", +// the function splits this into individual rules for each port in the range, +// here resulting in rules for ports 80 through 90. +// the original protoPort will be changed to "tcp/80", and the returned rules will have the same Outbound, Address, and HijackAddress. +// but the ProtoPort will be changed to "tcp/81", "tcp/82", ..., "tcp/90". +func splitPortRangeRules(rule *TextRule) []TextRule { protoPort := strings.ToLower(rule.ProtoPort) if protoPort == "" || protoPort == "*" || protoPort == "*/*" { - return nil, 0 + return nil } parts := strings.SplitN(protoPort, "/", 2) if len(parts) != 2 { - return nil, 0 + return nil } ports := strings.SplitN(strings.TrimSpace(parts[1]), "-", 2) if len(ports) != 2 { - return nil, 0 + return nil } minPorts, err := strconv.Atoi(ports[0]) if err != nil { - return nil, 0 + return nil } maxPorts, err := strconv.Atoi(ports[1]) if err != nil { - return nil, 0 + return nil } portLength := maxPorts - minPorts if portLength <= 0 || minPorts == 0 { - return nil, 0 + return nil } // port range: minPort < port <= MaxPort @@ -321,6 +328,7 @@ func splitPortRangeRules(rule *TextRule) ([]TextRule, int) { extraRules[i] = *rule extraRules[i].ProtoPort = fmt.Sprintf("%s/%d", parts[0], minPorts+i+1) } + // edit ProtoPort from port range to a single port that value is minPort. For example, 80-90 -> 80 rule.ProtoPort = fmt.Sprintf("%s/%d", parts[0], minPorts) - return extraRules, portLength + return extraRules } From 9349f0a1a383d6119bc4ae123c0be5478e4d5757 Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Tue, 12 Mar 2024 11:00:47 +0800 Subject: [PATCH 015/161] refactor the method that support range format ProtoPort --- extras/outbounds/acl/compile.go | 101 ++++++++--------------- extras/outbounds/acl/compile_test.go | 117 +++++++++++++-------------- 2 files changed, 92 insertions(+), 126 deletions(-) diff --git a/extras/outbounds/acl/compile.go b/extras/outbounds/acl/compile.go index ebf5207..d27c498 100644 --- a/extras/outbounds/acl/compile.go +++ b/extras/outbounds/acl/compile.go @@ -41,7 +41,8 @@ type compiledRule[O Outbound] struct { Outbound O HostMatcher hostMatcher Protocol Protocol - Port uint16 + StartPort uint16 + EndPoint uint16 HijackAddress net.IP } @@ -49,7 +50,7 @@ func (r *compiledRule[O]) Match(host HostInfo, proto Protocol, port uint16) bool if r.Protocol != ProtocolBoth && r.Protocol != proto { return false } - if r.Port != 0 && r.Port != port { + if r.StartPort != 0 && (port < r.StartPort || port > r.EndPoint) { return false } return r.HostMatcher.Match(host) @@ -107,11 +108,6 @@ type GeoLoader interface { func Compile[O Outbound](rules []TextRule, outbounds map[string]O, cacheSize int, geoLoader GeoLoader, ) (CompiledRuleSet[O], error) { - for _, rule := range rules { - if extra := splitPortRangeRules(&rule); extra != nil { - rules = append(rules, extra...) - } - } compiledRules := make([]compiledRule[O], len(rules)) for i, rule := range rules { outbound, ok := outbounds[strings.ToLower(rule.Outbound)] @@ -122,7 +118,7 @@ func Compile[O Outbound](rules []TextRule, outbounds map[string]O, if errStr != "" { return nil, &CompilationError{rule.LineNum, errStr} } - proto, port, ok := parseProtoPort(rule.ProtoPort) + proto, startPort, endPort, ok := parseProtoPort(rule.ProtoPort) if !ok { return nil, &CompilationError{rule.LineNum, fmt.Sprintf("invalid protocol/port: %s", rule.ProtoPort)} } @@ -133,7 +129,7 @@ func Compile[O Outbound](rules []TextRule, outbounds map[string]O, return nil, &CompilationError{rule.LineNum, fmt.Sprintf("invalid hijack address (must be an IP address): %s", rule.HijackAddress)} } } - compiledRules[i] = compiledRule[O]{outbound, hm, proto, port, hijackAddress} + compiledRules[i] = compiledRule[O]{outbound, hm, proto, startPort, endPort, hijackAddress} } cache, err := lru.New[string, matchResult[O]](cacheSize) if err != nil { @@ -154,26 +150,26 @@ func Compile[O Outbound](rules []TextRule, outbounds map[string]O, // [empty] (same as *) // // proto must be either "tcp" or "udp", case-insensitive. -func parseProtoPort(protoPort string) (Protocol, uint16, bool) { +func parseProtoPort(protoPort string) (Protocol, uint16, uint16, bool) { protoPort = strings.ToLower(protoPort) if protoPort == "" || protoPort == "*" || protoPort == "*/*" { - return ProtocolBoth, 0, true + return ProtocolBoth, 0, 0, true } parts := strings.SplitN(protoPort, "/", 2) if len(parts) == 1 { // No port, only protocol switch parts[0] { case "tcp": - return ProtocolTCP, 0, true + return ProtocolTCP, 0, 0, true case "udp": - return ProtocolUDP, 0, true + return ProtocolUDP, 0, 0, true default: - return ProtocolBoth, 0, false + return ProtocolBoth, 0, 0, false } } else { // Both protocol and port var proto Protocol - var port uint16 + var startPort, endPort uint16 switch parts[0] { case "tcp": proto = ProtocolTCP @@ -182,16 +178,34 @@ func parseProtoPort(protoPort string) (Protocol, uint16, bool) { case "*": proto = ProtocolBoth default: - return ProtocolBoth, 0, false + return ProtocolBoth, 0, 0, false } if parts[1] != "*" { - p64, err := strconv.ParseUint(parts[1], 10, 16) - if err != nil { - return ProtocolBoth, 0, false + ports := strings.SplitN(strings.TrimSpace(parts[1]), "-", 2) + if len(ports) == 1 { + p64, err := strconv.ParseUint(parts[1], 10, 16) + if err != nil { + return ProtocolBoth, 0, 0, false + } + startPort = uint16(p64) + endPort = startPort + } else { + p64, err := strconv.ParseUint(ports[0], 10, 16) + if err != nil { + return ProtocolBoth, 0, 0, false + } + startPort = uint16(p64) + p64, err = strconv.ParseUint(ports[1], 10, 16) + if err != nil { + return ProtocolBoth, 0, 0, false + } + endPort = uint16(p64) + if startPort > endPort { + return ProtocolBoth, 0, 0, false + } } - port = uint16(p64) } - return proto, port, true + return proto, startPort, endPort, true } } @@ -287,48 +301,3 @@ func parseGeoSiteName(s string) (string, []string) { } return base, attrs } - -// splitPortRangeRules splits a rule containing a port range and divides it into multiple rules, each specifying a single port. -// -// If protoPort has a port range, such as "tcp/80-90", -// the function splits this into individual rules for each port in the range, -// here resulting in rules for ports 80 through 90. -// the original protoPort will be changed to "tcp/80", and the returned rules will have the same Outbound, Address, and HijackAddress. -// but the ProtoPort will be changed to "tcp/81", "tcp/82", ..., "tcp/90". -func splitPortRangeRules(rule *TextRule) []TextRule { - protoPort := strings.ToLower(rule.ProtoPort) - if protoPort == "" || protoPort == "*" || protoPort == "*/*" { - return nil - } - parts := strings.SplitN(protoPort, "/", 2) - if len(parts) != 2 { - return nil - } - ports := strings.SplitN(strings.TrimSpace(parts[1]), "-", 2) - if len(ports) != 2 { - return nil - } - minPorts, err := strconv.Atoi(ports[0]) - if err != nil { - return nil - } - maxPorts, err := strconv.Atoi(ports[1]) - if err != nil { - return nil - } - - portLength := maxPorts - minPorts - if portLength <= 0 || minPorts == 0 { - return nil - } - - // port range: minPort < port <= MaxPort - extraRules := make([]TextRule, portLength) - for i := range extraRules { - extraRules[i] = *rule - extraRules[i].ProtoPort = fmt.Sprintf("%s/%d", parts[0], minPorts+i+1) - } - // edit ProtoPort from port range to a single port that value is minPort. For example, 80-90 -> 80 - rule.ProtoPort = fmt.Sprintf("%s/%d", parts[0], minPorts) - return extraRules -} diff --git a/extras/outbounds/acl/compile_test.go b/extras/outbounds/acl/compile_test.go index 87245b7..aaae8c2 100644 --- a/extras/outbounds/acl/compile_test.go +++ b/extras/outbounds/acl/compile_test.go @@ -304,75 +304,72 @@ func Test_parseGeoSiteName(t *testing.T) { } } -func Test_splitPortRangeRules(t *testing.T) { +func TestCompileRangePort(t *testing.T) { + ob1, ob2, ob3, ob4 := 1, 2, 3, 4 rules := []TextRule{ - { - Outbound: "ob1", - Address: "1.2.3.4", - ProtoPort: "tcp/1-1024", - HijackAddress: "", - }, - { - Outbound: "ob1", - Address: "1.2.3.4", - ProtoPort: "udp/1-1024", - HijackAddress: "", - }, - { - Outbound: "ob1", - Address: "1.2.3.4", - ProtoPort: "*/1-1024", - HijackAddress: "", - }, - { - Outbound: "ob1", - Address: "1.2.3.4", - ProtoPort: "tcp/0-222", - HijackAddress: "", - }, - { - Outbound: "ob1", - Address: "1.2.3.4", - ProtoPort: "tcp/1024", - HijackAddress: "", - }, - { - Outbound: "ob1", - Address: "1.2.3.4", - ProtoPort: "tcp/-1-9", - HijackAddress: "", - }, { Outbound: "ob1", Address: "1.2.3.4", ProtoPort: "tcp/6881-6889", HijackAddress: "", }, + { + Outbound: "ob2", + Address: "8.8.8.0/24", + ProtoPort: "udp/2525-3333", + HijackAddress: "1.1.1.1", + }, + { + Outbound: "ob3", + Address: "1.1.1.0/24", + ProtoPort: "*/1-65535", + HijackAddress: "", + }, + { + Outbound: "ob4", + Address: "1.1.1.0/24", + ProtoPort: "*/22", + HijackAddress: "", + }, } - _, rangeLen0 := splitPortRangeRules(&rules[0]) - assert.Equal(t, 1023, rangeLen0) + _, err := Compile[int](rules, map[string]int{ + "ob1": ob1, + "ob2": ob2, + "ob3": ob3, + "ob4": ob4, + }, 100, &testGeoLoader{}) + assert.NoError(t, err) - _, rangeLen1 := splitPortRangeRules(&rules[1]) - assert.Equal(t, 1023, rangeLen1) + ob11 := 1 + rules2 := []TextRule{ - _, rangeLen2 := splitPortRangeRules(&rules[2]) - assert.Equal(t, 1023, rangeLen2) - - _, rangeLen3 := splitPortRangeRules(&rules[3]) - assert.Equal(t, 0, rangeLen3) - - _, rangeLen4 := splitPortRangeRules(&rules[4]) - assert.Equal(t, 0, rangeLen4) - - _, rangeLen5 := splitPortRangeRules(&rules[5]) - assert.Equal(t, 0, rangeLen5) - - rangeRule, _ := splitPortRangeRules(&rules[6]) - for _, rule := range rangeRule { - assert.Equal(t, "ob1", rule.Outbound) - assert.Equal(t, "1.2.3.4", rule.Address) - t.Log(rule.ProtoPort) - assert.Equal(t, "", rule.HijackAddress) + { + Outbound: "ob11", + Address: "1.1.2.0/24", + ProtoPort: "*/3-1", // invalid range + HijackAddress: "", + }, } - assert.Equal(t, "tcp/6881", rules[6].ProtoPort) + + _, err = Compile[int](rules2, map[string]int{ + "ob11": ob11, + }, 100, &testGeoLoader{}) + assert.Error(t, err) + + ob21 := 1 + rules3 := []TextRule{ + + { + Outbound: "ob21", + Address: "1.1.2.0/24", + ProtoPort: "*/-114-514", // invalid range + HijackAddress: "", + }, + } + + _, err = Compile[int](rules3, map[string]int{ + "ob21": ob21, + }, 100, &testGeoLoader{}) + assert.Error(t, err) + } From fda93579f0e2c864571d130a9e853215984c8719 Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Wed, 13 Mar 2024 10:51:36 +0800 Subject: [PATCH 016/161] fix typo --- extras/outbounds/acl/compile.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extras/outbounds/acl/compile.go b/extras/outbounds/acl/compile.go index d27c498..45f97df 100644 --- a/extras/outbounds/acl/compile.go +++ b/extras/outbounds/acl/compile.go @@ -42,7 +42,7 @@ type compiledRule[O Outbound] struct { HostMatcher hostMatcher Protocol Protocol StartPort uint16 - EndPoint uint16 + EndPort uint16 HijackAddress net.IP } @@ -50,7 +50,7 @@ func (r *compiledRule[O]) Match(host HostInfo, proto Protocol, port uint16) bool if r.Protocol != ProtocolBoth && r.Protocol != proto { return false } - if r.StartPort != 0 && (port < r.StartPort || port > r.EndPoint) { + if r.StartPort != 0 && (port < r.StartPort || port > r.EndPort) { return false } return r.HostMatcher.Match(host) From 9a80fe589a954d62f8b97df00f043c8ff712837b Mon Sep 17 00:00:00 2001 From: Toby Date: Tue, 12 Mar 2024 19:54:18 -0700 Subject: [PATCH 017/161] fix: format --- extras/outbounds/acl/compile_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/extras/outbounds/acl/compile_test.go b/extras/outbounds/acl/compile_test.go index aaae8c2..65c1e0c 100644 --- a/extras/outbounds/acl/compile_test.go +++ b/extras/outbounds/acl/compile_test.go @@ -342,7 +342,6 @@ func TestCompileRangePort(t *testing.T) { ob11 := 1 rules2 := []TextRule{ - { Outbound: "ob11", Address: "1.1.2.0/24", @@ -358,7 +357,6 @@ func TestCompileRangePort(t *testing.T) { ob21 := 1 rules3 := []TextRule{ - { Outbound: "ob21", Address: "1.1.2.0/24", @@ -371,5 +369,4 @@ func TestCompileRangePort(t *testing.T) { "ob21": ob21, }, 100, &testGeoLoader{}) assert.Error(t, err) - } From 15b94d5c404d73a8f3efec86da004202cfd16296 Mon Sep 17 00:00:00 2001 From: Toby Date: Tue, 12 Mar 2024 20:04:06 -0700 Subject: [PATCH 018/161] chore: adjust comments --- extras/outbounds/acl/compile.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extras/outbounds/acl/compile.go b/extras/outbounds/acl/compile.go index 45f97df..b93e80f 100644 --- a/extras/outbounds/acl/compile.go +++ b/extras/outbounds/acl/compile.go @@ -101,10 +101,9 @@ type GeoLoader interface { // Compile compiles TextRules into a CompiledRuleSet. // Names in the outbounds map MUST be in all lower case. -// geoipFunc is a function that returns the GeoIP database needed by the GeoIP matcher. -// It will be called every time a GeoIP matcher is used during compilation, but won't -// be called if there is no GeoIP rule. We use a function here so that database loading -// is on-demand (only required if used by rules). +// We want on-demand loading of GeoIP/GeoSite databases, so instead of passing the +// databases directly, we use a GeoLoader interface to load them. This way, they are +// only loaded when there are at least one rule that uses them. func Compile[O Outbound](rules []TextRule, outbounds map[string]O, cacheSize int, geoLoader GeoLoader, ) (CompiledRuleSet[O], error) { @@ -181,6 +180,7 @@ func parseProtoPort(protoPort string) (Protocol, uint16, uint16, bool) { return ProtocolBoth, 0, 0, false } if parts[1] != "*" { + // We allow either a single port or a range (e.g. "1000-2000") ports := strings.SplitN(strings.TrimSpace(parts[1]), "-", 2) if len(ports) == 1 { p64, err := strconv.ParseUint(parts[1], 10, 16) From b5c1980202f254581e90feb35cf884520919a2f5 Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Wed, 13 Mar 2024 11:22:18 +0800 Subject: [PATCH 019/161] simplify compile_test code --- extras/outbounds/acl/compile_test.go | 102 +++++++++------------------ 1 file changed, 34 insertions(+), 68 deletions(-) diff --git a/extras/outbounds/acl/compile_test.go b/extras/outbounds/acl/compile_test.go index 65c1e0c..e12ad3d 100644 --- a/extras/outbounds/acl/compile_test.go +++ b/extras/outbounds/acl/compile_test.go @@ -22,7 +22,7 @@ func (l *testGeoLoader) LoadGeoSite() (map[string]*v2geo.GeoSite, error) { } func TestCompile(t *testing.T) { - ob1, ob2, ob3, ob4, ob5 := 1, 2, 3, 4, 5 + ob1, ob2, ob3, ob4, ob5, ob6 := 1, 2, 3, 4, 5, 6 rules := []TextRule{ { Outbound: "ob1", @@ -90,6 +90,12 @@ func TestCompile(t *testing.T) { ProtoPort: "*/*", HijackAddress: "", }, + { + Outbound: "ob6", + Address: "all", + ProtoPort: "tcp/6881-6889", + HijackAddress: "", + }, } comp, err := Compile[int](rules, map[string]int{ "ob1": ob1, @@ -97,6 +103,7 @@ func TestCompile(t *testing.T) { "ob3": ob3, "ob4": ob4, "ob5": ob5, + "ob6": ob6, }, 100, &testGeoLoader{}) assert.NoError(t, err) @@ -242,6 +249,15 @@ func TestCompile(t *testing.T) { wantOutbound: 0, // no match default wantIP: nil, }, + { + host: HostInfo{ + IPv4: net.ParseIP("223.1.1.1"), + }, + proto: ProtocolTCP, + port: 6883, + wantOutbound: ob6, // match range port rule 6881-6889 + wantIP: nil, + }, } for _, test := range tests { @@ -249,6 +265,23 @@ func TestCompile(t *testing.T) { assert.Equal(t, test.wantOutbound, gotOutbound) assert.Equal(t, test.wantIP, gotIP) } + + // Test Invalid Port Range Rule + eb1 := 1 + invalidRules := []TextRule{ + + { + Outbound: "eb1", + Address: "1.1.2.0/24", + ProtoPort: "*/3-1", + HijackAddress: "", + }, + } + + _, err = Compile[int](invalidRules, map[string]int{ + "eb1": eb1, + }, 100, &testGeoLoader{}) + assert.Error(t, err) } func Test_parseGeoSiteName(t *testing.T) { @@ -303,70 +336,3 @@ func Test_parseGeoSiteName(t *testing.T) { }) } } - -func TestCompileRangePort(t *testing.T) { - ob1, ob2, ob3, ob4 := 1, 2, 3, 4 - rules := []TextRule{ - { - Outbound: "ob1", - Address: "1.2.3.4", - ProtoPort: "tcp/6881-6889", - HijackAddress: "", - }, - { - Outbound: "ob2", - Address: "8.8.8.0/24", - ProtoPort: "udp/2525-3333", - HijackAddress: "1.1.1.1", - }, - { - Outbound: "ob3", - Address: "1.1.1.0/24", - ProtoPort: "*/1-65535", - HijackAddress: "", - }, - { - Outbound: "ob4", - Address: "1.1.1.0/24", - ProtoPort: "*/22", - HijackAddress: "", - }, - } - _, err := Compile[int](rules, map[string]int{ - "ob1": ob1, - "ob2": ob2, - "ob3": ob3, - "ob4": ob4, - }, 100, &testGeoLoader{}) - assert.NoError(t, err) - - ob11 := 1 - rules2 := []TextRule{ - { - Outbound: "ob11", - Address: "1.1.2.0/24", - ProtoPort: "*/3-1", // invalid range - HijackAddress: "", - }, - } - - _, err = Compile[int](rules2, map[string]int{ - "ob11": ob11, - }, 100, &testGeoLoader{}) - assert.Error(t, err) - - ob21 := 1 - rules3 := []TextRule{ - { - Outbound: "ob21", - Address: "1.1.2.0/24", - ProtoPort: "*/-114-514", // invalid range - HijackAddress: "", - }, - } - - _, err = Compile[int](rules3, map[string]int{ - "ob21": ob21, - }, 100, &testGeoLoader{}) - assert.Error(t, err) -} From b07b12a651af121be17c14548f4ca8f412d57892 Mon Sep 17 00:00:00 2001 From: Toby Date: Tue, 12 Mar 2024 20:26:13 -0700 Subject: [PATCH 020/161] chore: trivial fixes --- extras/outbounds/acl/compile.go | 4 ++-- extras/outbounds/acl/compile_test.go | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/extras/outbounds/acl/compile.go b/extras/outbounds/acl/compile.go index b93e80f..cdd0a94 100644 --- a/extras/outbounds/acl/compile.go +++ b/extras/outbounds/acl/compile.go @@ -102,8 +102,8 @@ type GeoLoader interface { // Compile compiles TextRules into a CompiledRuleSet. // Names in the outbounds map MUST be in all lower case. // We want on-demand loading of GeoIP/GeoSite databases, so instead of passing the -// databases directly, we use a GeoLoader interface to load them. This way, they are -// only loaded when there are at least one rule that uses them. +// databases directly, we use a GeoLoader interface to load them only when needed +// by at least one rule. func Compile[O Outbound](rules []TextRule, outbounds map[string]O, cacheSize int, geoLoader GeoLoader, ) (CompiledRuleSet[O], error) { diff --git a/extras/outbounds/acl/compile_test.go b/extras/outbounds/acl/compile_test.go index e12ad3d..a11c4e8 100644 --- a/extras/outbounds/acl/compile_test.go +++ b/extras/outbounds/acl/compile_test.go @@ -269,7 +269,6 @@ func TestCompile(t *testing.T) { // Test Invalid Port Range Rule eb1 := 1 invalidRules := []TextRule{ - { Outbound: "eb1", Address: "1.1.2.0/24", From 1f05791a4e9894dbd2c3a59683dd67065003ba26 Mon Sep 17 00:00:00 2001 From: Toby Date: Wed, 13 Mar 2024 19:36:32 -0700 Subject: [PATCH 021/161] chore(deps): bump google.golang.org/protobuf from 1.28.1 to 1.33.0 --- app/go.mod | 2 +- app/go.sum | 7 ++----- core/go.mod | 2 +- core/go.sum | 8 ++------ extras/go.mod | 2 +- extras/go.sum | 8 ++------ 6 files changed, 9 insertions(+), 20 deletions(-) diff --git a/app/go.mod b/app/go.mod index 57e7c76..b2449d1 100644 --- a/app/go.mod +++ b/app/go.mod @@ -53,7 +53,7 @@ require ( golang.org/x/sys v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.11.1 // indirect - google.golang.org/protobuf v1.28.1 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect rsc.io/qr v0.2.0 // indirect diff --git a/app/go.sum b/app/go.sum index aedcb4c..cd4f24f 100644 --- a/app/go.sum +++ b/app/go.sum @@ -102,7 +102,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -116,7 +115,6 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -571,9 +569,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= diff --git a/core/go.mod b/core/go.mod index 3268576..d9d3fd9 100644 --- a/core/go.mod +++ b/core/go.mod @@ -27,7 +27,7 @@ require ( golang.org/x/sys v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.11.1 // indirect - google.golang.org/protobuf v1.28.1 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/core/go.sum b/core/go.sum index fa8c8b8..b51f59b 100644 --- a/core/go.sum +++ b/core/go.sum @@ -11,10 +11,8 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= @@ -64,10 +62,8 @@ golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY= golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.11.1 h1:ojD5zOW8+7dOGzdnNgersm8aPfcDjhMp12UfG93NIMc= golang.org/x/tools v0.11.1/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/extras/go.mod b/extras/go.mod index 57a25e0..dbaccf5 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -11,7 +11,7 @@ require ( github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 golang.org/x/crypto v0.17.0 golang.org/x/net v0.17.0 - google.golang.org/protobuf v1.28.1 + google.golang.org/protobuf v1.33.0 ) require ( diff --git a/extras/go.sum b/extras/go.sum index a02e3a8..61762b3 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -12,10 +12,8 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= @@ -104,10 +102,8 @@ golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.11.1 h1:ojD5zOW8+7dOGzdnNgersm8aPfcDjhMp12UfG93NIMc= golang.org/x/tools v0.11.1/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 45593c02fc621a30baecbd6a727b0a490ebef443 Mon Sep 17 00:00:00 2001 From: Toby Date: Wed, 13 Mar 2024 19:39:55 -0700 Subject: [PATCH 022/161] fix: typo in PROTOCOL.md --- PROTOCOL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PROTOCOL.md b/PROTOCOL.md index 8ad16c2..835c2e9 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -66,7 +66,7 @@ After (and only after) a client passes authentication, the server MUST consider ### TCP -For each TCP connection, the client MUST create a new QUIC unidirectional stream and send the following TCPRequest message: +For each TCP connection, the client MUST create a new QUIC bidirectional stream and send the following TCPRequest message: ``` [varint] 0x401 (TCPRequest ID) From 57e6e47f190abbe41de7d4adf26b53f7cc811c86 Mon Sep 17 00:00:00 2001 From: kovacs Date: Thu, 14 Mar 2024 11:01:36 +0800 Subject: [PATCH 023/161] feat(acme): support acme listen host support acme listen host ref #978 Signed-off-by: kovacs --- app/cmd/server.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/cmd/server.go b/app/cmd/server.go index 0f289dc..5a770cc 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -83,6 +83,7 @@ type serverConfigACME struct { CA string `mapstructure:"ca"` DisableHTTP bool `mapstructure:"disableHTTP"` DisableTLSALPN bool `mapstructure:"disableTLSALPN"` + ListenHost string `mapstructure:"listenHost"` AltHTTPPort int `mapstructure:"altHTTPPort"` AltTLSALPNPort int `mapstructure:"altTLSALPNPort"` Dir string `mapstructure:"dir"` @@ -280,6 +281,7 @@ func (c *serverConfig) fillTLSConfig(hyConfig *server.Config) error { Agreed: true, DisableHTTPChallenge: c.ACME.DisableHTTP, DisableTLSALPNChallenge: c.ACME.DisableTLSALPN, + ListenHost: c.ACME.ListenHost, AltHTTPPort: c.ACME.AltHTTPPort, AltTLSALPNPort: c.ACME.AltTLSALPNPort, Logger: logger, From f10805dc135ea578e3bc4e5427f6e37b92791a20 Mon Sep 17 00:00:00 2001 From: Haruue Date: Tue, 19 Mar 2024 02:13:50 +0800 Subject: [PATCH 024/161] init: tun support with sing-tun --- app/cmd/client.go | 86 +++++++++++++++ app/go.mod | 18 +++- app/go.sum | 39 +++++-- app/internal/tun/log.go | 62 +++++++++++ app/internal/tun/server.go | 211 +++++++++++++++++++++++++++++++++++++ go.work.sum | 8 +- 6 files changed, 404 insertions(+), 20 deletions(-) create mode 100644 app/internal/tun/log.go create mode 100644 app/internal/tun/server.go diff --git a/app/cmd/client.go b/app/cmd/client.go index 9a785d2..ba92fc7 100644 --- a/app/cmd/client.go +++ b/app/cmd/client.go @@ -6,11 +6,14 @@ import ( "encoding/hex" "errors" "net" + "net/netip" "os" "strconv" "strings" "time" + "github.com/apernet/hysteria/app/internal/tun" + "github.com/spf13/cobra" "github.com/spf13/viper" "go.uber.org/zap" @@ -65,6 +68,7 @@ type clientConfig struct { TCPTProxy *tcpTProxyConfig `mapstructure:"tcpTProxy"` UDPTProxy *udpTProxyConfig `mapstructure:"udpTProxy"` TCPRedirect *tcpRedirectConfig `mapstructure:"tcpRedirect"` + TUN *tunConfig `mapstructure:"tun"` } type clientConfigTransportUDP struct { @@ -145,6 +149,14 @@ type tcpRedirectConfig struct { Listen string `mapstructure:"listen"` } +type tunConfig struct { + Name string `mapstructure:"name"` + MTU uint32 `mapstructure:"mtu"` + UDPTimeout int64 `mapstructure:"udpTimeout"` + Prefix4 string `mapstructure:"prefix4"` + Prefix6 string `mapstructure:"prefix6"` +} + func (c *clientConfig) fillServerAddr(hyConfig *client.Config) error { if c.Server == "" { return configError{Field: "server", Err: errors.New("server address is empty")} @@ -459,6 +471,11 @@ func runClient(cmd *cobra.Command, args []string) { return clientTCPRedirect(*config.TCPRedirect, c) }) } + if config.TUN != nil { + runner.Add("TUN", func() error { + return clientTUN(*config.TUN, c) + }) + } runner.Run() } @@ -656,6 +673,49 @@ func clientTCPRedirect(config tcpRedirectConfig, c client.Client) error { return p.ListenAndServe(laddr) } +func clientTUN(config tunConfig, c client.Client) error { + if config.Name == "" { + return configError{Field: "name", Err: errors.New("name is empty")} + } + if config.MTU == 0 { + config.MTU = 1500 + } + if config.UDPTimeout == 0 { + config.UDPTimeout = 300 + } + if config.Prefix4 == "" { + config.Prefix4 = "100.100.100.101/30" + } + prefix4, err := netip.ParsePrefix(config.Prefix4) + if err != nil { + return configError{Field: "prefix4", Err: err} + } + if config.Prefix6 == "" { + config.Prefix6 = "2001::ffff:ffff:ffff:fff0/127" + } + prefix6, err := netip.ParsePrefix(config.Prefix6) + if err != nil { + return configError{Field: "prefix6", Err: err} + } + server := &tun.Server{ + HyClient: c, + EventLogger: &tunLogger{}, + Logger: logger, + IfName: config.Name, + MTU: config.MTU, + UDPTimeout: config.UDPTimeout, + Inet4Address: []netip.Prefix{prefix4}, + Inet6Address: []netip.Prefix{prefix6}, + } + err = server.Start() + if err != nil { + return err + } + logger.Info("TUN listening", zap.String("interface", config.Name)) + // Block forever as sing-tun routine is running in the background + select {} +} + // parseServerAddrString parses server address string. // Server address can be in either "host:port" or "host" format (in which case we assume port 443). func parseServerAddrString(addrStr string) (host, port, hostPort string) { @@ -826,3 +886,29 @@ func (l *tcpRedirectLogger) Error(addr, reqAddr net.Addr, err error) { logger.Error("TCP redirect error", zap.String("addr", addr.String()), zap.String("reqAddr", reqAddr.String()), zap.Error(err)) } } + +type tunLogger struct{} + +func (l *tunLogger) TCPRequest(addr, reqAddr string) { + logger.Debug("TUN TCP request", zap.String("addr", addr), zap.String("reqAddr", reqAddr)) +} + +func (l *tunLogger) TCPError(addr, reqAddr string, err error) { + if err == nil { + logger.Debug("TUN TCP closed", zap.String("addr", addr), zap.String("reqAddr", reqAddr)) + } else { + logger.Error("TUN TCP error", zap.String("addr", addr), zap.String("reqAddr", reqAddr), zap.Error(err)) + } +} + +func (l *tunLogger) UDPRequest(addr string) { + logger.Debug("TUN UDP request", zap.String("addr", addr)) +} + +func (l *tunLogger) UDPError(addr string, err error) { + if err == nil { + logger.Debug("TUN UDP closed", zap.String("addr", addr)) + } else { + logger.Error("TUN UDP error", zap.String("addr", addr), zap.Error(err)) + } +} diff --git a/app/go.mod b/app/go.mod index b2449d1..83be31e 100644 --- a/app/go.mod +++ b/app/go.mod @@ -9,6 +9,8 @@ require ( github.com/caddyserver/certmagic v0.17.2 github.com/mdp/qrterminal/v3 v3.1.1 github.com/mholt/acmez v1.0.4 + github.com/sagernet/sing v0.3.2 + github.com/sagernet/sing-tun v0.2.4 github.com/spf13/cobra v1.7.0 github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.4 @@ -20,8 +22,10 @@ require ( github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d // indirect github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/google/btree v1.1.2 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect github.com/hashicorp/hcl v1.0.0 // indirect @@ -36,6 +40,9 @@ require ( github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect + github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e // indirect + github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect + github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect github.com/spf13/afero v1.9.3 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -43,15 +50,18 @@ require ( github.com/stretchr/objx v0.5.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect + github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/mock v0.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.17.0 // indirect + go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect + golang.org/x/crypto v0.19.0 // indirect golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.11.1 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/app/go.sum b/app/go.sum index cd4f24f..a7ea0f6 100644 --- a/app/go.sum +++ b/app/go.sum @@ -68,13 +68,15 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -106,6 +108,8 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -196,6 +200,16 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e h1:DOkjByVeAR56dkszjnMZke4wr7yM/1xHaJF3G9olkEE= +github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e/go.mod h1:fLxq/gtp0qzkaEwywlRRiGmjOK5ES/xUzyIKIFP2Asw= +github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= +github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= +github.com/sagernet/sing v0.3.2 h1:CwWcxUBPkMvwgfe2/zUgY5oHG9qOL8Aob/evIFYK9jo= +github.com/sagernet/sing v0.3.2/go.mod h1:qHySJ7u8po9DABtMYEkNBcOumx7ZZJf/fbv2sfTkNHE= +github.com/sagernet/sing-tun v0.2.4 h1:+ko31GRYoSrucgssbdaTKBBI37s+eeqE2gsV5cGgDgk= +github.com/sagernet/sing-tun v0.2.4/go.mod h1:GtKY1Sr2CCWLHPjVj9GZpBFZ/KoXOzVBSxXvmCCPxT4= +github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg= +github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s= github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= @@ -229,6 +243,8 @@ github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf h1:7PflaKRt github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf/go.mod h1:CLUSJbazqETbaR+i0YAhXBICV9TrKH93pziccMhmhpM= github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 h1:d/Wr/Vl/wiJHc3AHYbYs5I3PucJvRuw3SvbmlIRf+oM= github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301/go.mod h1:ntmMHL/xPq1WLeKiw8p/eRATaae6PiVRNipHFJxI8PM= +github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg= +github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -255,6 +271,8 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8 go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= +go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -263,8 +281,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -340,8 +358,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -382,6 +400,7 @@ golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -405,10 +424,10 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -425,6 +444,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= diff --git a/app/internal/tun/log.go b/app/internal/tun/log.go new file mode 100644 index 0000000..3afa7c4 --- /dev/null +++ b/app/internal/tun/log.go @@ -0,0 +1,62 @@ +package tun + +import ( + "github.com/sagernet/sing/common/logger" + "go.uber.org/zap" +) + +var _ logger.Logger = (*singLogger)(nil) + +type singLogger struct { + tag string + zapLogger *zap.Logger +} + +func (l *singLogger) Trace(args ...any) { + if l.zapLogger == nil { + return + } + l.zapLogger.Debug(l.tag, zap.Any("args", args)) +} + +func (l *singLogger) Debug(args ...any) { + if l.zapLogger == nil { + return + } + l.zapLogger.Debug(l.tag, zap.Any("args", args)) +} + +func (l *singLogger) Info(args ...any) { + if l.zapLogger == nil { + return + } + l.zapLogger.Info(l.tag, zap.Any("args", args)) +} + +func (l *singLogger) Warn(args ...any) { + if l.zapLogger == nil { + return + } + l.zapLogger.Warn(l.tag, zap.Any("args", args)) +} + +func (l *singLogger) Error(args ...any) { + if l.zapLogger == nil { + return + } + l.zapLogger.Error(l.tag, zap.Any("args", args)) +} + +func (l *singLogger) Fatal(args ...any) { + if l.zapLogger == nil { + return + } + l.zapLogger.Fatal(l.tag, zap.Any("args", args)) +} + +func (l *singLogger) Panic(args ...any) { + if l.zapLogger == nil { + return + } + l.zapLogger.Panic(l.tag, zap.Any("args", args)) +} diff --git a/app/internal/tun/server.go b/app/internal/tun/server.go new file mode 100644 index 0000000..000fbb4 --- /dev/null +++ b/app/internal/tun/server.go @@ -0,0 +1,211 @@ +package tun + +import ( + "context" + "errors" + "fmt" + "io" + "net" + "net/netip" + + "github.com/apernet/hysteria/core/client" + tun "github.com/sagernet/sing-tun" + "github.com/sagernet/sing/common/buf" + "github.com/sagernet/sing/common/metadata" + "github.com/sagernet/sing/common/network" + "go.uber.org/zap" +) + +type Server struct { + HyClient client.Client + EventLogger EventLogger + + // for debugging + Logger *zap.Logger + + IfName string + MTU uint32 + UDPTimeout int64 // in seconds + + // required by system stack + Inet4Address []netip.Prefix + Inet6Address []netip.Prefix + + tunIf tun.Tun + tunStack tun.Stack +} + +type EventLogger interface { + TCPRequest(addr, reqAddr string) + TCPError(addr, reqAddr string, err error) + UDPRequest(addr string) + UDPError(addr string, err error) +} + +func (s *Server) Start() error { + tunOpts := tun.Options{ + Name: s.IfName, + Inet4Address: s.Inet4Address, + Inet6Address: s.Inet6Address, + MTU: s.MTU, + GSO: true, + Logger: &singLogger{ + tag: "tun", + zapLogger: s.Logger, + }, + } + tunIf, err := tun.New(tunOpts) + if err != nil { + return fmt.Errorf("failed to create tun interface: %w", err) + } + s.tunIf = tunIf + + tunStack, err := tun.NewStack("system", tun.StackOptions{ + Context: context.Background(), + Tun: tunIf, + TunOptions: tunOpts, + UDPTimeout: s.UDPTimeout, + Handler: &tunHandler{s}, + Logger: &singLogger{ + tag: "tun-stack", + zapLogger: s.Logger, + }, + }) + if err != nil { + return fmt.Errorf("failed to create tun stack: %w", err) + } + s.tunStack = tunStack + err = tunStack.Start() + if err != nil { + return fmt.Errorf("failed to start tun stack: %w", err) + } + return nil +} + +func (s *Server) Close() error { + var ifErr, stackErr error + if s.tunIf != nil { + ifErr = s.tunIf.Close() + } + if s.tunStack != nil { + stackErr = s.tunStack.Close() + } + return errors.Join(ifErr, stackErr) +} + +type tunHandler struct { + *Server +} + +var _ tun.Handler = (*tunHandler)(nil) + +func (t *tunHandler) NewConnection(ctx context.Context, conn net.Conn, m metadata.Metadata) error { + addr := m.Source.String() + reqAddr := m.Destination.String() + if t.EventLogger != nil { + t.EventLogger.TCPRequest(addr, reqAddr) + } + var closeErr error + defer func() { + if t.EventLogger != nil { + t.EventLogger.TCPError(addr, reqAddr, closeErr) + } + }() + rc, err := t.HyClient.TCP(reqAddr) + if err != nil { + closeErr = err + // the returned err is ignored by caller + return nil + } + defer rc.Close() + + // start forwarding + copyErrChan := make(chan error, 3) + go func() { + <-ctx.Done() + copyErrChan <- ctx.Err() + }() + go func() { + _, copyErr := io.Copy(rc, conn) + copyErrChan <- copyErr + }() + go func() { + _, copyErr := io.Copy(conn, rc) + copyErrChan <- copyErr + }() + closeErr = <-copyErrChan + return nil +} + +func (t *tunHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, m metadata.Metadata) error { + addr := m.Source.String() + if t.EventLogger != nil { + t.EventLogger.UDPRequest(addr) + } + var closeErr error + defer func() { + if t.EventLogger != nil { + t.EventLogger.UDPError(addr, closeErr) + } + }() + rc, err := t.HyClient.UDP() + if err != nil { + closeErr = err + // the returned err is simply called into NewError again + return nil + } + defer rc.Close() + + // start forwarding + copyErrChan := make(chan error, 3) + go func() { + <-ctx.Done() + copyErrChan <- ctx.Err() + }() + // local <- remote + go func() { + for { + bs, from, err := rc.Receive() + if err != nil { + copyErrChan <- err + return + } + var fromAddr metadata.Socksaddr + if ap, perr := netip.ParseAddrPort(from); perr == nil { + fromAddr = metadata.SocksaddrFromNetIP(ap) + } else { + fromAddr.Fqdn = from + } + err = conn.WritePacket(buf.As(bs), fromAddr) + if err != nil { + copyErrChan <- err + return + } + } + }() + // local -> remote + go func() { + buffer := buf.NewPacket() + defer buffer.Release() + + for { + buffer.Reset() + addr, err := conn.ReadPacket(buffer) + if err != nil { + copyErrChan <- err + return + } + err = rc.Send(buffer.Bytes(), addr.String()) + if err != nil { + copyErrChan <- err + return + } + } + }() + closeErr = <-copyErrChan + return nil +} + +func (t *tunHandler) NewError(ctx context.Context, err error) { + // unused +} diff --git a/go.work.sum b/go.work.sum index ed76dd6..897e7c0 100644 --- a/go.work.sum +++ b/go.work.sum @@ -237,8 +237,6 @@ golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI= @@ -264,14 +262,11 @@ golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= @@ -281,9 +276,8 @@ golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 4aec8166b362e93371246df4fb584f71308ded94 Mon Sep 17 00:00:00 2001 From: Haruue Date: Tue, 19 Mar 2024 15:15:54 +0800 Subject: [PATCH 025/161] chore: switch to apernet sing-tun fork --- app/cmd/client.go | 7 +------ app/go.mod | 5 +---- app/go.sum | 12 ++++-------- app/internal/tun/log.go | 18 ++++++++++++++++++ app/internal/tun/server.go | 31 ++++++------------------------- go.work.sum | 9 +++++++++ 6 files changed, 39 insertions(+), 43 deletions(-) diff --git a/app/cmd/client.go b/app/cmd/client.go index ba92fc7..5c6868d 100644 --- a/app/cmd/client.go +++ b/app/cmd/client.go @@ -707,13 +707,8 @@ func clientTUN(config tunConfig, c client.Client) error { Inet4Address: []netip.Prefix{prefix4}, Inet6Address: []netip.Prefix{prefix6}, } - err = server.Start() - if err != nil { - return err - } logger.Info("TUN listening", zap.String("interface", config.Name)) - // Block forever as sing-tun routine is running in the background - select {} + return server.Serve() } // parseServerAddrString parses server address string. diff --git a/app/go.mod b/app/go.mod index 83be31e..d9f598e 100644 --- a/app/go.mod +++ b/app/go.mod @@ -6,11 +6,11 @@ require ( github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f github.com/apernet/hysteria/core v0.0.0-00010101000000-000000000000 github.com/apernet/hysteria/extras v0.0.0-00010101000000-000000000000 + github.com/apernet/sing-tun v0.2.5-0.20240319071017-94362fc6c4cd github.com/caddyserver/certmagic v0.17.2 github.com/mdp/qrterminal/v3 v3.1.1 github.com/mholt/acmez v1.0.4 github.com/sagernet/sing v0.3.2 - github.com/sagernet/sing-tun v0.2.4 github.com/spf13/cobra v1.7.0 github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.4 @@ -25,7 +25,6 @@ require ( github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect - github.com/google/btree v1.1.2 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect github.com/hashicorp/hcl v1.0.0 // indirect @@ -40,7 +39,6 @@ require ( github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e // indirect github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect github.com/spf13/afero v1.9.3 // indirect @@ -61,7 +59,6 @@ require ( golang.org/x/net v0.21.0 // indirect golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.11.1 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/app/go.sum b/app/go.sum index a7ea0f6..fe0b734 100644 --- a/app/go.sum +++ b/app/go.sum @@ -42,6 +42,10 @@ github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f h1:uVh0qpEslrWjg github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f/go.mod h1:xkkq9D4ygcldQQhKS/w9CadiCKwCngU7K9E3DaKahpM= github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d h1:K1DMSNtPcaZ/lihYmOHnjThNfUX7cD6SNuVRFnVLVmI= github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d/go.mod h1:4GInxO6ypy63J2NaO5rQx1wRp6K8YHI6zqLG+VswU6I= +github.com/apernet/sing-tun v0.2.5-0.20240319051541-59e6f4bd5d1e h1:yFDb2wKpWQmzi/c7FEFMgpJfOW36wldmOxAAkJkxOtA= +github.com/apernet/sing-tun v0.2.5-0.20240319051541-59e6f4bd5d1e/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= +github.com/apernet/sing-tun v0.2.5-0.20240319071017-94362fc6c4cd h1:2EOhAnrNARO2xt7doxlV8+6c6W0hi48hFHiqhlYqtDM= +github.com/apernet/sing-tun v0.2.5-0.20240319071017-94362fc6c4cd/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= @@ -108,8 +112,6 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= -github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -200,14 +202,10 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e h1:DOkjByVeAR56dkszjnMZke4wr7yM/1xHaJF3G9olkEE= -github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e/go.mod h1:fLxq/gtp0qzkaEwywlRRiGmjOK5ES/xUzyIKIFP2Asw= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/sing v0.3.2 h1:CwWcxUBPkMvwgfe2/zUgY5oHG9qOL8Aob/evIFYK9jo= github.com/sagernet/sing v0.3.2/go.mod h1:qHySJ7u8po9DABtMYEkNBcOumx7ZZJf/fbv2sfTkNHE= -github.com/sagernet/sing-tun v0.2.4 h1:+ko31GRYoSrucgssbdaTKBBI37s+eeqE2gsV5cGgDgk= -github.com/sagernet/sing-tun v0.2.4/go.mod h1:GtKY1Sr2CCWLHPjVj9GZpBFZ/KoXOzVBSxXvmCCPxT4= github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg= github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s= github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= @@ -444,8 +442,6 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= diff --git a/app/internal/tun/log.go b/app/internal/tun/log.go index 3afa7c4..b475f8f 100644 --- a/app/internal/tun/log.go +++ b/app/internal/tun/log.go @@ -1,6 +1,8 @@ package tun import ( + "runtime/debug" + "github.com/sagernet/sing/common/logger" "go.uber.org/zap" ) @@ -12,10 +14,19 @@ type singLogger struct { zapLogger *zap.Logger } +func extractSingExceptions(args []any) { + for i, arg := range args { + if err, ok := arg.(error); ok { + args[i] = err.Error() + } + } +} + func (l *singLogger) Trace(args ...any) { if l.zapLogger == nil { return } + extractSingExceptions(args) l.zapLogger.Debug(l.tag, zap.Any("args", args)) } @@ -23,6 +34,7 @@ func (l *singLogger) Debug(args ...any) { if l.zapLogger == nil { return } + extractSingExceptions(args) l.zapLogger.Debug(l.tag, zap.Any("args", args)) } @@ -30,6 +42,7 @@ func (l *singLogger) Info(args ...any) { if l.zapLogger == nil { return } + extractSingExceptions(args) l.zapLogger.Info(l.tag, zap.Any("args", args)) } @@ -37,6 +50,7 @@ func (l *singLogger) Warn(args ...any) { if l.zapLogger == nil { return } + extractSingExceptions(args) l.zapLogger.Warn(l.tag, zap.Any("args", args)) } @@ -44,13 +58,16 @@ func (l *singLogger) Error(args ...any) { if l.zapLogger == nil { return } + extractSingExceptions(args) l.zapLogger.Error(l.tag, zap.Any("args", args)) + debug.PrintStack() } func (l *singLogger) Fatal(args ...any) { if l.zapLogger == nil { return } + extractSingExceptions(args) l.zapLogger.Fatal(l.tag, zap.Any("args", args)) } @@ -58,5 +75,6 @@ func (l *singLogger) Panic(args ...any) { if l.zapLogger == nil { return } + extractSingExceptions(args) l.zapLogger.Panic(l.tag, zap.Any("args", args)) } diff --git a/app/internal/tun/server.go b/app/internal/tun/server.go index 000fbb4..452a42f 100644 --- a/app/internal/tun/server.go +++ b/app/internal/tun/server.go @@ -2,14 +2,13 @@ package tun import ( "context" - "errors" "fmt" "io" "net" "net/netip" "github.com/apernet/hysteria/core/client" - tun "github.com/sagernet/sing-tun" + tun "github.com/apernet/sing-tun" "github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/metadata" "github.com/sagernet/sing/common/network" @@ -30,9 +29,6 @@ type Server struct { // required by system stack Inet4Address []netip.Prefix Inet6Address []netip.Prefix - - tunIf tun.Tun - tunStack tun.Stack } type EventLogger interface { @@ -42,7 +38,7 @@ type EventLogger interface { UDPError(addr string, err error) } -func (s *Server) Start() error { +func (s *Server) Serve() error { tunOpts := tun.Options{ Name: s.IfName, Inet4Address: s.Inet4Address, @@ -58,9 +54,9 @@ func (s *Server) Start() error { if err != nil { return fmt.Errorf("failed to create tun interface: %w", err) } - s.tunIf = tunIf + defer tunIf.Close() - tunStack, err := tun.NewStack("system", tun.StackOptions{ + tunStack, err := tun.NewSystem(tun.StackOptions{ Context: context.Background(), Tun: tunIf, TunOptions: tunOpts, @@ -74,23 +70,8 @@ func (s *Server) Start() error { if err != nil { return fmt.Errorf("failed to create tun stack: %w", err) } - s.tunStack = tunStack - err = tunStack.Start() - if err != nil { - return fmt.Errorf("failed to start tun stack: %w", err) - } - return nil -} - -func (s *Server) Close() error { - var ifErr, stackErr error - if s.tunIf != nil { - ifErr = s.tunIf.Close() - } - if s.tunStack != nil { - stackErr = s.tunStack.Close() - } - return errors.Join(ifErr, stackErr) + defer tunStack.Close() + return tunStack.(tun.StackRunner).Run() } type tunHandler struct { diff --git a/go.work.sum b/go.work.sum index 897e7c0..62f146c 100644 --- a/go.work.sum +++ b/go.work.sum @@ -26,6 +26,8 @@ dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999 h1:OR8VhtwhcAI3U48/rzBsVOuHi0zDPzYI1xASVcdSgR8= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= @@ -53,6 +55,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHH github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415 h1:q1oJaUPdmpDm/VyXosjgPgr6wS7c5iV2p0PwJD73bUI= +github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad h1:EmNYJhPYy0pOFjCx2PrgtaBXmee0iUX9hLlxE1xHOJE= github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= @@ -237,6 +241,8 @@ golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= +golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a h1:Jw5wfR+h9mnIYH+OtGT2im5wV1YGGDora5vTv/aa5bE= +golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI= @@ -276,6 +282,7 @@ golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -318,6 +325,8 @@ grpc.go4.org v0.0.0-20170609214715-11d0a25b4919 h1:tmXTu+dfa+d9Evp8NpJdgOy6+rt8/ grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= +honnef.co/go/tools v0.4.5 h1:YGD4H+SuIOOqsyoLOpZDWcieM28W47/zRO7f+9V3nvo= +honnef.co/go/tools v0.4.5/go.mod h1:GUV+uIBCLpdf0/v6UhHHG/yzI/z6qPskBeQCjcNB96k= rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= From 0cde4f405fe517c132f130fcc72f839f94af08c3 Mon Sep 17 00:00:00 2001 From: Haruue Date: Tue, 19 Mar 2024 15:48:57 +0800 Subject: [PATCH 026/161] feat(tun): use time.Duration for timeout config matches timeout config of other inbounds --- app/cmd/client.go | 17 +++++++++-------- app/internal/tun/server.go | 8 ++++---- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/app/cmd/client.go b/app/cmd/client.go index 5c6868d..dca5ab0 100644 --- a/app/cmd/client.go +++ b/app/cmd/client.go @@ -150,11 +150,11 @@ type tcpRedirectConfig struct { } type tunConfig struct { - Name string `mapstructure:"name"` - MTU uint32 `mapstructure:"mtu"` - UDPTimeout int64 `mapstructure:"udpTimeout"` - Prefix4 string `mapstructure:"prefix4"` - Prefix6 string `mapstructure:"prefix6"` + Name string `mapstructure:"name"` + MTU uint32 `mapstructure:"mtu"` + Timeout time.Duration `mapstructure:"timeout"` + Prefix4 string `mapstructure:"prefix4"` + Prefix6 string `mapstructure:"prefix6"` } func (c *clientConfig) fillServerAddr(hyConfig *client.Config) error { @@ -680,8 +680,9 @@ func clientTUN(config tunConfig, c client.Client) error { if config.MTU == 0 { config.MTU = 1500 } - if config.UDPTimeout == 0 { - config.UDPTimeout = 300 + timeout := int64(config.Timeout.Seconds()) + if timeout == 0 { + timeout = 300 } if config.Prefix4 == "" { config.Prefix4 = "100.100.100.101/30" @@ -703,7 +704,7 @@ func clientTUN(config tunConfig, c client.Client) error { Logger: logger, IfName: config.Name, MTU: config.MTU, - UDPTimeout: config.UDPTimeout, + Timeout: timeout, Inet4Address: []netip.Prefix{prefix4}, Inet6Address: []netip.Prefix{prefix6}, } diff --git a/app/internal/tun/server.go b/app/internal/tun/server.go index 452a42f..f1816e1 100644 --- a/app/internal/tun/server.go +++ b/app/internal/tun/server.go @@ -22,9 +22,9 @@ type Server struct { // for debugging Logger *zap.Logger - IfName string - MTU uint32 - UDPTimeout int64 // in seconds + IfName string + MTU uint32 + Timeout int64 // in seconds, also applied to TCP in system stack // required by system stack Inet4Address []netip.Prefix @@ -60,7 +60,7 @@ func (s *Server) Serve() error { Context: context.Background(), Tun: tunIf, TunOptions: tunOpts, - UDPTimeout: s.UDPTimeout, + UDPTimeout: s.Timeout, Handler: &tunHandler{s}, Logger: &singLogger{ tag: "tun-stack", From 38d9248acd54f65f7fb11c7e2f910537beb62af3 Mon Sep 17 00:00:00 2001 From: Haruue Date: Tue, 19 Mar 2024 16:56:26 +0800 Subject: [PATCH 027/161] rm(tun): debug.PrintStack() in logger --- app/internal/tun/log.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/internal/tun/log.go b/app/internal/tun/log.go index b475f8f..b30309d 100644 --- a/app/internal/tun/log.go +++ b/app/internal/tun/log.go @@ -1,8 +1,6 @@ package tun import ( - "runtime/debug" - "github.com/sagernet/sing/common/logger" "go.uber.org/zap" ) @@ -60,7 +58,6 @@ func (l *singLogger) Error(args ...any) { } extractSingExceptions(args) l.zapLogger.Error(l.tag, zap.Any("args", args)) - debug.PrintStack() } func (l *singLogger) Fatal(args ...any) { From 92ed8f5e6a66d7a83fb10f627f79ca10e2414266 Mon Sep 17 00:00:00 2001 From: Haruue Date: Tue, 19 Mar 2024 19:38:43 +0800 Subject: [PATCH 028/161] chore(tun): enable ForwarderBindInterface --- app/internal/tun/server.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/app/internal/tun/server.go b/app/internal/tun/server.go index f1816e1..5953647 100644 --- a/app/internal/tun/server.go +++ b/app/internal/tun/server.go @@ -7,6 +7,8 @@ import ( "net" "net/netip" + "github.com/sagernet/sing/common/control" + "github.com/apernet/hysteria/core/client" tun "github.com/apernet/sing-tun" "github.com/sagernet/sing/common/buf" @@ -66,6 +68,8 @@ func (s *Server) Serve() error { tag: "tun-stack", zapLogger: s.Logger, }, + ForwarderBindInterface: true, + InterfaceFinder: &interfaceFinder{}, }) if err != nil { return fmt.Errorf("failed to create tun stack: %w", err) @@ -190,3 +194,23 @@ func (t *tunHandler) NewPacketConnection(ctx context.Context, conn network.Packe func (t *tunHandler) NewError(ctx context.Context, err error) { // unused } + +type interfaceFinder struct{} + +var _ control.InterfaceFinder = (*interfaceFinder)(nil) + +func (f *interfaceFinder) InterfaceIndexByName(name string) (int, error) { + ifce, err := net.InterfaceByName(name) + if err != nil { + return -1, err + } + return ifce.Index, nil +} + +func (f *interfaceFinder) InterfaceNameByIndex(index int) (string, error) { + ifce, err := net.InterfaceByIndex(index) + if err != nil { + return "", err + } + return ifce.Name, nil +} From 91406ab0f9c4c004dc9cc3ecaefc8defbba6db46 Mon Sep 17 00:00:00 2001 From: Haruue Date: Tue, 19 Mar 2024 20:34:42 +0800 Subject: [PATCH 029/161] chore(tun): use /126 length in default prefix6 --- app/cmd/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/cmd/client.go b/app/cmd/client.go index dca5ab0..755032e 100644 --- a/app/cmd/client.go +++ b/app/cmd/client.go @@ -692,7 +692,7 @@ func clientTUN(config tunConfig, c client.Client) error { return configError{Field: "prefix4", Err: err} } if config.Prefix6 == "" { - config.Prefix6 = "2001::ffff:ffff:ffff:fff0/127" + config.Prefix6 = "2001::ffff:ffff:ffff:fff1/126" } prefix6, err := netip.ParsePrefix(config.Prefix6) if err != nil { From 2e93c12cdc384d9195a1dd5050e676f912b40555 Mon Sep 17 00:00:00 2001 From: Haruue Date: Wed, 20 Mar 2024 13:45:12 +0800 Subject: [PATCH 030/161] feat(tun): export sing-tun auto route config --- app/cmd/client.go | 63 ++++++++++++++++++++++++++++++++------ app/go.mod | 2 +- app/go.sum | 2 ++ app/internal/tun/server.go | 24 ++++++++++++--- 4 files changed, 75 insertions(+), 16 deletions(-) diff --git a/app/cmd/client.go b/app/cmd/client.go index 755032e..42c5a3a 100644 --- a/app/cmd/client.go +++ b/app/cmd/client.go @@ -5,6 +5,7 @@ import ( "crypto/x509" "encoding/hex" "errors" + "fmt" "net" "net/netip" "os" @@ -153,8 +154,17 @@ type tunConfig struct { Name string `mapstructure:"name"` MTU uint32 `mapstructure:"mtu"` Timeout time.Duration `mapstructure:"timeout"` - Prefix4 string `mapstructure:"prefix4"` - Prefix6 string `mapstructure:"prefix6"` + Address struct { + IPv4 string `mapstructure:"ipv4"` + IPv6 string `mapstructure:"ipv6"` + } `mapstructure:"address"` + Route *struct { + Strict bool `mapstructure:"strict"` + IPv4 []string `mapstructure:"ipv4"` + IPv6 []string `mapstructure:"ipv6"` + IPv4Exclude []string `mapstructure:"ipv4Exclude"` + IPv6Exclude []string `mapstructure:"ipv6Exclude"` + } `mapstructure:"route"` } func (c *clientConfig) fillServerAddr(hyConfig *client.Config) error { @@ -684,19 +694,19 @@ func clientTUN(config tunConfig, c client.Client) error { if timeout == 0 { timeout = 300 } - if config.Prefix4 == "" { - config.Prefix4 = "100.100.100.101/30" + if config.Address.IPv4 == "" { + config.Address.IPv4 = "100.100.100.101/30" } - prefix4, err := netip.ParsePrefix(config.Prefix4) + prefix4, err := netip.ParsePrefix(config.Address.IPv4) if err != nil { - return configError{Field: "prefix4", Err: err} + return configError{Field: "address.ipv4", Err: err} } - if config.Prefix6 == "" { - config.Prefix6 = "2001::ffff:ffff:ffff:fff1/126" + if config.Address.IPv6 == "" { + config.Address.IPv6 = "2001::ffff:ffff:ffff:fff1/126" } - prefix6, err := netip.ParsePrefix(config.Prefix6) + prefix6, err := netip.ParsePrefix(config.Address.IPv6) if err != nil { - return configError{Field: "prefix6", Err: err} + return configError{Field: "address.ipv6", Err: err} } server := &tun.Server{ HyClient: c, @@ -708,6 +718,39 @@ func clientTUN(config tunConfig, c client.Client) error { Inet4Address: []netip.Prefix{prefix4}, Inet6Address: []netip.Prefix{prefix6}, } + if config.Route != nil { + server.AutoRoute = true + server.StructRoute = config.Route.Strict + + parsePrefixes := func(field string, s []string) ([]netip.Prefix, error) { + var prefixes []netip.Prefix + for i, s := range s { + p, err := netip.ParsePrefix(s) + if err != nil { + return nil, configError{Field: fmt.Sprintf("%s[%d]", field, i), Err: err} + } + prefixes = append(prefixes, p) + } + return prefixes, nil + } + + server.Inet4RouteAddress, err = parsePrefixes("route.ipv4", config.Route.IPv4) + if err != nil { + return err + } + server.Inet6RouteAddress, err = parsePrefixes("route.ipv6", config.Route.IPv6) + if err != nil { + return err + } + server.Inet4RouteExcludeAddress, err = parsePrefixes("route.ipv4Exclude", config.Route.IPv4Exclude) + if err != nil { + return err + } + server.Inet6RouteExcludeAddress, err = parsePrefixes("route.ipv6Exclude", config.Route.IPv6Exclude) + if err != nil { + return err + } + } logger.Info("TUN listening", zap.String("interface", config.Name)) return server.Serve() } diff --git a/app/go.mod b/app/go.mod index d9f598e..8beb77b 100644 --- a/app/go.mod +++ b/app/go.mod @@ -6,7 +6,7 @@ require ( github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f github.com/apernet/hysteria/core v0.0.0-00010101000000-000000000000 github.com/apernet/hysteria/extras v0.0.0-00010101000000-000000000000 - github.com/apernet/sing-tun v0.2.5-0.20240319071017-94362fc6c4cd + github.com/apernet/sing-tun v0.2.5-0.20240320024814-b1e6f9d4930f github.com/caddyserver/certmagic v0.17.2 github.com/mdp/qrterminal/v3 v3.1.1 github.com/mholt/acmez v1.0.4 diff --git a/app/go.sum b/app/go.sum index fe0b734..9f92233 100644 --- a/app/go.sum +++ b/app/go.sum @@ -46,6 +46,8 @@ github.com/apernet/sing-tun v0.2.5-0.20240319051541-59e6f4bd5d1e h1:yFDb2wKpWQmz github.com/apernet/sing-tun v0.2.5-0.20240319051541-59e6f4bd5d1e/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= github.com/apernet/sing-tun v0.2.5-0.20240319071017-94362fc6c4cd h1:2EOhAnrNARO2xt7doxlV8+6c6W0hi48hFHiqhlYqtDM= github.com/apernet/sing-tun v0.2.5-0.20240319071017-94362fc6c4cd/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= +github.com/apernet/sing-tun v0.2.5-0.20240320024814-b1e6f9d4930f h1:IK/7rFSnRRLXCjcuWreyPzfPEfZd2Xz9v2+RsFoodbs= +github.com/apernet/sing-tun v0.2.5-0.20240320024814-b1e6f9d4930f/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= diff --git a/app/internal/tun/server.go b/app/internal/tun/server.go index 5953647..03ddc56 100644 --- a/app/internal/tun/server.go +++ b/app/internal/tun/server.go @@ -31,6 +31,14 @@ type Server struct { // required by system stack Inet4Address []netip.Prefix Inet6Address []netip.Prefix + + // auto route + AutoRoute bool + StructRoute bool + Inet4RouteAddress []netip.Prefix + Inet6RouteAddress []netip.Prefix + Inet4RouteExcludeAddress []netip.Prefix + Inet6RouteExcludeAddress []netip.Prefix } type EventLogger interface { @@ -42,11 +50,17 @@ type EventLogger interface { func (s *Server) Serve() error { tunOpts := tun.Options{ - Name: s.IfName, - Inet4Address: s.Inet4Address, - Inet6Address: s.Inet6Address, - MTU: s.MTU, - GSO: true, + Name: s.IfName, + Inet4Address: s.Inet4Address, + Inet6Address: s.Inet6Address, + MTU: s.MTU, + GSO: true, + AutoRoute: s.AutoRoute, + StrictRoute: s.StructRoute, + Inet4RouteAddress: s.Inet4RouteAddress, + Inet6RouteAddress: s.Inet6RouteAddress, + Inet4RouteExcludeAddress: s.Inet4RouteExcludeAddress, + Inet6RouteExcludeAddress: s.Inet6RouteExcludeAddress, Logger: &singLogger{ tag: "tun", zapLogger: s.Logger, From b287020daa691adad2b2e4915b87c04aef3f6d7d Mon Sep 17 00:00:00 2001 From: Haruue Date: Wed, 20 Mar 2024 22:09:03 +0800 Subject: [PATCH 031/161] chore(tun): show error on unsupported platform --- app/cmd/client.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/cmd/client.go b/app/cmd/client.go index 42c5a3a..79afd59 100644 --- a/app/cmd/client.go +++ b/app/cmd/client.go @@ -9,6 +9,8 @@ import ( "net" "net/netip" "os" + "runtime" + "slices" "strconv" "strings" "time" @@ -684,6 +686,10 @@ func clientTCPRedirect(config tcpRedirectConfig, c client.Client) error { } func clientTUN(config tunConfig, c client.Client) error { + supportedPlatforms := []string{"linux", "darwin", "windows", "android"} + if !slices.Contains(supportedPlatforms, runtime.GOOS) { + logger.Error("TUN is not supported on this platform", zap.String("platform", runtime.GOOS)) + } if config.Name == "" { return configError{Field: "name", Err: errors.New("name is empty")} } From 87bbf17bc5e7a6a4b54270bb02ce25b48514ea12 Mon Sep 17 00:00:00 2001 From: Toby Date: Fri, 22 Mar 2024 13:32:24 -0700 Subject: [PATCH 032/161] chore: go mod tidy --- app/go.sum | 4 ---- core/go.mod | 6 +++--- core/go.sum | 12 ++++++------ extras/go.mod | 6 +++--- extras/go.sum | 12 ++++++------ 5 files changed, 18 insertions(+), 22 deletions(-) diff --git a/app/go.sum b/app/go.sum index 9f92233..56696cc 100644 --- a/app/go.sum +++ b/app/go.sum @@ -42,10 +42,6 @@ github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f h1:uVh0qpEslrWjg github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f/go.mod h1:xkkq9D4ygcldQQhKS/w9CadiCKwCngU7K9E3DaKahpM= github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d h1:K1DMSNtPcaZ/lihYmOHnjThNfUX7cD6SNuVRFnVLVmI= github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d/go.mod h1:4GInxO6ypy63J2NaO5rQx1wRp6K8YHI6zqLG+VswU6I= -github.com/apernet/sing-tun v0.2.5-0.20240319051541-59e6f4bd5d1e h1:yFDb2wKpWQmzi/c7FEFMgpJfOW36wldmOxAAkJkxOtA= -github.com/apernet/sing-tun v0.2.5-0.20240319051541-59e6f4bd5d1e/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= -github.com/apernet/sing-tun v0.2.5-0.20240319071017-94362fc6c4cd h1:2EOhAnrNARO2xt7doxlV8+6c6W0hi48hFHiqhlYqtDM= -github.com/apernet/sing-tun v0.2.5-0.20240319071017-94362fc6c4cd/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= github.com/apernet/sing-tun v0.2.5-0.20240320024814-b1e6f9d4930f h1:IK/7rFSnRRLXCjcuWreyPzfPEfZd2Xz9v2+RsFoodbs= github.com/apernet/sing-tun v0.2.5-0.20240320024814-b1e6f9d4930f/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= diff --git a/core/go.mod b/core/go.mod index d9d3fd9..76bc927 100644 --- a/core/go.mod +++ b/core/go.mod @@ -21,10 +21,10 @@ require ( github.com/quic-go/qpack v0.4.0 // indirect github.com/stretchr/objx v0.5.0 // indirect go.uber.org/mock v0.3.0 // indirect - golang.org/x/crypto v0.17.0 // indirect + golang.org/x/crypto v0.19.0 // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.11.1 // indirect google.golang.org/protobuf v1.33.0 // indirect diff --git a/core/go.sum b/core/go.sum index b51f59b..7b21427 100644 --- a/core/go.sum +++ b/core/go.sum @@ -45,17 +45,17 @@ go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY= diff --git a/extras/go.mod b/extras/go.mod index dbaccf5..3bb0dcd 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -9,8 +9,8 @@ require ( github.com/miekg/dns v1.1.55 github.com/stretchr/testify v1.8.4 github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 - golang.org/x/crypto v0.17.0 - golang.org/x/net v0.17.0 + golang.org/x/crypto v0.19.0 + golang.org/x/net v0.21.0 google.golang.org/protobuf v1.33.0 ) @@ -28,7 +28,7 @@ require ( go.uber.org/mock v0.3.0 // indirect golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.11.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/extras/go.sum b/extras/go.sum index 61762b3..c635709 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -58,8 +58,8 @@ go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -70,8 +70,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -84,8 +84,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= From dc8fe45a1a450a8bf35c273f1503d5af6c648497 Mon Sep 17 00:00:00 2001 From: Toby Date: Fri, 22 Mar 2024 15:50:58 -0700 Subject: [PATCH 033/161] chore: adjust imports --- app/cmd/client.go | 3 +-- app/internal/tun/server.go | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/cmd/client.go b/app/cmd/client.go index 79afd59..ce7b2ba 100644 --- a/app/cmd/client.go +++ b/app/cmd/client.go @@ -15,8 +15,6 @@ import ( "strings" "time" - "github.com/apernet/hysteria/app/internal/tun" - "github.com/spf13/cobra" "github.com/spf13/viper" "go.uber.org/zap" @@ -26,6 +24,7 @@ import ( "github.com/apernet/hysteria/app/internal/redirect" "github.com/apernet/hysteria/app/internal/socks5" "github.com/apernet/hysteria/app/internal/tproxy" + "github.com/apernet/hysteria/app/internal/tun" "github.com/apernet/hysteria/app/internal/url" "github.com/apernet/hysteria/app/internal/utils" "github.com/apernet/hysteria/core/client" diff --git a/app/internal/tun/server.go b/app/internal/tun/server.go index 03ddc56..4ae5393 100644 --- a/app/internal/tun/server.go +++ b/app/internal/tun/server.go @@ -7,14 +7,14 @@ import ( "net" "net/netip" - "github.com/sagernet/sing/common/control" - - "github.com/apernet/hysteria/core/client" tun "github.com/apernet/sing-tun" "github.com/sagernet/sing/common/buf" + "github.com/sagernet/sing/common/control" "github.com/sagernet/sing/common/metadata" "github.com/sagernet/sing/common/network" "go.uber.org/zap" + + "github.com/apernet/hysteria/core/client" ) type Server struct { From aab104ae2e282ae1ee20fff2367596d731dd8eda Mon Sep 17 00:00:00 2001 From: Toby Date: Fri, 22 Mar 2024 16:20:03 -0700 Subject: [PATCH 034/161] feat: update config test --- app/cmd/client_test.go | 22 ++++++++++++++++++++++ app/cmd/client_test.yaml | 14 ++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/app/cmd/client_test.go b/app/cmd/client_test.go index faee251..c586949 100644 --- a/app/cmd/client_test.go +++ b/app/cmd/client_test.go @@ -88,6 +88,28 @@ func TestClientConfig(t *testing.T) { TCPRedirect: &tcpRedirectConfig{ Listen: "127.0.0.1:3500", }, + TUN: &tunConfig{ + Name: "hytun", + MTU: 1500, + Timeout: 60 * time.Second, + Address: struct { + IPv4 string `mapstructure:"ipv4"` + IPv6 string `mapstructure:"ipv6"` + }{IPv4: "100.100.100.101/30", IPv6: "2001::ffff:ffff:ffff:fff1/126"}, + Route: &struct { + Strict bool `mapstructure:"strict"` + IPv4 []string `mapstructure:"ipv4"` + IPv6 []string `mapstructure:"ipv6"` + IPv4Exclude []string `mapstructure:"ipv4Exclude"` + IPv6Exclude []string `mapstructure:"ipv6Exclude"` + }{ + Strict: true, + IPv4: []string{"0.0.0.0/0"}, + IPv6: []string{"2000::/3"}, + IPv4Exclude: []string{"192.0.2.1/32"}, + IPv6Exclude: []string{"2001:db8::1/128"}, + }, + }, }) } diff --git a/app/cmd/client_test.yaml b/app/cmd/client_test.yaml index 3e2f4aa..4f919df 100644 --- a/app/cmd/client_test.yaml +++ b/app/cmd/client_test.yaml @@ -65,3 +65,17 @@ udpTProxy: tcpRedirect: listen: 127.0.0.1:3500 + +tun: + name: "hytun" + mtu: 1500 + timeout: 1m + address: + ipv4: 100.100.100.101/30 + ipv6: 2001::ffff:ffff:ffff:fff1/126 + route: + strict: true + ipv4: [0.0.0.0/0] + ipv6: ["2000::/3"] + ipv4Exclude: [192.0.2.1/32] + ipv6Exclude: ["2001:db8::1/128"] From 6374ea11c4e706d585a369c9f069e41e0e0b792e Mon Sep 17 00:00:00 2001 From: Haruue Date: Sat, 23 Mar 2024 11:13:43 +0800 Subject: [PATCH 035/161] feat(tun): allow omit pfxlen in full len pfx route --- app/cmd/client.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/app/cmd/client.go b/app/cmd/client.go index ce7b2ba..e3cf756 100644 --- a/app/cmd/client.go +++ b/app/cmd/client.go @@ -727,12 +727,22 @@ func clientTUN(config tunConfig, c client.Client) error { server.AutoRoute = true server.StructRoute = config.Route.Strict - parsePrefixes := func(field string, s []string) ([]netip.Prefix, error) { + parsePrefixes := func(field string, ss []string) ([]netip.Prefix, error) { var prefixes []netip.Prefix - for i, s := range s { - p, err := netip.ParsePrefix(s) - if err != nil { - return nil, configError{Field: fmt.Sprintf("%s[%d]", field, i), Err: err} + for i, s := range ss { + var p netip.Prefix + if strings.Contains(s, "/") { + var err error + p, err = netip.ParsePrefix(s) + if err != nil { + return nil, configError{Field: fmt.Sprintf("%s[%d]", field, i), Err: err} + } + } else { + pa, err := netip.ParseAddr(s) + if err != nil { + return nil, configError{Field: fmt.Sprintf("%s[%d]", field, i), Err: err} + } + p = netip.PrefixFrom(pa, pa.BitLen()) } prefixes = append(prefixes, p) } From ddb5b511fcc5686a0be5698807be575764cbd148 Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Sat, 23 Mar 2024 14:17:25 +0800 Subject: [PATCH 036/161] Optimize the log level adjustment for tcpError and udpError by shifting from error to warning. --- app/cmd/server.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/cmd/server.go b/app/cmd/server.go index 5a770cc..9b6665e 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -815,7 +815,7 @@ func (l *serverLogger) TCPError(addr net.Addr, id, reqAddr string, err error) { if err == nil { logger.Debug("TCP closed", zap.String("addr", addr.String()), zap.String("id", id), zap.String("reqAddr", reqAddr)) } else { - logger.Error("TCP error", zap.String("addr", addr.String()), zap.String("id", id), zap.String("reqAddr", reqAddr), zap.Error(err)) + logger.Warn("TCP error", zap.String("addr", addr.String()), zap.String("id", id), zap.String("reqAddr", reqAddr), zap.Error(err)) } } @@ -827,7 +827,7 @@ func (l *serverLogger) UDPError(addr net.Addr, id string, sessionID uint32, err if err == nil { logger.Debug("UDP closed", zap.String("addr", addr.String()), zap.String("id", id), zap.Uint32("sessionID", sessionID)) } else { - logger.Error("UDP error", zap.String("addr", addr.String()), zap.String("id", id), zap.Uint32("sessionID", sessionID), zap.Error(err)) + logger.Warn("UDP error", zap.String("addr", addr.String()), zap.String("id", id), zap.Uint32("sessionID", sessionID), zap.Error(err)) } } From 9dfb5808e00a87acee654b9634cf65ef1ec9cd3b Mon Sep 17 00:00:00 2001 From: Haruue Date: Sat, 23 Mar 2024 21:13:09 +0800 Subject: [PATCH 037/161] chore(deps): bump sing-tun from v0.2.4 to v0.2.5 --- app/go.mod | 2 +- app/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/go.mod b/app/go.mod index 8beb77b..6b5d2c3 100644 --- a/app/go.mod +++ b/app/go.mod @@ -6,7 +6,7 @@ require ( github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f github.com/apernet/hysteria/core v0.0.0-00010101000000-000000000000 github.com/apernet/hysteria/extras v0.0.0-00010101000000-000000000000 - github.com/apernet/sing-tun v0.2.5-0.20240320024814-b1e6f9d4930f + github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad github.com/caddyserver/certmagic v0.17.2 github.com/mdp/qrterminal/v3 v3.1.1 github.com/mholt/acmez v1.0.4 diff --git a/app/go.sum b/app/go.sum index 56696cc..152e338 100644 --- a/app/go.sum +++ b/app/go.sum @@ -42,8 +42,8 @@ github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f h1:uVh0qpEslrWjg github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f/go.mod h1:xkkq9D4ygcldQQhKS/w9CadiCKwCngU7K9E3DaKahpM= github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d h1:K1DMSNtPcaZ/lihYmOHnjThNfUX7cD6SNuVRFnVLVmI= github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d/go.mod h1:4GInxO6ypy63J2NaO5rQx1wRp6K8YHI6zqLG+VswU6I= -github.com/apernet/sing-tun v0.2.5-0.20240320024814-b1e6f9d4930f h1:IK/7rFSnRRLXCjcuWreyPzfPEfZd2Xz9v2+RsFoodbs= -github.com/apernet/sing-tun v0.2.5-0.20240320024814-b1e6f9d4930f/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= +github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad h1:QzQ2sKpc9o42HNRR8ukM5uMC/RzR2HgZd/Nvaqol2C0= +github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= From 2d7d67bf27e511c2687feaa0c7e458133deb7097 Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 23 Mar 2024 10:58:11 -0700 Subject: [PATCH 038/161] feat: also change client side errors to warns --- app/cmd/client.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/cmd/client.go b/app/cmd/client.go index e3cf756..c1d04bd 100644 --- a/app/cmd/client.go +++ b/app/cmd/client.go @@ -829,7 +829,7 @@ func (l *socks5Logger) TCPError(addr net.Addr, reqAddr string, err error) { if err == nil { logger.Debug("SOCKS5 TCP closed", zap.String("addr", addr.String()), zap.String("reqAddr", reqAddr)) } else { - logger.Error("SOCKS5 TCP error", zap.String("addr", addr.String()), zap.String("reqAddr", reqAddr), zap.Error(err)) + logger.Warn("SOCKS5 TCP error", zap.String("addr", addr.String()), zap.String("reqAddr", reqAddr), zap.Error(err)) } } @@ -841,7 +841,7 @@ func (l *socks5Logger) UDPError(addr net.Addr, err error) { if err == nil { logger.Debug("SOCKS5 UDP closed", zap.String("addr", addr.String())) } else { - logger.Error("SOCKS5 UDP error", zap.String("addr", addr.String()), zap.Error(err)) + logger.Warn("SOCKS5 UDP error", zap.String("addr", addr.String()), zap.Error(err)) } } @@ -855,7 +855,7 @@ func (l *httpLogger) ConnectError(addr net.Addr, reqAddr string, err error) { if err == nil { logger.Debug("HTTP CONNECT closed", zap.String("addr", addr.String()), zap.String("reqAddr", reqAddr)) } else { - logger.Error("HTTP CONNECT error", zap.String("addr", addr.String()), zap.String("reqAddr", reqAddr), zap.Error(err)) + logger.Warn("HTTP CONNECT error", zap.String("addr", addr.String()), zap.String("reqAddr", reqAddr), zap.Error(err)) } } @@ -867,7 +867,7 @@ func (l *httpLogger) HTTPError(addr net.Addr, reqURL string, err error) { if err == nil { logger.Debug("HTTP closed", zap.String("addr", addr.String()), zap.String("reqURL", reqURL)) } else { - logger.Error("HTTP error", zap.String("addr", addr.String()), zap.String("reqURL", reqURL), zap.Error(err)) + logger.Warn("HTTP error", zap.String("addr", addr.String()), zap.String("reqURL", reqURL), zap.Error(err)) } } @@ -881,7 +881,7 @@ func (l *tcpLogger) Error(addr net.Addr, err error) { if err == nil { logger.Debug("TCP forwarding closed", zap.String("addr", addr.String())) } else { - logger.Error("TCP forwarding error", zap.String("addr", addr.String()), zap.Error(err)) + logger.Warn("TCP forwarding error", zap.String("addr", addr.String()), zap.Error(err)) } } @@ -895,7 +895,7 @@ func (l *udpLogger) Error(addr net.Addr, err error) { if err == nil { logger.Debug("UDP forwarding closed", zap.String("addr", addr.String())) } else { - logger.Error("UDP forwarding error", zap.String("addr", addr.String()), zap.Error(err)) + logger.Warn("UDP forwarding error", zap.String("addr", addr.String()), zap.Error(err)) } } @@ -909,7 +909,7 @@ func (l *tcpTProxyLogger) Error(addr, reqAddr net.Addr, err error) { if err == nil { logger.Debug("TCP transparent proxy closed", zap.String("addr", addr.String()), zap.String("reqAddr", reqAddr.String())) } else { - logger.Error("TCP transparent proxy error", zap.String("addr", addr.String()), zap.String("reqAddr", reqAddr.String()), zap.Error(err)) + logger.Warn("TCP transparent proxy error", zap.String("addr", addr.String()), zap.String("reqAddr", reqAddr.String()), zap.Error(err)) } } @@ -923,7 +923,7 @@ func (l *udpTProxyLogger) Error(addr, reqAddr net.Addr, err error) { if err == nil { logger.Debug("UDP transparent proxy closed", zap.String("addr", addr.String()), zap.String("reqAddr", reqAddr.String())) } else { - logger.Error("UDP transparent proxy error", zap.String("addr", addr.String()), zap.String("reqAddr", reqAddr.String()), zap.Error(err)) + logger.Warn("UDP transparent proxy error", zap.String("addr", addr.String()), zap.String("reqAddr", reqAddr.String()), zap.Error(err)) } } @@ -937,7 +937,7 @@ func (l *tcpRedirectLogger) Error(addr, reqAddr net.Addr, err error) { if err == nil { logger.Debug("TCP redirect closed", zap.String("addr", addr.String()), zap.String("reqAddr", reqAddr.String())) } else { - logger.Error("TCP redirect error", zap.String("addr", addr.String()), zap.String("reqAddr", reqAddr.String()), zap.Error(err)) + logger.Warn("TCP redirect error", zap.String("addr", addr.String()), zap.String("reqAddr", reqAddr.String()), zap.Error(err)) } } @@ -951,7 +951,7 @@ func (l *tunLogger) TCPError(addr, reqAddr string, err error) { if err == nil { logger.Debug("TUN TCP closed", zap.String("addr", addr), zap.String("reqAddr", reqAddr)) } else { - logger.Error("TUN TCP error", zap.String("addr", addr), zap.String("reqAddr", reqAddr), zap.Error(err)) + logger.Warn("TUN TCP error", zap.String("addr", addr), zap.String("reqAddr", reqAddr), zap.Error(err)) } } @@ -963,6 +963,6 @@ func (l *tunLogger) UDPError(addr string, err error) { if err == nil { logger.Debug("TUN UDP closed", zap.String("addr", addr)) } else { - logger.Error("TUN UDP error", zap.String("addr", addr), zap.Error(err)) + logger.Warn("TUN UDP error", zap.String("addr", addr), zap.Error(err)) } } From 89a99a08bfd7dc54ed9876fce72af32b56c10024 Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 23 Mar 2024 11:17:51 -0700 Subject: [PATCH 039/161] fix: flaky tests caused by occasionally closing channel multiple times --- core/internal/integration_tests/close_test.go | 7 +++++-- core/internal/integration_tests/trafficlogger_test.go | 11 +++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/core/internal/integration_tests/close_test.go b/core/internal/integration_tests/close_test.go index 4160b3c..ab6d016 100644 --- a/core/internal/integration_tests/close_test.go +++ b/core/internal/integration_tests/close_test.go @@ -2,6 +2,7 @@ package integration_tests import ( "io" + "sync" "testing" "time" @@ -48,13 +49,14 @@ func TestClientServerTCPClose(t *testing.T) { // Server outbound connection should write the same thing, then close. sobConn := mocks.NewMockConn(t) sobConnCh := make(chan struct{}) // For close signal only + sobConnChCloseFunc := sync.OnceFunc(func() { close(sobConnCh) }) sobConn.EXPECT().Read(mock.Anything).RunAndReturn(func(bs []byte) (int, error) { <-sobConnCh return 0, io.EOF }) sobConn.EXPECT().Write([]byte("happy")).Return(5, nil) sobConn.EXPECT().Close().RunAndReturn(func() error { - close(sobConnCh) + sobConnChCloseFunc() return nil }) serverOb.EXPECT().TCP(addr).Return(sobConn, nil).Once() @@ -133,6 +135,7 @@ func TestClientServerUDPIdleTimeout(t *testing.T) { // to trigger the server's UDP idle timeout. sobConn := mocks.NewMockUDPConn(t) sobConnCh := make(chan []byte, 1) + sobConnChCloseFunc := sync.OnceFunc(func() { close(sobConnCh) }) sobConn.EXPECT().ReadFrom(mock.Anything).RunAndReturn(func(bs []byte) (int, string, error) { d := <-sobConnCh if d == nil { @@ -167,7 +170,7 @@ func TestClientServerUDPIdleTimeout(t *testing.T) { } // Now we wait for 3 seconds, the server should close the UDP session. sobConn.EXPECT().Close().RunAndReturn(func() error { - close(sobConnCh) + sobConnChCloseFunc() return nil }) eventLogger.EXPECT().UDPError(mock.Anything, mock.Anything, uint32(1), nil).Once() diff --git a/core/internal/integration_tests/trafficlogger_test.go b/core/internal/integration_tests/trafficlogger_test.go index ff1d66e..abbea8c 100644 --- a/core/internal/integration_tests/trafficlogger_test.go +++ b/core/internal/integration_tests/trafficlogger_test.go @@ -2,6 +2,7 @@ package integration_tests import ( "io" + "sync" "testing" "time" @@ -46,6 +47,7 @@ func TestClientServerTrafficLoggerTCP(t *testing.T) { sobConn := mocks.NewMockConn(t) sobConnCh := make(chan []byte, 1) + sobConnChCloseFunc := sync.OnceFunc(func() { close(sobConnCh) }) sobConn.EXPECT().Read(mock.Anything).RunAndReturn(func(bs []byte) (int, error) { b := <-sobConnCh if b == nil { @@ -55,9 +57,9 @@ func TestClientServerTrafficLoggerTCP(t *testing.T) { } }) sobConn.EXPECT().Close().RunAndReturn(func() error { - close(sobConnCh) + sobConnChCloseFunc() return nil - }).Once() + }) serverOb.EXPECT().TCP(addr).Return(sobConn, nil).Once() conn, err := c.TCP(addr) @@ -125,6 +127,7 @@ func TestClientServerTrafficLoggerUDP(t *testing.T) { sobConn := mocks.NewMockUDPConn(t) sobConnCh := make(chan []byte, 1) + sobConnChCloseFunc := sync.OnceFunc(func() { close(sobConnCh) }) sobConn.EXPECT().ReadFrom(mock.Anything).RunAndReturn(func(bs []byte) (int, string, error) { b := <-sobConnCh if b == nil { @@ -134,9 +137,9 @@ func TestClientServerTrafficLoggerUDP(t *testing.T) { } }) sobConn.EXPECT().Close().RunAndReturn(func() error { - close(sobConnCh) + sobConnChCloseFunc() return nil - }).Once() + }) serverOb.EXPECT().UDP(addr).Return(sobConn, nil).Once() conn, err := c.UDP() From 7aa0becd8477d11055cfae107e2d1d1b363aedd4 Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 23 Mar 2024 15:05:10 -0700 Subject: [PATCH 040/161] feat: quic-go v0.42.0 --- app/go.mod | 4 ++-- app/go.sum | 10 ++++++---- core/go.mod | 6 +++--- core/go.sum | 12 ++++++------ extras/go.mod | 4 ++-- extras/go.sum | 10 ++++++---- 6 files changed, 25 insertions(+), 21 deletions(-) diff --git a/app/go.mod b/app/go.mod index 6b5d2c3..a5025c1 100644 --- a/app/go.mod +++ b/app/go.mod @@ -19,7 +19,7 @@ require ( ) require ( - github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d // indirect + github.com/apernet/quic-go v0.42.1-0.20240323215309-32a339817822 // indirect github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -50,7 +50,7 @@ require ( github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect go.uber.org/atomic v1.11.0 // indirect - go.uber.org/mock v0.3.0 // indirect + go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect golang.org/x/crypto v0.19.0 // indirect diff --git a/app/go.sum b/app/go.sum index 152e338..4f2b0fe 100644 --- a/app/go.sum +++ b/app/go.sum @@ -40,8 +40,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f h1:uVh0qpEslrWjgzx9vOcyCqsOY3c9kofDZ1n+qaw35ZY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f/go.mod h1:xkkq9D4ygcldQQhKS/w9CadiCKwCngU7K9E3DaKahpM= -github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d h1:K1DMSNtPcaZ/lihYmOHnjThNfUX7cD6SNuVRFnVLVmI= -github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d/go.mod h1:4GInxO6ypy63J2NaO5rQx1wRp6K8YHI6zqLG+VswU6I= +github.com/apernet/quic-go v0.42.1-0.20240323215309-32a339817822 h1:+ZSzRxSMg1+fLTQKcIUvD2cCCgS+1rtyRhs+NL5oBgA= +github.com/apernet/quic-go v0.42.1-0.20240323215309-32a339817822/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad h1:QzQ2sKpc9o42HNRR8ukM5uMC/RzR2HgZd/Nvaqol2C0= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= @@ -259,8 +259,8 @@ go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= -go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= -go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= @@ -440,6 +440,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= diff --git a/core/go.mod b/core/go.mod index 76bc927..91d4cb9 100644 --- a/core/go.mod +++ b/core/go.mod @@ -3,11 +3,11 @@ module github.com/apernet/hysteria/core go 1.21 require ( - github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d + github.com/apernet/quic-go v0.42.1-0.20240323215309-32a339817822 github.com/stretchr/testify v1.8.4 go.uber.org/goleak v1.2.1 golang.org/x/exp v0.0.0-20221205204356-47842c84f3db - golang.org/x/time v0.4.0 + golang.org/x/time v0.5.0 ) require ( @@ -20,7 +20,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect github.com/stretchr/objx v0.5.0 // indirect - go.uber.org/mock v0.3.0 // indirect + go.uber.org/mock v0.4.0 // indirect golang.org/x/crypto v0.19.0 // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.21.0 // indirect diff --git a/core/go.sum b/core/go.sum index 7b21427..908b18e 100644 --- a/core/go.sum +++ b/core/go.sum @@ -1,5 +1,5 @@ -github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d h1:K1DMSNtPcaZ/lihYmOHnjThNfUX7cD6SNuVRFnVLVmI= -github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d/go.mod h1:4GInxO6ypy63J2NaO5rQx1wRp6K8YHI6zqLG+VswU6I= +github.com/apernet/quic-go v0.42.1-0.20240323215309-32a339817822 h1:+ZSzRxSMg1+fLTQKcIUvD2cCCgS+1rtyRhs+NL5oBgA= +github.com/apernet/quic-go v0.42.1-0.20240323215309-32a339817822/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -43,8 +43,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= -go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= -go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= @@ -58,8 +58,8 @@ golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY= -golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.11.1 h1:ojD5zOW8+7dOGzdnNgersm8aPfcDjhMp12UfG93NIMc= golang.org/x/tools v0.11.1/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= diff --git a/extras/go.mod b/extras/go.mod index 3bb0dcd..bc21044 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -15,7 +15,7 @@ require ( ) require ( - github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d // indirect + github.com/apernet/quic-go v0.42.1-0.20240323215309-32a339817822 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect @@ -25,7 +25,7 @@ require ( github.com/quic-go/qpack v0.4.0 // indirect github.com/stretchr/objx v0.5.0 // indirect github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect - go.uber.org/mock v0.3.0 // indirect + go.uber.org/mock v0.4.0 // indirect golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/sys v0.17.0 // indirect diff --git a/extras/go.sum b/extras/go.sum index c635709..390cc97 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -1,5 +1,5 @@ -github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d h1:K1DMSNtPcaZ/lihYmOHnjThNfUX7cD6SNuVRFnVLVmI= -github.com/apernet/quic-go v0.41.1-0.20240301003057-e18162de481d/go.mod h1:4GInxO6ypy63J2NaO5rQx1wRp6K8YHI6zqLG+VswU6I= +github.com/apernet/quic-go v0.42.1-0.20240323215309-32a339817822 h1:+ZSzRxSMg1+fLTQKcIUvD2cCCgS+1rtyRhs+NL5oBgA= +github.com/apernet/quic-go v0.42.1-0.20240323215309-32a339817822/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -54,8 +54,8 @@ github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301/go.mod h1:ntmMHL github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= -go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= -go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= @@ -95,6 +95,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= From 02fa2cde0ac8f054ac162268492deb13d117c62d Mon Sep 17 00:00:00 2001 From: xmapst Date: Mon, 25 Mar 2024 10:15:11 +0800 Subject: [PATCH 041/161] =?UTF-8?q?=E5=A2=9E=E5=BC=BA:=20HTTP/SOCKS5?= =?UTF-8?q?=E6=B7=B7=E5=90=88=E7=AB=AF=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/cmd/client.go | 54 ++++++++++++++ app/internal/proxymux/mux.go | 141 +++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 app/internal/proxymux/mux.go diff --git a/app/cmd/client.go b/app/cmd/client.go index c1d04bd..b818569 100644 --- a/app/cmd/client.go +++ b/app/cmd/client.go @@ -21,6 +21,7 @@ import ( "github.com/apernet/hysteria/app/internal/forwarding" "github.com/apernet/hysteria/app/internal/http" + "github.com/apernet/hysteria/app/internal/proxymux" "github.com/apernet/hysteria/app/internal/redirect" "github.com/apernet/hysteria/app/internal/socks5" "github.com/apernet/hysteria/app/internal/tproxy" @@ -63,6 +64,7 @@ type clientConfig struct { Bandwidth clientConfigBandwidth `mapstructure:"bandwidth"` FastOpen bool `mapstructure:"fastOpen"` Lazy bool `mapstructure:"lazy"` + Mixed *mixedConfig `mapstructure:"mixed"` SOCKS5 *socks5Config `mapstructure:"socks5"` HTTP *httpConfig `mapstructure:"http"` TCPForwarding []tcpForwardingEntry `mapstructure:"tcpForwarding"` @@ -113,6 +115,14 @@ type clientConfigBandwidth struct { Down string `mapstructure:"down"` } +type mixedConfig struct { + Listen string `mapstructure:"listen"` + Username string `mapstructure:"username"` + Password string `mapstructure:"password"` + DisableUDP bool `mapstructure:"disableUDP"` + Realm string `mapstructure:"realm"` +} + type socks5Config struct { Listen string `mapstructure:"listen"` Username string `mapstructure:"username"` @@ -447,6 +457,11 @@ func runClient(cmd *cobra.Command, args []string) { // Register modes var runner clientModeRunner + if config.Mixed != nil { + runner.Add("Mixed server", func() error { + return clientMixed(*config.Mixed, c) + }) + } if config.SOCKS5 != nil { runner.Add("SOCKS5 server", func() error { return clientSOCKS5(*config.SOCKS5, c) @@ -527,6 +542,45 @@ func (r *clientModeRunner) Run() { } } +func clientMixed(config mixedConfig, c client.Client) error { + if config.Listen == "" { + return configError{Field: "listen", Err: errors.New("listen address is empty")} + } + l, err := correctnet.Listen("tcp", config.Listen) + if err != nil { + return configError{Field: "listen", Err: err} + } + var authFunc func(username, password string) bool + username, password := config.Username, config.Password + if username != "" && password != "" { + authFunc = func(u, p string) bool { + return u == username && p == password + } + } + s := socks5.Server{ + HyClient: c, + AuthFunc: authFunc, + DisableUDP: config.DisableUDP, + EventLogger: &socks5Logger{}, + } + logger.Info("SOCKS5 server listening", zap.String("addr", config.Listen)) + + h := http.Server{ + HyClient: c, + AuthFunc: authFunc, + AuthRealm: config.Realm, + EventLogger: &httpLogger{}, + } + logger.Info("HTTP proxy server listening", zap.String("addr", config.Listen)) + socks5Ln, httpLn := proxymux.SplitSOCKSAndHTTP(l) + go func() { + if err = h.Serve(httpLn); err != nil { + logger.Fatal(err.Error()) + } + }() + return s.Serve(socks5Ln) +} + func clientSOCKS5(config socks5Config, c client.Client) error { if config.Listen == "" { return configError{Field: "listen", Err: errors.New("listen address is empty")} diff --git a/app/internal/proxymux/mux.go b/app/internal/proxymux/mux.go new file mode 100644 index 0000000..d99b122 --- /dev/null +++ b/app/internal/proxymux/mux.go @@ -0,0 +1,141 @@ +// Package proxymux splits a net.Listener in two, routing SOCKS5 +// connections to one and HTTP requests to the other. +// +// It allows for hosting both a SOCKS5 proxy and an HTTP proxy on the +// same listener. +package proxymux + +import ( + "io" + "net" + "sync" + "time" +) + +// SplitSOCKSAndHTTP accepts connections on ln and passes connections +// through to either socksListener or httpListener, depending the +// first byte sent by the client. +func SplitSOCKSAndHTTP(ln net.Listener) (socksListener, httpListener net.Listener) { + sl := &listener{ + addr: ln.Addr(), + c: make(chan net.Conn), + closed: make(chan struct{}), + } + hl := &listener{ + addr: ln.Addr(), + c: make(chan net.Conn), + closed: make(chan struct{}), + } + + go splitSOCKSAndHTTPListener(ln, sl, hl) + + return sl, hl +} + +func splitSOCKSAndHTTPListener(ln net.Listener, sl, hl *listener) { + for { + conn, err := ln.Accept() + if err != nil { + sl.Close() + hl.Close() + return + } + go routeConn(conn, sl, hl) + } +} + +func routeConn(c net.Conn, socksListener, httpListener *listener) { + if err := c.SetReadDeadline(time.Now().Add(15 * time.Second)); err != nil { + c.Close() + return + } + + var b [1]byte + if _, err := io.ReadFull(c, b[:]); err != nil { + c.Close() + return + } + + if err := c.SetReadDeadline(time.Time{}); err != nil { + c.Close() + return + } + + conn := &connWithOneByte{ + Conn: c, + b: b[0], + } + + // First byte of a SOCKS5 session is a version byte set to 5. + var ln *listener + if b[0] == 5 { + ln = socksListener + } else { + ln = httpListener + } + select { + case ln.c <- conn: + case <-ln.closed: + c.Close() + } +} + +type listener struct { + addr net.Addr + c chan net.Conn + mu sync.Mutex // serializes close() on closed. It's okay to receive on closed without locking. + closed chan struct{} +} + +func (ln *listener) Accept() (net.Conn, error) { + // Once closed, reliably stay closed, don't race with attempts at + // further connections. + select { + case <-ln.closed: + return nil, net.ErrClosed + default: + } + select { + case ret := <-ln.c: + return ret, nil + case <-ln.closed: + return nil, net.ErrClosed + } +} + +func (ln *listener) Close() error { + ln.mu.Lock() + defer ln.mu.Unlock() + select { + case <-ln.closed: + // Already closed + default: + close(ln.closed) + } + return nil +} + +func (ln *listener) Addr() net.Addr { + return ln.addr +} + +// connWithOneByte is a net.Conn that returns b for the first read +// request, then forwards everything else to Conn. +type connWithOneByte struct { + net.Conn + + b byte + bRead bool +} + +func (c *connWithOneByte) Read(bs []byte) (int, error) { + if c.bRead { + return c.Conn.Read(bs) + } + if len(bs) == 0 { + return 0, nil + } + c.bRead = true + bs[0] = c.b + return 1, nil +} From de7d7dc51ea04b5a09151d60de6e8d7e279975da Mon Sep 17 00:00:00 2001 From: xmapst Date: Mon, 25 Mar 2024 10:17:08 +0800 Subject: [PATCH 042/161] =?UTF-8?q?=E5=A2=9E=E5=BC=BA:=20HTTP/SOCKS5?= =?UTF-8?q?=E6=B7=B7=E5=90=88=E7=AB=AF=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/cmd/client_test.go | 7 ++ app/cmd/client_test.yaml | 169 ++++++++++++++++++++------------------- 2 files changed, 95 insertions(+), 81 deletions(-) diff --git a/app/cmd/client_test.go b/app/cmd/client_test.go index c586949..4773114 100644 --- a/app/cmd/client_test.go +++ b/app/cmd/client_test.go @@ -53,6 +53,13 @@ func TestClientConfig(t *testing.T) { }, FastOpen: true, Lazy: true, + Mixed: &mixedConfig{ + Listen: "127.0.0.1:1080", + Username: "anon", + Password: "bro", + DisableUDP: true, + Realm: "martian", + }, SOCKS5: &socks5Config{ Listen: "127.0.0.1:1080", Username: "anon", diff --git a/app/cmd/client_test.yaml b/app/cmd/client_test.yaml index 4f919df..7058871 100644 --- a/app/cmd/client_test.yaml +++ b/app/cmd/client_test.yaml @@ -1,81 +1,88 @@ -server: example.com - -auth: weak_ahh_password - -transport: - type: udp - udp: - hopInterval: 30s - -obfs: - type: salamander - salamander: - password: cry_me_a_r1ver - -tls: - sni: another.example.com - insecure: true - pinSHA256: 114515DEADBEEF - ca: custom_ca.crt - -quic: - initStreamReceiveWindow: 1145141 - maxStreamReceiveWindow: 1145142 - initConnReceiveWindow: 1145143 - maxConnReceiveWindow: 1145144 - maxIdleTimeout: 10s - keepAlivePeriod: 4s - disablePathMTUDiscovery: true - -bandwidth: - up: 200 mbps - down: 1 gbps - -fastOpen: true - -lazy: true - -socks5: - listen: 127.0.0.1:1080 - username: anon - password: bro - disableUDP: true - -http: - listen: 127.0.0.1:8080 - username: qqq - password: bruh - realm: martian - -tcpForwarding: - - listen: 127.0.0.1:8088 - remote: internal.example.com:80 - -udpForwarding: - - listen: 127.0.0.1:5353 - remote: internal.example.com:53 - timeout: 50s - -tcpTProxy: - listen: 127.0.0.1:2500 - -udpTProxy: - listen: 127.0.0.1:2501 - timeout: 20s - -tcpRedirect: - listen: 127.0.0.1:3500 - -tun: - name: "hytun" - mtu: 1500 - timeout: 1m - address: - ipv4: 100.100.100.101/30 - ipv6: 2001::ffff:ffff:ffff:fff1/126 - route: - strict: true - ipv4: [0.0.0.0/0] - ipv6: ["2000::/3"] - ipv4Exclude: [192.0.2.1/32] - ipv6Exclude: ["2001:db8::1/128"] +server: example.com + +auth: weak_ahh_password + +transport: + type: udp + udp: + hopInterval: 30s + +obfs: + type: salamander + salamander: + password: cry_me_a_r1ver + +tls: + sni: another.example.com + insecure: true + pinSHA256: 114515DEADBEEF + ca: custom_ca.crt + +quic: + initStreamReceiveWindow: 1145141 + maxStreamReceiveWindow: 1145142 + initConnReceiveWindow: 1145143 + maxConnReceiveWindow: 1145144 + maxIdleTimeout: 10s + keepAlivePeriod: 4s + disablePathMTUDiscovery: true + +bandwidth: + up: 200 mbps + down: 1 gbps + +fastOpen: true + +lazy: true + +mixed: + listen: 127.0.0.1:1080 + username: anon + password: bro + disableUDP: true + realm: martian + +socks5: + listen: 127.0.0.1:1080 + username: anon + password: bro + disableUDP: true + +http: + listen: 127.0.0.1:8080 + username: qqq + password: bruh + realm: martian + +tcpForwarding: + - listen: 127.0.0.1:8088 + remote: internal.example.com:80 + +udpForwarding: + - listen: 127.0.0.1:5353 + remote: internal.example.com:53 + timeout: 50s + +tcpTProxy: + listen: 127.0.0.1:2500 + +udpTProxy: + listen: 127.0.0.1:2501 + timeout: 20s + +tcpRedirect: + listen: 127.0.0.1:3500 + +tun: + name: "hytun" + mtu: 1500 + timeout: 1m + address: + ipv4: 100.100.100.101/30 + ipv6: 2001::ffff:ffff:ffff:fff1/126 + route: + strict: true + ipv4: [0.0.0.0/0] + ipv6: ["2000::/3"] + ipv4Exclude: [192.0.2.1/32] + ipv6Exclude: ["2001:db8::1/128"] From d34ff757c3084d2406ff0b71ff12793bcdeb52c5 Mon Sep 17 00:00:00 2001 From: Haruue Date: Mon, 25 Mar 2024 11:06:09 +0800 Subject: [PATCH 043/161] chore: dos2unix client_test.yaml NOTE: squash this commit with previous one before merge --- app/cmd/client_test.yaml | 176 +++++++++++++++++++-------------------- 1 file changed, 88 insertions(+), 88 deletions(-) diff --git a/app/cmd/client_test.yaml b/app/cmd/client_test.yaml index 7058871..eb9ea39 100644 --- a/app/cmd/client_test.yaml +++ b/app/cmd/client_test.yaml @@ -1,88 +1,88 @@ -server: example.com - -auth: weak_ahh_password - -transport: - type: udp - udp: - hopInterval: 30s - -obfs: - type: salamander - salamander: - password: cry_me_a_r1ver - -tls: - sni: another.example.com - insecure: true - pinSHA256: 114515DEADBEEF - ca: custom_ca.crt - -quic: - initStreamReceiveWindow: 1145141 - maxStreamReceiveWindow: 1145142 - initConnReceiveWindow: 1145143 - maxConnReceiveWindow: 1145144 - maxIdleTimeout: 10s - keepAlivePeriod: 4s - disablePathMTUDiscovery: true - -bandwidth: - up: 200 mbps - down: 1 gbps - -fastOpen: true - -lazy: true - -mixed: - listen: 127.0.0.1:1080 - username: anon - password: bro - disableUDP: true - realm: martian - -socks5: - listen: 127.0.0.1:1080 - username: anon - password: bro - disableUDP: true - -http: - listen: 127.0.0.1:8080 - username: qqq - password: bruh - realm: martian - -tcpForwarding: - - listen: 127.0.0.1:8088 - remote: internal.example.com:80 - -udpForwarding: - - listen: 127.0.0.1:5353 - remote: internal.example.com:53 - timeout: 50s - -tcpTProxy: - listen: 127.0.0.1:2500 - -udpTProxy: - listen: 127.0.0.1:2501 - timeout: 20s - -tcpRedirect: - listen: 127.0.0.1:3500 - -tun: - name: "hytun" - mtu: 1500 - timeout: 1m - address: - ipv4: 100.100.100.101/30 - ipv6: 2001::ffff:ffff:ffff:fff1/126 - route: - strict: true - ipv4: [0.0.0.0/0] - ipv6: ["2000::/3"] - ipv4Exclude: [192.0.2.1/32] - ipv6Exclude: ["2001:db8::1/128"] +server: example.com + +auth: weak_ahh_password + +transport: + type: udp + udp: + hopInterval: 30s + +obfs: + type: salamander + salamander: + password: cry_me_a_r1ver + +tls: + sni: another.example.com + insecure: true + pinSHA256: 114515DEADBEEF + ca: custom_ca.crt + +quic: + initStreamReceiveWindow: 1145141 + maxStreamReceiveWindow: 1145142 + initConnReceiveWindow: 1145143 + maxConnReceiveWindow: 1145144 + maxIdleTimeout: 10s + keepAlivePeriod: 4s + disablePathMTUDiscovery: true + +bandwidth: + up: 200 mbps + down: 1 gbps + +fastOpen: true + +lazy: true + +mixed: + listen: 127.0.0.1:1080 + username: anon + password: bro + disableUDP: true + realm: martian + +socks5: + listen: 127.0.0.1:1080 + username: anon + password: bro + disableUDP: true + +http: + listen: 127.0.0.1:8080 + username: qqq + password: bruh + realm: martian + +tcpForwarding: + - listen: 127.0.0.1:8088 + remote: internal.example.com:80 + +udpForwarding: + - listen: 127.0.0.1:5353 + remote: internal.example.com:53 + timeout: 50s + +tcpTProxy: + listen: 127.0.0.1:2500 + +udpTProxy: + listen: 127.0.0.1:2501 + timeout: 20s + +tcpRedirect: + listen: 127.0.0.1:3500 + +tun: + name: "hytun" + mtu: 1500 + timeout: 1m + address: + ipv4: 100.100.100.101/30 + ipv6: 2001::ffff:ffff:ffff:fff1/126 + route: + strict: true + ipv4: [0.0.0.0/0] + ipv6: ["2000::/3"] + ipv4Exclude: [192.0.2.1/32] + ipv6Exclude: ["2001:db8::1/128"] From 2cb06620759cd88a1a2a1ef9148a6526522afabe Mon Sep 17 00:00:00 2001 From: Toby Date: Mon, 25 Mar 2024 18:45:28 -0700 Subject: [PATCH 044/161] chore: update README --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index f6c2171..f8521af 100644 --- a/README.md +++ b/README.md @@ -23,23 +23,23 @@
-

🛠️ Packed to the gills

-

Expansive range of modes including SOCKS5, HTTP proxy, TCP/UDP forwarding, Linux TProxy - not to mention additional features continually being added.

+

🛠️ Jack of all trades

+

Wide range of modes including SOCKS5, HTTP Proxy, TCP/UDP Forwarding, Linux TProxy, TUN - with more features being added constantly.

-

⚡ Lightning fast

-

Powered by a custom QUIC protocol, Hysteria delivers unparalleled performance over even the most unreliable and lossy networks.

+

⚡ Blazing fast

+

Powered by a customized QUIC protocol, Hysteria is designed to deliver unparalleled performance over unreliable and lossy networks.

✊ Censorship resistant

-

Our protocol is designed to masquerade as standard HTTP/3 traffic, making it very difficult to detect and block without widespread collateral damage.

+

The protocol masquerades as standard HTTP/3 traffic, making it very difficult for censors to detect and block without widespread collateral damage.

💻 Cross-platform

-

We have builds for all major platforms and architectures. Deploy anywhere & use everywhere.

+

We have builds for every major platform and architecture. Deploy anywhere & use everywhere. Not to mention the long list of 3rd party apps.

@@ -48,8 +48,8 @@
-

🤗 Open standards

-

We have well-documented specifications and code for developers to contribute and build their own apps.

+

🤗 Cards on the table

+

We have well-documented specifications and code for developers to contribute and build their own apps. And a helpful community, too.

From 3de65357d43c439f239f93b79b4ba0e9a656308e Mon Sep 17 00:00:00 2001 From: Haruue Date: Sat, 30 Mar 2024 22:31:27 +0800 Subject: [PATCH 045/161] chore(scripts): run apt update on apt based distro Running apt update to avoid "package not found" error on some pre-installed Ubuntu / Debian. I have tested that other supported Linux distributions do not need this. dnf/yum/zypper: update metadata regularly by default, and checked when installing any package pacman: run with -Sy so metadata is always updated cherry-picked from: apernet/tcp-brutal@efcc08b936dec95a7f4645bb82d501784b21b156 --- scripts/install_server.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/install_server.sh b/scripts/install_server.sh index 3dae096..8e9b2af 100644 --- a/scripts/install_server.sh +++ b/scripts/install_server.sh @@ -236,6 +236,7 @@ detect_package_manager() { fi if has_command apt; then + apt update PACKAGE_MANAGEMENT_INSTALL='apt -y --no-install-recommends install' return 0 fi From 03c8b5e6b91f956636f9995dfe4261f00362547f Mon Sep 17 00:00:00 2001 From: HystericalDragon Date: Sun, 31 Mar 2024 10:58:34 +0800 Subject: [PATCH 046/161] feat: support Android protect path about: https://github.com/apernet/hysteria/commit/1ac9d4956bd16bbd59157da8e4532b2db6104ef9 Signed-off-by: HystericalDragon --- app/cmd/client.go | 6 ++- extras/go.mod | 2 +- extras/protect/protect.go | 9 +++++ extras/protect/protect_linux.go | 72 +++++++++++++++++++++++++++++++++ extras/protect/protect_stub.go | 13 ++++++ extras/transport/udphop/conn.go | 8 ++-- 6 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 extras/protect/protect.go create mode 100644 extras/protect/protect_linux.go create mode 100644 extras/protect/protect_stub.go diff --git a/app/cmd/client.go b/app/cmd/client.go index c1d04bd..78071a5 100644 --- a/app/cmd/client.go +++ b/app/cmd/client.go @@ -30,6 +30,7 @@ import ( "github.com/apernet/hysteria/core/client" "github.com/apernet/hysteria/extras/correctnet" "github.com/apernet/hysteria/extras/obfs" + "github.com/apernet/hysteria/extras/protect" "github.com/apernet/hysteria/extras/transport/udphop" ) @@ -55,6 +56,7 @@ func initClientFlags() { type clientConfig struct { Server string `mapstructure:"server"` + ProtectPath string `mapstructure:"protectPath"` Auth string `mapstructure:"auth"` Transport clientConfigTransport `mapstructure:"transport"` Obfs clientConfigObfs `mapstructure:"obfs"` @@ -202,11 +204,11 @@ func (c *clientConfig) fillConnFactory(hyConfig *client.Config) error { if hyConfig.ServerAddr.Network() == "udphop" { hopAddr := hyConfig.ServerAddr.(*udphop.UDPHopAddr) newFunc = func(addr net.Addr) (net.PacketConn, error) { - return udphop.NewUDPHopPacketConn(hopAddr, c.Transport.UDP.HopInterval, nil) + return udphop.NewUDPHopPacketConn(hopAddr, c.Transport.UDP.HopInterval, protect.ListenUDP(c.ProtectPath)) } } else { newFunc = func(addr net.Addr) (net.PacketConn, error) { - return net.ListenUDP("udp", nil) + return protect.ListenUDP(c.ProtectPath)() } } default: diff --git a/extras/go.mod b/extras/go.mod index bc21044..a470d1e 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -11,6 +11,7 @@ require ( github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 golang.org/x/crypto v0.19.0 golang.org/x/net v0.21.0 + golang.org/x/sys v0.17.0 google.golang.org/protobuf v1.33.0 ) @@ -28,7 +29,6 @@ require ( go.uber.org/mock v0.4.0 // indirect golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.11.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/extras/protect/protect.go b/extras/protect/protect.go new file mode 100644 index 0000000..1b5cd08 --- /dev/null +++ b/extras/protect/protect.go @@ -0,0 +1,9 @@ +// Package protect set VPN protect for every conns to bypass route. +package protect + +import ( + "net" +) + +// ListenUDPFunc listen UDP with VPN protect. +type ListenUDPFunc func() (net.PacketConn, error) diff --git a/extras/protect/protect_linux.go b/extras/protect/protect_linux.go new file mode 100644 index 0000000..0fffe13 --- /dev/null +++ b/extras/protect/protect_linux.go @@ -0,0 +1,72 @@ +//go:build linux + +package protect + +import ( + "errors" + "net" + + "golang.org/x/sys/unix" +) + +const ( + timevalSec = 3 +) + +func protect(connFd int, path string) error { + if path == "" { + return nil + } + + socketFd, err := unix.Socket(unix.AF_UNIX, unix.SOCK_STREAM, unix.PROT_NONE) + if err != nil { + return err + } + defer unix.Close(socketFd) + + _ = unix.SetsockoptTimeval(socketFd, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &unix.Timeval{Sec: timevalSec}) + _ = unix.SetsockoptTimeval(socketFd, unix.SOL_SOCKET, unix.SO_SNDTIMEO, &unix.Timeval{Sec: timevalSec}) + + err = unix.Connect(socketFd, &unix.SockaddrUnix{Name: path}) + if err != nil { + return err + } + + err = unix.Sendmsg(socketFd, nil, unix.UnixRights(connFd), nil, 0) + if err != nil { + return err + } + + dummy := []byte{1} + n, err := unix.Read(socketFd, dummy) + if err != nil { + return err + } + if n != 1 { + return errors.New("protect failed") + } + + return nil +} + +func ListenUDP(protectPath string) ListenUDPFunc { + return func() (net.PacketConn, error) { + udpConn, err := net.ListenUDP("udp", nil) + if err != nil { + return nil, err + } + + udpFile, err := udpConn.File() + if err != nil { + return nil, err + } + + err = protect(int(udpFile.Fd()), protectPath) + if err != nil { + _ = udpConn.Close() + return nil, err + } + + return udpConn, nil + } +} diff --git a/extras/protect/protect_stub.go b/extras/protect/protect_stub.go new file mode 100644 index 0000000..a8da279 --- /dev/null +++ b/extras/protect/protect_stub.go @@ -0,0 +1,13 @@ +//go:build !linux + +package protect + +import ( + "net" +) + +func ListenUDP(protectPath string) ListenUDPFunc { + return func() (net.PacketConn, error) { + return net.ListenUDP("udp", nil) + } +} diff --git a/extras/transport/udphop/conn.go b/extras/transport/udphop/conn.go index f20c583..722aa1c 100644 --- a/extras/transport/udphop/conn.go +++ b/extras/transport/udphop/conn.go @@ -7,6 +7,8 @@ import ( "sync" "syscall" "time" + + "github.com/apernet/hysteria/extras/protect" ) const ( @@ -20,7 +22,7 @@ type udpHopPacketConn struct { Addr net.Addr Addrs []net.Addr HopInterval time.Duration - ListenUDPFunc ListenUDPFunc + ListenUDPFunc protect.ListenUDPFunc connMutex sync.RWMutex prevConn net.PacketConn @@ -44,9 +46,7 @@ type udpPacket struct { Err error } -type ListenUDPFunc func() (net.PacketConn, error) - -func NewUDPHopPacketConn(addr *UDPHopAddr, hopInterval time.Duration, listenUDPFunc ListenUDPFunc) (net.PacketConn, error) { +func NewUDPHopPacketConn(addr *UDPHopAddr, hopInterval time.Duration, listenUDPFunc protect.ListenUDPFunc) (net.PacketConn, error) { if hopInterval == 0 { hopInterval = defaultHopInterval } else if hopInterval < 5*time.Second { From a05383c2a186054ca160203cac6e24db9decf468 Mon Sep 17 00:00:00 2001 From: HystericalDragon Date: Wed, 3 Apr 2024 18:40:25 +0800 Subject: [PATCH 047/161] fix: use reflect to get fd conn.File() not returns real file. Signed-off-by: HystericalDragon --- extras/protect/protect_linux.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/extras/protect/protect_linux.go b/extras/protect/protect_linux.go index 0fffe13..003eb13 100644 --- a/extras/protect/protect_linux.go +++ b/extras/protect/protect_linux.go @@ -5,6 +5,7 @@ package protect import ( "errors" "net" + "reflect" "golang.org/x/sys/unix" ) @@ -13,6 +14,7 @@ const ( timevalSec = 3 ) +// protect try to connect with path by unix socket, then send the conn's fd to it. func protect(connFd int, path string) error { if path == "" { return nil @@ -56,12 +58,7 @@ func ListenUDP(protectPath string) ListenUDPFunc { return nil, err } - udpFile, err := udpConn.File() - if err != nil { - return nil, err - } - - err = protect(int(udpFile.Fd()), protectPath) + err = protect(fdFromConn(udpConn), protectPath) if err != nil { _ = udpConn.Close() return nil, err @@ -70,3 +67,12 @@ func ListenUDP(protectPath string) ListenUDPFunc { return udpConn, nil } } + +// fdFromConn get net.Conn's file descriptor. +func fdFromConn(conn net.Conn) int { + v := reflect.ValueOf(conn) + netFD := reflect.Indirect(reflect.Indirect(v).FieldByName("fd")) + pfd := reflect.Indirect(netFD.FieldByName("pfd")) + fd := int(pfd.FieldByName("Sysfd").Int()) + return fd +} From 3e34da1aa87f8409307e2ade59f85826f4236849 Mon Sep 17 00:00:00 2001 From: Haruue Date: Fri, 5 Apr 2024 02:20:45 +0800 Subject: [PATCH 048/161] refactor: protect => quic.sockopts Android's VpnService.protect() itself is confusing, so we rename the "protect" feature with the name `fdControlUnixSocket` and make it a sub-option under `quic.sockopts`. A unit test is added to make sure the protect feature works. I also added two other common options to `quic.sockopts` that I copied from my other projects but did not fully test here. --- app/cmd/client.go | 43 ++++++--- .../sockopts/fd_control_unix_socket_test.py | 65 +++++++++++++ app/internal/sockopts/sockopts.go | 76 ++++++++++++++++ app/internal/sockopts/sockopts_linux.go | 91 +++++++++++++++++++ app/internal/sockopts/sockopts_linux_test.go | 53 +++++++++++ extras/protect/protect.go | 9 -- extras/protect/protect_linux.go | 78 ---------------- extras/protect/protect_stub.go | 13 --- extras/transport/udphop/conn.go | 8 +- 9 files changed, 321 insertions(+), 115 deletions(-) create mode 100644 app/internal/sockopts/fd_control_unix_socket_test.py create mode 100644 app/internal/sockopts/sockopts.go create mode 100644 app/internal/sockopts/sockopts_linux.go create mode 100644 app/internal/sockopts/sockopts_linux_test.go delete mode 100644 extras/protect/protect.go delete mode 100644 extras/protect/protect_linux.go delete mode 100644 extras/protect/protect_stub.go diff --git a/app/cmd/client.go b/app/cmd/client.go index 78071a5..c230140 100644 --- a/app/cmd/client.go +++ b/app/cmd/client.go @@ -22,6 +22,7 @@ import ( "github.com/apernet/hysteria/app/internal/forwarding" "github.com/apernet/hysteria/app/internal/http" "github.com/apernet/hysteria/app/internal/redirect" + "github.com/apernet/hysteria/app/internal/sockopts" "github.com/apernet/hysteria/app/internal/socks5" "github.com/apernet/hysteria/app/internal/tproxy" "github.com/apernet/hysteria/app/internal/tun" @@ -30,7 +31,6 @@ import ( "github.com/apernet/hysteria/core/client" "github.com/apernet/hysteria/extras/correctnet" "github.com/apernet/hysteria/extras/obfs" - "github.com/apernet/hysteria/extras/protect" "github.com/apernet/hysteria/extras/transport/udphop" ) @@ -56,7 +56,6 @@ func initClientFlags() { type clientConfig struct { Server string `mapstructure:"server"` - ProtectPath string `mapstructure:"protectPath"` Auth string `mapstructure:"auth"` Transport clientConfigTransport `mapstructure:"transport"` Obfs clientConfigObfs `mapstructure:"obfs"` @@ -101,13 +100,20 @@ type clientConfigTLS struct { } type clientConfigQUIC struct { - InitStreamReceiveWindow uint64 `mapstructure:"initStreamReceiveWindow"` - MaxStreamReceiveWindow uint64 `mapstructure:"maxStreamReceiveWindow"` - InitConnectionReceiveWindow uint64 `mapstructure:"initConnReceiveWindow"` - MaxConnectionReceiveWindow uint64 `mapstructure:"maxConnReceiveWindow"` - MaxIdleTimeout time.Duration `mapstructure:"maxIdleTimeout"` - KeepAlivePeriod time.Duration `mapstructure:"keepAlivePeriod"` - DisablePathMTUDiscovery bool `mapstructure:"disablePathMTUDiscovery"` + InitStreamReceiveWindow uint64 `mapstructure:"initStreamReceiveWindow"` + MaxStreamReceiveWindow uint64 `mapstructure:"maxStreamReceiveWindow"` + InitConnectionReceiveWindow uint64 `mapstructure:"initConnReceiveWindow"` + MaxConnectionReceiveWindow uint64 `mapstructure:"maxConnReceiveWindow"` + MaxIdleTimeout time.Duration `mapstructure:"maxIdleTimeout"` + KeepAlivePeriod time.Duration `mapstructure:"keepAlivePeriod"` + DisablePathMTUDiscovery bool `mapstructure:"disablePathMTUDiscovery"` + Sockopts clientConfigQUICSockopts `mapstructure:"sockopts"` +} + +type clientConfigQUICSockopts struct { + BindInterface *string `mapstructure:"bindInterface"` + FirewallMark *uint32 `mapstructure:"fwmark"` + FdControlUnixSocket *string `mapstructure:"fdControlUnixSocket"` } type clientConfigBandwidth struct { @@ -197,6 +203,21 @@ func (c *clientConfig) fillServerAddr(hyConfig *client.Config) error { // fillConnFactory must be called after fillServerAddr, as we have different logic // for ConnFactory depending on whether we have a port hopping address. func (c *clientConfig) fillConnFactory(hyConfig *client.Config) error { + so := &sockopts.SocketOptions{ + BindInterface: c.QUIC.Sockopts.BindInterface, + FirewallMark: c.QUIC.Sockopts.FirewallMark, + FdControlUnixSocket: c.QUIC.Sockopts.FdControlUnixSocket, + } + if err := so.CheckSupported(); err != nil { + var unsupportedErr *sockopts.UnsupportedError + if errors.As(err, &unsupportedErr) { + return configError{ + Field: "quic.sockopts." + unsupportedErr.Field, + Err: errors.New("unsupported on this platform"), + } + } + return configError{Field: "quic.sockopts", Err: err} + } // Inner PacketConn var newFunc func(addr net.Addr) (net.PacketConn, error) switch strings.ToLower(c.Transport.Type) { @@ -204,11 +225,11 @@ func (c *clientConfig) fillConnFactory(hyConfig *client.Config) error { if hyConfig.ServerAddr.Network() == "udphop" { hopAddr := hyConfig.ServerAddr.(*udphop.UDPHopAddr) newFunc = func(addr net.Addr) (net.PacketConn, error) { - return udphop.NewUDPHopPacketConn(hopAddr, c.Transport.UDP.HopInterval, protect.ListenUDP(c.ProtectPath)) + return udphop.NewUDPHopPacketConn(hopAddr, c.Transport.UDP.HopInterval, so.ListenUDP) } } else { newFunc = func(addr net.Addr) (net.PacketConn, error) { - return protect.ListenUDP(c.ProtectPath)() + return so.ListenUDP() } } default: diff --git a/app/internal/sockopts/fd_control_unix_socket_test.py b/app/internal/sockopts/fd_control_unix_socket_test.py new file mode 100644 index 0000000..e47a6f6 --- /dev/null +++ b/app/internal/sockopts/fd_control_unix_socket_test.py @@ -0,0 +1,65 @@ +import socket +import array +import os +import struct +import sys + + +def serve(path): + try: + os.unlink(path) + except OSError: + if os.path.exists(path): + raise + + server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + server.bind(path) + server.listen() + print(f"Listening on {path}") + + try: + while True: + connection, client_address = server.accept() + print(f"Client connected") + + try: + # Receiving fd from client + fds = array.array("i") + msg, ancdata, flags, addr = connection.recvmsg(1, socket.CMSG_LEN(struct.calcsize('i'))) + for cmsg_level, cmsg_type, cmsg_data in ancdata: + if cmsg_level == socket.SOL_SOCKET and cmsg_type == socket.SCM_RIGHTS: + fds.frombytes(cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) + + fd = fds[0] + + # We make a call to setsockopt(2) here, so client can verify we have received the fd + # In the real scenario, the server would set things like SO_MARK, + # we use SO_RCVBUF as it doesn't require any special capabilities. + nbytes = struct.pack("i", 2500) + fdsocket = fd_to_socket(fd) + fdsocket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, nbytes) + fdsocket.close() + + # The only protocol-like thing specified in the client implementation. + connection.send(b'\x01') + finally: + connection.close() + print("Connection closed") + + except KeyboardInterrupt: + print("Exit") + + finally: + server.close() + os.unlink(path) + + +def fd_to_socket(fd): + return socket.fromfd(fd, socket.AF_UNIX, socket.SOCK_STREAM) + + +if __name__ == "__main__": + if len(sys.argv) < 2: + raise ValueError("unix socket path is required") + + serve(sys.argv[1]) diff --git a/app/internal/sockopts/sockopts.go b/app/internal/sockopts/sockopts.go new file mode 100644 index 0000000..9c08922 --- /dev/null +++ b/app/internal/sockopts/sockopts.go @@ -0,0 +1,76 @@ +package sockopts + +import ( + "fmt" + "net" +) + +type SocketOptions struct { + BindInterface *string + FirewallMark *uint32 + FdControlUnixSocket *string +} + +// implemented in platform-specific files +var ( + bindInterfaceFunc func(c *net.UDPConn, device string) error + firewallMarkFunc func(c *net.UDPConn, fwmark uint32) error + fdControlUnixSocketFunc func(c *net.UDPConn, path string) error +) + +func (o *SocketOptions) CheckSupported() (err error) { + if o.BindInterface != nil && bindInterfaceFunc == nil { + return &UnsupportedError{"bindInterface"} + } + if o.FirewallMark != nil && firewallMarkFunc == nil { + return &UnsupportedError{"fwmark"} + } + if o.FdControlUnixSocket != nil && fdControlUnixSocketFunc == nil { + return &UnsupportedError{"fdControlUnixSocket"} + } + return nil +} + +type UnsupportedError struct { + Field string +} + +func (e *UnsupportedError) Error() string { + return fmt.Sprintf("%s is not supported on this platform", e.Field) +} + +func (o *SocketOptions) ListenUDP() (uconn net.PacketConn, err error) { + uconn, err = net.ListenUDP("udp", nil) + if err != nil { + return + } + err = o.applyToUDPConn(uconn.(*net.UDPConn)) + if err != nil { + uconn.Close() + uconn = nil + return + } + return +} + +func (o *SocketOptions) applyToUDPConn(c *net.UDPConn) (err error) { + if o.BindInterface != nil && bindInterfaceFunc != nil { + err = bindInterfaceFunc(c, *o.BindInterface) + if err != nil { + err = fmt.Errorf("failed to bind to interface: %w", err) + } + } + if o.FirewallMark != nil && firewallMarkFunc != nil { + err = firewallMarkFunc(c, *o.FirewallMark) + if err != nil { + err = fmt.Errorf("failed to set fwmark: %w", err) + } + } + if o.FdControlUnixSocket != nil && fdControlUnixSocketFunc != nil { + err = fdControlUnixSocketFunc(c, *o.FdControlUnixSocket) + if err != nil { + err = fmt.Errorf("failed to send fd to control unix socket: %w", err) + } + } + return +} diff --git a/app/internal/sockopts/sockopts_linux.go b/app/internal/sockopts/sockopts_linux.go new file mode 100644 index 0000000..f4d8621 --- /dev/null +++ b/app/internal/sockopts/sockopts_linux.go @@ -0,0 +1,91 @@ +//go:build linux + +package sockopts + +import ( + "fmt" + "net" + "time" + + "golang.org/x/sys/unix" +) + +const ( + fdControlUnixTimeout = 3 * time.Second +) + +func init() { + bindInterfaceFunc = bindInterfaceImpl + firewallMarkFunc = firewallMarkImpl + fdControlUnixSocketFunc = fdControlUnixSocketImpl +} + +func controlUDPConn(c *net.UDPConn, cb func(fd int) error) (err error) { + rconn, err := c.SyscallConn() + if err != nil { + return + } + cerr := rconn.Control(func(fd uintptr) { + err = cb(int(fd)) + }) + if err != nil { + return + } + if cerr != nil { + err = fmt.Errorf("failed to control fd: %w", cerr) + return + } + return +} + +func bindInterfaceImpl(c *net.UDPConn, device string) error { + return controlUDPConn(c, func(fd int) error { + return unix.BindToDevice(fd, device) + }) +} + +func firewallMarkImpl(c *net.UDPConn, fwmark uint32) error { + return controlUDPConn(c, func(fd int) error { + return unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_MARK, int(fwmark)) + }) +} + +func fdControlUnixSocketImpl(c *net.UDPConn, path string) error { + return controlUDPConn(c, func(fd int) error { + socketFd, err := unix.Socket(unix.AF_UNIX, unix.SOCK_STREAM, unix.PROT_NONE) + if err != nil { + return fmt.Errorf("failed to create unix socket: %w", err) + } + defer unix.Close(socketFd) + + timeoutUsec := fdControlUnixTimeout.Microseconds() + timeout := unix.Timeval{ + Sec: timeoutUsec / 1e6, + Usec: timeoutUsec % 1e6, + } + + _ = unix.SetsockoptTimeval(socketFd, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &timeout) + _ = unix.SetsockoptTimeval(socketFd, unix.SOL_SOCKET, unix.SO_SNDTIMEO, &timeout) + + err = unix.Connect(socketFd, &unix.SockaddrUnix{Name: path}) + if err != nil { + return fmt.Errorf("failed to connect: %w", err) + } + + err = unix.Sendmsg(socketFd, nil, unix.UnixRights(fd), nil, 0) + if err != nil { + return fmt.Errorf("failed to send: %w", err) + } + + dummy := []byte{1} + n, err := unix.Read(socketFd, dummy) + if err != nil { + return fmt.Errorf("failed to receive: %w", err) + } + if n != 1 { + return fmt.Errorf("socket closed unexpectedly") + } + + return nil + }) +} diff --git a/app/internal/sockopts/sockopts_linux_test.go b/app/internal/sockopts/sockopts_linux_test.go new file mode 100644 index 0000000..66614a4 --- /dev/null +++ b/app/internal/sockopts/sockopts_linux_test.go @@ -0,0 +1,53 @@ +//go:build linux + +package sockopts + +import ( + "net" + "os" + "os/exec" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "golang.org/x/sys/unix" +) + +func Test_fdControlUnixSocketImpl(t *testing.T) { + sockPath := "./fd_control_unix_socket_test.sock" + defer os.Remove(sockPath) + + // Run test server + cmd := exec.Command("python", "fd_control_unix_socket_test.py", sockPath) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err := cmd.Start() + if !assert.NoError(t, err) { + return + } + defer cmd.Process.Kill() + + // Wait for the server to start + time.Sleep(1 * time.Second) + + so := SocketOptions{ + FdControlUnixSocket: &sockPath, + } + conn, err := so.ListenUDP() + if !assert.NoError(t, err) { + return + } + defer conn.Close() + + err = controlUDPConn(conn.(*net.UDPConn), func(fd int) (err error) { + rcvbuf, err := unix.GetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_RCVBUF) + if err != nil { + return + } + // The test server called setsockopt(fd, SOL_SOCKET, SO_RCVBUF, 2500), + // and kernel will double this value for getsockopt(). + assert.Equal(t, 5000, rcvbuf) + return + }) + assert.NoError(t, err) +} diff --git a/extras/protect/protect.go b/extras/protect/protect.go deleted file mode 100644 index 1b5cd08..0000000 --- a/extras/protect/protect.go +++ /dev/null @@ -1,9 +0,0 @@ -// Package protect set VPN protect for every conns to bypass route. -package protect - -import ( - "net" -) - -// ListenUDPFunc listen UDP with VPN protect. -type ListenUDPFunc func() (net.PacketConn, error) diff --git a/extras/protect/protect_linux.go b/extras/protect/protect_linux.go deleted file mode 100644 index 003eb13..0000000 --- a/extras/protect/protect_linux.go +++ /dev/null @@ -1,78 +0,0 @@ -//go:build linux - -package protect - -import ( - "errors" - "net" - "reflect" - - "golang.org/x/sys/unix" -) - -const ( - timevalSec = 3 -) - -// protect try to connect with path by unix socket, then send the conn's fd to it. -func protect(connFd int, path string) error { - if path == "" { - return nil - } - - socketFd, err := unix.Socket(unix.AF_UNIX, unix.SOCK_STREAM, unix.PROT_NONE) - if err != nil { - return err - } - defer unix.Close(socketFd) - - _ = unix.SetsockoptTimeval(socketFd, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &unix.Timeval{Sec: timevalSec}) - _ = unix.SetsockoptTimeval(socketFd, unix.SOL_SOCKET, unix.SO_SNDTIMEO, &unix.Timeval{Sec: timevalSec}) - - err = unix.Connect(socketFd, &unix.SockaddrUnix{Name: path}) - if err != nil { - return err - } - - err = unix.Sendmsg(socketFd, nil, unix.UnixRights(connFd), nil, 0) - if err != nil { - return err - } - - dummy := []byte{1} - n, err := unix.Read(socketFd, dummy) - if err != nil { - return err - } - if n != 1 { - return errors.New("protect failed") - } - - return nil -} - -func ListenUDP(protectPath string) ListenUDPFunc { - return func() (net.PacketConn, error) { - udpConn, err := net.ListenUDP("udp", nil) - if err != nil { - return nil, err - } - - err = protect(fdFromConn(udpConn), protectPath) - if err != nil { - _ = udpConn.Close() - return nil, err - } - - return udpConn, nil - } -} - -// fdFromConn get net.Conn's file descriptor. -func fdFromConn(conn net.Conn) int { - v := reflect.ValueOf(conn) - netFD := reflect.Indirect(reflect.Indirect(v).FieldByName("fd")) - pfd := reflect.Indirect(netFD.FieldByName("pfd")) - fd := int(pfd.FieldByName("Sysfd").Int()) - return fd -} diff --git a/extras/protect/protect_stub.go b/extras/protect/protect_stub.go deleted file mode 100644 index a8da279..0000000 --- a/extras/protect/protect_stub.go +++ /dev/null @@ -1,13 +0,0 @@ -//go:build !linux - -package protect - -import ( - "net" -) - -func ListenUDP(protectPath string) ListenUDPFunc { - return func() (net.PacketConn, error) { - return net.ListenUDP("udp", nil) - } -} diff --git a/extras/transport/udphop/conn.go b/extras/transport/udphop/conn.go index 722aa1c..32cc31c 100644 --- a/extras/transport/udphop/conn.go +++ b/extras/transport/udphop/conn.go @@ -7,8 +7,6 @@ import ( "sync" "syscall" "time" - - "github.com/apernet/hysteria/extras/protect" ) const ( @@ -22,7 +20,7 @@ type udpHopPacketConn struct { Addr net.Addr Addrs []net.Addr HopInterval time.Duration - ListenUDPFunc protect.ListenUDPFunc + ListenUDPFunc ListenUDPFunc connMutex sync.RWMutex prevConn net.PacketConn @@ -46,7 +44,9 @@ type udpPacket struct { Err error } -func NewUDPHopPacketConn(addr *UDPHopAddr, hopInterval time.Duration, listenUDPFunc protect.ListenUDPFunc) (net.PacketConn, error) { +type ListenUDPFunc = func() (net.PacketConn, error) + +func NewUDPHopPacketConn(addr *UDPHopAddr, hopInterval time.Duration, listenUDPFunc ListenUDPFunc) (net.PacketConn, error) { if hopInterval == 0 { hopInterval = defaultHopInterval } else if hopInterval < 5*time.Second { From 65f5e9caa535fc9022f8673d1ef13e542a2da622 Mon Sep 17 00:00:00 2001 From: Haruue Date: Fri, 5 Apr 2024 02:30:52 +0800 Subject: [PATCH 049/161] chore: go mod tidy --- app/go.mod | 2 +- extras/go.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/go.mod b/app/go.mod index a5025c1..7aea843 100644 --- a/app/go.mod +++ b/app/go.mod @@ -16,6 +16,7 @@ require ( github.com/stretchr/testify v1.8.4 github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 go.uber.org/zap v1.24.0 + golang.org/x/sys v0.17.0 ) require ( @@ -57,7 +58,6 @@ require ( golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.21.0 // indirect - golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.11.1 // indirect google.golang.org/protobuf v1.33.0 // indirect diff --git a/extras/go.mod b/extras/go.mod index a470d1e..bc21044 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -11,7 +11,6 @@ require ( github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 golang.org/x/crypto v0.19.0 golang.org/x/net v0.21.0 - golang.org/x/sys v0.17.0 google.golang.org/protobuf v1.33.0 ) @@ -29,6 +28,7 @@ require ( go.uber.org/mock v0.4.0 // indirect golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect golang.org/x/mod v0.12.0 // indirect + golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.11.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect From 13586df2bae9335652d73a3a476fc4281a7691ab Mon Sep 17 00:00:00 2001 From: HystericalDragon Date: Fri, 5 Apr 2024 08:36:04 +0800 Subject: [PATCH 050/161] fix: invalid const usage Signed-off-by: HystericalDragon --- app/internal/sockopts/sockopts_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/internal/sockopts/sockopts_linux.go b/app/internal/sockopts/sockopts_linux.go index f4d8621..19777b5 100644 --- a/app/internal/sockopts/sockopts_linux.go +++ b/app/internal/sockopts/sockopts_linux.go @@ -52,7 +52,7 @@ func firewallMarkImpl(c *net.UDPConn, fwmark uint32) error { func fdControlUnixSocketImpl(c *net.UDPConn, path string) error { return controlUDPConn(c, func(fd int) error { - socketFd, err := unix.Socket(unix.AF_UNIX, unix.SOCK_STREAM, unix.PROT_NONE) + socketFd, err := unix.Socket(unix.AF_UNIX, unix.SOCK_STREAM, 0) if err != nil { return fmt.Errorf("failed to create unix socket: %w", err) } From 9520d840942d3950ed43af281935c262fac35615 Mon Sep 17 00:00:00 2001 From: HystericalDragon Date: Fri, 5 Apr 2024 09:11:37 +0800 Subject: [PATCH 051/161] fix: timeval in different arch Signed-off-by: HystericalDragon --- app/internal/sockopts/sockopts_linux.go | 6 +----- app/internal/sockopts/timeval_linux_32.go | 15 +++++++++++++++ app/internal/sockopts/timeval_linux_64.go | 15 +++++++++++++++ 3 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 app/internal/sockopts/timeval_linux_32.go create mode 100644 app/internal/sockopts/timeval_linux_64.go diff --git a/app/internal/sockopts/sockopts_linux.go b/app/internal/sockopts/sockopts_linux.go index 19777b5..e51e75a 100644 --- a/app/internal/sockopts/sockopts_linux.go +++ b/app/internal/sockopts/sockopts_linux.go @@ -58,11 +58,7 @@ func fdControlUnixSocketImpl(c *net.UDPConn, path string) error { } defer unix.Close(socketFd) - timeoutUsec := fdControlUnixTimeout.Microseconds() - timeout := unix.Timeval{ - Sec: timeoutUsec / 1e6, - Usec: timeoutUsec % 1e6, - } + timeout := unixTimeval() _ = unix.SetsockoptTimeval(socketFd, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &timeout) _ = unix.SetsockoptTimeval(socketFd, unix.SOL_SOCKET, unix.SO_SNDTIMEO, &timeout) diff --git a/app/internal/sockopts/timeval_linux_32.go b/app/internal/sockopts/timeval_linux_32.go new file mode 100644 index 0000000..af8c262 --- /dev/null +++ b/app/internal/sockopts/timeval_linux_32.go @@ -0,0 +1,15 @@ +//go:build linux && (386 || arm || mips || mipsle || ppc) + +package sockopts + +import ( + "golang.org/x/sys/unix" +) + +func unixTimeval() unix.Timeval { + timeUsec := fdControlUnixTimeout.Microseconds() + return unix.Timeval{ + Sec: int32(timeUsec / 1e6), + Usec: int32(timeUsec % 1e6), + } +} diff --git a/app/internal/sockopts/timeval_linux_64.go b/app/internal/sockopts/timeval_linux_64.go new file mode 100644 index 0000000..407438c --- /dev/null +++ b/app/internal/sockopts/timeval_linux_64.go @@ -0,0 +1,15 @@ +//go:build linux && (amd64 || arm64 || loong64 || mips64 || mips64le || ppc64 || ppc64le || riscv64 || s390x || sparc64) + +package sockopts + +import ( + "golang.org/x/sys/unix" +) + +func unixTimeval() unix.Timeval { + timeUsec := fdControlUnixTimeout.Microseconds() + return unix.Timeval{ + Sec: timeUsec / 1e6, + Usec: timeUsec % 1e6, + } +} From e1d7ce4640c4c897c19398c4c1d7522db35db34d Mon Sep 17 00:00:00 2001 From: Haruue Date: Fri, 5 Apr 2024 10:49:03 +0800 Subject: [PATCH 052/161] chore: a better fix for 32-bit unix.Timeval Why there is no decltype() in Golang? At least we got generics now. ref: 9520d840942d3950ed43af281935c262fac35615 --- app/internal/sockopts/sockopts_linux.go | 12 +++++++++++- app/internal/sockopts/timeval_linux_32.go | 15 --------------- app/internal/sockopts/timeval_linux_64.go | 15 --------------- 3 files changed, 11 insertions(+), 31 deletions(-) delete mode 100644 app/internal/sockopts/timeval_linux_32.go delete mode 100644 app/internal/sockopts/timeval_linux_64.go diff --git a/app/internal/sockopts/sockopts_linux.go b/app/internal/sockopts/sockopts_linux.go index e51e75a..ce188d9 100644 --- a/app/internal/sockopts/sockopts_linux.go +++ b/app/internal/sockopts/sockopts_linux.go @@ -7,6 +7,8 @@ import ( "net" "time" + "golang.org/x/exp/constraints" + "golang.org/x/sys/unix" ) @@ -58,7 +60,11 @@ func fdControlUnixSocketImpl(c *net.UDPConn, path string) error { } defer unix.Close(socketFd) - timeout := unixTimeval() + var timeout unix.Timeval + timeUsec := fdControlUnixTimeout.Microseconds() + castAssignInteger(timeUsec/1e6, &timeout.Sec) + // Specifying the type explicitly is not necessary here, but it makes GoLand happy. + castAssignInteger[int64](timeUsec%1e6, &timeout.Usec) _ = unix.SetsockoptTimeval(socketFd, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &timeout) _ = unix.SetsockoptTimeval(socketFd, unix.SOL_SOCKET, unix.SO_SNDTIMEO, &timeout) @@ -85,3 +91,7 @@ func fdControlUnixSocketImpl(c *net.UDPConn, path string) error { return nil }) } + +func castAssignInteger[F, T constraints.Integer](from F, to *T) { + *to = T(from) +} diff --git a/app/internal/sockopts/timeval_linux_32.go b/app/internal/sockopts/timeval_linux_32.go deleted file mode 100644 index af8c262..0000000 --- a/app/internal/sockopts/timeval_linux_32.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build linux && (386 || arm || mips || mipsle || ppc) - -package sockopts - -import ( - "golang.org/x/sys/unix" -) - -func unixTimeval() unix.Timeval { - timeUsec := fdControlUnixTimeout.Microseconds() - return unix.Timeval{ - Sec: int32(timeUsec / 1e6), - Usec: int32(timeUsec % 1e6), - } -} diff --git a/app/internal/sockopts/timeval_linux_64.go b/app/internal/sockopts/timeval_linux_64.go deleted file mode 100644 index 407438c..0000000 --- a/app/internal/sockopts/timeval_linux_64.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build linux && (amd64 || arm64 || loong64 || mips64 || mips64le || ppc64 || ppc64le || riscv64 || s390x || sparc64) - -package sockopts - -import ( - "golang.org/x/sys/unix" -) - -func unixTimeval() unix.Timeval { - timeUsec := fdControlUnixTimeout.Microseconds() - return unix.Timeval{ - Sec: timeUsec / 1e6, - Usec: timeUsec % 1e6, - } -} From 297d64e48f99501d2e3f7dd4f4cec30213ae25cc Mon Sep 17 00:00:00 2001 From: HystericalDragon Date: Fri, 5 Apr 2024 11:26:22 +0800 Subject: [PATCH 053/161] chore: format code --- app/go.mod | 2 +- app/internal/sockopts/sockopts_linux.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/go.mod b/app/go.mod index 7aea843..97c5bbb 100644 --- a/app/go.mod +++ b/app/go.mod @@ -16,6 +16,7 @@ require ( github.com/stretchr/testify v1.8.4 github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 go.uber.org/zap v1.24.0 + golang.org/x/exp v0.0.0-20221205204356-47842c84f3db golang.org/x/sys v0.17.0 ) @@ -55,7 +56,6 @@ require ( go.uber.org/multierr v1.11.0 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect golang.org/x/crypto v0.19.0 // indirect - golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.21.0 // indirect golang.org/x/text v0.14.0 // indirect diff --git a/app/internal/sockopts/sockopts_linux.go b/app/internal/sockopts/sockopts_linux.go index ce188d9..d1e5d23 100644 --- a/app/internal/sockopts/sockopts_linux.go +++ b/app/internal/sockopts/sockopts_linux.go @@ -8,7 +8,6 @@ import ( "time" "golang.org/x/exp/constraints" - "golang.org/x/sys/unix" ) From 5bebfd5732c914e24a4e36febdd3c4baaee8297f Mon Sep 17 00:00:00 2001 From: Haruue Date: Fri, 5 Apr 2024 13:17:21 +0800 Subject: [PATCH 054/161] fix(sockopts): error handling in applyToUDPConn --- app/internal/sockopts/sockopts.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/internal/sockopts/sockopts.go b/app/internal/sockopts/sockopts.go index 9c08922..e068f98 100644 --- a/app/internal/sockopts/sockopts.go +++ b/app/internal/sockopts/sockopts.go @@ -58,18 +58,21 @@ func (o *SocketOptions) applyToUDPConn(c *net.UDPConn) (err error) { err = bindInterfaceFunc(c, *o.BindInterface) if err != nil { err = fmt.Errorf("failed to bind to interface: %w", err) + return } } if o.FirewallMark != nil && firewallMarkFunc != nil { err = firewallMarkFunc(c, *o.FirewallMark) if err != nil { err = fmt.Errorf("failed to set fwmark: %w", err) + return } } if o.FdControlUnixSocket != nil && fdControlUnixSocketFunc != nil { err = fdControlUnixSocketFunc(c, *o.FdControlUnixSocket) if err != nil { err = fmt.Errorf("failed to send fd to control unix socket: %w", err) + return } } return From e6da1f348c0cf0e9aad6936fcb44fa8fc4f071bf Mon Sep 17 00:00:00 2001 From: Haruue Date: Fri, 5 Apr 2024 13:20:17 +0800 Subject: [PATCH 055/161] fix(sockopts): error handling in applyToUDPConn it is just no reason to use named err retval here --- app/internal/sockopts/sockopts.go | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/app/internal/sockopts/sockopts.go b/app/internal/sockopts/sockopts.go index e068f98..14ee0c0 100644 --- a/app/internal/sockopts/sockopts.go +++ b/app/internal/sockopts/sockopts.go @@ -53,27 +53,24 @@ func (o *SocketOptions) ListenUDP() (uconn net.PacketConn, err error) { return } -func (o *SocketOptions) applyToUDPConn(c *net.UDPConn) (err error) { +func (o *SocketOptions) applyToUDPConn(c *net.UDPConn) error { if o.BindInterface != nil && bindInterfaceFunc != nil { - err = bindInterfaceFunc(c, *o.BindInterface) + err := bindInterfaceFunc(c, *o.BindInterface) if err != nil { - err = fmt.Errorf("failed to bind to interface: %w", err) - return + return fmt.Errorf("failed to bind to interface: %w", err) } } if o.FirewallMark != nil && firewallMarkFunc != nil { - err = firewallMarkFunc(c, *o.FirewallMark) + err := firewallMarkFunc(c, *o.FirewallMark) if err != nil { - err = fmt.Errorf("failed to set fwmark: %w", err) - return + return fmt.Errorf("failed to set fwmark: %w", err) } } if o.FdControlUnixSocket != nil && fdControlUnixSocketFunc != nil { - err = fdControlUnixSocketFunc(c, *o.FdControlUnixSocket) + err := fdControlUnixSocketFunc(c, *o.FdControlUnixSocket) if err != nil { - err = fmt.Errorf("failed to send fd to control unix socket: %w", err) - return + return fmt.Errorf("failed to send fd to control unix socket: %w", err) } } - return + return nil } From 6b5486fc09d22c3fb4a1cc78c799c8cfe81e6dce Mon Sep 17 00:00:00 2001 From: Toby Date: Fri, 5 Apr 2024 16:15:29 -0700 Subject: [PATCH 056/161] feat: add test for sockopts config fields --- app/cmd/client_test.go | 13 +++++++++++++ app/cmd/client_test.yaml | 12 ++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/app/cmd/client_test.go b/app/cmd/client_test.go index c586949..10b2d99 100644 --- a/app/cmd/client_test.go +++ b/app/cmd/client_test.go @@ -46,6 +46,11 @@ func TestClientConfig(t *testing.T) { MaxIdleTimeout: 10 * time.Second, KeepAlivePeriod: 4 * time.Second, DisablePathMTUDiscovery: true, + Sockopts: clientConfigQUICSockopts{ + BindInterface: stringRef("eth0"), + FirewallMark: uint32Ref(1234), + FdControlUnixSocket: stringRef("test.sock"), + }, }, Bandwidth: clientConfigBandwidth{ Up: "200 mbps", @@ -189,3 +194,11 @@ func TestClientConfigURI(t *testing.T) { }) } } + +func stringRef(s string) *string { + return &s +} + +func uint32Ref(i uint32) *uint32 { + return &i +} diff --git a/app/cmd/client_test.yaml b/app/cmd/client_test.yaml index 4f919df..e8438f6 100644 --- a/app/cmd/client_test.yaml +++ b/app/cmd/client_test.yaml @@ -26,6 +26,10 @@ quic: maxIdleTimeout: 10s keepAlivePeriod: 4s disablePathMTUDiscovery: true + sockopts: + bindInterface: eth0 + fwmark: 1234 + fdControlUnixSocket: test.sock bandwidth: up: 200 mbps @@ -75,7 +79,7 @@ tun: ipv6: 2001::ffff:ffff:ffff:fff1/126 route: strict: true - ipv4: [0.0.0.0/0] - ipv6: ["2000::/3"] - ipv4Exclude: [192.0.2.1/32] - ipv6Exclude: ["2001:db8::1/128"] + ipv4: [ 0.0.0.0/0 ] + ipv6: [ "2000::/3" ] + ipv4Exclude: [ 192.0.2.1/32 ] + ipv6Exclude: [ "2001:db8::1/128" ] From 44b36f56accd1aba93c01e11c5dbf09b015acca6 Mon Sep 17 00:00:00 2001 From: Haruue Date: Tue, 9 Apr 2024 19:23:33 +0800 Subject: [PATCH 057/161] fix(scripts): set secontext for systemd unit --- scripts/install_server.sh | 41 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/scripts/install_server.sh b/scripts/install_server.sh index 8e9b2af..b71f075 100644 --- a/scripts/install_server.sh +++ b/scripts/install_server.sh @@ -60,6 +60,9 @@ HYSTERIA_USER="${HYSTERIA_USER:-}" # Directory for ACME certificates storage HYSTERIA_HOME_DIR="${HYSTERIA_HOME_DIR:-}" +# SELinux context of systemd unit files +SECONTEXT_SYSTEMD_UNIT="${SECONTEXT_SYSTEMD_UNIT:-}" + ### # ARGUMENTS @@ -176,6 +179,14 @@ systemctl() { command systemctl "$@" } +chcon() { + if ! has_command chcon || [[ "x$FORCE_NO_SELINUX" == "x1" ]]; then + return + fi + + command chcon "$@" +} + show_argument_error_and_exit() { local _error_msg="$1" @@ -221,6 +232,7 @@ exec_sudo() { $(env | grep "^OPERATING_SYSTEM=" || true) $(env | grep "^ARCHITECTURE=" || true) $(env | grep "^HYSTERIA_\w*=" || true) + $(env | grep "^SECONTEXT_SYSTEMD_UNIT=" || true) $(env | grep "^FORCE_\w*=" || true) ) IFS="$_saved_ifs" @@ -407,6 +419,30 @@ check_environment_systemd() { esac } +check_environment_selinux() { + if ! has_command chcon; then + return + fi + + note "SELinux is detected" + + if [[ "x$FORCE_NO_SELINUX" == "x1" ]]; then + warning "FORCE_NO_SELINUX=1, we will skip all SELinux related commands." + return + fi + + if [[ -z "$SECONTEXT_SYSTEMD_UNIT" ]]; then + if [[ -z "$FORCE_NO_SYSTEMD" ]] && [[ -e "$SYSTEMD_SERVICES_DIR" ]]; then + local _sectx="$(ls -ldZ "$SYSTEMD_SERVICES_DIR" | cut -d ' ' -f 5)" + if [[ "x$_sectx" == "x?" ]]; then + warning "Failed to obtain SEContext of $SYSTEMD_SERVICES_DIR" + else + SECONTEXT_SYSTEMD_UNIT="$_sectx" + fi + fi + fi +} + check_environment_curl() { if has_command curl; then return @@ -427,6 +463,7 @@ check_environment() { check_environment_operating_system check_environment_architecture check_environment_systemd + check_environment_selinux check_environment_curl check_environment_grep } @@ -918,6 +955,10 @@ perform_install_hysteria_systemd() { install_content -Dm644 "$(tpl_hysteria_server_service)" "$SYSTEMD_SERVICES_DIR/hysteria-server.service" "1" install_content -Dm644 "$(tpl_hysteria_server_x_service)" "$SYSTEMD_SERVICES_DIR/hysteria-server@.service" "1" + if [[ -n "$SECONTEXT_SYSTEMD_UNIT" ]]; then + chcon "$SECONTEXT_SYSTEMD_UNIT" "$SYSTEMD_SERVICES_DIR/hysteria-server.service" + chcon "$SECONTEXT_SYSTEMD_UNIT" "$SYSTEMD_SERVICES_DIR/hysteria-server@.service" + fi systemctl daemon-reload } From 34574e0339b0543dce6832901f7ca42b42ec4c2d Mon Sep 17 00:00:00 2001 From: Haruue Date: Thu, 11 Apr 2024 20:53:28 +0800 Subject: [PATCH 058/161] refactor: proxymux This commit rewrites proxymux package to provide following functions: + proxymux.ListenSOCKS(address string) + proxymux.ListenHTTP(address string) both are drop-in replacements for net.Listen("tcp", address) The above functions can be called with the same address to take advantage of the mux feature. Tests are not included, but we will have them very soon. This commit should be in PR #1006, but I ended up with it in a separate branch here. Please rebase if you want to merge it. --- app/cmd/client.go | 60 +------ app/cmd/client_test.go | 7 - app/cmd/client_test.yaml | 7 - app/internal/proxymux/manager.go | 72 ++++++++ app/internal/proxymux/mux.go | 300 +++++++++++++++++++++++-------- 5 files changed, 301 insertions(+), 145 deletions(-) create mode 100644 app/internal/proxymux/manager.go diff --git a/app/cmd/client.go b/app/cmd/client.go index b818569..9e09070 100644 --- a/app/cmd/client.go +++ b/app/cmd/client.go @@ -15,13 +15,14 @@ import ( "strings" "time" + "github.com/apernet/hysteria/app/internal/proxymux" + "github.com/spf13/cobra" "github.com/spf13/viper" "go.uber.org/zap" "github.com/apernet/hysteria/app/internal/forwarding" "github.com/apernet/hysteria/app/internal/http" - "github.com/apernet/hysteria/app/internal/proxymux" "github.com/apernet/hysteria/app/internal/redirect" "github.com/apernet/hysteria/app/internal/socks5" "github.com/apernet/hysteria/app/internal/tproxy" @@ -64,7 +65,6 @@ type clientConfig struct { Bandwidth clientConfigBandwidth `mapstructure:"bandwidth"` FastOpen bool `mapstructure:"fastOpen"` Lazy bool `mapstructure:"lazy"` - Mixed *mixedConfig `mapstructure:"mixed"` SOCKS5 *socks5Config `mapstructure:"socks5"` HTTP *httpConfig `mapstructure:"http"` TCPForwarding []tcpForwardingEntry `mapstructure:"tcpForwarding"` @@ -115,14 +115,6 @@ type clientConfigBandwidth struct { Down string `mapstructure:"down"` } -type mixedConfig struct { - Listen string `mapstructure:"listen"` - Username string `mapstructure:"username"` - Password string `mapstructure:"password"` - DisableUDP bool `mapstructure:"disableUDP"` - Realm string `mapstructure:"realm"` -} - type socks5Config struct { Listen string `mapstructure:"listen"` Username string `mapstructure:"username"` @@ -457,11 +449,6 @@ func runClient(cmd *cobra.Command, args []string) { // Register modes var runner clientModeRunner - if config.Mixed != nil { - runner.Add("Mixed server", func() error { - return clientMixed(*config.Mixed, c) - }) - } if config.SOCKS5 != nil { runner.Add("SOCKS5 server", func() error { return clientSOCKS5(*config.SOCKS5, c) @@ -542,50 +529,11 @@ func (r *clientModeRunner) Run() { } } -func clientMixed(config mixedConfig, c client.Client) error { - if config.Listen == "" { - return configError{Field: "listen", Err: errors.New("listen address is empty")} - } - l, err := correctnet.Listen("tcp", config.Listen) - if err != nil { - return configError{Field: "listen", Err: err} - } - var authFunc func(username, password string) bool - username, password := config.Username, config.Password - if username != "" && password != "" { - authFunc = func(u, p string) bool { - return u == username && p == password - } - } - s := socks5.Server{ - HyClient: c, - AuthFunc: authFunc, - DisableUDP: config.DisableUDP, - EventLogger: &socks5Logger{}, - } - logger.Info("SOCKS5 server listening", zap.String("addr", config.Listen)) - - h := http.Server{ - HyClient: c, - AuthFunc: authFunc, - AuthRealm: config.Realm, - EventLogger: &httpLogger{}, - } - logger.Info("HTTP proxy server listening", zap.String("addr", config.Listen)) - socks5Ln, httpLn := proxymux.SplitSOCKSAndHTTP(l) - go func() { - if err = h.Serve(httpLn); err != nil { - logger.Fatal(err.Error()) - } - }() - return s.Serve(socks5Ln) -} - func clientSOCKS5(config socks5Config, c client.Client) error { if config.Listen == "" { return configError{Field: "listen", Err: errors.New("listen address is empty")} } - l, err := correctnet.Listen("tcp", config.Listen) + l, err := proxymux.ListenSOCKS(config.Listen) if err != nil { return configError{Field: "listen", Err: err} } @@ -610,7 +558,7 @@ func clientHTTP(config httpConfig, c client.Client) error { if config.Listen == "" { return configError{Field: "listen", Err: errors.New("listen address is empty")} } - l, err := correctnet.Listen("tcp", config.Listen) + l, err := proxymux.ListenHTTP(config.Listen) if err != nil { return configError{Field: "listen", Err: err} } diff --git a/app/cmd/client_test.go b/app/cmd/client_test.go index 4773114..c586949 100644 --- a/app/cmd/client_test.go +++ b/app/cmd/client_test.go @@ -53,13 +53,6 @@ func TestClientConfig(t *testing.T) { }, FastOpen: true, Lazy: true, - Mixed: &mixedConfig{ - Listen: "127.0.0.1:1080", - Username: "anon", - Password: "bro", - DisableUDP: true, - Realm: "martian", - }, SOCKS5: &socks5Config{ Listen: "127.0.0.1:1080", Username: "anon", diff --git a/app/cmd/client_test.yaml b/app/cmd/client_test.yaml index eb9ea39..4f919df 100644 --- a/app/cmd/client_test.yaml +++ b/app/cmd/client_test.yaml @@ -35,13 +35,6 @@ fastOpen: true lazy: true -mixed: - listen: 127.0.0.1:1080 - username: anon - password: bro - disableUDP: true - realm: martian - socks5: listen: 127.0.0.1:1080 username: anon diff --git a/app/internal/proxymux/manager.go b/app/internal/proxymux/manager.go new file mode 100644 index 0000000..92df918 --- /dev/null +++ b/app/internal/proxymux/manager.go @@ -0,0 +1,72 @@ +package proxymux + +import ( + "net" + "sync" + + "github.com/apernet/hysteria/extras/correctnet" +) + +type muxManager struct { + listeners map[string]*muxListener + lock sync.Mutex +} + +var globalMuxManager *muxManager + +func init() { + globalMuxManager = &muxManager{ + listeners: make(map[string]*muxListener), + } +} + +func (m *muxManager) GetOrCreate(address string) (*muxListener, error) { + key, err := m.canonicalizeAddrPort(address) + if err != nil { + return nil, err + } + + m.lock.Lock() + defer m.lock.Unlock() + + if ml, ok := m.listeners[key]; ok { + return ml, nil + } + + listener, err := correctnet.Listen("tcp", key) + if err != nil { + return nil, err + } + + ml := newMuxListener(listener, func() { + m.lock.Lock() + defer m.lock.Unlock() + delete(m.listeners, key) + }) + m.listeners[key] = ml + return ml, nil +} + +func (m *muxManager) canonicalizeAddrPort(address string) (string, error) { + taddr, err := net.ResolveTCPAddr("tcp", address) + if err != nil { + return "", err + } + return taddr.String(), nil +} + +func ListenHTTP(address string) (net.Listener, error) { + ml, err := globalMuxManager.GetOrCreate(address) + if err != nil { + return nil, err + } + return ml.ListenHTTP() +} + +func ListenSOCKS(address string) (net.Listener, error) { + ml, err := globalMuxManager.GetOrCreate(address) + if err != nil { + return nil, err + } + return ml.ListenSOCKS() +} diff --git a/app/internal/proxymux/mux.go b/app/internal/proxymux/mux.go index d99b122..4070e13 100644 --- a/app/internal/proxymux/mux.go +++ b/app/internal/proxymux/mux.go @@ -1,124 +1,257 @@ -// Package proxymux splits a net.Listener in two, routing SOCKS5 -// connections to one and HTTP requests to the other. -// -// It allows for hosting both a SOCKS5 proxy and an HTTP proxy on the -// same listener. package proxymux import ( + "errors" + "fmt" "io" "net" "sync" - "time" ) -// SplitSOCKSAndHTTP accepts connections on ln and passes connections -// through to either socksListener or httpListener, depending the -// first byte sent by the client. -func SplitSOCKSAndHTTP(ln net.Listener) (socksListener, httpListener net.Listener) { - sl := &listener{ - addr: ln.Addr(), - c: make(chan net.Conn), - closed: make(chan struct{}), +func newMuxListener(listener net.Listener, deleteFunc func()) *muxListener { + l := &muxListener{ + base: listener, + acceptChan: make(chan net.Conn), + closeChan: make(chan struct{}), + deleteFunc: deleteFunc, } - hl := &listener{ - addr: ln.Addr(), - c: make(chan net.Conn), - closed: make(chan struct{}), - } - - go splitSOCKSAndHTTPListener(ln, sl, hl) - - return sl, hl + go l.acceptLoop() + go l.mainLoop() + return l } -func splitSOCKSAndHTTPListener(ln net.Listener, sl, hl *listener) { +type muxListener struct { + lock sync.Mutex + base net.Listener + acceptErr error + + acceptChan chan net.Conn + closeChan chan struct{} + + socksListener *subListener + httpListener *subListener + + deleteFunc func() +} + +func (l *muxListener) acceptLoop() { + defer close(l.acceptChan) + for { - conn, err := ln.Accept() + conn, err := l.base.Accept() if err != nil { - sl.Close() - hl.Close() + l.lock.Lock() + l.acceptErr = err + l.lock.Unlock() return } - go routeConn(conn, sl, hl) + select { + case <-l.closeChan: + return + case l.acceptChan <- conn: + } } } -func routeConn(c net.Conn, socksListener, httpListener *listener) { - if err := c.SetReadDeadline(time.Now().Add(15 * time.Second)); err != nil { - c.Close() - return - } +func (l *muxListener) mainLoop() { + defer func() { + l.deleteFunc() + l.base.Close() + close(l.closeChan) + + l.lock.Lock() + defer l.lock.Unlock() + + if sl := l.httpListener; sl != nil { + close(sl.acceptChan) + l.httpListener = nil + } + if sl := l.socksListener; sl != nil { + close(sl.acceptChan) + l.socksListener = nil + } + }() + + for { + var socksCloseChan, httpCloseChan chan struct{} + if l.httpListener != nil { + httpCloseChan = l.httpListener.closeChan + } + if l.socksListener != nil { + socksCloseChan = l.socksListener.closeChan + } + select { + case <-l.closeChan: + return + case conn, ok := <-l.acceptChan: + if !ok { + return + } + go l.dispatch(conn) + case <-socksCloseChan: + l.lock.Lock() + l.socksListener = nil + l.lock.Unlock() + if l.checkIdle() { + return + } + case <-httpCloseChan: + l.lock.Lock() + l.httpListener = nil + l.lock.Unlock() + if l.checkIdle() { + return + } + } + } +} + +func (l *muxListener) dispatch(conn net.Conn) { var b [1]byte - if _, err := io.ReadFull(c, b[:]); err != nil { - c.Close() + if _, err := io.ReadFull(conn, b[:]); err != nil { + conn.Close() return } - if err := c.SetReadDeadline(time.Time{}); err != nil { - c.Close() - return - } - - conn := &connWithOneByte{ - Conn: c, - b: b[0], - } - - // First byte of a SOCKS5 session is a version byte set to 5. - var ln *listener + l.lock.Lock() + var target *subListener if b[0] == 5 { - ln = socksListener + target = l.socksListener } else { - ln = httpListener + target = l.httpListener } + l.lock.Unlock() + + if target == nil { + conn.Close() + return + } + + wconn := &connWithOneByte{Conn: conn, b: b[0]} + select { - case ln.c <- conn: - case <-ln.closed: - c.Close() + case <-target.closeChan: + case target.acceptChan <- wconn: } } -type listener struct { - addr net.Addr - c chan net.Conn - mu sync.Mutex // serializes close() on closed. It's okay to receive on closed without locking. - closed chan struct{} +func (l *muxListener) checkIdle() bool { + l.lock.Lock() + defer l.lock.Unlock() + + return l.httpListener == nil && l.socksListener == nil } -func (ln *listener) Accept() (net.Conn, error) { - // Once closed, reliably stay closed, don't race with attempts at - // further connections. +func (l *muxListener) getAndClearAcceptError() error { + l.lock.Lock() + defer l.lock.Unlock() + + if l.acceptErr == nil { + return nil + } + err := l.acceptErr + l.acceptErr = nil + return err +} + +func (l *muxListener) ListenHTTP() (net.Listener, error) { + l.lock.Lock() + defer l.lock.Unlock() + + if l.httpListener != nil { + return nil, OpErr{ + Addr: l.base.Addr(), + Protocol: "http", + Op: "bind-protocol", + Err: ErrProtocolInUse, + } + } + select { - case <-ln.closed: + case <-l.closeChan: return nil, net.ErrClosed default: } + + sl := newSubListener(l.getAndClearAcceptError, l.base.Addr) + l.httpListener = sl + return sl, nil +} + +func (l *muxListener) ListenSOCKS() (net.Listener, error) { + l.lock.Lock() + defer l.lock.Unlock() + + if l.socksListener != nil { + return nil, OpErr{ + Addr: l.base.Addr(), + Protocol: "socks", + Op: "bind-protocol", + Err: ErrProtocolInUse, + } + } + select { - case ret := <-ln.c: - return ret, nil - case <-ln.closed: + case <-l.closeChan: return nil, net.ErrClosed + default: + } + + sl := newSubListener(l.getAndClearAcceptError, l.base.Addr) + l.socksListener = sl + return sl, nil +} + +func newSubListener(acceptErrorFunc func() error, addrFunc func() net.Addr) *subListener { + return &subListener{ + acceptChan: make(chan net.Conn), + acceptErrorFunc: acceptErrorFunc, + closeChan: make(chan struct{}), + addrFunc: addrFunc, } } -func (ln *listener) Close() error { - ln.mu.Lock() - defer ln.mu.Unlock() +type subListener struct { + // receive connections or closure from upstream + acceptChan chan net.Conn + // get an error of Accept() from upstream + acceptErrorFunc func() error + // notify upstream that we are closed + closeChan chan struct{} + + // Listener.Addr() implementation of base listener + addrFunc func() net.Addr +} + +func (l *subListener) Accept() (net.Conn, error) { select { - case <-ln.closed: - // Already closed - default: - close(ln.closed) + case <-l.closeChan: + // closed by ourselves + return nil, net.ErrClosed + case conn, ok := <-l.acceptChan: + if !ok { + // closed by upstream + if acceptErr := l.acceptErrorFunc(); acceptErr != nil { + return nil, acceptErr + } + return nil, net.ErrClosed + } + return conn, nil } +} + +func (l *subListener) Addr() net.Addr { + return l.addrFunc() +} + +// Close implements net.Listener.Close. +// Upstream should use close(l.acceptChan) instead. +func (l *subListener) Close() error { + close(l.closeChan) return nil } -func (ln *listener) Addr() net.Addr { - return ln.addr -} - // connWithOneByte is a net.Conn that returns b for the first read // request, then forwards everything else to Conn. type connWithOneByte struct { @@ -139,3 +272,20 @@ func (c *connWithOneByte) Read(bs []byte) (int, error) { bs[0] = c.b return 1, nil } + +type OpErr struct { + Addr net.Addr + Protocol string + Op string + Err error +} + +func (m OpErr) Error() string { + return fmt.Sprintf("mux-listen: %s[%s]: %s: %v", m.Addr, m.Protocol, m.Op, m.Err) +} + +func (m OpErr) Unwrap() error { + return m.Err +} + +var ErrProtocolInUse = errors.New("protocol already in use") From 8d9b10a2593ce273ecc6fd943d8a12af84c7ae90 Mon Sep 17 00:00:00 2001 From: Haruue Date: Thu, 11 Apr 2024 23:07:44 +0800 Subject: [PATCH 059/161] fix(proxymux): close of closed channel when call listener.Close() twice --- app/internal/proxymux/mux.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/internal/proxymux/mux.go b/app/internal/proxymux/mux.go index 4070e13..2d94a76 100644 --- a/app/internal/proxymux/mux.go +++ b/app/internal/proxymux/mux.go @@ -248,6 +248,11 @@ func (l *subListener) Addr() net.Addr { // Close implements net.Listener.Close. // Upstream should use close(l.acceptChan) instead. func (l *subListener) Close() error { + select { + case <-l.closeChan: + return nil + default: + } close(l.closeChan) return nil } From 6d9c4fd4e544a3a224f26a0a99aed61e651464d0 Mon Sep 17 00:00:00 2001 From: Haruue Date: Thu, 11 Apr 2024 23:21:32 +0800 Subject: [PATCH 060/161] test(proxymux): add unit test --- app/internal/proxymux/.mockery.yaml | 12 + .../proxymux/internal/mocks/mock_Conn.go | 427 ++++++++++++++++++ .../proxymux/internal/mocks/mock_Listener.go | 185 ++++++++ app/internal/proxymux/manager_test.go | 116 +++++ app/internal/proxymux/mux_test.go | 111 +++++ 5 files changed, 851 insertions(+) create mode 100644 app/internal/proxymux/.mockery.yaml create mode 100644 app/internal/proxymux/internal/mocks/mock_Conn.go create mode 100644 app/internal/proxymux/internal/mocks/mock_Listener.go create mode 100644 app/internal/proxymux/manager_test.go create mode 100644 app/internal/proxymux/mux_test.go diff --git a/app/internal/proxymux/.mockery.yaml b/app/internal/proxymux/.mockery.yaml new file mode 100644 index 0000000..7d3fac0 --- /dev/null +++ b/app/internal/proxymux/.mockery.yaml @@ -0,0 +1,12 @@ +with-expecter: true +dir: internal/mocks +outpkg: mocks +packages: + net: + interfaces: + Listener: + config: + mockname: MockListener + Conn: + config: + mockname: MockConn diff --git a/app/internal/proxymux/internal/mocks/mock_Conn.go b/app/internal/proxymux/internal/mocks/mock_Conn.go new file mode 100644 index 0000000..0bcbe65 --- /dev/null +++ b/app/internal/proxymux/internal/mocks/mock_Conn.go @@ -0,0 +1,427 @@ +// Code generated by mockery v2.42.2. DO NOT EDIT. + +package mocks + +import ( + net "net" + + mock "github.com/stretchr/testify/mock" + + time "time" +) + +// MockConn is an autogenerated mock type for the Conn type +type MockConn struct { + mock.Mock +} + +type MockConn_Expecter struct { + mock *mock.Mock +} + +func (_m *MockConn) EXPECT() *MockConn_Expecter { + return &MockConn_Expecter{mock: &_m.Mock} +} + +// Close provides a mock function with given fields: +func (_m *MockConn) Close() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Close") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockConn_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' +type MockConn_Close_Call struct { + *mock.Call +} + +// Close is a helper method to define mock.On call +func (_e *MockConn_Expecter) Close() *MockConn_Close_Call { + return &MockConn_Close_Call{Call: _e.mock.On("Close")} +} + +func (_c *MockConn_Close_Call) Run(run func()) *MockConn_Close_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockConn_Close_Call) Return(_a0 error) *MockConn_Close_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockConn_Close_Call) RunAndReturn(run func() error) *MockConn_Close_Call { + _c.Call.Return(run) + return _c +} + +// LocalAddr provides a mock function with given fields: +func (_m *MockConn) LocalAddr() net.Addr { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for LocalAddr") + } + + var r0 net.Addr + if rf, ok := ret.Get(0).(func() net.Addr); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(net.Addr) + } + } + + return r0 +} + +// MockConn_LocalAddr_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LocalAddr' +type MockConn_LocalAddr_Call struct { + *mock.Call +} + +// LocalAddr is a helper method to define mock.On call +func (_e *MockConn_Expecter) LocalAddr() *MockConn_LocalAddr_Call { + return &MockConn_LocalAddr_Call{Call: _e.mock.On("LocalAddr")} +} + +func (_c *MockConn_LocalAddr_Call) Run(run func()) *MockConn_LocalAddr_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockConn_LocalAddr_Call) Return(_a0 net.Addr) *MockConn_LocalAddr_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockConn_LocalAddr_Call) RunAndReturn(run func() net.Addr) *MockConn_LocalAddr_Call { + _c.Call.Return(run) + return _c +} + +// Read provides a mock function with given fields: b +func (_m *MockConn) Read(b []byte) (int, error) { + ret := _m.Called(b) + + if len(ret) == 0 { + panic("no return value specified for Read") + } + + var r0 int + var r1 error + if rf, ok := ret.Get(0).(func([]byte) (int, error)); ok { + return rf(b) + } + if rf, ok := ret.Get(0).(func([]byte) int); ok { + r0 = rf(b) + } else { + r0 = ret.Get(0).(int) + } + + if rf, ok := ret.Get(1).(func([]byte) error); ok { + r1 = rf(b) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockConn_Read_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Read' +type MockConn_Read_Call struct { + *mock.Call +} + +// Read is a helper method to define mock.On call +// - b []byte +func (_e *MockConn_Expecter) Read(b interface{}) *MockConn_Read_Call { + return &MockConn_Read_Call{Call: _e.mock.On("Read", b)} +} + +func (_c *MockConn_Read_Call) Run(run func(b []byte)) *MockConn_Read_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].([]byte)) + }) + return _c +} + +func (_c *MockConn_Read_Call) Return(n int, err error) *MockConn_Read_Call { + _c.Call.Return(n, err) + return _c +} + +func (_c *MockConn_Read_Call) RunAndReturn(run func([]byte) (int, error)) *MockConn_Read_Call { + _c.Call.Return(run) + return _c +} + +// RemoteAddr provides a mock function with given fields: +func (_m *MockConn) RemoteAddr() net.Addr { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for RemoteAddr") + } + + var r0 net.Addr + if rf, ok := ret.Get(0).(func() net.Addr); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(net.Addr) + } + } + + return r0 +} + +// MockConn_RemoteAddr_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoteAddr' +type MockConn_RemoteAddr_Call struct { + *mock.Call +} + +// RemoteAddr is a helper method to define mock.On call +func (_e *MockConn_Expecter) RemoteAddr() *MockConn_RemoteAddr_Call { + return &MockConn_RemoteAddr_Call{Call: _e.mock.On("RemoteAddr")} +} + +func (_c *MockConn_RemoteAddr_Call) Run(run func()) *MockConn_RemoteAddr_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockConn_RemoteAddr_Call) Return(_a0 net.Addr) *MockConn_RemoteAddr_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockConn_RemoteAddr_Call) RunAndReturn(run func() net.Addr) *MockConn_RemoteAddr_Call { + _c.Call.Return(run) + return _c +} + +// SetDeadline provides a mock function with given fields: t +func (_m *MockConn) SetDeadline(t time.Time) error { + ret := _m.Called(t) + + if len(ret) == 0 { + panic("no return value specified for SetDeadline") + } + + var r0 error + if rf, ok := ret.Get(0).(func(time.Time) error); ok { + r0 = rf(t) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockConn_SetDeadline_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetDeadline' +type MockConn_SetDeadline_Call struct { + *mock.Call +} + +// SetDeadline is a helper method to define mock.On call +// - t time.Time +func (_e *MockConn_Expecter) SetDeadline(t interface{}) *MockConn_SetDeadline_Call { + return &MockConn_SetDeadline_Call{Call: _e.mock.On("SetDeadline", t)} +} + +func (_c *MockConn_SetDeadline_Call) Run(run func(t time.Time)) *MockConn_SetDeadline_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(time.Time)) + }) + return _c +} + +func (_c *MockConn_SetDeadline_Call) Return(_a0 error) *MockConn_SetDeadline_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockConn_SetDeadline_Call) RunAndReturn(run func(time.Time) error) *MockConn_SetDeadline_Call { + _c.Call.Return(run) + return _c +} + +// SetReadDeadline provides a mock function with given fields: t +func (_m *MockConn) SetReadDeadline(t time.Time) error { + ret := _m.Called(t) + + if len(ret) == 0 { + panic("no return value specified for SetReadDeadline") + } + + var r0 error + if rf, ok := ret.Get(0).(func(time.Time) error); ok { + r0 = rf(t) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockConn_SetReadDeadline_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetReadDeadline' +type MockConn_SetReadDeadline_Call struct { + *mock.Call +} + +// SetReadDeadline is a helper method to define mock.On call +// - t time.Time +func (_e *MockConn_Expecter) SetReadDeadline(t interface{}) *MockConn_SetReadDeadline_Call { + return &MockConn_SetReadDeadline_Call{Call: _e.mock.On("SetReadDeadline", t)} +} + +func (_c *MockConn_SetReadDeadline_Call) Run(run func(t time.Time)) *MockConn_SetReadDeadline_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(time.Time)) + }) + return _c +} + +func (_c *MockConn_SetReadDeadline_Call) Return(_a0 error) *MockConn_SetReadDeadline_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockConn_SetReadDeadline_Call) RunAndReturn(run func(time.Time) error) *MockConn_SetReadDeadline_Call { + _c.Call.Return(run) + return _c +} + +// SetWriteDeadline provides a mock function with given fields: t +func (_m *MockConn) SetWriteDeadline(t time.Time) error { + ret := _m.Called(t) + + if len(ret) == 0 { + panic("no return value specified for SetWriteDeadline") + } + + var r0 error + if rf, ok := ret.Get(0).(func(time.Time) error); ok { + r0 = rf(t) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockConn_SetWriteDeadline_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetWriteDeadline' +type MockConn_SetWriteDeadline_Call struct { + *mock.Call +} + +// SetWriteDeadline is a helper method to define mock.On call +// - t time.Time +func (_e *MockConn_Expecter) SetWriteDeadline(t interface{}) *MockConn_SetWriteDeadline_Call { + return &MockConn_SetWriteDeadline_Call{Call: _e.mock.On("SetWriteDeadline", t)} +} + +func (_c *MockConn_SetWriteDeadline_Call) Run(run func(t time.Time)) *MockConn_SetWriteDeadline_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(time.Time)) + }) + return _c +} + +func (_c *MockConn_SetWriteDeadline_Call) Return(_a0 error) *MockConn_SetWriteDeadline_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockConn_SetWriteDeadline_Call) RunAndReturn(run func(time.Time) error) *MockConn_SetWriteDeadline_Call { + _c.Call.Return(run) + return _c +} + +// Write provides a mock function with given fields: b +func (_m *MockConn) Write(b []byte) (int, error) { + ret := _m.Called(b) + + if len(ret) == 0 { + panic("no return value specified for Write") + } + + var r0 int + var r1 error + if rf, ok := ret.Get(0).(func([]byte) (int, error)); ok { + return rf(b) + } + if rf, ok := ret.Get(0).(func([]byte) int); ok { + r0 = rf(b) + } else { + r0 = ret.Get(0).(int) + } + + if rf, ok := ret.Get(1).(func([]byte) error); ok { + r1 = rf(b) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockConn_Write_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Write' +type MockConn_Write_Call struct { + *mock.Call +} + +// Write is a helper method to define mock.On call +// - b []byte +func (_e *MockConn_Expecter) Write(b interface{}) *MockConn_Write_Call { + return &MockConn_Write_Call{Call: _e.mock.On("Write", b)} +} + +func (_c *MockConn_Write_Call) Run(run func(b []byte)) *MockConn_Write_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].([]byte)) + }) + return _c +} + +func (_c *MockConn_Write_Call) Return(n int, err error) *MockConn_Write_Call { + _c.Call.Return(n, err) + return _c +} + +func (_c *MockConn_Write_Call) RunAndReturn(run func([]byte) (int, error)) *MockConn_Write_Call { + _c.Call.Return(run) + return _c +} + +// NewMockConn creates a new instance of MockConn. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockConn(t interface { + mock.TestingT + Cleanup(func()) +}) *MockConn { + mock := &MockConn{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/app/internal/proxymux/internal/mocks/mock_Listener.go b/app/internal/proxymux/internal/mocks/mock_Listener.go new file mode 100644 index 0000000..e4ca2f4 --- /dev/null +++ b/app/internal/proxymux/internal/mocks/mock_Listener.go @@ -0,0 +1,185 @@ +// Code generated by mockery v2.42.2. DO NOT EDIT. + +package mocks + +import ( + net "net" + + mock "github.com/stretchr/testify/mock" +) + +// MockListener is an autogenerated mock type for the Listener type +type MockListener struct { + mock.Mock +} + +type MockListener_Expecter struct { + mock *mock.Mock +} + +func (_m *MockListener) EXPECT() *MockListener_Expecter { + return &MockListener_Expecter{mock: &_m.Mock} +} + +// Accept provides a mock function with given fields: +func (_m *MockListener) Accept() (net.Conn, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Accept") + } + + var r0 net.Conn + var r1 error + if rf, ok := ret.Get(0).(func() (net.Conn, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() net.Conn); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(net.Conn) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockListener_Accept_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Accept' +type MockListener_Accept_Call struct { + *mock.Call +} + +// Accept is a helper method to define mock.On call +func (_e *MockListener_Expecter) Accept() *MockListener_Accept_Call { + return &MockListener_Accept_Call{Call: _e.mock.On("Accept")} +} + +func (_c *MockListener_Accept_Call) Run(run func()) *MockListener_Accept_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockListener_Accept_Call) Return(_a0 net.Conn, _a1 error) *MockListener_Accept_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockListener_Accept_Call) RunAndReturn(run func() (net.Conn, error)) *MockListener_Accept_Call { + _c.Call.Return(run) + return _c +} + +// Addr provides a mock function with given fields: +func (_m *MockListener) Addr() net.Addr { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Addr") + } + + var r0 net.Addr + if rf, ok := ret.Get(0).(func() net.Addr); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(net.Addr) + } + } + + return r0 +} + +// MockListener_Addr_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Addr' +type MockListener_Addr_Call struct { + *mock.Call +} + +// Addr is a helper method to define mock.On call +func (_e *MockListener_Expecter) Addr() *MockListener_Addr_Call { + return &MockListener_Addr_Call{Call: _e.mock.On("Addr")} +} + +func (_c *MockListener_Addr_Call) Run(run func()) *MockListener_Addr_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockListener_Addr_Call) Return(_a0 net.Addr) *MockListener_Addr_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockListener_Addr_Call) RunAndReturn(run func() net.Addr) *MockListener_Addr_Call { + _c.Call.Return(run) + return _c +} + +// Close provides a mock function with given fields: +func (_m *MockListener) Close() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Close") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockListener_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' +type MockListener_Close_Call struct { + *mock.Call +} + +// Close is a helper method to define mock.On call +func (_e *MockListener_Expecter) Close() *MockListener_Close_Call { + return &MockListener_Close_Call{Call: _e.mock.On("Close")} +} + +func (_c *MockListener_Close_Call) Run(run func()) *MockListener_Close_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockListener_Close_Call) Return(_a0 error) *MockListener_Close_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockListener_Close_Call) RunAndReturn(run func() error) *MockListener_Close_Call { + _c.Call.Return(run) + return _c +} + +// NewMockListener creates a new instance of MockListener. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockListener(t interface { + mock.TestingT + Cleanup(func()) +}) *MockListener { + mock := &MockListener{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/app/internal/proxymux/manager_test.go b/app/internal/proxymux/manager_test.go new file mode 100644 index 0000000..1d5a7af --- /dev/null +++ b/app/internal/proxymux/manager_test.go @@ -0,0 +1,116 @@ +package proxymux + +import ( + "net" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestListenSOCKS(t *testing.T) { + address := "127.2.39.129:11081" + + sl, err := ListenSOCKS(address) + if !assert.NoError(t, err) { + return + } + defer func() { + sl.Close() + }() + + hl, err := ListenHTTP(address) + if !assert.NoError(t, err) { + return + } + defer hl.Close() + + _, err = ListenSOCKS(address) + if !assert.ErrorIs(t, err, ErrProtocolInUse) { + return + } + sl.Close() + + // Wait for muxListener.socksListener released + time.Sleep(time.Second) + + sl, err = ListenSOCKS(address) + if !assert.NoError(t, err) { + return + } +} + +func TestListenHTTP(t *testing.T) { + address := "127.2.39.129:11082" + + hl, err := ListenHTTP(address) + if !assert.NoError(t, err) { + return + } + defer func() { + hl.Close() + }() + + sl, err := ListenSOCKS(address) + if !assert.NoError(t, err) { + return + } + defer sl.Close() + + _, err = ListenHTTP(address) + if !assert.ErrorIs(t, err, ErrProtocolInUse) { + return + } + hl.Close() + + // Wait for muxListener.socksListener released + time.Sleep(time.Second) + + hl, err = ListenHTTP(address) + if !assert.NoError(t, err) { + return + } +} + +func TestRelease(t *testing.T) { + address := "127.2.39.129:11083" + + hl, err := ListenHTTP(address) + if !assert.NoError(t, err) { + return + } + sl, err := ListenSOCKS(address) + if !assert.NoError(t, err) { + return + } + + if !assert.True(t, globalMuxManager.testAddressExists(address)) { + return + } + _, err = net.Listen("tcp", address) + if !assert.Error(t, err) { + return + } + + hl.Close() + sl.Close() + + // Wait for muxListener released + time.Sleep(time.Second) + if !assert.False(t, globalMuxManager.testAddressExists(address)) { + return + } + lis, err := net.Listen("tcp", address) + if !assert.NoError(t, err) { + return + } + defer lis.Close() +} + +func (m *muxManager) testAddressExists(address string) bool { + m.lock.Lock() + defer m.lock.Unlock() + + _, ok := m.listeners[address] + return ok +} diff --git a/app/internal/proxymux/mux_test.go b/app/internal/proxymux/mux_test.go new file mode 100644 index 0000000..0e24f95 --- /dev/null +++ b/app/internal/proxymux/mux_test.go @@ -0,0 +1,111 @@ +package proxymux + +import ( + "net" + "testing" + "time" + + "github.com/apernet/hysteria/app/internal/proxymux/internal/mocks" + "github.com/stretchr/testify/mock" + + "github.com/stretchr/testify/assert" +) + +//go:generate mockery + +func testMockListener(t *testing.T, firstByte byte) net.Listener { + mockConn := mocks.NewMockConn(t) + mockConn.EXPECT().Read(mock.Anything).RunAndReturn(func(b []byte) (int, error) { + b[0] = firstByte + return 1, nil + }) + mockConn.EXPECT().Close().Return(nil) + mockListener := mocks.NewMockListener(t) + mockListener.EXPECT().Accept().RunAndReturn(func() (net.Conn, error) { + // Wait for all listener set up + time.Sleep(200 * time.Millisecond) + return mockConn, nil + }) + mockListener.EXPECT().Close().Return(nil) + return mockListener +} + +func TestMuxHTTP(t *testing.T) { + mockListener := testMockListener(t, 'C') + + mux := newMuxListener(mockListener, func() {}) + hl, err := mux.ListenHTTP() + if !assert.NoError(t, err) { + return + } + sl, err := mux.ListenSOCKS() + if !assert.NoError(t, err) { + return + } + + var socksConn, httpConn net.Conn + var socksErr, httpErr error + + go func() { + socksConn, socksErr = sl.Accept() + }() + + go func() { + httpConn, httpErr = hl.Accept() + }() + + time.Sleep(1 * time.Second) + sl.Close() + hl.Close() + // Wait for unmatched handler error + time.Sleep(1 * time.Second) + + assert.Nil(t, socksConn) + assert.ErrorIs(t, socksErr, net.ErrClosed) + assert.NotNil(t, httpConn) + httpConn.Close() + assert.NoError(t, httpErr) + + // Wait for muxListener released + time.Sleep(time.Second) +} + +func TestMuxSOCKS(t *testing.T) { + mockListener := testMockListener(t, '\x05') + + mux := newMuxListener(mockListener, func() {}) + hl, err := mux.ListenHTTP() + if !assert.NoError(t, err) { + return + } + sl, err := mux.ListenSOCKS() + if !assert.NoError(t, err) { + return + } + + var socksConn, httpConn net.Conn + var socksErr, httpErr error + + go func() { + socksConn, socksErr = sl.Accept() + }() + + go func() { + httpConn, httpErr = hl.Accept() + }() + + time.Sleep(1 * time.Second) + sl.Close() + hl.Close() + // Wait for unmatched handler error + time.Sleep(1 * time.Second) + + assert.NotNil(t, socksConn) + socksConn.Close() + assert.NoError(t, socksErr) + assert.Nil(t, httpConn) + assert.ErrorIs(t, httpErr, net.ErrClosed) + + // Wait for muxListener released + time.Sleep(time.Second) +} From 044620a5db63c2156a49c4e7888d60720a8c030e Mon Sep 17 00:00:00 2001 From: Haruue Date: Fri, 12 Apr 2024 14:47:12 +0800 Subject: [PATCH 061/161] chore(proxymux): make subListener dereg immediate Now you can call ListenHTTP() again immediately after previous closed. --- app/internal/proxymux/mux.go | 48 +++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/app/internal/proxymux/mux.go b/app/internal/proxymux/mux.go index 2d94a76..1f0b7b0 100644 --- a/app/internal/proxymux/mux.go +++ b/app/internal/proxymux/mux.go @@ -91,14 +91,20 @@ func (l *muxListener) mainLoop() { go l.dispatch(conn) case <-socksCloseChan: l.lock.Lock() - l.socksListener = nil + if socksCloseChan == l.socksListener.closeChan { + // not replaced by another ListenSOCKS() + l.socksListener = nil + } l.lock.Unlock() if l.checkIdle() { return } case <-httpCloseChan: l.lock.Lock() - l.httpListener = nil + if httpCloseChan == l.httpListener.closeChan { + // not replaced by another ListenHTTP() + l.httpListener = nil + } l.lock.Unlock() if l.checkIdle() { return @@ -160,12 +166,21 @@ func (l *muxListener) ListenHTTP() (net.Listener, error) { defer l.lock.Unlock() if l.httpListener != nil { - return nil, OpErr{ - Addr: l.base.Addr(), - Protocol: "http", - Op: "bind-protocol", - Err: ErrProtocolInUse, + subListenerPendingClosed := false + select { + case <-l.httpListener.closeChan: + subListenerPendingClosed = true + default: } + if !subListenerPendingClosed { + return nil, OpErr{ + Addr: l.base.Addr(), + Protocol: "http", + Op: "bind-protocol", + Err: ErrProtocolInUse, + } + } + l.httpListener = nil } select { @@ -184,12 +199,21 @@ func (l *muxListener) ListenSOCKS() (net.Listener, error) { defer l.lock.Unlock() if l.socksListener != nil { - return nil, OpErr{ - Addr: l.base.Addr(), - Protocol: "socks", - Op: "bind-protocol", - Err: ErrProtocolInUse, + subListenerPendingClosed := false + select { + case <-l.socksListener.closeChan: + subListenerPendingClosed = true + default: } + if !subListenerPendingClosed { + return nil, OpErr{ + Addr: l.base.Addr(), + Protocol: "socks", + Op: "bind-protocol", + Err: ErrProtocolInUse, + } + } + l.socksListener = nil } select { From 8e886b6e0572227169b62af4e341f49e81944dd8 Mon Sep 17 00:00:00 2001 From: Haruue Date: Fri, 12 Apr 2024 14:55:17 +0800 Subject: [PATCH 062/161] test(proxymux): reduce wait in the tests --- app/internal/proxymux/manager_test.go | 6 -- app/internal/proxymux/mux_test.go | 89 ++++++++++++++++++++------- 2 files changed, 66 insertions(+), 29 deletions(-) diff --git a/app/internal/proxymux/manager_test.go b/app/internal/proxymux/manager_test.go index 1d5a7af..c776058 100644 --- a/app/internal/proxymux/manager_test.go +++ b/app/internal/proxymux/manager_test.go @@ -31,9 +31,6 @@ func TestListenSOCKS(t *testing.T) { } sl.Close() - // Wait for muxListener.socksListener released - time.Sleep(time.Second) - sl, err = ListenSOCKS(address) if !assert.NoError(t, err) { return @@ -63,9 +60,6 @@ func TestListenHTTP(t *testing.T) { } hl.Close() - // Wait for muxListener.socksListener released - time.Sleep(time.Second) - hl, err = ListenHTTP(address) if !assert.NoError(t, err) { return diff --git a/app/internal/proxymux/mux_test.go b/app/internal/proxymux/mux_test.go index 0e24f95..7a42b83 100644 --- a/app/internal/proxymux/mux_test.go +++ b/app/internal/proxymux/mux_test.go @@ -1,7 +1,9 @@ package proxymux import ( + "bytes" "net" + "sync" "testing" "time" @@ -13,25 +15,52 @@ import ( //go:generate mockery -func testMockListener(t *testing.T, firstByte byte) net.Listener { - mockConn := mocks.NewMockConn(t) - mockConn.EXPECT().Read(mock.Anything).RunAndReturn(func(b []byte) (int, error) { - b[0] = firstByte - return 1, nil - }) - mockConn.EXPECT().Close().Return(nil) +func testMockListener(t *testing.T, connChan <-chan net.Conn) net.Listener { + closedChan := make(chan struct{}) mockListener := mocks.NewMockListener(t) mockListener.EXPECT().Accept().RunAndReturn(func() (net.Conn, error) { - // Wait for all listener set up - time.Sleep(200 * time.Millisecond) - return mockConn, nil + select { + case <-closedChan: + return nil, net.ErrClosed + case conn, ok := <-connChan: + if !ok { + panic("unexpected closed channel (connChan)") + } + return conn, nil + } + }) + mockListener.EXPECT().Close().RunAndReturn(func() error { + select { + case <-closedChan: + default: + close(closedChan) + } + return nil }) - mockListener.EXPECT().Close().Return(nil) return mockListener } +func testMockConn(t *testing.T, b []byte) net.Conn { + buf := bytes.NewReader(b) + isClosed := false + mockConn := mocks.NewMockConn(t) + mockConn.EXPECT().Read(mock.Anything).RunAndReturn(func(b []byte) (int, error) { + if isClosed { + return 0, net.ErrClosed + } + return buf.Read(b) + }) + mockConn.EXPECT().Close().RunAndReturn(func() error { + isClosed = true + return nil + }) + return mockConn +} + func TestMuxHTTP(t *testing.T) { - mockListener := testMockListener(t, 'C') + connChan := make(chan net.Conn) + mockListener := testMockListener(t, connChan) + mockConn := testMockConn(t, []byte("CONNECT example.com:443 HTTP/1.1\r\n\r\n")) mux := newMuxListener(mockListener, func() {}) hl, err := mux.ListenHTTP() @@ -43,22 +72,28 @@ func TestMuxHTTP(t *testing.T) { return } + connChan <- mockConn + var socksConn, httpConn net.Conn var socksErr, httpErr error + var wg sync.WaitGroup + wg.Add(2) go func() { socksConn, socksErr = sl.Accept() + wg.Done() }() - go func() { httpConn, httpErr = hl.Accept() + wg.Done() }() - time.Sleep(1 * time.Second) + time.Sleep(time.Second) + sl.Close() hl.Close() - // Wait for unmatched handler error - time.Sleep(1 * time.Second) + + wg.Wait() assert.Nil(t, socksConn) assert.ErrorIs(t, socksErr, net.ErrClosed) @@ -67,11 +102,13 @@ func TestMuxHTTP(t *testing.T) { assert.NoError(t, httpErr) // Wait for muxListener released - time.Sleep(time.Second) + <-mux.acceptChan } func TestMuxSOCKS(t *testing.T) { - mockListener := testMockListener(t, '\x05') + connChan := make(chan net.Conn) + mockListener := testMockListener(t, connChan) + mockConn := testMockConn(t, []byte{0x05, 0x02, 0x00, 0x01}) // SOCKS5 Connect Request: NOAUTH+GSSAPI mux := newMuxListener(mockListener, func() {}) hl, err := mux.ListenHTTP() @@ -83,22 +120,28 @@ func TestMuxSOCKS(t *testing.T) { return } + connChan <- mockConn + var socksConn, httpConn net.Conn var socksErr, httpErr error + var wg sync.WaitGroup + wg.Add(2) go func() { socksConn, socksErr = sl.Accept() + wg.Done() }() - go func() { httpConn, httpErr = hl.Accept() + wg.Done() }() - time.Sleep(1 * time.Second) + time.Sleep(time.Second) + sl.Close() hl.Close() - // Wait for unmatched handler error - time.Sleep(1 * time.Second) + + wg.Wait() assert.NotNil(t, socksConn) socksConn.Close() @@ -107,5 +150,5 @@ func TestMuxSOCKS(t *testing.T) { assert.ErrorIs(t, httpErr, net.ErrClosed) // Wait for muxListener released - time.Sleep(time.Second) + <-mux.acceptChan } From e1d8901c165f1984111c9df0c839f2885749ce04 Mon Sep 17 00:00:00 2001 From: Toby Date: Fri, 12 Apr 2024 10:49:56 -0700 Subject: [PATCH 063/161] chore: adjust import format --- app/cmd/client.go | 3 +-- app/internal/proxymux/mux_test.go | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/cmd/client.go b/app/cmd/client.go index 9e09070..1e9bca5 100644 --- a/app/cmd/client.go +++ b/app/cmd/client.go @@ -15,14 +15,13 @@ import ( "strings" "time" - "github.com/apernet/hysteria/app/internal/proxymux" - "github.com/spf13/cobra" "github.com/spf13/viper" "go.uber.org/zap" "github.com/apernet/hysteria/app/internal/forwarding" "github.com/apernet/hysteria/app/internal/http" + "github.com/apernet/hysteria/app/internal/proxymux" "github.com/apernet/hysteria/app/internal/redirect" "github.com/apernet/hysteria/app/internal/socks5" "github.com/apernet/hysteria/app/internal/tproxy" diff --git a/app/internal/proxymux/mux_test.go b/app/internal/proxymux/mux_test.go index 7a42b83..46cbf95 100644 --- a/app/internal/proxymux/mux_test.go +++ b/app/internal/proxymux/mux_test.go @@ -8,9 +8,9 @@ import ( "time" "github.com/apernet/hysteria/app/internal/proxymux/internal/mocks" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) //go:generate mockery From 2408301c987b832c4a8f0ce2ceda140043489a30 Mon Sep 17 00:00:00 2001 From: Toby Date: Sun, 14 Apr 2024 15:07:43 -0700 Subject: [PATCH 064/161] feat: pacer code improvements --- core/internal/congestion/common/pacer.go | 46 ++++++++---------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/core/internal/congestion/common/pacer.go b/core/internal/congestion/common/pacer.go index 4e089a3..6adbd81 100644 --- a/core/internal/congestion/common/pacer.go +++ b/core/internal/congestion/common/pacer.go @@ -1,14 +1,14 @@ package common import ( - "math" "time" "github.com/apernet/quic-go/congestion" ) const ( - maxBurstPackets = 10 + maxBurstPackets = 10 + maxBurstPacingDelayMultiplier = 4 ) // Pacer implements a token bucket pacing algorithm. @@ -46,12 +46,12 @@ func (p *Pacer) Budget(now time.Time) congestion.ByteCount { if budget < 0 { // protect against overflows budget = congestion.ByteCount(1<<62 - 1) } - return minByteCount(p.maxBurstSize(), budget) + return min(p.maxBurstSize(), budget) } func (p *Pacer) maxBurstSize() congestion.ByteCount { - return maxByteCount( - congestion.ByteCount((congestion.MinPacingDelay+time.Millisecond).Nanoseconds())*p.getBandwidth()/1e9, + return max( + congestion.ByteCount((maxBurstPacingDelayMultiplier*congestion.MinPacingDelay).Nanoseconds())*p.getBandwidth()/1e9, maxBurstPackets*p.maxDatagramSize, ) } @@ -62,34 +62,18 @@ func (p *Pacer) TimeUntilSend() time.Time { if p.budgetAtLastSent >= p.maxDatagramSize { return time.Time{} } - return p.lastSentTime.Add(maxDuration( - congestion.MinPacingDelay, - time.Duration(math.Ceil(float64(p.maxDatagramSize-p.budgetAtLastSent)*1e9/ - float64(p.getBandwidth())))*time.Nanosecond, - )) + diff := 1e9 * uint64(p.maxDatagramSize-p.budgetAtLastSent) + bw := uint64(p.getBandwidth()) + // We might need to round up this value. + // Otherwise, we might have a budget (slightly) smaller than the datagram size when the timer expires. + d := diff / bw + // this is effectively a math.Ceil, but using only integer math + if diff%bw > 0 { + d++ + } + return p.lastSentTime.Add(max(congestion.MinPacingDelay, time.Duration(d)*time.Nanosecond)) } func (p *Pacer) SetMaxDatagramSize(s congestion.ByteCount) { p.maxDatagramSize = s } - -func maxByteCount(a, b congestion.ByteCount) congestion.ByteCount { - if a < b { - return b - } - return a -} - -func minByteCount(a, b congestion.ByteCount) congestion.ByteCount { - if a < b { - return a - } - return b -} - -func maxDuration(a, b time.Duration) time.Duration { - if a > b { - return a - } - return b -} From 97523470735df59742b31ba7cff7606d9a03c0b8 Mon Sep 17 00:00:00 2001 From: Haruue Date: Mon, 15 Apr 2024 19:31:23 +0800 Subject: [PATCH 065/161] fix: check if cert-key is loadable on server start close: #1040 --- app/cmd/server.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/cmd/server.go b/app/cmd/server.go index 9b6665e..3f52c28 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -10,6 +10,7 @@ import ( "net/http" "net/http/httputil" "net/url" + "os" "strconv" "strings" "time" @@ -254,6 +255,19 @@ func (c *serverConfig) fillTLSConfig(hyConfig *server.Config) error { if c.TLS.Cert == "" || c.TLS.Key == "" { return configError{Field: "tls", Err: errors.New("empty cert or key path")} } + // Load cert-key pair here for early error reporting (especially permission denied) + certPEMBlock, err := os.ReadFile(c.TLS.Cert) + if err != nil { + return configError{Field: "tls.cert", Err: err} + } + keyPEMBlock, err := os.ReadFile(c.TLS.Key) + if err != nil { + return configError{Field: "tls.key", Err: err} + } + _, err = tls.X509KeyPair(certPEMBlock, keyPEMBlock) + if err != nil { + return configError{Field: "tls", Err: fmt.Errorf("invalid cert-key pair: %w", err)} + } // Use GetCertificate instead of Certificates so that // users can update the cert without restarting the server. hyConfig.TLSConfig.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { From dc1f58414a723cf5bec8ea6ecebc0e5163eb9b41 Mon Sep 17 00:00:00 2001 From: Toby Date: Mon, 15 Apr 2024 14:58:09 -0700 Subject: [PATCH 066/161] chore: improve comments --- app/cmd/server.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/cmd/server.go b/app/cmd/server.go index 3f52c28..9e81312 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -255,7 +255,8 @@ func (c *serverConfig) fillTLSConfig(hyConfig *server.Config) error { if c.TLS.Cert == "" || c.TLS.Key == "" { return configError{Field: "tls", Err: errors.New("empty cert or key path")} } - // Load cert-key pair here for early error reporting (especially permission denied) + // Try loading the cert-key pair here to catch errors early + // (e.g. invalid files or insufficient permissions) certPEMBlock, err := os.ReadFile(c.TLS.Cert) if err != nil { return configError{Field: "tls.cert", Err: err} From 1b78b2ec90db8f7974cb22359fc7e11760ec1ec0 Mon Sep 17 00:00:00 2001 From: Toby Date: Mon, 15 Apr 2024 15:06:16 -0700 Subject: [PATCH 067/161] chore: sync README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f8521af..94fa335 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,8 @@
-

🤗 Cards on the table

-

We have well-documented specifications and code for developers to contribute and build their own apps. And a helpful community, too.

+

🤗 Chill and supportive

+

We have well-documented specifications and code for developers to contribute and/or build their own apps. And a helpful community, too.

From c392b0338bd55913a2a0eac3721c4d35b9ee2c57 Mon Sep 17 00:00:00 2001 From: Toby Date: Fri, 19 Apr 2024 14:42:06 -0700 Subject: [PATCH 068/161] chore(deps): bump golang.org/x/net from 0.21.0 to 0.24.0 --- app/go.mod | 6 +++--- app/go.sum | 12 ++++++------ core/go.mod | 6 +++--- core/go.sum | 12 ++++++------ extras/go.mod | 6 +++--- extras/go.sum | 12 ++++++------ go.work.sum | 3 +++ 7 files changed, 30 insertions(+), 27 deletions(-) diff --git a/app/go.mod b/app/go.mod index 97c5bbb..f2e16b7 100644 --- a/app/go.mod +++ b/app/go.mod @@ -17,7 +17,7 @@ require ( github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 go.uber.org/zap v1.24.0 golang.org/x/exp v0.0.0-20221205204356-47842c84f3db - golang.org/x/sys v0.17.0 + golang.org/x/sys v0.19.0 ) require ( @@ -55,9 +55,9 @@ require ( go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect - golang.org/x/crypto v0.19.0 // indirect + golang.org/x/crypto v0.22.0 // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.21.0 // indirect + golang.org/x/net v0.24.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.11.1 // indirect google.golang.org/protobuf v1.33.0 // indirect diff --git a/app/go.sum b/app/go.sum index 4f2b0fe..58483a3 100644 --- a/app/go.sum +++ b/app/go.sum @@ -277,8 +277,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -354,8 +354,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -422,8 +422,8 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= diff --git a/core/go.mod b/core/go.mod index 91d4cb9..9928286 100644 --- a/core/go.mod +++ b/core/go.mod @@ -21,10 +21,10 @@ require ( github.com/quic-go/qpack v0.4.0 // indirect github.com/stretchr/objx v0.5.0 // indirect go.uber.org/mock v0.4.0 // indirect - golang.org/x/crypto v0.19.0 // indirect + golang.org/x/crypto v0.22.0 // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.21.0 // indirect - golang.org/x/sys v0.17.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.11.1 // indirect google.golang.org/protobuf v1.33.0 // indirect diff --git a/core/go.sum b/core/go.sum index 908b18e..c205cfa 100644 --- a/core/go.sum +++ b/core/go.sum @@ -45,17 +45,17 @@ go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= diff --git a/extras/go.mod b/extras/go.mod index bc21044..6fa6bda 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -9,8 +9,8 @@ require ( github.com/miekg/dns v1.1.55 github.com/stretchr/testify v1.8.4 github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 - golang.org/x/crypto v0.19.0 - golang.org/x/net v0.21.0 + golang.org/x/crypto v0.22.0 + golang.org/x/net v0.24.0 google.golang.org/protobuf v1.33.0 ) @@ -28,7 +28,7 @@ require ( go.uber.org/mock v0.4.0 // indirect golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/sys v0.17.0 // indirect + golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.11.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/extras/go.sum b/extras/go.sum index 390cc97..68b0aae 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -58,8 +58,8 @@ go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -70,8 +70,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -84,8 +84,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= diff --git a/go.work.sum b/go.work.sum index 62f146c..e1d4747 100644 --- a/go.work.sum +++ b/go.work.sum @@ -241,6 +241,7 @@ golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a h1:Jw5wfR+h9mnIYH+OtGT2im5wV1YGGDora5vTv/aa5bE= golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= @@ -273,6 +274,7 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= @@ -284,6 +286,7 @@ golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= From e7d7dbbf8ff942c0e70cd6c70cfc6379fe1e0243 Mon Sep 17 00:00:00 2001 From: Haruue Date: Fri, 26 Apr 2024 14:52:34 +0800 Subject: [PATCH 069/161] fix(scripts): chcon error on CentOS 7 --- scripts/install_server.sh | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/scripts/install_server.sh b/scripts/install_server.sh index b71f075..da6626a 100644 --- a/scripts/install_server.sh +++ b/scripts/install_server.sh @@ -187,6 +187,29 @@ chcon() { command chcon "$@" } +get_selinux_context() { + local _file="$1" + + local _lsres="$(ls -dZ "$_file" | head -1)" + local _sectx='' + case "$(echo "$_lsres" | wc -w)" in + 2) + _sectx="$(echo "$_lsres" | cut -d ' ' -f 1)" + ;; + 5) + _sectx="$(echo "$_lsres" | cut -d ' ' -f 4)" + ;; + *) + ;; + esac + + if [[ "x$_sectx" == "x?" ]]; then + _sectx="" + fi + + echo "$_sectx" +} + show_argument_error_and_exit() { local _error_msg="$1" @@ -433,8 +456,8 @@ check_environment_selinux() { if [[ -z "$SECONTEXT_SYSTEMD_UNIT" ]]; then if [[ -z "$FORCE_NO_SYSTEMD" ]] && [[ -e "$SYSTEMD_SERVICES_DIR" ]]; then - local _sectx="$(ls -ldZ "$SYSTEMD_SERVICES_DIR" | cut -d ' ' -f 5)" - if [[ "x$_sectx" == "x?" ]]; then + local _sectx="$(get_selinux_context "$SYSTEMD_SERVICES_DIR")" + if [[ -z "$_sectx" ]]; then warning "Failed to obtain SEContext of $SYSTEMD_SERVICES_DIR" else SECONTEXT_SYSTEMD_UNIT="$_sectx" From 00813c46221600844718434fe1364454b9f38207 Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 27 Apr 2024 13:04:51 -0700 Subject: [PATCH 070/161] feat: quic-go v0.43.0 --- app/go.mod | 2 +- app/go.sum | 4 ++-- core/client/client.go | 2 +- core/go.mod | 2 +- core/go.sum | 4 ++-- core/server/server.go | 2 +- extras/go.mod | 2 +- extras/go.sum | 4 ++-- go.work.sum | 3 +-- 9 files changed, 12 insertions(+), 13 deletions(-) diff --git a/app/go.mod b/app/go.mod index f2e16b7..9cf9146 100644 --- a/app/go.mod +++ b/app/go.mod @@ -21,7 +21,7 @@ require ( ) require ( - github.com/apernet/quic-go v0.42.1-0.20240323215309-32a339817822 // indirect + github.com/apernet/quic-go v0.43.1-0.20240427194602-3797cae21bac // indirect github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect diff --git a/app/go.sum b/app/go.sum index 58483a3..468fd07 100644 --- a/app/go.sum +++ b/app/go.sum @@ -40,8 +40,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f h1:uVh0qpEslrWjgzx9vOcyCqsOY3c9kofDZ1n+qaw35ZY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f/go.mod h1:xkkq9D4ygcldQQhKS/w9CadiCKwCngU7K9E3DaKahpM= -github.com/apernet/quic-go v0.42.1-0.20240323215309-32a339817822 h1:+ZSzRxSMg1+fLTQKcIUvD2cCCgS+1rtyRhs+NL5oBgA= -github.com/apernet/quic-go v0.42.1-0.20240323215309-32a339817822/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= +github.com/apernet/quic-go v0.43.1-0.20240427194602-3797cae21bac h1:mL7OmpIRnux5nkKC4801CzwNmhlIzzejN5H5AfmVFWc= +github.com/apernet/quic-go v0.43.1-0.20240427194602-3797cae21bac/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad h1:QzQ2sKpc9o42HNRR8ukM5uMC/RzR2HgZd/Nvaqol2C0= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= diff --git a/core/client/client.go b/core/client/client.go index 4148f05..4212820 100644 --- a/core/client/client.go +++ b/core/client/client.go @@ -89,7 +89,7 @@ func (c *clientImpl) connect() (*HandshakeInfo, error) { rt := &http3.RoundTripper{ EnableDatagrams: true, TLSClientConfig: tlsConfig, - QuicConfig: quicConfig, + QUICConfig: quicConfig, Dial: func(ctx context.Context, _ string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) { qc, err := quic.DialEarly(ctx, pktConn, c.config.ServerAddr, tlsCfg, cfg) if err != nil { diff --git a/core/go.mod b/core/go.mod index 9928286..d6634cf 100644 --- a/core/go.mod +++ b/core/go.mod @@ -3,7 +3,7 @@ module github.com/apernet/hysteria/core go 1.21 require ( - github.com/apernet/quic-go v0.42.1-0.20240323215309-32a339817822 + github.com/apernet/quic-go v0.43.1-0.20240427194602-3797cae21bac github.com/stretchr/testify v1.8.4 go.uber.org/goleak v1.2.1 golang.org/x/exp v0.0.0-20221205204356-47842c84f3db diff --git a/core/go.sum b/core/go.sum index c205cfa..356333f 100644 --- a/core/go.sum +++ b/core/go.sum @@ -1,5 +1,5 @@ -github.com/apernet/quic-go v0.42.1-0.20240323215309-32a339817822 h1:+ZSzRxSMg1+fLTQKcIUvD2cCCgS+1rtyRhs+NL5oBgA= -github.com/apernet/quic-go v0.42.1-0.20240323215309-32a339817822/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= +github.com/apernet/quic-go v0.43.1-0.20240427194602-3797cae21bac h1:mL7OmpIRnux5nkKC4801CzwNmhlIzzejN5H5AfmVFWc= +github.com/apernet/quic-go v0.43.1-0.20240427194602-3797cae21bac/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= diff --git a/core/server/server.go b/core/server/server.go index 812606a..3d0aebe 100644 --- a/core/server/server.go +++ b/core/server/server.go @@ -180,7 +180,7 @@ func (h *h3sHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } -func (h *h3sHandler) ProxyStreamHijacker(ft http3.FrameType, conn quic.Connection, stream quic.Stream, err error) (bool, error) { +func (h *h3sHandler) ProxyStreamHijacker(ft http3.FrameType, id quic.ConnectionTracingID, stream quic.Stream, err error) (bool, error) { if err != nil || !h.authenticated { return false, nil } diff --git a/extras/go.mod b/extras/go.mod index 6fa6bda..e43dfea 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -15,7 +15,7 @@ require ( ) require ( - github.com/apernet/quic-go v0.42.1-0.20240323215309-32a339817822 // indirect + github.com/apernet/quic-go v0.43.1-0.20240427194602-3797cae21bac // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect diff --git a/extras/go.sum b/extras/go.sum index 68b0aae..561a245 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -1,5 +1,5 @@ -github.com/apernet/quic-go v0.42.1-0.20240323215309-32a339817822 h1:+ZSzRxSMg1+fLTQKcIUvD2cCCgS+1rtyRhs+NL5oBgA= -github.com/apernet/quic-go v0.42.1-0.20240323215309-32a339817822/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= +github.com/apernet/quic-go v0.43.1-0.20240427194602-3797cae21bac h1:mL7OmpIRnux5nkKC4801CzwNmhlIzzejN5H5AfmVFWc= +github.com/apernet/quic-go v0.43.1-0.20240427194602-3797cae21bac/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= diff --git a/go.work.sum b/go.work.sum index e1d4747..b950560 100644 --- a/go.work.sum +++ b/go.work.sum @@ -241,7 +241,6 @@ golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a h1:Jw5wfR+h9mnIYH+OtGT2im5wV1YGGDora5vTv/aa5bE= golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= @@ -274,7 +273,6 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= @@ -286,6 +284,7 @@ golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 8412ec3ab3c081bb0f92ff64cb1a0246388a7e24 Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 27 Apr 2024 21:00:21 -0700 Subject: [PATCH 071/161] ci: update to go 1.22 --- .github/workflows/master.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 642829d..7a7c523 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -19,7 +19,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.21" + go-version: "1.22" - name: Setup Python # This is for the build script uses: actions/setup-python@v5 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 55e2aba..70710c0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,7 +23,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.21" + go-version: "1.22" - name: Setup Python # This is for the build script uses: actions/setup-python@v5 From b79c43171adbdcd1211dd58418b10faed4d8cf87 Mon Sep 17 00:00:00 2001 From: Toby Date: Sun, 28 Apr 2024 20:22:11 -0700 Subject: [PATCH 072/161] fix: update in quic-go (http3) broke UDP functionality --- app/go.mod | 2 +- app/go.sum | 4 ++-- core/client/client.go | 1 - core/go.mod | 2 +- core/go.sum | 4 ++-- core/internal/integration_tests/masq_test.go | 1 - core/server/server.go | 5 ++--- extras/go.mod | 2 +- extras/go.sum | 4 ++-- go.work.sum | 5 +++++ 10 files changed, 16 insertions(+), 14 deletions(-) diff --git a/app/go.mod b/app/go.mod index 9cf9146..6f92b7f 100644 --- a/app/go.mod +++ b/app/go.mod @@ -21,7 +21,7 @@ require ( ) require ( - github.com/apernet/quic-go v0.43.1-0.20240427194602-3797cae21bac // indirect + github.com/apernet/quic-go v0.43.1-0.20240429030958-51a0843014d6 // indirect github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect diff --git a/app/go.sum b/app/go.sum index 468fd07..b7eff5f 100644 --- a/app/go.sum +++ b/app/go.sum @@ -40,8 +40,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f h1:uVh0qpEslrWjgzx9vOcyCqsOY3c9kofDZ1n+qaw35ZY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f/go.mod h1:xkkq9D4ygcldQQhKS/w9CadiCKwCngU7K9E3DaKahpM= -github.com/apernet/quic-go v0.43.1-0.20240427194602-3797cae21bac h1:mL7OmpIRnux5nkKC4801CzwNmhlIzzejN5H5AfmVFWc= -github.com/apernet/quic-go v0.43.1-0.20240427194602-3797cae21bac/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= +github.com/apernet/quic-go v0.43.1-0.20240429030958-51a0843014d6 h1:ZfaQo52EyyhNCxfMNk64W1YHcIh+0rCkp3e0gR7BO5E= +github.com/apernet/quic-go v0.43.1-0.20240429030958-51a0843014d6/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad h1:QzQ2sKpc9o42HNRR8ukM5uMC/RzR2HgZd/Nvaqol2C0= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= diff --git a/core/client/client.go b/core/client/client.go index 4212820..5a25d37 100644 --- a/core/client/client.go +++ b/core/client/client.go @@ -87,7 +87,6 @@ func (c *clientImpl) connect() (*HandshakeInfo, error) { // Prepare RoundTripper var conn quic.EarlyConnection rt := &http3.RoundTripper{ - EnableDatagrams: true, TLSClientConfig: tlsConfig, QUICConfig: quicConfig, Dial: func(ctx context.Context, _ string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) { diff --git a/core/go.mod b/core/go.mod index d6634cf..34e0124 100644 --- a/core/go.mod +++ b/core/go.mod @@ -3,7 +3,7 @@ module github.com/apernet/hysteria/core go 1.21 require ( - github.com/apernet/quic-go v0.43.1-0.20240427194602-3797cae21bac + github.com/apernet/quic-go v0.43.1-0.20240429030958-51a0843014d6 github.com/stretchr/testify v1.8.4 go.uber.org/goleak v1.2.1 golang.org/x/exp v0.0.0-20221205204356-47842c84f3db diff --git a/core/go.sum b/core/go.sum index 356333f..62a41c9 100644 --- a/core/go.sum +++ b/core/go.sum @@ -1,5 +1,5 @@ -github.com/apernet/quic-go v0.43.1-0.20240427194602-3797cae21bac h1:mL7OmpIRnux5nkKC4801CzwNmhlIzzejN5H5AfmVFWc= -github.com/apernet/quic-go v0.43.1-0.20240427194602-3797cae21bac/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= +github.com/apernet/quic-go v0.43.1-0.20240429030958-51a0843014d6 h1:ZfaQo52EyyhNCxfMNk64W1YHcIh+0rCkp3e0gR7BO5E= +github.com/apernet/quic-go v0.43.1-0.20240429030958-51a0843014d6/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= diff --git a/core/internal/integration_tests/masq_test.go b/core/internal/integration_tests/masq_test.go index e507da9..6f49053 100644 --- a/core/internal/integration_tests/masq_test.go +++ b/core/internal/integration_tests/masq_test.go @@ -41,7 +41,6 @@ func TestServerMasquerade(t *testing.T) { // QUIC connection & RoundTripper var conn quic.EarlyConnection rt := &http3.RoundTripper{ - EnableDatagrams: true, TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, diff --git a/core/server/server.go b/core/server/server.go index 3d0aebe..78ab531 100644 --- a/core/server/server.go +++ b/core/server/server.go @@ -77,9 +77,8 @@ func (s *serverImpl) Close() error { func (s *serverImpl) handleClient(conn quic.Connection) { handler := newH3sHandler(s.config, conn) h3s := http3.Server{ - EnableDatagrams: true, - Handler: handler, - StreamHijacker: handler.ProxyStreamHijacker, + Handler: handler, + StreamHijacker: handler.ProxyStreamHijacker, } err := h3s.ServeQUICConn(conn) // If the client is authenticated, we need to log the disconnect event diff --git a/extras/go.mod b/extras/go.mod index e43dfea..db1d0c4 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -15,7 +15,7 @@ require ( ) require ( - github.com/apernet/quic-go v0.43.1-0.20240427194602-3797cae21bac // indirect + github.com/apernet/quic-go v0.43.1-0.20240429030958-51a0843014d6 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect diff --git a/extras/go.sum b/extras/go.sum index 561a245..b79179a 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -1,5 +1,5 @@ -github.com/apernet/quic-go v0.43.1-0.20240427194602-3797cae21bac h1:mL7OmpIRnux5nkKC4801CzwNmhlIzzejN5H5AfmVFWc= -github.com/apernet/quic-go v0.43.1-0.20240427194602-3797cae21bac/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= +github.com/apernet/quic-go v0.43.1-0.20240429030958-51a0843014d6 h1:ZfaQo52EyyhNCxfMNk64W1YHcIh+0rCkp3e0gR7BO5E= +github.com/apernet/quic-go v0.43.1-0.20240429030958-51a0843014d6/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= diff --git a/go.work.sum b/go.work.sum index b950560..31d9ab2 100644 --- a/go.work.sum +++ b/go.work.sum @@ -158,7 +158,11 @@ github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a github.com/openzipkin/zipkin-go v0.1.1 h1:A/ADD6HaPnAKj3yS7HjGHRK77qi41Hi0DirOOIQAeIw= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/pkg/sftp v1.13.1 h1:I2qBYMChEhIjOgazfJmV3/mZM256btk6wkCDRmW7JYs= +github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= +github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -258,6 +262,7 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk= From 415ef42b5a2fd09626d1253eceb1f85dd4fe3ae2 Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Thu, 9 May 2024 09:42:14 +0800 Subject: [PATCH 073/161] add getOnline feature --- extras/trafficlogger/http.go | 47 ++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/extras/trafficlogger/http.go b/extras/trafficlogger/http.go index 428dd94..077d103 100644 --- a/extras/trafficlogger/http.go +++ b/extras/trafficlogger/http.go @@ -5,6 +5,7 @@ import ( "net/http" "strconv" "sync" + "time" "github.com/apernet/hysteria/core/server" ) @@ -22,17 +23,19 @@ type TrafficStatsServer interface { func NewTrafficStatsServer(secret string) TrafficStatsServer { return &trafficStatsServerImpl{ - StatsMap: make(map[string]*trafficStatsEntry), - KickMap: make(map[string]struct{}), - Secret: secret, + StatsMap: make(map[string]*trafficStatsEntry), + KickMap: make(map[string]struct{}), + OnlineMap: make(map[string]int64), + Secret: secret, } } type trafficStatsServerImpl struct { - Mutex sync.RWMutex - StatsMap map[string]*trafficStatsEntry - KickMap map[string]struct{} - Secret string + Mutex sync.RWMutex + StatsMap map[string]*trafficStatsEntry + OnlineMap map[string]int64 + KickMap map[string]struct{} + Secret string } type trafficStatsEntry struct { @@ -58,6 +61,8 @@ func (s *trafficStatsServerImpl) Log(id string, tx, rx uint64) (ok bool) { entry.Tx += tx entry.Rx += rx + s.OnlineMap[id] = time.Now().Unix() + return true } @@ -78,6 +83,10 @@ func (s *trafficStatsServerImpl) ServeHTTP(w http.ResponseWriter, r *http.Reques s.kick(w, r) return } + if r.Method == http.MethodPost && r.URL.Path == "/online" { + s.getOnline(w, r) + return + } http.NotFound(w, r) } @@ -103,6 +112,30 @@ func (s *trafficStatsServerImpl) getTraffic(w http.ResponseWriter, r *http.Reque _, _ = w.Write(jb) } +func (s *trafficStatsServerImpl) getOnline(w http.ResponseWriter, r *http.Request) { + var jb []byte + var err error + + timeNow := time.Now().Unix() + + for id, lastSeen := range s.OnlineMap { + if timeNow-lastSeen > 180 { + delete(s.OnlineMap, id) + } + } + + s.Mutex.RLock() + jb, err = json.Marshal(s.OnlineMap) + s.Mutex.RUnlock() + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json; charset=utf-8") + _, _ = w.Write(jb) +} + func (s *trafficStatsServerImpl) kick(w http.ResponseWriter, r *http.Request) { var ids []string err := json.NewDecoder(r.Body).Decode(&ids) From 2366882bd6e5872e33b8ad7d6dc075704fde7056 Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Thu, 9 May 2024 10:10:20 +0800 Subject: [PATCH 074/161] add getOnline feature --- extras/trafficlogger/http.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/trafficlogger/http.go b/extras/trafficlogger/http.go index 077d103..a5a8fc6 100644 --- a/extras/trafficlogger/http.go +++ b/extras/trafficlogger/http.go @@ -83,7 +83,7 @@ func (s *trafficStatsServerImpl) ServeHTTP(w http.ResponseWriter, r *http.Reques s.kick(w, r) return } - if r.Method == http.MethodPost && r.URL.Path == "/online" { + if r.Method == http.MethodGet && r.URL.Path == "/online" { s.getOnline(w, r) return } From 88eef7617fd7f9dc701e57c544005e2fc9ca8997 Mon Sep 17 00:00:00 2001 From: HynoR <20227709+HynoR@users.noreply.github.com> Date: Thu, 9 May 2024 10:56:19 +0800 Subject: [PATCH 075/161] refactor getOnline feature --- core/server/config.go | 2 + core/server/server.go | 2 + extras/trafficlogger/http.go | 74 +++++++++++++++++++++++++++++------- 3 files changed, 64 insertions(+), 14 deletions(-) diff --git a/core/server/config.go b/core/server/config.go index f647f0d..fe64201 100644 --- a/core/server/config.go +++ b/core/server/config.go @@ -196,4 +196,6 @@ type EventLogger interface { // The implementation of this interface must be thread-safe. type TrafficLogger interface { Log(id string, tx, rx uint64) (ok bool) + LogOnline(id string, addr net.Addr) + LogOffline(id string, addr net.Addr) } diff --git a/core/server/server.go b/core/server/server.go index 78ab531..99351b0 100644 --- a/core/server/server.go +++ b/core/server/server.go @@ -83,6 +83,7 @@ func (s *serverImpl) handleClient(conn quic.Connection) { err := h3s.ServeQUICConn(conn) // If the client is authenticated, we need to log the disconnect event if handler.authenticated && s.config.EventLogger != nil { + s.config.TrafficLogger.LogOffline(handler.authID, conn.RemoteAddr()) s.config.EventLogger.Disconnect(conn.RemoteAddr(), handler.authID, err) } _ = conn.CloseWithError(closeErrCodeOK, "") @@ -154,6 +155,7 @@ func (h *h3sHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.WriteHeader(protocol.StatusAuthOK) // Call event logger if h.config.EventLogger != nil { + h.config.TrafficLogger.LogOnline(id, h.conn.RemoteAddr()) h.config.EventLogger.Connect(h.conn.RemoteAddr(), id, actualTx) } // Initialize UDP session manager (if UDP is enabled) diff --git a/extras/trafficlogger/http.go b/extras/trafficlogger/http.go index a5a8fc6..5f46e0e 100644 --- a/extras/trafficlogger/http.go +++ b/extras/trafficlogger/http.go @@ -2,12 +2,11 @@ package trafficlogger import ( "encoding/json" + "github.com/apernet/hysteria/core/server" + "net" "net/http" "strconv" "sync" - "time" - - "github.com/apernet/hysteria/core/server" ) const ( @@ -25,7 +24,7 @@ func NewTrafficStatsServer(secret string) TrafficStatsServer { return &trafficStatsServerImpl{ StatsMap: make(map[string]*trafficStatsEntry), KickMap: make(map[string]struct{}), - OnlineMap: make(map[string]int64), + OnlineMap: make(map[string]map[string]bool), Secret: secret, } } @@ -33,7 +32,7 @@ func NewTrafficStatsServer(secret string) TrafficStatsServer { type trafficStatsServerImpl struct { Mutex sync.RWMutex StatsMap map[string]*trafficStatsEntry - OnlineMap map[string]int64 + OnlineMap map[string]map[string]bool KickMap map[string]struct{} Secret string } @@ -61,11 +60,45 @@ func (s *trafficStatsServerImpl) Log(id string, tx, rx uint64) (ok bool) { entry.Tx += tx entry.Rx += rx - s.OnlineMap[id] = time.Now().Unix() - return true } +// LogOnline adds the user to the online map. +func (s *trafficStatsServerImpl) LogOnline(id string, addr net.Addr) { + s.Mutex.Lock() + defer s.Mutex.Unlock() + if _, ok := s.OnlineMap[id]; !ok { + s.OnlineMap[id] = make(map[string]bool) + } + userIp, _, err := net.SplitHostPort(addr.String()) + if err != nil { + return + } + s.OnlineMap[id][userIp] = true +} + +// LogOffline removes the user from the online map. +func (s *trafficStatsServerImpl) LogOffline(id string, addr net.Addr) { + s.Mutex.Lock() + defer s.Mutex.Unlock() + userIp, _, err := net.SplitHostPort(addr.String()) + if err != nil { + return + } + if onlineUsers, ok := s.OnlineMap[id]; ok { + if !onlineUsers[userIp] { + //if the user's ip is not in the online map, delete the whole entry + delete(s.OnlineMap, id) + return + } + delete(onlineUsers, userIp) + if len(onlineUsers) == 0 { + delete(s.OnlineMap, id) + } + } + +} + func (s *trafficStatsServerImpl) ServeHTTP(w http.ResponseWriter, r *http.Request) { if s.Secret != "" && r.Header.Get("Authorization") != s.Secret { http.Error(w, "unauthorized", http.StatusUnauthorized) @@ -116,17 +149,30 @@ func (s *trafficStatsServerImpl) getOnline(w http.ResponseWriter, r *http.Reques var jb []byte var err error - timeNow := time.Now().Unix() + bClear, _ := strconv.ParseBool(r.URL.Query().Get("clear")) + OnlineSet := make(map[string][]string) - for id, lastSeen := range s.OnlineMap { - if timeNow-lastSeen > 180 { - delete(s.OnlineMap, id) + if bClear { + s.Mutex.Lock() + for id, addrs := range s.OnlineMap { + for addr := range addrs { + OnlineSet[id] = append(OnlineSet[id], addr) + } } + s.OnlineMap = make(map[string]map[string]bool) + s.Mutex.Unlock() + + } else { + s.Mutex.RLock() + for id, addrs := range s.OnlineMap { + for addr := range addrs { + OnlineSet[id] = append(OnlineSet[id], addr) + } + } + s.Mutex.RUnlock() } - s.Mutex.RLock() - jb, err = json.Marshal(s.OnlineMap) - s.Mutex.RUnlock() + jb, err = json.Marshal(OnlineSet) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) From ba9b3cdebbb3514d4e6d94ab16065b932807e6ed Mon Sep 17 00:00:00 2001 From: Haruue Date: Sat, 11 May 2024 11:05:32 +0800 Subject: [PATCH 076/161] refactor(online): track count instead of raddr --- core/server/config.go | 3 +- core/server/server.go | 4 +- extras/trafficlogger/http.go | 73 +++++++----------------------------- 3 files changed, 17 insertions(+), 63 deletions(-) diff --git a/core/server/config.go b/core/server/config.go index fe64201..c630abc 100644 --- a/core/server/config.go +++ b/core/server/config.go @@ -196,6 +196,5 @@ type EventLogger interface { // The implementation of this interface must be thread-safe. type TrafficLogger interface { Log(id string, tx, rx uint64) (ok bool) - LogOnline(id string, addr net.Addr) - LogOffline(id string, addr net.Addr) + LogOnlineStateChanged(id string, online bool) } diff --git a/core/server/server.go b/core/server/server.go index 99351b0..5daf36e 100644 --- a/core/server/server.go +++ b/core/server/server.go @@ -83,7 +83,7 @@ func (s *serverImpl) handleClient(conn quic.Connection) { err := h3s.ServeQUICConn(conn) // If the client is authenticated, we need to log the disconnect event if handler.authenticated && s.config.EventLogger != nil { - s.config.TrafficLogger.LogOffline(handler.authID, conn.RemoteAddr()) + s.config.TrafficLogger.LogOnlineStateChanged(handler.authID, false) s.config.EventLogger.Disconnect(conn.RemoteAddr(), handler.authID, err) } _ = conn.CloseWithError(closeErrCodeOK, "") @@ -155,7 +155,7 @@ func (h *h3sHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.WriteHeader(protocol.StatusAuthOK) // Call event logger if h.config.EventLogger != nil { - h.config.TrafficLogger.LogOnline(id, h.conn.RemoteAddr()) + h.config.TrafficLogger.LogOnlineStateChanged(id, true) h.config.EventLogger.Connect(h.conn.RemoteAddr(), id, actualTx) } // Initialize UDP session manager (if UDP is enabled) diff --git a/extras/trafficlogger/http.go b/extras/trafficlogger/http.go index 5f46e0e..827d64d 100644 --- a/extras/trafficlogger/http.go +++ b/extras/trafficlogger/http.go @@ -2,11 +2,11 @@ package trafficlogger import ( "encoding/json" - "github.com/apernet/hysteria/core/server" - "net" "net/http" "strconv" "sync" + + "github.com/apernet/hysteria/core/server" ) const ( @@ -24,7 +24,7 @@ func NewTrafficStatsServer(secret string) TrafficStatsServer { return &trafficStatsServerImpl{ StatsMap: make(map[string]*trafficStatsEntry), KickMap: make(map[string]struct{}), - OnlineMap: make(map[string]map[string]bool), + OnlineMap: make(map[string]int), Secret: secret, } } @@ -32,7 +32,7 @@ func NewTrafficStatsServer(secret string) TrafficStatsServer { type trafficStatsServerImpl struct { Mutex sync.RWMutex StatsMap map[string]*trafficStatsEntry - OnlineMap map[string]map[string]bool + OnlineMap map[string]int KickMap map[string]struct{} Secret string } @@ -63,40 +63,19 @@ func (s *trafficStatsServerImpl) Log(id string, tx, rx uint64) (ok bool) { return true } -// LogOnline adds the user to the online map. -func (s *trafficStatsServerImpl) LogOnline(id string, addr net.Addr) { +// LogOnlineStateChanged updates the online state to the online map. +func (s *trafficStatsServerImpl) LogOnlineStateChanged(id string, online bool) { s.Mutex.Lock() defer s.Mutex.Unlock() - if _, ok := s.OnlineMap[id]; !ok { - s.OnlineMap[id] = make(map[string]bool) - } - userIp, _, err := net.SplitHostPort(addr.String()) - if err != nil { - return - } - s.OnlineMap[id][userIp] = true -} -// LogOffline removes the user from the online map. -func (s *trafficStatsServerImpl) LogOffline(id string, addr net.Addr) { - s.Mutex.Lock() - defer s.Mutex.Unlock() - userIp, _, err := net.SplitHostPort(addr.String()) - if err != nil { - return - } - if onlineUsers, ok := s.OnlineMap[id]; ok { - if !onlineUsers[userIp] { - //if the user's ip is not in the online map, delete the whole entry - delete(s.OnlineMap, id) - return - } - delete(onlineUsers, userIp) - if len(onlineUsers) == 0 { + if online { + s.OnlineMap[id]++ + } else { + s.OnlineMap[id]-- + if s.OnlineMap[id] <= 0 { delete(s.OnlineMap, id) } } - } func (s *trafficStatsServerImpl) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -146,34 +125,10 @@ func (s *trafficStatsServerImpl) getTraffic(w http.ResponseWriter, r *http.Reque } func (s *trafficStatsServerImpl) getOnline(w http.ResponseWriter, r *http.Request) { - var jb []byte - var err error - - bClear, _ := strconv.ParseBool(r.URL.Query().Get("clear")) - OnlineSet := make(map[string][]string) - - if bClear { - s.Mutex.Lock() - for id, addrs := range s.OnlineMap { - for addr := range addrs { - OnlineSet[id] = append(OnlineSet[id], addr) - } - } - s.OnlineMap = make(map[string]map[string]bool) - s.Mutex.Unlock() - - } else { - s.Mutex.RLock() - for id, addrs := range s.OnlineMap { - for addr := range addrs { - OnlineSet[id] = append(OnlineSet[id], addr) - } - } - s.Mutex.RUnlock() - } - - jb, err = json.Marshal(OnlineSet) + s.Mutex.RLock() + defer s.Mutex.RUnlock() + jb, err := json.Marshal(s.OnlineMap) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return From 6a34a9e7a073bdf4b7692687f6c991768ba1a85f Mon Sep 17 00:00:00 2001 From: Haruue Date: Sat, 11 May 2024 15:07:25 +0800 Subject: [PATCH 077/161] test: fix unit tests mock files regenerated with mockery v2.43.0 --- .../mocks/mock_Authenticator.go | 6 ++- .../integration_tests/mocks/mock_Conn.go | 34 +++++++++++++++- .../mocks/mock_EventLogger.go | 2 +- .../integration_tests/mocks/mock_Outbound.go | 10 ++++- .../mocks/mock_TrafficLogger.go | 40 ++++++++++++++++++- .../integration_tests/mocks/mock_UDPConn.go | 14 ++++++- core/server/server.go | 18 ++++++--- 7 files changed, 112 insertions(+), 12 deletions(-) diff --git a/core/internal/integration_tests/mocks/mock_Authenticator.go b/core/internal/integration_tests/mocks/mock_Authenticator.go index 20329f7..b42c737 100644 --- a/core/internal/integration_tests/mocks/mock_Authenticator.go +++ b/core/internal/integration_tests/mocks/mock_Authenticator.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.32.0. DO NOT EDIT. +// Code generated by mockery v2.43.0. DO NOT EDIT. package mocks @@ -25,6 +25,10 @@ func (_m *MockAuthenticator) EXPECT() *MockAuthenticator_Expecter { func (_m *MockAuthenticator) Authenticate(addr net.Addr, auth string, tx uint64) (bool, string) { ret := _m.Called(addr, auth, tx) + if len(ret) == 0 { + panic("no return value specified for Authenticate") + } + var r0 bool var r1 string if rf, ok := ret.Get(0).(func(net.Addr, string, uint64) (bool, string)); ok { diff --git a/core/internal/integration_tests/mocks/mock_Conn.go b/core/internal/integration_tests/mocks/mock_Conn.go index 6840332..13e363e 100644 --- a/core/internal/integration_tests/mocks/mock_Conn.go +++ b/core/internal/integration_tests/mocks/mock_Conn.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.32.0. DO NOT EDIT. +// Code generated by mockery v2.43.0. DO NOT EDIT. package mocks @@ -27,6 +27,10 @@ func (_m *MockConn) EXPECT() *MockConn_Expecter { func (_m *MockConn) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -68,6 +72,10 @@ func (_c *MockConn_Close_Call) RunAndReturn(run func() error) *MockConn_Close_Ca func (_m *MockConn) LocalAddr() net.Addr { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for LocalAddr") + } + var r0 net.Addr if rf, ok := ret.Get(0).(func() net.Addr); ok { r0 = rf() @@ -111,6 +119,10 @@ func (_c *MockConn_LocalAddr_Call) RunAndReturn(run func() net.Addr) *MockConn_L func (_m *MockConn) Read(b []byte) (int, error) { ret := _m.Called(b) + if len(ret) == 0 { + panic("no return value specified for Read") + } + var r0 int var r1 error if rf, ok := ret.Get(0).(func([]byte) (int, error)); ok { @@ -163,6 +175,10 @@ func (_c *MockConn_Read_Call) RunAndReturn(run func([]byte) (int, error)) *MockC func (_m *MockConn) RemoteAddr() net.Addr { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for RemoteAddr") + } + var r0 net.Addr if rf, ok := ret.Get(0).(func() net.Addr); ok { r0 = rf() @@ -206,6 +222,10 @@ func (_c *MockConn_RemoteAddr_Call) RunAndReturn(run func() net.Addr) *MockConn_ func (_m *MockConn) SetDeadline(t time.Time) error { ret := _m.Called(t) + if len(ret) == 0 { + panic("no return value specified for SetDeadline") + } + var r0 error if rf, ok := ret.Get(0).(func(time.Time) error); ok { r0 = rf(t) @@ -248,6 +268,10 @@ func (_c *MockConn_SetDeadline_Call) RunAndReturn(run func(time.Time) error) *Mo func (_m *MockConn) SetReadDeadline(t time.Time) error { ret := _m.Called(t) + if len(ret) == 0 { + panic("no return value specified for SetReadDeadline") + } + var r0 error if rf, ok := ret.Get(0).(func(time.Time) error); ok { r0 = rf(t) @@ -290,6 +314,10 @@ func (_c *MockConn_SetReadDeadline_Call) RunAndReturn(run func(time.Time) error) func (_m *MockConn) SetWriteDeadline(t time.Time) error { ret := _m.Called(t) + if len(ret) == 0 { + panic("no return value specified for SetWriteDeadline") + } + var r0 error if rf, ok := ret.Get(0).(func(time.Time) error); ok { r0 = rf(t) @@ -332,6 +360,10 @@ func (_c *MockConn_SetWriteDeadline_Call) RunAndReturn(run func(time.Time) error func (_m *MockConn) Write(b []byte) (int, error) { ret := _m.Called(b) + if len(ret) == 0 { + panic("no return value specified for Write") + } + var r0 int var r1 error if rf, ok := ret.Get(0).(func([]byte) (int, error)); ok { diff --git a/core/internal/integration_tests/mocks/mock_EventLogger.go b/core/internal/integration_tests/mocks/mock_EventLogger.go index cf63773..c9f6920 100644 --- a/core/internal/integration_tests/mocks/mock_EventLogger.go +++ b/core/internal/integration_tests/mocks/mock_EventLogger.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.32.0. DO NOT EDIT. +// Code generated by mockery v2.43.0. DO NOT EDIT. package mocks diff --git a/core/internal/integration_tests/mocks/mock_Outbound.go b/core/internal/integration_tests/mocks/mock_Outbound.go index 32a747b..1a2d0c6 100644 --- a/core/internal/integration_tests/mocks/mock_Outbound.go +++ b/core/internal/integration_tests/mocks/mock_Outbound.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.32.0. DO NOT EDIT. +// Code generated by mockery v2.43.0. DO NOT EDIT. package mocks @@ -27,6 +27,10 @@ func (_m *MockOutbound) EXPECT() *MockOutbound_Expecter { func (_m *MockOutbound) TCP(reqAddr string) (net.Conn, error) { ret := _m.Called(reqAddr) + if len(ret) == 0 { + panic("no return value specified for TCP") + } + var r0 net.Conn var r1 error if rf, ok := ret.Get(0).(func(string) (net.Conn, error)); ok { @@ -81,6 +85,10 @@ func (_c *MockOutbound_TCP_Call) RunAndReturn(run func(string) (net.Conn, error) func (_m *MockOutbound) UDP(reqAddr string) (server.UDPConn, error) { ret := _m.Called(reqAddr) + if len(ret) == 0 { + panic("no return value specified for UDP") + } + var r0 server.UDPConn var r1 error if rf, ok := ret.Get(0).(func(string) (server.UDPConn, error)); ok { diff --git a/core/internal/integration_tests/mocks/mock_TrafficLogger.go b/core/internal/integration_tests/mocks/mock_TrafficLogger.go index 0188ab3..5da6aaf 100644 --- a/core/internal/integration_tests/mocks/mock_TrafficLogger.go +++ b/core/internal/integration_tests/mocks/mock_TrafficLogger.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.32.0. DO NOT EDIT. +// Code generated by mockery v2.43.0. DO NOT EDIT. package mocks @@ -21,6 +21,10 @@ func (_m *MockTrafficLogger) EXPECT() *MockTrafficLogger_Expecter { func (_m *MockTrafficLogger) Log(id string, tx uint64, rx uint64) bool { ret := _m.Called(id, tx, rx) + if len(ret) == 0 { + panic("no return value specified for Log") + } + var r0 bool if rf, ok := ret.Get(0).(func(string, uint64, uint64) bool); ok { r0 = rf(id, tx, rx) @@ -61,6 +65,40 @@ func (_c *MockTrafficLogger_Log_Call) RunAndReturn(run func(string, uint64, uint return _c } +// LogOnlineStateChanged provides a mock function with given fields: id, online +func (_m *MockTrafficLogger) LogOnlineStateChanged(id string, online bool) { + _m.Called(id, online) +} + +// MockTrafficLogger_LogOnlineStateChanged_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LogOnlineStateChanged' +type MockTrafficLogger_LogOnlineStateChanged_Call struct { + *mock.Call +} + +// LogOnlineStateChanged is a helper method to define mock.On call +// - id string +// - online bool +func (_e *MockTrafficLogger_Expecter) LogOnlineStateChanged(id interface{}, online interface{}) *MockTrafficLogger_LogOnlineStateChanged_Call { + return &MockTrafficLogger_LogOnlineStateChanged_Call{Call: _e.mock.On("LogOnlineStateChanged", id, online)} +} + +func (_c *MockTrafficLogger_LogOnlineStateChanged_Call) Run(run func(id string, online bool)) *MockTrafficLogger_LogOnlineStateChanged_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(bool)) + }) + return _c +} + +func (_c *MockTrafficLogger_LogOnlineStateChanged_Call) Return() *MockTrafficLogger_LogOnlineStateChanged_Call { + _c.Call.Return() + return _c +} + +func (_c *MockTrafficLogger_LogOnlineStateChanged_Call) RunAndReturn(run func(string, bool)) *MockTrafficLogger_LogOnlineStateChanged_Call { + _c.Call.Return(run) + return _c +} + // NewMockTrafficLogger creates a new instance of MockTrafficLogger. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewMockTrafficLogger(t interface { diff --git a/core/internal/integration_tests/mocks/mock_UDPConn.go b/core/internal/integration_tests/mocks/mock_UDPConn.go index 808c18b..b883870 100644 --- a/core/internal/integration_tests/mocks/mock_UDPConn.go +++ b/core/internal/integration_tests/mocks/mock_UDPConn.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.32.0. DO NOT EDIT. +// Code generated by mockery v2.43.0. DO NOT EDIT. package mocks @@ -21,6 +21,10 @@ func (_m *MockUDPConn) EXPECT() *MockUDPConn_Expecter { func (_m *MockUDPConn) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -62,6 +66,10 @@ func (_c *MockUDPConn_Close_Call) RunAndReturn(run func() error) *MockUDPConn_Cl func (_m *MockUDPConn) ReadFrom(b []byte) (int, string, error) { ret := _m.Called(b) + if len(ret) == 0 { + panic("no return value specified for ReadFrom") + } + var r0 int var r1 string var r2 error @@ -121,6 +129,10 @@ func (_c *MockUDPConn_ReadFrom_Call) RunAndReturn(run func([]byte) (int, string, func (_m *MockUDPConn) WriteTo(b []byte, addr string) (int, error) { ret := _m.Called(b, addr) + if len(ret) == 0 { + panic("no return value specified for WriteTo") + } + var r0 int var r1 error if rf, ok := ret.Get(0).(func([]byte, string) (int, error)); ok { diff --git a/core/server/server.go b/core/server/server.go index 5daf36e..74640d4 100644 --- a/core/server/server.go +++ b/core/server/server.go @@ -82,9 +82,13 @@ func (s *serverImpl) handleClient(conn quic.Connection) { } err := h3s.ServeQUICConn(conn) // If the client is authenticated, we need to log the disconnect event - if handler.authenticated && s.config.EventLogger != nil { - s.config.TrafficLogger.LogOnlineStateChanged(handler.authID, false) - s.config.EventLogger.Disconnect(conn.RemoteAddr(), handler.authID, err) + if handler.authenticated { + if tl := s.config.TrafficLogger; tl != nil { + tl.LogOnlineStateChanged(handler.authID, false) + } + if el := s.config.EventLogger; el != nil { + el.Disconnect(conn.RemoteAddr(), handler.authID, err) + } } _ = conn.CloseWithError(closeErrCodeOK, "") } @@ -154,9 +158,11 @@ func (h *h3sHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { }) w.WriteHeader(protocol.StatusAuthOK) // Call event logger - if h.config.EventLogger != nil { - h.config.TrafficLogger.LogOnlineStateChanged(id, true) - h.config.EventLogger.Connect(h.conn.RemoteAddr(), id, actualTx) + if tl := h.config.TrafficLogger; tl != nil { + tl.LogOnlineStateChanged(id, true) + } + if el := h.config.EventLogger; el != nil { + el.Connect(h.conn.RemoteAddr(), id, actualTx) } // Initialize UDP session manager (if UDP is enabled) // We use sync.Once to make sure that only one goroutine is started, From 9d4b3e608af2a3e539a018b222986c22a7fa3c83 Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 11 May 2024 13:55:55 -0700 Subject: [PATCH 078/161] chore: small changes to TrafficLogger function names & update all mocks to mockery v2.43.0 --- .../proxymux/internal/mocks/mock_Conn.go | 2 +- .../proxymux/internal/mocks/mock_Listener.go | 2 +- core/client/mock_udpIO.go | 10 ++- .../mocks/mock_TrafficLogger.go | 90 +++++++++---------- .../integration_tests/trafficlogger_test.go | 16 ++-- core/server/config.go | 4 +- core/server/copy.go | 4 +- core/server/mock_UDPConn.go | 14 ++- core/server/mock_udpEventLogger.go | 2 +- core/server/mock_udpIO.go | 14 ++- core/server/server.go | 8 +- extras/outbounds/mock_PluggableOutbound.go | 10 ++- extras/outbounds/mock_UDPConn.go | 14 ++- extras/trafficlogger/http.go | 4 +- 14 files changed, 125 insertions(+), 69 deletions(-) diff --git a/app/internal/proxymux/internal/mocks/mock_Conn.go b/app/internal/proxymux/internal/mocks/mock_Conn.go index 0bcbe65..13e363e 100644 --- a/app/internal/proxymux/internal/mocks/mock_Conn.go +++ b/app/internal/proxymux/internal/mocks/mock_Conn.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.0. DO NOT EDIT. package mocks diff --git a/app/internal/proxymux/internal/mocks/mock_Listener.go b/app/internal/proxymux/internal/mocks/mock_Listener.go index e4ca2f4..842b88f 100644 --- a/app/internal/proxymux/internal/mocks/mock_Listener.go +++ b/app/internal/proxymux/internal/mocks/mock_Listener.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.43.0. DO NOT EDIT. package mocks diff --git a/core/client/mock_udpIO.go b/core/client/mock_udpIO.go index 6e0b1ba..a85e006 100644 --- a/core/client/mock_udpIO.go +++ b/core/client/mock_udpIO.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.32.0. DO NOT EDIT. +// Code generated by mockery v2.43.0. DO NOT EDIT. package client @@ -24,6 +24,10 @@ func (_m *mockUDPIO) EXPECT() *mockUDPIO_Expecter { func (_m *mockUDPIO) ReceiveMessage() (*protocol.UDPMessage, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ReceiveMessage") + } + var r0 *protocol.UDPMessage var r1 error if rf, ok := ret.Get(0).(func() (*protocol.UDPMessage, error)); ok { @@ -77,6 +81,10 @@ func (_c *mockUDPIO_ReceiveMessage_Call) RunAndReturn(run func() (*protocol.UDPM func (_m *mockUDPIO) SendMessage(_a0 []byte, _a1 *protocol.UDPMessage) error { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for SendMessage") + } + var r0 error if rf, ok := ret.Get(0).(func([]byte, *protocol.UDPMessage) error); ok { r0 = rf(_a0, _a1) diff --git a/core/internal/integration_tests/mocks/mock_TrafficLogger.go b/core/internal/integration_tests/mocks/mock_TrafficLogger.go index 5da6aaf..9de44b9 100644 --- a/core/internal/integration_tests/mocks/mock_TrafficLogger.go +++ b/core/internal/integration_tests/mocks/mock_TrafficLogger.go @@ -17,12 +17,46 @@ func (_m *MockTrafficLogger) EXPECT() *MockTrafficLogger_Expecter { return &MockTrafficLogger_Expecter{mock: &_m.Mock} } -// Log provides a mock function with given fields: id, tx, rx -func (_m *MockTrafficLogger) Log(id string, tx uint64, rx uint64) bool { +// LogOnlineState provides a mock function with given fields: id, online +func (_m *MockTrafficLogger) LogOnlineState(id string, online bool) { + _m.Called(id, online) +} + +// MockTrafficLogger_LogOnlineState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LogOnlineState' +type MockTrafficLogger_LogOnlineState_Call struct { + *mock.Call +} + +// LogOnlineState is a helper method to define mock.On call +// - id string +// - online bool +func (_e *MockTrafficLogger_Expecter) LogOnlineState(id interface{}, online interface{}) *MockTrafficLogger_LogOnlineState_Call { + return &MockTrafficLogger_LogOnlineState_Call{Call: _e.mock.On("LogOnlineState", id, online)} +} + +func (_c *MockTrafficLogger_LogOnlineState_Call) Run(run func(id string, online bool)) *MockTrafficLogger_LogOnlineState_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(bool)) + }) + return _c +} + +func (_c *MockTrafficLogger_LogOnlineState_Call) Return() *MockTrafficLogger_LogOnlineState_Call { + _c.Call.Return() + return _c +} + +func (_c *MockTrafficLogger_LogOnlineState_Call) RunAndReturn(run func(string, bool)) *MockTrafficLogger_LogOnlineState_Call { + _c.Call.Return(run) + return _c +} + +// LogTraffic provides a mock function with given fields: id, tx, rx +func (_m *MockTrafficLogger) LogTraffic(id string, tx uint64, rx uint64) bool { ret := _m.Called(id, tx, rx) if len(ret) == 0 { - panic("no return value specified for Log") + panic("no return value specified for LogTraffic") } var r0 bool @@ -35,66 +69,32 @@ func (_m *MockTrafficLogger) Log(id string, tx uint64, rx uint64) bool { return r0 } -// MockTrafficLogger_Log_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Log' -type MockTrafficLogger_Log_Call struct { +// MockTrafficLogger_LogTraffic_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LogTraffic' +type MockTrafficLogger_LogTraffic_Call struct { *mock.Call } -// Log is a helper method to define mock.On call +// LogTraffic is a helper method to define mock.On call // - id string // - tx uint64 // - rx uint64 -func (_e *MockTrafficLogger_Expecter) Log(id interface{}, tx interface{}, rx interface{}) *MockTrafficLogger_Log_Call { - return &MockTrafficLogger_Log_Call{Call: _e.mock.On("Log", id, tx, rx)} +func (_e *MockTrafficLogger_Expecter) LogTraffic(id interface{}, tx interface{}, rx interface{}) *MockTrafficLogger_LogTraffic_Call { + return &MockTrafficLogger_LogTraffic_Call{Call: _e.mock.On("LogTraffic", id, tx, rx)} } -func (_c *MockTrafficLogger_Log_Call) Run(run func(id string, tx uint64, rx uint64)) *MockTrafficLogger_Log_Call { +func (_c *MockTrafficLogger_LogTraffic_Call) Run(run func(id string, tx uint64, rx uint64)) *MockTrafficLogger_LogTraffic_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(string), args[1].(uint64), args[2].(uint64)) }) return _c } -func (_c *MockTrafficLogger_Log_Call) Return(ok bool) *MockTrafficLogger_Log_Call { +func (_c *MockTrafficLogger_LogTraffic_Call) Return(ok bool) *MockTrafficLogger_LogTraffic_Call { _c.Call.Return(ok) return _c } -func (_c *MockTrafficLogger_Log_Call) RunAndReturn(run func(string, uint64, uint64) bool) *MockTrafficLogger_Log_Call { - _c.Call.Return(run) - return _c -} - -// LogOnlineStateChanged provides a mock function with given fields: id, online -func (_m *MockTrafficLogger) LogOnlineStateChanged(id string, online bool) { - _m.Called(id, online) -} - -// MockTrafficLogger_LogOnlineStateChanged_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LogOnlineStateChanged' -type MockTrafficLogger_LogOnlineStateChanged_Call struct { - *mock.Call -} - -// LogOnlineStateChanged is a helper method to define mock.On call -// - id string -// - online bool -func (_e *MockTrafficLogger_Expecter) LogOnlineStateChanged(id interface{}, online interface{}) *MockTrafficLogger_LogOnlineStateChanged_Call { - return &MockTrafficLogger_LogOnlineStateChanged_Call{Call: _e.mock.On("LogOnlineStateChanged", id, online)} -} - -func (_c *MockTrafficLogger_LogOnlineStateChanged_Call) Run(run func(id string, online bool)) *MockTrafficLogger_LogOnlineStateChanged_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(bool)) - }) - return _c -} - -func (_c *MockTrafficLogger_LogOnlineStateChanged_Call) Return() *MockTrafficLogger_LogOnlineStateChanged_Call { - _c.Call.Return() - return _c -} - -func (_c *MockTrafficLogger_LogOnlineStateChanged_Call) RunAndReturn(run func(string, bool)) *MockTrafficLogger_LogOnlineStateChanged_Call { +func (_c *MockTrafficLogger_LogTraffic_Call) RunAndReturn(run func(string, uint64, uint64) bool) *MockTrafficLogger_LogTraffic_Call { _c.Call.Return(run) return _c } diff --git a/core/internal/integration_tests/trafficlogger_test.go b/core/internal/integration_tests/trafficlogger_test.go index abbea8c..690a348 100644 --- a/core/internal/integration_tests/trafficlogger_test.go +++ b/core/internal/integration_tests/trafficlogger_test.go @@ -36,6 +36,7 @@ func TestClientServerTrafficLoggerTCP(t *testing.T) { go s.Serve() // Create client + trafficLogger.EXPECT().LogOnlineState("nobody", true).Return().Once() c, _, err := client.NewClient(&client.Config{ ServerAddr: udpAddr, TLSConfig: client.TLSConfig{InsecureSkipVerify: true}, @@ -66,7 +67,7 @@ func TestClientServerTrafficLoggerTCP(t *testing.T) { assert.NoError(t, err) // Client reads from server - trafficLogger.EXPECT().Log("nobody", uint64(0), uint64(11)).Return(true).Once() + trafficLogger.EXPECT().LogTraffic("nobody", uint64(0), uint64(11)).Return(true).Once() sobConnCh <- []byte("knock knock") buf := make([]byte, 100) n, err := conn.Read(buf) @@ -75,7 +76,7 @@ func TestClientServerTrafficLoggerTCP(t *testing.T) { assert.Equal(t, "knock knock", string(buf[:n])) // Client writes to server - trafficLogger.EXPECT().Log("nobody", uint64(12), uint64(0)).Return(true).Once() + trafficLogger.EXPECT().LogTraffic("nobody", uint64(12), uint64(0)).Return(true).Once() sobConn.EXPECT().Write([]byte("who is there")).Return(12, nil).Once() n, err = conn.Write([]byte("who is there")) assert.NoError(t, err) @@ -83,7 +84,8 @@ func TestClientServerTrafficLoggerTCP(t *testing.T) { time.Sleep(1 * time.Second) // Need some time for the server to receive the data // Client reads from server again but blocked - trafficLogger.EXPECT().Log("nobody", uint64(0), uint64(4)).Return(false).Once() + trafficLogger.EXPECT().LogTraffic("nobody", uint64(0), uint64(4)).Return(false).Once() + trafficLogger.EXPECT().LogOnlineState("nobody", false).Return().Once() sobConnCh <- []byte("nope") n, err = conn.Read(buf) assert.Zero(t, n) @@ -116,6 +118,7 @@ func TestClientServerTrafficLoggerUDP(t *testing.T) { go s.Serve() // Create client + trafficLogger.EXPECT().LogOnlineState("nobody", true).Return().Once() c, _, err := client.NewClient(&client.Config{ ServerAddr: udpAddr, TLSConfig: client.TLSConfig{InsecureSkipVerify: true}, @@ -146,14 +149,14 @@ func TestClientServerTrafficLoggerUDP(t *testing.T) { assert.NoError(t, err) // Client writes to server - trafficLogger.EXPECT().Log("nobody", uint64(9), uint64(0)).Return(true).Once() + trafficLogger.EXPECT().LogTraffic("nobody", uint64(9), uint64(0)).Return(true).Once() sobConn.EXPECT().WriteTo([]byte("small sad"), addr).Return(9, nil).Once() err = conn.Send([]byte("small sad"), addr) assert.NoError(t, err) time.Sleep(1 * time.Second) // Need some time for the server to receive the data // Client reads from server - trafficLogger.EXPECT().Log("nobody", uint64(0), uint64(7)).Return(true).Once() + trafficLogger.EXPECT().LogTraffic("nobody", uint64(0), uint64(7)).Return(true).Once() sobConnCh <- []byte("big mad") bs, rAddr, err := conn.Receive() assert.NoError(t, err) @@ -161,7 +164,8 @@ func TestClientServerTrafficLoggerUDP(t *testing.T) { assert.Equal(t, "big mad", string(bs)) // Client reads from server again but blocked - trafficLogger.EXPECT().Log("nobody", uint64(0), uint64(4)).Return(false).Once() + trafficLogger.EXPECT().LogTraffic("nobody", uint64(0), uint64(4)).Return(false).Once() + trafficLogger.EXPECT().LogOnlineState("nobody", false).Return().Once() sobConnCh <- []byte("nope") bs, rAddr, err = conn.Receive() assert.Equal(t, err, io.EOF) diff --git a/core/server/config.go b/core/server/config.go index c630abc..8d5f5db 100644 --- a/core/server/config.go +++ b/core/server/config.go @@ -195,6 +195,6 @@ type EventLogger interface { // bandwidth limits or post-connection authentication, for example. // The implementation of this interface must be thread-safe. type TrafficLogger interface { - Log(id string, tx, rx uint64) (ok bool) - LogOnlineStateChanged(id string, online bool) + LogTraffic(id string, tx, rx uint64) (ok bool) + LogOnlineState(id string, online bool) } diff --git a/core/server/copy.go b/core/server/copy.go index 25831e5..d55dcef 100644 --- a/core/server/copy.go +++ b/core/server/copy.go @@ -35,12 +35,12 @@ func copyTwoWayWithLogger(id string, serverRw, remoteRw io.ReadWriter, l Traffic errChan := make(chan error, 2) go func() { errChan <- copyBufferLog(serverRw, remoteRw, func(n uint64) bool { - return l.Log(id, 0, n) + return l.LogTraffic(id, 0, n) }) }() go func() { errChan <- copyBufferLog(remoteRw, serverRw, func(n uint64) bool { - return l.Log(id, n, 0) + return l.LogTraffic(id, n, 0) }) }() // Block until one of the two goroutines returns diff --git a/core/server/mock_UDPConn.go b/core/server/mock_UDPConn.go index 34a34ee..7299975 100644 --- a/core/server/mock_UDPConn.go +++ b/core/server/mock_UDPConn.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.32.0. DO NOT EDIT. +// Code generated by mockery v2.43.0. DO NOT EDIT. package server @@ -21,6 +21,10 @@ func (_m *mockUDPConn) EXPECT() *mockUDPConn_Expecter { func (_m *mockUDPConn) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -62,6 +66,10 @@ func (_c *mockUDPConn_Close_Call) RunAndReturn(run func() error) *mockUDPConn_Cl func (_m *mockUDPConn) ReadFrom(b []byte) (int, string, error) { ret := _m.Called(b) + if len(ret) == 0 { + panic("no return value specified for ReadFrom") + } + var r0 int var r1 string var r2 error @@ -121,6 +129,10 @@ func (_c *mockUDPConn_ReadFrom_Call) RunAndReturn(run func([]byte) (int, string, func (_m *mockUDPConn) WriteTo(b []byte, addr string) (int, error) { ret := _m.Called(b, addr) + if len(ret) == 0 { + panic("no return value specified for WriteTo") + } + var r0 int var r1 error if rf, ok := ret.Get(0).(func([]byte, string) (int, error)); ok { diff --git a/core/server/mock_udpEventLogger.go b/core/server/mock_udpEventLogger.go index 08f3ff8..5a54b0b 100644 --- a/core/server/mock_udpEventLogger.go +++ b/core/server/mock_udpEventLogger.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.32.0. DO NOT EDIT. +// Code generated by mockery v2.43.0. DO NOT EDIT. package server diff --git a/core/server/mock_udpIO.go b/core/server/mock_udpIO.go index ddd44b3..6df862b 100644 --- a/core/server/mock_udpIO.go +++ b/core/server/mock_udpIO.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.32.0. DO NOT EDIT. +// Code generated by mockery v2.43.0. DO NOT EDIT. package server @@ -24,6 +24,10 @@ func (_m *mockUDPIO) EXPECT() *mockUDPIO_Expecter { func (_m *mockUDPIO) ReceiveMessage() (*protocol.UDPMessage, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ReceiveMessage") + } + var r0 *protocol.UDPMessage var r1 error if rf, ok := ret.Get(0).(func() (*protocol.UDPMessage, error)); ok { @@ -77,6 +81,10 @@ func (_c *mockUDPIO_ReceiveMessage_Call) RunAndReturn(run func() (*protocol.UDPM func (_m *mockUDPIO) SendMessage(_a0 []byte, _a1 *protocol.UDPMessage) error { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for SendMessage") + } + var r0 error if rf, ok := ret.Get(0).(func([]byte, *protocol.UDPMessage) error); ok { r0 = rf(_a0, _a1) @@ -120,6 +128,10 @@ func (_c *mockUDPIO_SendMessage_Call) RunAndReturn(run func([]byte, *protocol.UD func (_m *mockUDPIO) UDP(reqAddr string) (UDPConn, error) { ret := _m.Called(reqAddr) + if len(ret) == 0 { + panic("no return value specified for UDP") + } + var r0 UDPConn var r1 error if rf, ok := ret.Get(0).(func(string) (UDPConn, error)); ok { diff --git a/core/server/server.go b/core/server/server.go index 74640d4..d38097d 100644 --- a/core/server/server.go +++ b/core/server/server.go @@ -84,7 +84,7 @@ func (s *serverImpl) handleClient(conn quic.Connection) { // If the client is authenticated, we need to log the disconnect event if handler.authenticated { if tl := s.config.TrafficLogger; tl != nil { - tl.LogOnlineStateChanged(handler.authID, false) + tl.LogOnlineState(handler.authID, false) } if el := s.config.EventLogger; el != nil { el.Disconnect(conn.RemoteAddr(), handler.authID, err) @@ -159,7 +159,7 @@ func (h *h3sHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.WriteHeader(protocol.StatusAuthOK) // Call event logger if tl := h.config.TrafficLogger; tl != nil { - tl.LogOnlineStateChanged(id, true) + tl.LogOnlineState(id, true) } if el := h.config.EventLogger; el != nil { el.Connect(h.conn.RemoteAddr(), id, actualTx) @@ -276,7 +276,7 @@ func (io *udpIOImpl) ReceiveMessage() (*protocol.UDPMessage, error) { continue } if io.TrafficLogger != nil { - ok := io.TrafficLogger.Log(io.AuthID, uint64(len(udpMsg.Data)), 0) + ok := io.TrafficLogger.LogTraffic(io.AuthID, uint64(len(udpMsg.Data)), 0) if !ok { // TrafficLogger requested to disconnect the client _ = io.Conn.CloseWithError(closeErrCodeTrafficLimitReached, "") @@ -289,7 +289,7 @@ func (io *udpIOImpl) ReceiveMessage() (*protocol.UDPMessage, error) { func (io *udpIOImpl) SendMessage(buf []byte, msg *protocol.UDPMessage) error { if io.TrafficLogger != nil { - ok := io.TrafficLogger.Log(io.AuthID, 0, uint64(len(msg.Data))) + ok := io.TrafficLogger.LogTraffic(io.AuthID, 0, uint64(len(msg.Data))) if !ok { // TrafficLogger requested to disconnect the client _ = io.Conn.CloseWithError(closeErrCodeTrafficLimitReached, "") diff --git a/extras/outbounds/mock_PluggableOutbound.go b/extras/outbounds/mock_PluggableOutbound.go index c8620f2..79b38e4 100644 --- a/extras/outbounds/mock_PluggableOutbound.go +++ b/extras/outbounds/mock_PluggableOutbound.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.32.0. DO NOT EDIT. +// Code generated by mockery v2.43.0. DO NOT EDIT. package outbounds @@ -25,6 +25,10 @@ func (_m *mockPluggableOutbound) EXPECT() *mockPluggableOutbound_Expecter { func (_m *mockPluggableOutbound) TCP(reqAddr *AddrEx) (net.Conn, error) { ret := _m.Called(reqAddr) + if len(ret) == 0 { + panic("no return value specified for TCP") + } + var r0 net.Conn var r1 error if rf, ok := ret.Get(0).(func(*AddrEx) (net.Conn, error)); ok { @@ -79,6 +83,10 @@ func (_c *mockPluggableOutbound_TCP_Call) RunAndReturn(run func(*AddrEx) (net.Co func (_m *mockPluggableOutbound) UDP(reqAddr *AddrEx) (UDPConn, error) { ret := _m.Called(reqAddr) + if len(ret) == 0 { + panic("no return value specified for UDP") + } + var r0 UDPConn var r1 error if rf, ok := ret.Get(0).(func(*AddrEx) (UDPConn, error)); ok { diff --git a/extras/outbounds/mock_UDPConn.go b/extras/outbounds/mock_UDPConn.go index e71e1da..d450322 100644 --- a/extras/outbounds/mock_UDPConn.go +++ b/extras/outbounds/mock_UDPConn.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.32.0. DO NOT EDIT. +// Code generated by mockery v2.43.0. DO NOT EDIT. package outbounds @@ -21,6 +21,10 @@ func (_m *mockUDPConn) EXPECT() *mockUDPConn_Expecter { func (_m *mockUDPConn) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -62,6 +66,10 @@ func (_c *mockUDPConn_Close_Call) RunAndReturn(run func() error) *mockUDPConn_Cl func (_m *mockUDPConn) ReadFrom(b []byte) (int, *AddrEx, error) { ret := _m.Called(b) + if len(ret) == 0 { + panic("no return value specified for ReadFrom") + } + var r0 int var r1 *AddrEx var r2 error @@ -123,6 +131,10 @@ func (_c *mockUDPConn_ReadFrom_Call) RunAndReturn(run func([]byte) (int, *AddrEx func (_m *mockUDPConn) WriteTo(b []byte, addr *AddrEx) (int, error) { ret := _m.Called(b, addr) + if len(ret) == 0 { + panic("no return value specified for WriteTo") + } + var r0 int var r1 error if rf, ok := ret.Get(0).(func([]byte, *AddrEx) (int, error)); ok { diff --git a/extras/trafficlogger/http.go b/extras/trafficlogger/http.go index 827d64d..d0ec0a4 100644 --- a/extras/trafficlogger/http.go +++ b/extras/trafficlogger/http.go @@ -42,7 +42,7 @@ type trafficStatsEntry struct { Rx uint64 `json:"rx"` } -func (s *trafficStatsServerImpl) Log(id string, tx, rx uint64) (ok bool) { +func (s *trafficStatsServerImpl) LogTraffic(id string, tx, rx uint64) (ok bool) { s.Mutex.Lock() defer s.Mutex.Unlock() @@ -64,7 +64,7 @@ func (s *trafficStatsServerImpl) Log(id string, tx, rx uint64) (ok bool) { } // LogOnlineStateChanged updates the online state to the online map. -func (s *trafficStatsServerImpl) LogOnlineStateChanged(id string, online bool) { +func (s *trafficStatsServerImpl) LogOnlineState(id string, online bool) { s.Mutex.Lock() defer s.Mutex.Unlock() From 2701a6e23f8383f7257dd5dd02ad929e0bba1e6e Mon Sep 17 00:00:00 2001 From: Toby Date: Fri, 17 May 2024 17:10:59 -0700 Subject: [PATCH 079/161] fix: quic-go memory leak --- app/go.mod | 2 +- app/go.sum | 4 ++-- core/go.mod | 2 +- core/go.sum | 4 ++-- extras/go.mod | 2 +- extras/go.sum | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/go.mod b/app/go.mod index 6f92b7f..bc7ceef 100644 --- a/app/go.mod +++ b/app/go.mod @@ -21,7 +21,7 @@ require ( ) require ( - github.com/apernet/quic-go v0.43.1-0.20240429030958-51a0843014d6 // indirect + github.com/apernet/quic-go v0.43.1-0.20240515053213-5e9e635fd9f0 // indirect github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect diff --git a/app/go.sum b/app/go.sum index b7eff5f..da0a071 100644 --- a/app/go.sum +++ b/app/go.sum @@ -40,8 +40,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f h1:uVh0qpEslrWjgzx9vOcyCqsOY3c9kofDZ1n+qaw35ZY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f/go.mod h1:xkkq9D4ygcldQQhKS/w9CadiCKwCngU7K9E3DaKahpM= -github.com/apernet/quic-go v0.43.1-0.20240429030958-51a0843014d6 h1:ZfaQo52EyyhNCxfMNk64W1YHcIh+0rCkp3e0gR7BO5E= -github.com/apernet/quic-go v0.43.1-0.20240429030958-51a0843014d6/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= +github.com/apernet/quic-go v0.43.1-0.20240515053213-5e9e635fd9f0 h1:zilb2vx37DiBV5tfJRapxbXJqKavuCBKUl6kE4guShQ= +github.com/apernet/quic-go v0.43.1-0.20240515053213-5e9e635fd9f0/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad h1:QzQ2sKpc9o42HNRR8ukM5uMC/RzR2HgZd/Nvaqol2C0= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= diff --git a/core/go.mod b/core/go.mod index 34e0124..2be1569 100644 --- a/core/go.mod +++ b/core/go.mod @@ -3,7 +3,7 @@ module github.com/apernet/hysteria/core go 1.21 require ( - github.com/apernet/quic-go v0.43.1-0.20240429030958-51a0843014d6 + github.com/apernet/quic-go v0.43.1-0.20240515053213-5e9e635fd9f0 github.com/stretchr/testify v1.8.4 go.uber.org/goleak v1.2.1 golang.org/x/exp v0.0.0-20221205204356-47842c84f3db diff --git a/core/go.sum b/core/go.sum index 62a41c9..a4a87b6 100644 --- a/core/go.sum +++ b/core/go.sum @@ -1,5 +1,5 @@ -github.com/apernet/quic-go v0.43.1-0.20240429030958-51a0843014d6 h1:ZfaQo52EyyhNCxfMNk64W1YHcIh+0rCkp3e0gR7BO5E= -github.com/apernet/quic-go v0.43.1-0.20240429030958-51a0843014d6/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= +github.com/apernet/quic-go v0.43.1-0.20240515053213-5e9e635fd9f0 h1:zilb2vx37DiBV5tfJRapxbXJqKavuCBKUl6kE4guShQ= +github.com/apernet/quic-go v0.43.1-0.20240515053213-5e9e635fd9f0/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= diff --git a/extras/go.mod b/extras/go.mod index db1d0c4..5274da0 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -15,7 +15,7 @@ require ( ) require ( - github.com/apernet/quic-go v0.43.1-0.20240429030958-51a0843014d6 // indirect + github.com/apernet/quic-go v0.43.1-0.20240515053213-5e9e635fd9f0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect diff --git a/extras/go.sum b/extras/go.sum index b79179a..a12d66d 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -1,5 +1,5 @@ -github.com/apernet/quic-go v0.43.1-0.20240429030958-51a0843014d6 h1:ZfaQo52EyyhNCxfMNk64W1YHcIh+0rCkp3e0gR7BO5E= -github.com/apernet/quic-go v0.43.1-0.20240429030958-51a0843014d6/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= +github.com/apernet/quic-go v0.43.1-0.20240515053213-5e9e635fd9f0 h1:zilb2vx37DiBV5tfJRapxbXJqKavuCBKUl6kE4guShQ= +github.com/apernet/quic-go v0.43.1-0.20240515053213-5e9e635fd9f0/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= From b216c4f128e4714b4c237cf532497efe1f9afcf6 Mon Sep 17 00:00:00 2001 From: Toby Date: Fri, 17 May 2024 18:02:58 -0700 Subject: [PATCH 080/161] feat: graceful client shutdown --- app/cmd/client.go | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/app/cmd/client.go b/app/cmd/client.go index b6745ab..355523a 100644 --- a/app/cmd/client.go +++ b/app/cmd/client.go @@ -9,10 +9,12 @@ import ( "net" "net/netip" "os" + "os/signal" "runtime" "slices" "strconv" "strings" + "syscall" "time" "github.com/spf13/cobra" @@ -512,13 +514,42 @@ func runClient(cmd *cobra.Command, args []string) { }) } - runner.Run() + signalChan := make(chan os.Signal, 1) + signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM) + defer signal.Stop(signalChan) + + runnerChan := make(chan clientModeRunnerResult, 1) + go func() { + runnerChan <- runner.Run() + }() + + select { + case <-signalChan: + logger.Info("received signal, shutting down gracefully") + case r := <-runnerChan: + if r.OK { + logger.Info(r.Msg) + } else { + _ = c.Close() // Close the client here as Fatal will exit the program without running defer + if r.Err != nil { + logger.Fatal(r.Msg, zap.Error(r.Err)) + } else { + logger.Fatal(r.Msg) + } + } + } } type clientModeRunner struct { ModeMap map[string]func() error } +type clientModeRunnerResult struct { + OK bool + Msg string + Err error +} + func (r *clientModeRunner) Add(name string, f func() error) { if r.ModeMap == nil { r.ModeMap = make(map[string]func() error) @@ -526,9 +557,9 @@ func (r *clientModeRunner) Add(name string, f func() error) { r.ModeMap[name] = f } -func (r *clientModeRunner) Run() { +func (r *clientModeRunner) Run() clientModeRunnerResult { if len(r.ModeMap) == 0 { - logger.Fatal("no mode specified") + return clientModeRunnerResult{OK: false, Msg: "no mode specified"} } type modeError struct { @@ -546,9 +577,13 @@ func (r *clientModeRunner) Run() { for i := 0; i < len(r.ModeMap); i++ { e := <-errChan if e.Err != nil { - logger.Fatal("failed to run "+e.Name, zap.Error(e.Err)) + return clientModeRunnerResult{OK: false, Msg: "failed to run " + e.Name, Err: e.Err} } } + + // We don't really have any such cases, as currently none of our modes would stop on themselves without error. + // But we leave the possibility here for future expansion. + return clientModeRunnerResult{OK: true, Msg: "finished without error"} } func clientSOCKS5(config socks5Config, c client.Client) error { From 0c198abd2e47fc669b0e3818c9c3d82e6c766835 Mon Sep 17 00:00:00 2001 From: Haruue Date: Sat, 18 May 2024 11:28:47 +0800 Subject: [PATCH 081/161] fix: mod name major version suffix v2 ref: https://go.dev/ref/mod#major-version-suffixes --- app/cmd/client.go | 28 +++++++++---------- app/cmd/ping.go | 2 +- app/cmd/server.go | 16 +++++------ app/cmd/speedtest.go | 8 +++--- app/cmd/update.go | 4 +-- app/go.mod | 10 +++---- app/internal/forwarding/tcp.go | 2 +- app/internal/forwarding/tcp_test.go | 2 +- app/internal/forwarding/udp.go | 2 +- app/internal/forwarding/udp_test.go | 2 +- app/internal/http/server.go | 2 +- app/internal/http/server_test.go | 2 +- app/internal/proxymux/manager.go | 2 +- app/internal/proxymux/mux_test.go | 2 +- app/internal/redirect/tcp_linux.go | 2 +- app/internal/redirect/tcp_others.go | 2 +- app/internal/socks5/server.go | 2 +- app/internal/socks5/server_test.go | 2 +- app/internal/tproxy/tcp_linux.go | 2 +- app/internal/tproxy/tcp_others.go | 2 +- app/internal/tproxy/udp_linux.go | 2 +- app/internal/tproxy/udp_others.go | 2 +- app/internal/tun/server.go | 2 +- app/internal/utils/geoloader.go | 4 +-- app/internal/utils/update.go | 2 +- app/internal/utils_test/mock.go | 2 +- app/main.go | 2 +- core/client/.mockery.yaml | 2 +- core/client/client.go | 8 +++--- core/client/config.go | 4 +-- core/client/mock_udpIO.go | 2 +- core/client/reconnect.go | 2 +- core/client/udp.go | 6 ++-- core/client/udp_test.go | 4 +-- core/go.mod | 2 +- core/internal/congestion/bbr/bbr_sender.go | 2 +- core/internal/congestion/brutal/brutal.go | 2 +- core/internal/congestion/utils.go | 4 +-- core/internal/frag/frag.go | 2 +- core/internal/frag/frag_test.go | 2 +- core/internal/integration_tests/.mockery.yaml | 2 +- core/internal/integration_tests/close_test.go | 8 +++--- core/internal/integration_tests/masq_test.go | 6 ++-- .../integration_tests/mocks/mock_Outbound.go | 2 +- core/internal/integration_tests/smoke_test.go | 8 +++--- .../internal/integration_tests/stress_test.go | 6 ++-- .../integration_tests/trafficlogger_test.go | 6 ++-- core/internal/integration_tests/utils_test.go | 2 +- core/internal/protocol/proxy.go | 2 +- core/server/.mockery.yaml | 2 +- core/server/config.go | 4 +-- core/server/mock_udpIO.go | 2 +- core/server/server.go | 6 ++-- core/server/udp.go | 6 ++-- core/server/udp_test.go | 2 +- extras/auth/command.go | 2 +- extras/auth/http.go | 2 +- extras/auth/password.go | 2 +- extras/auth/userpass.go | 2 +- extras/go.mod | 6 ++-- extras/masq/server.go | 2 +- extras/outbounds/.mockery.yaml | 2 +- extras/outbounds/acl.go | 2 +- extras/outbounds/acl/compile.go | 2 +- extras/outbounds/acl/compile_test.go | 2 +- extras/outbounds/acl/matchers_v2geo.go | 2 +- extras/outbounds/acl/matchers_v2geo_test.go | 2 +- extras/outbounds/interface.go | 2 +- extras/outbounds/speedtest.go | 2 +- extras/trafficlogger/http.go | 2 +- 70 files changed, 126 insertions(+), 126 deletions(-) diff --git a/app/cmd/client.go b/app/cmd/client.go index 355523a..8f05c1e 100644 --- a/app/cmd/client.go +++ b/app/cmd/client.go @@ -21,20 +21,20 @@ import ( "github.com/spf13/viper" "go.uber.org/zap" - "github.com/apernet/hysteria/app/internal/forwarding" - "github.com/apernet/hysteria/app/internal/http" - "github.com/apernet/hysteria/app/internal/proxymux" - "github.com/apernet/hysteria/app/internal/redirect" - "github.com/apernet/hysteria/app/internal/sockopts" - "github.com/apernet/hysteria/app/internal/socks5" - "github.com/apernet/hysteria/app/internal/tproxy" - "github.com/apernet/hysteria/app/internal/tun" - "github.com/apernet/hysteria/app/internal/url" - "github.com/apernet/hysteria/app/internal/utils" - "github.com/apernet/hysteria/core/client" - "github.com/apernet/hysteria/extras/correctnet" - "github.com/apernet/hysteria/extras/obfs" - "github.com/apernet/hysteria/extras/transport/udphop" + "github.com/apernet/hysteria/app/v2/internal/forwarding" + "github.com/apernet/hysteria/app/v2/internal/http" + "github.com/apernet/hysteria/app/v2/internal/proxymux" + "github.com/apernet/hysteria/app/v2/internal/redirect" + "github.com/apernet/hysteria/app/v2/internal/sockopts" + "github.com/apernet/hysteria/app/v2/internal/socks5" + "github.com/apernet/hysteria/app/v2/internal/tproxy" + "github.com/apernet/hysteria/app/v2/internal/tun" + "github.com/apernet/hysteria/app/v2/internal/url" + "github.com/apernet/hysteria/app/v2/internal/utils" + "github.com/apernet/hysteria/core/v2/client" + "github.com/apernet/hysteria/extras/v2/correctnet" + "github.com/apernet/hysteria/extras/v2/obfs" + "github.com/apernet/hysteria/extras/v2/transport/udphop" ) // Client flags diff --git a/app/cmd/ping.go b/app/cmd/ping.go index 856595b..db45052 100644 --- a/app/cmd/ping.go +++ b/app/cmd/ping.go @@ -7,7 +7,7 @@ import ( "github.com/spf13/viper" "go.uber.org/zap" - "github.com/apernet/hysteria/core/client" + "github.com/apernet/hysteria/core/v2/client" ) // pingCmd represents the ping command diff --git a/app/cmd/server.go b/app/cmd/server.go index 9e81312..2bf4efe 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -21,14 +21,14 @@ import ( "github.com/spf13/viper" "go.uber.org/zap" - "github.com/apernet/hysteria/app/internal/utils" - "github.com/apernet/hysteria/core/server" - "github.com/apernet/hysteria/extras/auth" - "github.com/apernet/hysteria/extras/correctnet" - "github.com/apernet/hysteria/extras/masq" - "github.com/apernet/hysteria/extras/obfs" - "github.com/apernet/hysteria/extras/outbounds" - "github.com/apernet/hysteria/extras/trafficlogger" + "github.com/apernet/hysteria/app/v2/internal/utils" + "github.com/apernet/hysteria/core/v2/server" + "github.com/apernet/hysteria/extras/v2/auth" + "github.com/apernet/hysteria/extras/v2/correctnet" + "github.com/apernet/hysteria/extras/v2/masq" + "github.com/apernet/hysteria/extras/v2/obfs" + "github.com/apernet/hysteria/extras/v2/outbounds" + "github.com/apernet/hysteria/extras/v2/trafficlogger" ) const ( diff --git a/app/cmd/speedtest.go b/app/cmd/speedtest.go index 86bdbdb..6195c72 100644 --- a/app/cmd/speedtest.go +++ b/app/cmd/speedtest.go @@ -9,10 +9,10 @@ import ( "github.com/spf13/viper" "go.uber.org/zap" - "github.com/apernet/hysteria/core/client" - hyErrors "github.com/apernet/hysteria/core/errors" - "github.com/apernet/hysteria/extras/outbounds" - "github.com/apernet/hysteria/extras/outbounds/speedtest" + "github.com/apernet/hysteria/core/v2/client" + hyErrors "github.com/apernet/hysteria/core/v2/errors" + "github.com/apernet/hysteria/extras/v2/outbounds" + "github.com/apernet/hysteria/extras/v2/outbounds/speedtest" ) var ( diff --git a/app/cmd/update.go b/app/cmd/update.go index 3b26740..11eadd0 100644 --- a/app/cmd/update.go +++ b/app/cmd/update.go @@ -6,8 +6,8 @@ import ( "github.com/spf13/cobra" "go.uber.org/zap" - "github.com/apernet/hysteria/app/internal/utils" - "github.com/apernet/hysteria/core/client" + "github.com/apernet/hysteria/app/v2/internal/utils" + "github.com/apernet/hysteria/core/v2/client" ) const ( diff --git a/app/go.mod b/app/go.mod index bc7ceef..dc12a22 100644 --- a/app/go.mod +++ b/app/go.mod @@ -1,11 +1,11 @@ -module github.com/apernet/hysteria/app +module github.com/apernet/hysteria/app/v2 go 1.21 require ( github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f - github.com/apernet/hysteria/core v0.0.0-00010101000000-000000000000 - github.com/apernet/hysteria/extras v0.0.0-00010101000000-000000000000 + github.com/apernet/hysteria/core/v2 v2.0.0-00010101000000-000000000000 + github.com/apernet/hysteria/extras/v2 v2.0.0-00010101000000-000000000000 github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad github.com/caddyserver/certmagic v0.17.2 github.com/mdp/qrterminal/v3 v3.1.1 @@ -66,6 +66,6 @@ require ( rsc.io/qr v0.2.0 // indirect ) -replace github.com/apernet/hysteria/core => ../core +replace github.com/apernet/hysteria/core/v2 => ../core -replace github.com/apernet/hysteria/extras => ../extras +replace github.com/apernet/hysteria/extras/v2 => ../extras diff --git a/app/internal/forwarding/tcp.go b/app/internal/forwarding/tcp.go index 7d22d33..8936385 100644 --- a/app/internal/forwarding/tcp.go +++ b/app/internal/forwarding/tcp.go @@ -4,7 +4,7 @@ import ( "io" "net" - "github.com/apernet/hysteria/core/client" + "github.com/apernet/hysteria/core/v2/client" ) type TCPTunnel struct { diff --git a/app/internal/forwarding/tcp_test.go b/app/internal/forwarding/tcp_test.go index 91ab0a1..075b233 100644 --- a/app/internal/forwarding/tcp_test.go +++ b/app/internal/forwarding/tcp_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/apernet/hysteria/app/internal/utils_test" + "github.com/apernet/hysteria/app/v2/internal/utils_test" ) func TestTCPTunnel(t *testing.T) { diff --git a/app/internal/forwarding/udp.go b/app/internal/forwarding/udp.go index 2f859e9..35886a8 100644 --- a/app/internal/forwarding/udp.go +++ b/app/internal/forwarding/udp.go @@ -6,7 +6,7 @@ import ( "sync/atomic" "time" - "github.com/apernet/hysteria/core/client" + "github.com/apernet/hysteria/core/v2/client" ) const ( diff --git a/app/internal/forwarding/udp_test.go b/app/internal/forwarding/udp_test.go index feb0c20..ba4f3ba 100644 --- a/app/internal/forwarding/udp_test.go +++ b/app/internal/forwarding/udp_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/apernet/hysteria/app/internal/utils_test" + "github.com/apernet/hysteria/app/v2/internal/utils_test" ) func TestUDPTunnel(t *testing.T) { diff --git a/app/internal/http/server.go b/app/internal/http/server.go index 4f11d14..e5761ce 100644 --- a/app/internal/http/server.go +++ b/app/internal/http/server.go @@ -12,7 +12,7 @@ import ( "strings" "time" - "github.com/apernet/hysteria/core/client" + "github.com/apernet/hysteria/core/v2/client" ) const ( diff --git a/app/internal/http/server_test.go b/app/internal/http/server_test.go index 960bef7..43f6e16 100644 --- a/app/internal/http/server_test.go +++ b/app/internal/http/server_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/apernet/hysteria/core/client" + "github.com/apernet/hysteria/core/v2/client" ) const ( diff --git a/app/internal/proxymux/manager.go b/app/internal/proxymux/manager.go index 92df918..23f4ad6 100644 --- a/app/internal/proxymux/manager.go +++ b/app/internal/proxymux/manager.go @@ -4,7 +4,7 @@ import ( "net" "sync" - "github.com/apernet/hysteria/extras/correctnet" + "github.com/apernet/hysteria/extras/v2/correctnet" ) type muxManager struct { diff --git a/app/internal/proxymux/mux_test.go b/app/internal/proxymux/mux_test.go index 46cbf95..7b57237 100644 --- a/app/internal/proxymux/mux_test.go +++ b/app/internal/proxymux/mux_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/apernet/hysteria/app/internal/proxymux/internal/mocks" + "github.com/apernet/hysteria/app/v2/internal/proxymux/internal/mocks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" diff --git a/app/internal/redirect/tcp_linux.go b/app/internal/redirect/tcp_linux.go index 8445fa3..5351543 100644 --- a/app/internal/redirect/tcp_linux.go +++ b/app/internal/redirect/tcp_linux.go @@ -8,7 +8,7 @@ import ( "syscall" "unsafe" - "github.com/apernet/hysteria/core/client" + "github.com/apernet/hysteria/core/v2/client" ) const ( diff --git a/app/internal/redirect/tcp_others.go b/app/internal/redirect/tcp_others.go index ddb770f..f5cf6f9 100644 --- a/app/internal/redirect/tcp_others.go +++ b/app/internal/redirect/tcp_others.go @@ -6,7 +6,7 @@ import ( "errors" "net" - "github.com/apernet/hysteria/core/client" + "github.com/apernet/hysteria/core/v2/client" ) type TCPRedirect struct { diff --git a/app/internal/socks5/server.go b/app/internal/socks5/server.go index 84b58ed..0fc7de6 100644 --- a/app/internal/socks5/server.go +++ b/app/internal/socks5/server.go @@ -7,7 +7,7 @@ import ( "github.com/txthinking/socks5" - "github.com/apernet/hysteria/core/client" + "github.com/apernet/hysteria/core/v2/client" ) const udpBufferSize = 4096 diff --git a/app/internal/socks5/server_test.go b/app/internal/socks5/server_test.go index 7aec82a..1290058 100644 --- a/app/internal/socks5/server_test.go +++ b/app/internal/socks5/server_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/apernet/hysteria/app/internal/utils_test" + "github.com/apernet/hysteria/app/v2/internal/utils_test" ) func TestServer(t *testing.T) { diff --git a/app/internal/tproxy/tcp_linux.go b/app/internal/tproxy/tcp_linux.go index 4c922f1..4d8c1e9 100644 --- a/app/internal/tproxy/tcp_linux.go +++ b/app/internal/tproxy/tcp_linux.go @@ -5,7 +5,7 @@ import ( "net" "github.com/apernet/go-tproxy" - "github.com/apernet/hysteria/core/client" + "github.com/apernet/hysteria/core/v2/client" ) type TCPTProxy struct { diff --git a/app/internal/tproxy/tcp_others.go b/app/internal/tproxy/tcp_others.go index 27e98c5..f33de6a 100644 --- a/app/internal/tproxy/tcp_others.go +++ b/app/internal/tproxy/tcp_others.go @@ -6,7 +6,7 @@ import ( "errors" "net" - "github.com/apernet/hysteria/core/client" + "github.com/apernet/hysteria/core/v2/client" ) type TCPTProxy struct { diff --git a/app/internal/tproxy/udp_linux.go b/app/internal/tproxy/udp_linux.go index 03e2f42..827bea2 100644 --- a/app/internal/tproxy/udp_linux.go +++ b/app/internal/tproxy/udp_linux.go @@ -6,7 +6,7 @@ import ( "time" "github.com/apernet/go-tproxy" - "github.com/apernet/hysteria/core/client" + "github.com/apernet/hysteria/core/v2/client" ) const ( diff --git a/app/internal/tproxy/udp_others.go b/app/internal/tproxy/udp_others.go index db9cedb..3d267e6 100644 --- a/app/internal/tproxy/udp_others.go +++ b/app/internal/tproxy/udp_others.go @@ -7,7 +7,7 @@ import ( "net" "time" - "github.com/apernet/hysteria/core/client" + "github.com/apernet/hysteria/core/v2/client" ) type UDPTProxy struct { diff --git a/app/internal/tun/server.go b/app/internal/tun/server.go index 4ae5393..303d4ec 100644 --- a/app/internal/tun/server.go +++ b/app/internal/tun/server.go @@ -14,7 +14,7 @@ import ( "github.com/sagernet/sing/common/network" "go.uber.org/zap" - "github.com/apernet/hysteria/core/client" + "github.com/apernet/hysteria/core/v2/client" ) type Server struct { diff --git a/app/internal/utils/geoloader.go b/app/internal/utils/geoloader.go index fa9775b..468c68a 100644 --- a/app/internal/utils/geoloader.go +++ b/app/internal/utils/geoloader.go @@ -7,8 +7,8 @@ import ( "os" "time" - "github.com/apernet/hysteria/extras/outbounds/acl" - "github.com/apernet/hysteria/extras/outbounds/acl/v2geo" + "github.com/apernet/hysteria/extras/v2/outbounds/acl" + "github.com/apernet/hysteria/extras/v2/outbounds/acl/v2geo" ) const ( diff --git a/app/internal/utils/update.go b/app/internal/utils/update.go index 8887cb4..60da91f 100644 --- a/app/internal/utils/update.go +++ b/app/internal/utils/update.go @@ -8,7 +8,7 @@ import ( "net/http" "time" - "github.com/apernet/hysteria/core/client" + "github.com/apernet/hysteria/core/v2/client" ) const ( diff --git a/app/internal/utils_test/mock.go b/app/internal/utils_test/mock.go index 04adad6..06057d8 100644 --- a/app/internal/utils_test/mock.go +++ b/app/internal/utils_test/mock.go @@ -5,7 +5,7 @@ import ( "net" "time" - "github.com/apernet/hysteria/core/client" + "github.com/apernet/hysteria/core/v2/client" ) type MockEchoHyClient struct{} diff --git a/app/main.go b/app/main.go index 6d1554b..81d8168 100644 --- a/app/main.go +++ b/app/main.go @@ -1,6 +1,6 @@ package main -import "github.com/apernet/hysteria/app/cmd" +import "github.com/apernet/hysteria/app/v2/cmd" func main() { cmd.Execute() diff --git a/core/client/.mockery.yaml b/core/client/.mockery.yaml index 9500c13..299e6f9 100644 --- a/core/client/.mockery.yaml +++ b/core/client/.mockery.yaml @@ -2,7 +2,7 @@ with-expecter: true inpackage: true dir: . packages: - github.com/apernet/hysteria/core/client: + github.com/apernet/hysteria/core/v2/client: interfaces: udpIO: config: diff --git a/core/client/client.go b/core/client/client.go index 5a25d37..91d59fd 100644 --- a/core/client/client.go +++ b/core/client/client.go @@ -8,10 +8,10 @@ import ( "net/url" "time" - coreErrs "github.com/apernet/hysteria/core/errors" - "github.com/apernet/hysteria/core/internal/congestion" - "github.com/apernet/hysteria/core/internal/protocol" - "github.com/apernet/hysteria/core/internal/utils" + coreErrs "github.com/apernet/hysteria/core/v2/errors" + "github.com/apernet/hysteria/core/v2/internal/congestion" + "github.com/apernet/hysteria/core/v2/internal/protocol" + "github.com/apernet/hysteria/core/v2/internal/utils" "github.com/apernet/quic-go" "github.com/apernet/quic-go/http3" diff --git a/core/client/config.go b/core/client/config.go index 41a72ba..7270c30 100644 --- a/core/client/config.go +++ b/core/client/config.go @@ -5,8 +5,8 @@ import ( "net" "time" - "github.com/apernet/hysteria/core/errors" - "github.com/apernet/hysteria/core/internal/pmtud" + "github.com/apernet/hysteria/core/v2/errors" + "github.com/apernet/hysteria/core/v2/internal/pmtud" ) const ( diff --git a/core/client/mock_udpIO.go b/core/client/mock_udpIO.go index a85e006..aa1444e 100644 --- a/core/client/mock_udpIO.go +++ b/core/client/mock_udpIO.go @@ -3,7 +3,7 @@ package client import ( - protocol "github.com/apernet/hysteria/core/internal/protocol" + protocol "github.com/apernet/hysteria/core/v2/internal/protocol" mock "github.com/stretchr/testify/mock" ) diff --git a/core/client/reconnect.go b/core/client/reconnect.go index 05d60b3..91c7eb4 100644 --- a/core/client/reconnect.go +++ b/core/client/reconnect.go @@ -4,7 +4,7 @@ import ( "net" "sync" - coreErrs "github.com/apernet/hysteria/core/errors" + coreErrs "github.com/apernet/hysteria/core/v2/errors" ) // reconnectableClientImpl is a wrapper of Client, which can reconnect when the connection is closed, diff --git a/core/client/udp.go b/core/client/udp.go index dfe574f..ca98095 100644 --- a/core/client/udp.go +++ b/core/client/udp.go @@ -8,9 +8,9 @@ import ( "github.com/apernet/quic-go" - coreErrs "github.com/apernet/hysteria/core/errors" - "github.com/apernet/hysteria/core/internal/frag" - "github.com/apernet/hysteria/core/internal/protocol" + coreErrs "github.com/apernet/hysteria/core/v2/errors" + "github.com/apernet/hysteria/core/v2/internal/frag" + "github.com/apernet/hysteria/core/v2/internal/protocol" ) const ( diff --git a/core/client/udp_test.go b/core/client/udp_test.go index 0ed9659..af1a6d0 100644 --- a/core/client/udp_test.go +++ b/core/client/udp_test.go @@ -10,8 +10,8 @@ import ( "github.com/stretchr/testify/mock" "go.uber.org/goleak" - coreErrs "github.com/apernet/hysteria/core/errors" - "github.com/apernet/hysteria/core/internal/protocol" + coreErrs "github.com/apernet/hysteria/core/v2/errors" + "github.com/apernet/hysteria/core/v2/internal/protocol" ) func TestUDPSessionManager(t *testing.T) { diff --git a/core/go.mod b/core/go.mod index 2be1569..6c88c35 100644 --- a/core/go.mod +++ b/core/go.mod @@ -1,4 +1,4 @@ -module github.com/apernet/hysteria/core +module github.com/apernet/hysteria/core/v2 go 1.21 diff --git a/core/internal/congestion/bbr/bbr_sender.go b/core/internal/congestion/bbr/bbr_sender.go index 4afb078..e1b3654 100644 --- a/core/internal/congestion/bbr/bbr_sender.go +++ b/core/internal/congestion/bbr/bbr_sender.go @@ -8,7 +8,7 @@ import ( "github.com/apernet/quic-go/congestion" - "github.com/apernet/hysteria/core/internal/congestion/common" + "github.com/apernet/hysteria/core/v2/internal/congestion/common" ) // BbrSender implements BBR congestion control algorithm. BBR aims to estimate diff --git a/core/internal/congestion/brutal/brutal.go b/core/internal/congestion/brutal/brutal.go index 4edd45d..de591a9 100644 --- a/core/internal/congestion/brutal/brutal.go +++ b/core/internal/congestion/brutal/brutal.go @@ -6,7 +6,7 @@ import ( "strconv" "time" - "github.com/apernet/hysteria/core/internal/congestion/common" + "github.com/apernet/hysteria/core/v2/internal/congestion/common" "github.com/apernet/quic-go/congestion" ) diff --git a/core/internal/congestion/utils.go b/core/internal/congestion/utils.go index 48ddaa2..1e06060 100644 --- a/core/internal/congestion/utils.go +++ b/core/internal/congestion/utils.go @@ -1,8 +1,8 @@ package congestion import ( - "github.com/apernet/hysteria/core/internal/congestion/bbr" - "github.com/apernet/hysteria/core/internal/congestion/brutal" + "github.com/apernet/hysteria/core/v2/internal/congestion/bbr" + "github.com/apernet/hysteria/core/v2/internal/congestion/brutal" "github.com/apernet/quic-go" ) diff --git a/core/internal/frag/frag.go b/core/internal/frag/frag.go index 75c8dac..237ba06 100644 --- a/core/internal/frag/frag.go +++ b/core/internal/frag/frag.go @@ -1,7 +1,7 @@ package frag import ( - "github.com/apernet/hysteria/core/internal/protocol" + "github.com/apernet/hysteria/core/v2/internal/protocol" ) func FragUDPMessage(m *protocol.UDPMessage, maxSize int) []protocol.UDPMessage { diff --git a/core/internal/frag/frag_test.go b/core/internal/frag/frag_test.go index b77eeb3..0dc0cd7 100644 --- a/core/internal/frag/frag_test.go +++ b/core/internal/frag/frag_test.go @@ -4,7 +4,7 @@ import ( "reflect" "testing" - "github.com/apernet/hysteria/core/internal/protocol" + "github.com/apernet/hysteria/core/v2/internal/protocol" ) func TestFragUDPMessage(t *testing.T) { diff --git a/core/internal/integration_tests/.mockery.yaml b/core/internal/integration_tests/.mockery.yaml index b1da36f..ad233ba 100644 --- a/core/internal/integration_tests/.mockery.yaml +++ b/core/internal/integration_tests/.mockery.yaml @@ -7,7 +7,7 @@ packages: Conn: config: mockname: MockConn - github.com/apernet/hysteria/core/server: + github.com/apernet/hysteria/core/v2/server: interfaces: Outbound: config: diff --git a/core/internal/integration_tests/close_test.go b/core/internal/integration_tests/close_test.go index ab6d016..ac7f84b 100644 --- a/core/internal/integration_tests/close_test.go +++ b/core/internal/integration_tests/close_test.go @@ -9,10 +9,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/apernet/hysteria/core/client" - "github.com/apernet/hysteria/core/errors" - "github.com/apernet/hysteria/core/internal/integration_tests/mocks" - "github.com/apernet/hysteria/core/server" + "github.com/apernet/hysteria/core/v2/client" + "github.com/apernet/hysteria/core/v2/errors" + "github.com/apernet/hysteria/core/v2/internal/integration_tests/mocks" + "github.com/apernet/hysteria/core/v2/server" ) // TestClientServerTCPClose tests whether the client/server propagates the close of a connection correctly. diff --git a/core/internal/integration_tests/masq_test.go b/core/internal/integration_tests/masq_test.go index 6f49053..e3dee14 100644 --- a/core/internal/integration_tests/masq_test.go +++ b/core/internal/integration_tests/masq_test.go @@ -12,9 +12,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/apernet/hysteria/core/internal/integration_tests/mocks" - "github.com/apernet/hysteria/core/internal/protocol" - "github.com/apernet/hysteria/core/server" + "github.com/apernet/hysteria/core/v2/internal/integration_tests/mocks" + "github.com/apernet/hysteria/core/v2/internal/protocol" + "github.com/apernet/hysteria/core/v2/server" "github.com/apernet/quic-go" "github.com/apernet/quic-go/http3" diff --git a/core/internal/integration_tests/mocks/mock_Outbound.go b/core/internal/integration_tests/mocks/mock_Outbound.go index 1a2d0c6..52f7178 100644 --- a/core/internal/integration_tests/mocks/mock_Outbound.go +++ b/core/internal/integration_tests/mocks/mock_Outbound.go @@ -7,7 +7,7 @@ import ( mock "github.com/stretchr/testify/mock" - server "github.com/apernet/hysteria/core/server" + server "github.com/apernet/hysteria/core/v2/server" ) // MockOutbound is an autogenerated mock type for the Outbound type diff --git a/core/internal/integration_tests/smoke_test.go b/core/internal/integration_tests/smoke_test.go index ab204cb..5288b61 100644 --- a/core/internal/integration_tests/smoke_test.go +++ b/core/internal/integration_tests/smoke_test.go @@ -8,10 +8,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/apernet/hysteria/core/client" - coreErrs "github.com/apernet/hysteria/core/errors" - "github.com/apernet/hysteria/core/internal/integration_tests/mocks" - "github.com/apernet/hysteria/core/server" + "github.com/apernet/hysteria/core/v2/client" + coreErrs "github.com/apernet/hysteria/core/v2/errors" + "github.com/apernet/hysteria/core/v2/internal/integration_tests/mocks" + "github.com/apernet/hysteria/core/v2/server" ) // Smoke tests that act as a sanity check for client & server to ensure they can talk to each other correctly. diff --git a/core/internal/integration_tests/stress_test.go b/core/internal/integration_tests/stress_test.go index f10ac3a..cf44ad6 100644 --- a/core/internal/integration_tests/stress_test.go +++ b/core/internal/integration_tests/stress_test.go @@ -13,9 +13,9 @@ import ( "github.com/stretchr/testify/mock" "golang.org/x/time/rate" - "github.com/apernet/hysteria/core/client" - "github.com/apernet/hysteria/core/internal/integration_tests/mocks" - "github.com/apernet/hysteria/core/server" + "github.com/apernet/hysteria/core/v2/client" + "github.com/apernet/hysteria/core/v2/internal/integration_tests/mocks" + "github.com/apernet/hysteria/core/v2/server" ) type tcpStressor struct { diff --git a/core/internal/integration_tests/trafficlogger_test.go b/core/internal/integration_tests/trafficlogger_test.go index 690a348..b5355ff 100644 --- a/core/internal/integration_tests/trafficlogger_test.go +++ b/core/internal/integration_tests/trafficlogger_test.go @@ -9,9 +9,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/apernet/hysteria/core/client" - "github.com/apernet/hysteria/core/internal/integration_tests/mocks" - "github.com/apernet/hysteria/core/server" + "github.com/apernet/hysteria/core/v2/client" + "github.com/apernet/hysteria/core/v2/internal/integration_tests/mocks" + "github.com/apernet/hysteria/core/v2/server" ) // TestClientServerTrafficLoggerTCP tests that the traffic logger is correctly called for TCP connections, diff --git a/core/internal/integration_tests/utils_test.go b/core/internal/integration_tests/utils_test.go index a1aa160..d7ecb68 100644 --- a/core/internal/integration_tests/utils_test.go +++ b/core/internal/integration_tests/utils_test.go @@ -5,7 +5,7 @@ import ( "io" "net" - "github.com/apernet/hysteria/core/server" + "github.com/apernet/hysteria/core/v2/server" ) // This file provides utilities for the integration tests. diff --git a/core/internal/protocol/proxy.go b/core/internal/protocol/proxy.go index 4a78fc2..9335c47 100644 --- a/core/internal/protocol/proxy.go +++ b/core/internal/protocol/proxy.go @@ -6,7 +6,7 @@ import ( "fmt" "io" - "github.com/apernet/hysteria/core/errors" + "github.com/apernet/hysteria/core/v2/errors" "github.com/apernet/quic-go/quicvarint" ) diff --git a/core/server/.mockery.yaml b/core/server/.mockery.yaml index b7cf743..d73136a 100644 --- a/core/server/.mockery.yaml +++ b/core/server/.mockery.yaml @@ -2,7 +2,7 @@ with-expecter: true inpackage: true dir: . packages: - github.com/apernet/hysteria/core/server: + github.com/apernet/hysteria/core/v2/server: interfaces: udpIO: config: diff --git a/core/server/config.go b/core/server/config.go index 8d5f5db..c6bf733 100644 --- a/core/server/config.go +++ b/core/server/config.go @@ -6,8 +6,8 @@ import ( "net/http" "time" - "github.com/apernet/hysteria/core/errors" - "github.com/apernet/hysteria/core/internal/pmtud" + "github.com/apernet/hysteria/core/v2/errors" + "github.com/apernet/hysteria/core/v2/internal/pmtud" ) const ( diff --git a/core/server/mock_udpIO.go b/core/server/mock_udpIO.go index 6df862b..3950cb9 100644 --- a/core/server/mock_udpIO.go +++ b/core/server/mock_udpIO.go @@ -3,7 +3,7 @@ package server import ( - protocol "github.com/apernet/hysteria/core/internal/protocol" + protocol "github.com/apernet/hysteria/core/v2/internal/protocol" mock "github.com/stretchr/testify/mock" ) diff --git a/core/server/server.go b/core/server/server.go index d38097d..bd61db0 100644 --- a/core/server/server.go +++ b/core/server/server.go @@ -9,9 +9,9 @@ import ( "github.com/apernet/quic-go" "github.com/apernet/quic-go/http3" - "github.com/apernet/hysteria/core/internal/congestion" - "github.com/apernet/hysteria/core/internal/protocol" - "github.com/apernet/hysteria/core/internal/utils" + "github.com/apernet/hysteria/core/v2/internal/congestion" + "github.com/apernet/hysteria/core/v2/internal/protocol" + "github.com/apernet/hysteria/core/v2/internal/utils" ) const ( diff --git a/core/server/udp.go b/core/server/udp.go index bea586b..320dc00 100644 --- a/core/server/udp.go +++ b/core/server/udp.go @@ -8,9 +8,9 @@ import ( "github.com/apernet/quic-go" - "github.com/apernet/hysteria/core/internal/frag" - "github.com/apernet/hysteria/core/internal/protocol" - "github.com/apernet/hysteria/core/internal/utils" + "github.com/apernet/hysteria/core/v2/internal/frag" + "github.com/apernet/hysteria/core/v2/internal/protocol" + "github.com/apernet/hysteria/core/v2/internal/utils" ) const ( diff --git a/core/server/udp_test.go b/core/server/udp_test.go index ccfb8cf..a0169e3 100644 --- a/core/server/udp_test.go +++ b/core/server/udp_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/mock" "go.uber.org/goleak" - "github.com/apernet/hysteria/core/internal/protocol" + "github.com/apernet/hysteria/core/v2/internal/protocol" ) func TestUDPSessionManager(t *testing.T) { diff --git a/extras/auth/command.go b/extras/auth/command.go index 9c4d193..a981a37 100644 --- a/extras/auth/command.go +++ b/extras/auth/command.go @@ -6,7 +6,7 @@ import ( "strconv" "strings" - "github.com/apernet/hysteria/core/server" + "github.com/apernet/hysteria/core/v2/server" ) var _ server.Authenticator = &CommandAuthenticator{} diff --git a/extras/auth/http.go b/extras/auth/http.go index 0212b7c..379a8d7 100644 --- a/extras/auth/http.go +++ b/extras/auth/http.go @@ -10,7 +10,7 @@ import ( "net/http" "time" - "github.com/apernet/hysteria/core/server" + "github.com/apernet/hysteria/core/v2/server" ) const ( diff --git a/extras/auth/password.go b/extras/auth/password.go index 00fd826..c00f399 100644 --- a/extras/auth/password.go +++ b/extras/auth/password.go @@ -3,7 +3,7 @@ package auth import ( "net" - "github.com/apernet/hysteria/core/server" + "github.com/apernet/hysteria/core/v2/server" ) var _ server.Authenticator = &PasswordAuthenticator{} diff --git a/extras/auth/userpass.go b/extras/auth/userpass.go index 86f011e..8faf87a 100644 --- a/extras/auth/userpass.go +++ b/extras/auth/userpass.go @@ -4,7 +4,7 @@ import ( "net" "strings" - "github.com/apernet/hysteria/core/server" + "github.com/apernet/hysteria/core/v2/server" ) const ( diff --git a/extras/go.mod b/extras/go.mod index 5274da0..3048dc3 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -1,9 +1,9 @@ -module github.com/apernet/hysteria/extras +module github.com/apernet/hysteria/extras/v2 go 1.21 require ( - github.com/apernet/hysteria/core v0.0.0-00010101000000-000000000000 + github.com/apernet/hysteria/core/v2 v2.0.0-00010101000000-000000000000 github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 github.com/hashicorp/golang-lru/v2 v2.0.5 github.com/miekg/dns v1.1.55 @@ -34,4 +34,4 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace github.com/apernet/hysteria/core => ../core +replace github.com/apernet/hysteria/core/v2 => ../core diff --git a/extras/masq/server.go b/extras/masq/server.go index 3acb177..6cd842d 100644 --- a/extras/masq/server.go +++ b/extras/masq/server.go @@ -7,7 +7,7 @@ import ( "net" "net/http" - "github.com/apernet/hysteria/extras/correctnet" + "github.com/apernet/hysteria/extras/v2/correctnet" ) // MasqTCPServer covers the TCP parts of a standard web server (TCP based HTTP/HTTPS). diff --git a/extras/outbounds/.mockery.yaml b/extras/outbounds/.mockery.yaml index c9e0b71..5f5cc6e 100644 --- a/extras/outbounds/.mockery.yaml +++ b/extras/outbounds/.mockery.yaml @@ -2,7 +2,7 @@ with-expecter: true inpackage: true dir: . packages: - github.com/apernet/hysteria/extras/outbounds: + github.com/apernet/hysteria/extras/v2/outbounds: interfaces: PluggableOutbound: config: diff --git a/extras/outbounds/acl.go b/extras/outbounds/acl.go index a4dd21c..ecdeaaa 100644 --- a/extras/outbounds/acl.go +++ b/extras/outbounds/acl.go @@ -6,7 +6,7 @@ import ( "os" "strings" - "github.com/apernet/hysteria/extras/outbounds/acl" + "github.com/apernet/hysteria/extras/v2/outbounds/acl" ) const ( diff --git a/extras/outbounds/acl/compile.go b/extras/outbounds/acl/compile.go index cdd0a94..caee138 100644 --- a/extras/outbounds/acl/compile.go +++ b/extras/outbounds/acl/compile.go @@ -6,7 +6,7 @@ import ( "strconv" "strings" - "github.com/apernet/hysteria/extras/outbounds/acl/v2geo" + "github.com/apernet/hysteria/extras/v2/outbounds/acl/v2geo" lru "github.com/hashicorp/golang-lru/v2" ) diff --git a/extras/outbounds/acl/compile_test.go b/extras/outbounds/acl/compile_test.go index a11c4e8..bf51f68 100644 --- a/extras/outbounds/acl/compile_test.go +++ b/extras/outbounds/acl/compile_test.go @@ -4,7 +4,7 @@ import ( "net" "testing" - "github.com/apernet/hysteria/extras/outbounds/acl/v2geo" + "github.com/apernet/hysteria/extras/v2/outbounds/acl/v2geo" "github.com/stretchr/testify/assert" ) diff --git a/extras/outbounds/acl/matchers_v2geo.go b/extras/outbounds/acl/matchers_v2geo.go index df83105..ad79b68 100644 --- a/extras/outbounds/acl/matchers_v2geo.go +++ b/extras/outbounds/acl/matchers_v2geo.go @@ -8,7 +8,7 @@ import ( "sort" "strings" - "github.com/apernet/hysteria/extras/outbounds/acl/v2geo" + "github.com/apernet/hysteria/extras/v2/outbounds/acl/v2geo" ) var _ hostMatcher = (*geoipMatcher)(nil) diff --git a/extras/outbounds/acl/matchers_v2geo_test.go b/extras/outbounds/acl/matchers_v2geo_test.go index 4970baf..cc59512 100644 --- a/extras/outbounds/acl/matchers_v2geo_test.go +++ b/extras/outbounds/acl/matchers_v2geo_test.go @@ -4,7 +4,7 @@ import ( "net" "testing" - "github.com/apernet/hysteria/extras/outbounds/acl/v2geo" + "github.com/apernet/hysteria/extras/v2/outbounds/acl/v2geo" "github.com/stretchr/testify/assert" ) diff --git a/extras/outbounds/interface.go b/extras/outbounds/interface.go index dbe89cb..1870290 100644 --- a/extras/outbounds/interface.go +++ b/extras/outbounds/interface.go @@ -4,7 +4,7 @@ import ( "net" "strconv" - "github.com/apernet/hysteria/core/server" + "github.com/apernet/hysteria/core/v2/server" ) // The PluggableOutbound system is designed to function in a chain-like manner. diff --git a/extras/outbounds/speedtest.go b/extras/outbounds/speedtest.go index b788424..162f4dc 100644 --- a/extras/outbounds/speedtest.go +++ b/extras/outbounds/speedtest.go @@ -3,7 +3,7 @@ package outbounds import ( "net" - "github.com/apernet/hysteria/extras/outbounds/speedtest" + "github.com/apernet/hysteria/extras/v2/outbounds/speedtest" ) const ( diff --git a/extras/trafficlogger/http.go b/extras/trafficlogger/http.go index d0ec0a4..9ab943a 100644 --- a/extras/trafficlogger/http.go +++ b/extras/trafficlogger/http.go @@ -6,7 +6,7 @@ import ( "strconv" "sync" - "github.com/apernet/hysteria/core/server" + "github.com/apernet/hysteria/core/v2/server" ) const ( From 1742f83b8ea390a66870750e4f301d17e8794275 Mon Sep 17 00:00:00 2001 From: Haruue Date: Sat, 18 May 2024 17:13:27 +0800 Subject: [PATCH 082/161] ci: create release tags for core/ and extras/ --- .github/workflows/autotag.yaml | 104 +++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 .github/workflows/autotag.yaml diff --git a/.github/workflows/autotag.yaml b/.github/workflows/autotag.yaml new file mode 100644 index 0000000..a066a74 --- /dev/null +++ b/.github/workflows/autotag.yaml @@ -0,0 +1,104 @@ +name: "Create release tags for nested modules" + +on: + push: + tags: + - app/v*.*.* + +permissions: + contents: write + +jobs: + tag: + name: "Create tags" + runs-on: ubuntu-latest + steps: + - name: "Extract tagbase" + id: extract_tagbase + uses: actions/github-script@v7 + with: + script: | + const ref = context.ref; + core.info(`context.ref: ${ref}`); + const refPrefix = 'refs/tags/app/'; + if (!ref.startsWith(refPrefix)) { + core.setFailed(`context.ref does not start with ${refPrefix}: ${ref}`); + return; + } + const tagbase = ref.slice(refPrefix.length); + core.info(`tagbase: ${tagbase}`); + core.setOutput('tagbase', tagbase); + + - name: "Tagging core/*" + uses: actions/github-script@v7 + env: + INPUT_TAGPREFIX: "core/" + INPUT_TAGBASE: ${{ steps.extract_tagbase.outputs.tagbase }} + with: + script: | + const tagbase = core.getInput('tagbase', { required: true }); + const tagprefix = core.getInput('tagprefix', { required: true }); + const refname = `tags/${tagprefix}${tagbase}`; + core.info(`creating ref ${refname}`); + try { + await github.rest.git.createRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: `refs/${refname}`, + sha: context.sha + }); + core.info(`created ref ${refname}`); + return; + } catch (error) { + core.info(`failed to create ref ${refname}: ${error}`); + } + core.info(`updating ref ${refname}`) + try { + await github.rest.git.updateRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: refname, + sha: context.sha + }); + core.info(`updated ref ${refname}`); + return; + } catch (error) { + core.setFailed(`failed to update ref ${refname}: ${error}`); + } + + - name: "Tagging extras/*" + uses: actions/github-script@v7 + env: + INPUT_TAGPREFIX: "extras/" + INPUT_TAGBASE: ${{ steps.extract_tagbase.outputs.tagbase }} + with: + script: | + const tagbase = core.getInput('tagbase', { required: true }); + const tagprefix = core.getInput('tagprefix', { required: true }); + const refname = `tags/${tagprefix}${tagbase}`; + core.info(`creating ref ${refname}`); + try { + await github.rest.git.createRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: `refs/${refname}`, + sha: context.sha + }); + core.info(`created ref ${refname}`); + return; + } catch (error) { + core.info(`failed to create ref ${refname}: ${error}`); + } + core.info(`updating ref ${refname}`) + try { + await github.rest.git.updateRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: refname, + sha: context.sha + }); + core.info(`updated ref ${refname}`); + return; + } catch (error) { + core.setFailed(`failed to update ref ${refname}: ${error}`); + } From e0e75c46309eec2531b8613370cfbada259b5af3 Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 18 May 2024 15:01:16 -0700 Subject: [PATCH 083/161] wip: BBR experimental changes --- core/internal/congestion/bbr/bbr_sender.go | 59 +++++++++++++++++++--- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/core/internal/congestion/bbr/bbr_sender.go b/core/internal/congestion/bbr/bbr_sender.go index 4afb078..eaaf002 100644 --- a/core/internal/congestion/bbr/bbr_sender.go +++ b/core/internal/congestion/bbr/bbr_sender.go @@ -4,6 +4,8 @@ import ( "fmt" "math/rand" "net" + "os" + "strconv" "time" "github.com/apernet/quic-go/congestion" @@ -37,6 +39,8 @@ const ( derivedHighGain = 2.773 // The newly derived CWND gain for STARTUP, 2. derivedHighCWNDGain = 2.0 + + debugEnv = "HYSTERIA_BBR_DEBUG" ) // The cycle of gains used during the PROBE_BW stage. @@ -61,7 +65,7 @@ const ( // Flag. defaultStartupFullLossCount = 8 quicBbr2DefaultLossThreshold = 0.02 - maxBbrBurstPackets = 3 + maxBbrBurstPackets = 10 ) type bbrMode int @@ -237,6 +241,8 @@ type bbrSender struct { maxDatagramSize congestion.ByteCount // Recorded on packet sent. equivalent |unacked_packets_->bytes_in_flight()| bytesInFlight congestion.ByteCount + + debug bool } var _ congestion.CongestionControl = &bbrSender{} @@ -259,6 +265,7 @@ func newBbrSender( initialCongestionWindow, initialMaxCongestionWindow congestion.ByteCount, ) *bbrSender { + debug, _ := strconv.ParseBool(os.Getenv(debugEnv)) b := &bbrSender{ clock: clock, mode: bbrModeStartup, @@ -284,6 +291,7 @@ func newBbrSender( cwndToCalculateMinPacingRate: initialCongestionWindow, maxCongestionWindowWithNetworkParametersAdjusted: initialMaxCongestionWindow, maxDatagramSize: initialMaxDatagramSize, + debug: debug, } b.pacer = common.NewPacer(b.bandwidthForPacer) @@ -332,6 +340,8 @@ func (b *bbrSender) OnPacketSent( } b.sampler.OnPacketSent(sentTime, packetNumber, bytes, bytesInFlight, isRetransmittable) + + b.maybeAppLimited(bytesInFlight) } // CanSend implements the SendAlgorithm interface. @@ -411,8 +421,6 @@ func (b *bbrSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, even // packet in lost_packets. var lastPacketSendState sendTimeState - b.maybeApplimited(priorInFlight) - // Update bytesInFlight b.bytesInFlight = priorInFlight for _, p := range ackedPackets { @@ -539,7 +547,7 @@ func (b *bbrSender) setDrainGain(drainGain float64) { b.drainGain = drainGain } -// What's the current estimated bandwidth in bytes per second. +// Get the current bandwidth estimate. Note that Bandwidth is in bits per second. func (b *bbrSender) bandwidthEstimate() Bandwidth { return b.maxBandwidth.GetBest() } @@ -607,6 +615,10 @@ func (b *bbrSender) enterStartupMode(now time.Time) { // b.maybeTraceStateChange(logging.CongestionStateStartup) b.pacingGain = b.highGain b.congestionWindowGain = b.highCwndGain + + if b.debug { + b.debugPrint("Phase: STARTUP") + } } // Enters the PROBE_BW mode. @@ -625,6 +637,10 @@ func (b *bbrSender) enterProbeBandwidthMode(now time.Time) { b.lastCycleStart = now b.pacingGain = pacingGain[b.cycleCurrentOffset] + + if b.debug { + b.debugPrint("Phase: PROBE_BW") + } } // Updates the round-trip counter if a round-trip has passed. Returns true if @@ -698,15 +714,17 @@ func (b *bbrSender) checkIfFullBandwidthReached(lastPacketSendState *sendTimeSta } } -func (b *bbrSender) maybeApplimited(bytesInFlight congestion.ByteCount) { +func (b *bbrSender) maybeAppLimited(bytesInFlight congestion.ByteCount) { congestionWindow := b.GetCongestionWindow() if bytesInFlight >= congestionWindow { return } availableBytes := congestionWindow - bytesInFlight - drainLimited := b.mode == bbrModeDrain && bytesInFlight > congestionWindow/2 - if !drainLimited || availableBytes > maxBbrBurstPackets*b.maxDatagramSize { + if availableBytes > maxBbrBurstPackets*b.maxDatagramSize { b.sampler.OnAppLimited() + if b.debug { + b.debugPrint("AppLimited, AvailableBytes: %d", availableBytes) + } } } @@ -718,6 +736,10 @@ func (b *bbrSender) maybeExitStartupOrDrain(now time.Time) { // b.maybeTraceStateChange(logging.CongestionStateDrain) b.pacingGain = b.drainGain b.congestionWindowGain = b.highCwndGain + + if b.debug { + b.debugPrint("Phase: DRAIN") + } } if b.mode == bbrModeDrain && b.bytesInFlight <= b.getTargetCongestionWindow(1) { b.enterProbeBandwidthMode(now) @@ -733,6 +755,12 @@ func (b *bbrSender) maybeEnterOrExitProbeRtt(now time.Time, isRoundStart, minRtt // Do not decide on the time to exit PROBE_RTT until the |bytes_in_flight| // is at the target small value. b.exitProbeRttAt = time.Time{} + + if b.debug { + b.debugPrint("BandwidthEstimate: %s, CongestionWindowGain: %.2f, PacingGain: %.2f, PacingRate: %s", + formatSpeed(b.bandwidthEstimate()), b.congestionWindowGain, b.pacingGain, formatSpeed(b.PacingRate())) + b.debugPrint("Phase: PROBE_RTT") + } } if b.mode == bbrModeProbeRtt { @@ -925,6 +953,12 @@ func (b *bbrSender) shouldExitStartupDueToLoss(lastPacketSendState *sendTimeStat return false } +func (b *bbrSender) debugPrint(format string, a ...any) { + fmt.Printf("[BBRSender] [%s] %s\n", + time.Now().Format("15:04:05"), + fmt.Sprintf(format, a...)) +} + func bdpFromRttAndBandwidth(rtt time.Duration, bandwidth Bandwidth) congestion.ByteCount { return congestion.ByteCount(rtt) * congestion.ByteCount(bandwidth) / congestion.ByteCount(BytesPerSecond) / congestion.ByteCount(time.Second) } @@ -942,3 +976,14 @@ func GetInitialPacketSize(addr net.Addr) congestion.ByteCount { return congestion.MinInitialPacketSize } } + +func formatSpeed(bw Bandwidth) string { + bwf := float64(bw) + units := []string{"bps", "Kbps", "Mbps", "Gbps"} + unitIndex := 0 + for bwf > 1024 && unitIndex < len(units)-1 { + bwf /= 1024 + unitIndex++ + } + return fmt.Sprintf("%.2f %s", bwf, units[unitIndex]) +} From cd512ce1c61716e0456a74ec0c78c0a072388085 Mon Sep 17 00:00:00 2001 From: Toby Date: Sun, 19 May 2024 11:46:52 -0700 Subject: [PATCH 084/161] chore: various tweaks --- core/internal/congestion/bbr/bbr_sender.go | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/core/internal/congestion/bbr/bbr_sender.go b/core/internal/congestion/bbr/bbr_sender.go index c4b22ae..6828dd6 100644 --- a/core/internal/congestion/bbr/bbr_sender.go +++ b/core/internal/congestion/bbr/bbr_sender.go @@ -340,8 +340,6 @@ func (b *bbrSender) OnPacketSent( } b.sampler.OnPacketSent(sentTime, packetNumber, bytes, bytesInFlight, isRetransmittable) - - b.maybeAppLimited(bytesInFlight) } // CanSend implements the SendAlgorithm interface. @@ -421,6 +419,8 @@ func (b *bbrSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, even // packet in lost_packets. var lastPacketSendState sendTimeState + b.maybeAppLimited(priorInFlight) + // Update bytesInFlight b.bytesInFlight = priorInFlight for _, p := range ackedPackets { @@ -716,16 +716,12 @@ func (b *bbrSender) checkIfFullBandwidthReached(lastPacketSendState *sendTimeSta func (b *bbrSender) maybeAppLimited(bytesInFlight congestion.ByteCount) { congestionWindow := b.GetCongestionWindow() - if bytesInFlight >= congestionWindow { + // HACK: consider it app-limited if bytes in flight is less than 90% of the congestion window. + if bytesInFlight >= congestionWindow*9/10 || + (b.mode == bbrModeDrain && bytesInFlight > congestionWindow/2) { return } - availableBytes := congestionWindow - bytesInFlight - if availableBytes > maxBbrBurstPackets*b.maxDatagramSize { - b.sampler.OnAppLimited() - if b.debug { - b.debugPrint("AppLimited, AvailableBytes: %d", availableBytes) - } - } + b.sampler.OnAppLimited() } // Transitions from STARTUP to DRAIN and from DRAIN to PROBE_BW if @@ -782,6 +778,9 @@ func (b *bbrSender) maybeEnterOrExitProbeRtt(now time.Time, isRoundStart, minRtt } if now.Sub(b.exitProbeRttAt) >= 0 && b.probeRttRoundPassed { b.minRttTimestamp = now + if b.debug { + b.debugPrint("MinRTT: %s", b.getMinRtt()) + } if !b.isAtFullBandwidth { b.enterStartupMode(now) } else { From 09b08fa494cc75416da191f18b46cd72f05121cf Mon Sep 17 00:00:00 2001 From: Toby Date: Mon, 20 May 2024 14:19:04 -0700 Subject: [PATCH 085/161] fix: try to fix maybeAppLimited 2 --- core/internal/congestion/bbr/bbr_sender.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/core/internal/congestion/bbr/bbr_sender.go b/core/internal/congestion/bbr/bbr_sender.go index 6828dd6..62868ec 100644 --- a/core/internal/congestion/bbr/bbr_sender.go +++ b/core/internal/congestion/bbr/bbr_sender.go @@ -715,13 +715,9 @@ func (b *bbrSender) checkIfFullBandwidthReached(lastPacketSendState *sendTimeSta } func (b *bbrSender) maybeAppLimited(bytesInFlight congestion.ByteCount) { - congestionWindow := b.GetCongestionWindow() - // HACK: consider it app-limited if bytes in flight is less than 90% of the congestion window. - if bytesInFlight >= congestionWindow*9/10 || - (b.mode == bbrModeDrain && bytesInFlight > congestionWindow/2) { - return + if bytesInFlight < b.getTargetCongestionWindow(1) { + b.sampler.OnAppLimited() } - b.sampler.OnAppLimited() } // Transitions from STARTUP to DRAIN and from DRAIN to PROBE_BW if From adee547c215b91f704d6c0af5baee2da0e737c36 Mon Sep 17 00:00:00 2001 From: Toby Date: Mon, 20 May 2024 15:20:31 -0700 Subject: [PATCH 086/161] feat: quic-go v0.44.0 --- app/go.mod | 17 +++++++++-------- app/go.sum | 40 ++++++++++++++++++++-------------------- core/go.mod | 16 ++++++++-------- core/go.sum | 38 ++++++++++++++++++++------------------ extras/go.mod | 17 +++++++++-------- extras/go.sum | 40 ++++++++++++++++++++-------------------- go.work.sum | 5 +++++ 7 files changed, 91 insertions(+), 82 deletions(-) diff --git a/app/go.mod b/app/go.mod index dc12a22..692b066 100644 --- a/app/go.mod +++ b/app/go.mod @@ -16,12 +16,12 @@ require ( github.com/stretchr/testify v1.8.4 github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 go.uber.org/zap v1.24.0 - golang.org/x/exp v0.0.0-20221205204356-47842c84f3db - golang.org/x/sys v0.19.0 + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 + golang.org/x/sys v0.20.0 ) require ( - github.com/apernet/quic-go v0.43.1-0.20240515053213-5e9e635fd9f0 // indirect + github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023 // indirect github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -55,11 +55,12 @@ require ( go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect - golang.org/x/crypto v0.22.0 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.24.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.11.1 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/text v0.15.0 // indirect + golang.org/x/tools v0.21.0 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/app/go.sum b/app/go.sum index da0a071..5527a24 100644 --- a/app/go.sum +++ b/app/go.sum @@ -40,8 +40,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f h1:uVh0qpEslrWjgzx9vOcyCqsOY3c9kofDZ1n+qaw35ZY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f/go.mod h1:xkkq9D4ygcldQQhKS/w9CadiCKwCngU7K9E3DaKahpM= -github.com/apernet/quic-go v0.43.1-0.20240515053213-5e9e635fd9f0 h1:zilb2vx37DiBV5tfJRapxbXJqKavuCBKUl6kE4guShQ= -github.com/apernet/quic-go v0.43.1-0.20240515053213-5e9e635fd9f0/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= +github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023 h1:UTrvVPt+GfeOeli9/3gvpCDz2Jd5UEn3YotfP0u/pok= +github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023/go.mod h1:UkcG7+34BM+bbH2RFVKtHQp3mR7h8yJHx4z95lZ7sx4= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad h1:QzQ2sKpc9o42HNRR8ukM5uMC/RzR2HgZd/Nvaqol2C0= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= @@ -119,8 +119,8 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -277,8 +277,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -289,8 +289,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= -golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -317,8 +317,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -354,8 +354,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -378,8 +378,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -422,8 +422,8 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -435,8 +435,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -492,8 +492,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= -golang.org/x/tools v0.11.1 h1:ojD5zOW8+7dOGzdnNgersm8aPfcDjhMp12UfG93NIMc= -golang.org/x/tools v0.11.1/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= +golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/core/go.mod b/core/go.mod index 6c88c35..aacf34c 100644 --- a/core/go.mod +++ b/core/go.mod @@ -3,10 +3,10 @@ module github.com/apernet/hysteria/core/v2 go 1.21 require ( - github.com/apernet/quic-go v0.43.1-0.20240515053213-5e9e635fd9f0 + github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023 github.com/stretchr/testify v1.8.4 go.uber.org/goleak v1.2.1 - golang.org/x/exp v0.0.0-20221205204356-47842c84f3db + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 golang.org/x/time v0.5.0 ) @@ -21,12 +21,12 @@ require ( github.com/quic-go/qpack v0.4.0 // indirect github.com/stretchr/objx v0.5.0 // indirect go.uber.org/mock v0.4.0 // indirect - golang.org/x/crypto v0.22.0 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.24.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.11.1 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + golang.org/x/tools v0.21.0 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/core/go.sum b/core/go.sum index a4a87b6..d46bf43 100644 --- a/core/go.sum +++ b/core/go.sum @@ -1,5 +1,5 @@ -github.com/apernet/quic-go v0.43.1-0.20240515053213-5e9e635fd9f0 h1:zilb2vx37DiBV5tfJRapxbXJqKavuCBKUl6kE4guShQ= -github.com/apernet/quic-go v0.43.1-0.20240515053213-5e9e635fd9f0/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= +github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023 h1:UTrvVPt+GfeOeli9/3gvpCDz2Jd5UEn3YotfP0u/pok= +github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023/go.mod h1:UkcG7+34BM+bbH2RFVKtHQp3mR7h8yJHx4z95lZ7sx4= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -13,8 +13,8 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEe github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -45,23 +45,25 @@ go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= -golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.11.1 h1:ojD5zOW8+7dOGzdnNgersm8aPfcDjhMp12UfG93NIMc= -golang.org/x/tools v0.11.1/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= +golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/extras/go.mod b/extras/go.mod index 3048dc3..0cfa732 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -9,13 +9,13 @@ require ( github.com/miekg/dns v1.1.55 github.com/stretchr/testify v1.8.4 github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 - golang.org/x/crypto v0.22.0 - golang.org/x/net v0.24.0 + golang.org/x/crypto v0.23.0 + golang.org/x/net v0.25.0 google.golang.org/protobuf v1.33.0 ) require ( - github.com/apernet/quic-go v0.43.1-0.20240515053213-5e9e635fd9f0 // indirect + github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect @@ -26,11 +26,12 @@ require ( github.com/stretchr/objx v0.5.0 // indirect github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect go.uber.org/mock v0.4.0 // indirect - golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.11.1 // indirect + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + golang.org/x/tools v0.21.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/extras/go.sum b/extras/go.sum index a12d66d..898bff3 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -1,5 +1,5 @@ -github.com/apernet/quic-go v0.43.1-0.20240515053213-5e9e635fd9f0 h1:zilb2vx37DiBV5tfJRapxbXJqKavuCBKUl6kE4guShQ= -github.com/apernet/quic-go v0.43.1-0.20240515053213-5e9e635fd9f0/go.mod h1:j3QaAM7sVJqptDQyIQRWA6mASCfuxoHJszn67JQh1GE= +github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023 h1:UTrvVPt+GfeOeli9/3gvpCDz2Jd5UEn3YotfP0u/pok= +github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023/go.mod h1:UkcG7+34BM+bbH2RFVKtHQp3mR7h8yJHx4z95lZ7sx4= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -14,8 +14,8 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEe github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4= @@ -58,25 +58,25 @@ go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= -golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -84,8 +84,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -93,16 +93,16 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= -golang.org/x/tools v0.11.1 h1:ojD5zOW8+7dOGzdnNgersm8aPfcDjhMp12UfG93NIMc= -golang.org/x/tools v0.11.1/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= +golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= diff --git a/go.work.sum b/go.work.sum index 31d9ab2..eb36f69 100644 --- a/go.work.sum +++ b/go.work.sum @@ -278,6 +278,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2 h1:IRJeR9r1pYWsHKTRe/IInb7lYvbBVIqOgsX/u0mbOWY= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= @@ -291,6 +293,8 @@ golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= @@ -300,6 +304,7 @@ golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= From 788d04cfdd049d0cf7481c585ef1120eaa932be7 Mon Sep 17 00:00:00 2001 From: Haruue Date: Sat, 25 May 2024 14:23:28 +0800 Subject: [PATCH 087/161] fix(scripts): WorkingDirectory on CentOS 7 WorkingDirectory=~ requires systemd v227 or later, which is released on Oct 2015, only CentOS 7 use an earlier version actually. ref: systemd/systemd@5f5d8eab1f2f5f5e088bc301533b3e4636de96c7 --- scripts/install_server.sh | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/scripts/install_server.sh b/scripts/install_server.sh index da6626a..3166e80 100644 --- a/scripts/install_server.sh +++ b/scripts/install_server.sh @@ -187,6 +187,28 @@ chcon() { command chcon "$@" } +get_systemd_version() { + if ! has_command systemctl; then + return + fi + + command systemctl --version | head -1 | cut -d ' ' -f 2 +} + +systemd_unit_working_directory() { + local _systemd_version="$(get_systemd_version || true)" + + # WorkingDirectory=~ requires systemd v227 or later. + # (released on Oct 2015, only CentOS 7 use an earlier version) + # ref: systemd/systemd@5f5d8eab1f2f5f5e088bc301533b3e4636de96c7 + if [[ -n "$_systemd_version" && "$_systemd_version" -lt "227" ]]; then + echo "$HYSTERIA_HOME_DIR" + return + fi + + echo "~" +} + get_selinux_context() { local _file="$1" @@ -738,7 +760,7 @@ After=network.target [Service] Type=simple ExecStart=$EXECUTABLE_INSTALL_PATH server --config ${CONFIG_DIR}/${_config_name}.yaml -WorkingDirectory=~ +WorkingDirectory=$(systemd_unit_working_directory) User=$HYSTERIA_USER Group=$HYSTERIA_USER Environment=HYSTERIA_LOG_LEVEL=info From 9e9b4dbc7d4b36c205763dc8b096e3646def5ea4 Mon Sep 17 00:00:00 2001 From: Haruue Date: Sat, 25 May 2024 17:05:20 +0800 Subject: [PATCH 088/161] feat(scripts): change HYSTERIA_USER w/o --force --- scripts/install_server.sh | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/scripts/install_server.sh b/scripts/install_server.sh index 3166e80..d222f33 100644 --- a/scripts/install_server.sh +++ b/scripts/install_server.sh @@ -1045,22 +1045,26 @@ perform_install() { _is_update_required=1 fi - if [[ -z "$_is_update_required" ]]; then - echo "$(tgreen)Installed version is up-to-date, there is nothing to do.$(treset)" - return - fi - if is_hysteria1_version "$VERSION"; then error "This script can only install Hysteria 2." exit 95 fi - perform_install_hysteria_binary + if [[ -n "$_is_update_required" ]]; then + perform_install_hysteria_binary + fi + + # Always install additional files, regardless of $_is_update_required. + # This allows changes to be made with environment variables (e.g. change HYSTERIA_USER without --force). perform_install_hysteria_example_config perform_install_hysteria_home_legacy perform_install_hysteria_systemd - if [[ -n "$_is_frash_install" ]]; then + if [[ -z "$_is_update_required" ]]; then + echo + echo "$(tgreen)Installed version is up-to-date, there is nothing to do.$(treset)" + echo + elif [[ -n "$_is_frash_install" ]]; then echo echo -e "$(tbold)Congratulation! Hysteria 2 has been successfully installed on your server.$(treset)" echo From 3024fc079c88b1086dab9840540eb7b1ddafee23 Mon Sep 17 00:00:00 2001 From: kovacs Date: Mon, 27 May 2024 11:43:31 +0800 Subject: [PATCH 089/161] feat(acme): add dns provider add dns provider Signed-off-by: kovacs --- app/cmd/server.go | 75 ++++++++++++++++++++++++++++++++++++++++------ app/go.mod | 26 ++++++++++++---- app/go.sum | 76 +++++++++++++++++++++++++++++++++++------------ go.work.sum | 27 +++++++++++++---- 4 files changed, 166 insertions(+), 38 deletions(-) diff --git a/app/cmd/server.go b/app/cmd/server.go index 2bf4efe..f86e97b 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -6,6 +6,12 @@ import ( "encoding/json" "errors" "fmt" + "github.com/libdns/cloudflare" + "github.com/libdns/duckdns" + "github.com/libdns/gandi" + "github.com/libdns/godaddy" + "github.com/libdns/namedotcom" + "github.com/libdns/vultr" "net" "net/http" "net/http/httputil" @@ -79,15 +85,21 @@ type serverConfigTLS struct { } type serverConfigACME struct { - Domains []string `mapstructure:"domains"` - Email string `mapstructure:"email"` - CA string `mapstructure:"ca"` - DisableHTTP bool `mapstructure:"disableHTTP"` - DisableTLSALPN bool `mapstructure:"disableTLSALPN"` - ListenHost string `mapstructure:"listenHost"` - AltHTTPPort int `mapstructure:"altHTTPPort"` - AltTLSALPNPort int `mapstructure:"altTLSALPNPort"` - Dir string `mapstructure:"dir"` + Domains []string `mapstructure:"domains"` + Email string `mapstructure:"email"` + CA string `mapstructure:"ca"` + DisableHTTP bool `mapstructure:"disableHTTP"` + DisableTLSALPN bool `mapstructure:"disableTLSALPN"` + ListenHost string `mapstructure:"listenHost"` + AltHTTPPort int `mapstructure:"altHTTPPort"` + AltTLSALPNPort int `mapstructure:"altTLSALPNPort"` + DNSProvider serverConfigACMEDNSProvider `mapstructure:"dnsProvider"` + Dir string `mapstructure:"dir"` +} + +type serverConfigACMEDNSProvider struct { + Name string `mapstructure:"provider"` + Config map[string]string `mapstructure:"config"` } type serverConfigQUIC struct { @@ -315,6 +327,51 @@ func (c *serverConfig) fillTLSConfig(hyConfig *server.Config) error { default: return configError{Field: "acme.ca", Err: errors.New("unknown CA")} } + + if c.ACME.DNSProvider.Name != "" && c.ACME.DNSProvider.Config != nil { + switch strings.ToLower(c.ACME.DNSProvider.Name) { + case "cloudflare": + cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ + DNSProvider: &cloudflare.Provider{ + APIToken: c.ACME.DNSProvider.Config["CLOUDFLARE_API_TOKEN"], + }, + } + case "duckdns": + cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ + DNSProvider: &duckdns.Provider{ + APIToken: c.ACME.DNSProvider.Config["DUCKDNS_API_TOKEN"], + OverrideDomain: c.ACME.DNSProvider.Config["DUCKDNS_OVERRIDE_DOMAIN"], + }, + } + case "gandi": + cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ + DNSProvider: &gandi.Provider{ + BearerToken: c.ACME.DNSProvider.Config["GANDI_API_TOKEN"], + }, + } + case "godaddy": + cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ + DNSProvider: &godaddy.Provider{ + APIToken: c.ACME.DNSProvider.Config["GODADDY_API_TOKEN"], + }, + } + case "namedotcom": + cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ + DNSProvider: &namedotcom.Provider{ + Token: c.ACME.DNSProvider.Config["NAMEDOTCOM_TOKEN"], + User: c.ACME.DNSProvider.Config["NAMEDOTCOM_USER"], + Server: c.ACME.DNSProvider.Config["NAMEDOTCOM_SERVER"], + }, + } + case "vultr": + cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ + DNSProvider: &vultr.Provider{ + APIToken: c.ACME.DNSProvider.Config["VULTR_API_TOKEN"], + }, + } + } + } + cmCfg.Issuers = []certmagic.Issuer{cmIssuer} cmCache := certmagic.NewCache(certmagic.CacheOptions{ GetConfigForCert: func(cert certmagic.Certificate) (*certmagic.Config, error) { diff --git a/app/go.mod b/app/go.mod index 692b066..46cf92c 100644 --- a/app/go.mod +++ b/app/go.mod @@ -8,12 +8,18 @@ require ( github.com/apernet/hysteria/extras/v2 v2.0.0-00010101000000-000000000000 github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad github.com/caddyserver/certmagic v0.17.2 + github.com/libdns/cloudflare v0.1.1 + github.com/libdns/duckdns v0.2.0 + github.com/libdns/gandi v1.0.3 + github.com/libdns/godaddy v1.0.3 + github.com/libdns/namedotcom v0.3.3 + github.com/libdns/vultr v1.0.0 github.com/mdp/qrterminal/v3 v3.1.1 github.com/mholt/acmez v1.0.4 github.com/sagernet/sing v0.3.2 github.com/spf13/cobra v1.7.0 github.com/spf13/viper v1.15.0 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 go.uber.org/zap v1.24.0 golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 @@ -27,30 +33,38 @@ require ( github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/go-querystring v1.1.0 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-retryablehttp v0.7.6 // indirect github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/cpuid/v2 v2.1.1 // indirect - github.com/libdns/libdns v0.2.1 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/libdns/libdns v0.2.2 // indirect github.com/magiconair/properties v1.8.7 // indirect - github.com/miekg/dns v1.1.55 // indirect + github.com/miekg/dns v1.1.59 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect github.com/spf13/afero v1.9.3 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/objx v0.5.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect + github.com/vultr/govultr/v3 v3.6.4 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect @@ -58,10 +72,12 @@ require ( golang.org/x/crypto v0.23.0 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.25.0 // indirect + golang.org/x/oauth2 v0.20.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/text v0.15.0 // indirect golang.org/x/tools v0.21.0 // indirect - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect rsc.io/qr v0.2.0 // indirect diff --git a/app/go.sum b/app/go.sum index 5527a24..8f07498 100644 --- a/app/go.sum +++ b/app/go.sum @@ -59,6 +59,7 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -68,6 +69,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -106,8 +109,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -121,6 +124,8 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -141,6 +146,12 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-retryablehttp v0.7.6 h1:TwRYfx2z2C4cLbXmT8I5PgP/xmuqASDyiVuGYfs9GZM= +github.com/hashicorp/go-retryablehttp v0.7.6/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4= @@ -158,27 +169,44 @@ github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdy github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis= -github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= +github.com/libdns/cloudflare v0.1.1 h1:FVPfWwP8zZCqj268LZjmkDleXlHPlFU9KC4OJ3yn054= +github.com/libdns/cloudflare v0.1.1/go.mod h1:9VK91idpOjg6v7/WbjkEW49bSCxj00ALesIFDhJ8PBU= +github.com/libdns/duckdns v0.2.0 h1:vd3pE09G2qTx1Zh1o3LmrivWSByD3Z5FbL7csX5vDgE= +github.com/libdns/duckdns v0.2.0/go.mod h1:jCQ/7+qvhLK39+28qXvKEYGBBvmHBCmIwNqdJTCUmVs= +github.com/libdns/gandi v1.0.3 h1:FIvipWOg/O4zi75fPRmtcolRKqI6MgrbpFy2p5KYdUk= +github.com/libdns/gandi v1.0.3/go.mod h1:G6dw58Xnji2xX+lb+uZxGbtmfxKllm1CGHE2bOPG3WA= +github.com/libdns/godaddy v1.0.3 h1:PX1FOYDQ1HGQzz8mVOmtwm3aa6Sv5MwCkNzivUUTA44= +github.com/libdns/godaddy v1.0.3/go.mod h1:vuKWUXnvblDvcaiRwutOoLl7DuB21x8tI06owsF/JTM= +github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= +github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s= +github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= +github.com/libdns/namedotcom v0.3.3 h1:R10C7+IqQGVeC4opHHMiFNBxdNBg1bi65ZwqLESl+jE= +github.com/libdns/namedotcom v0.3.3/go.mod h1:GbYzsAF2yRUpI0WgIK5fs5UX+kDVUPaYCFLpTnKQm0s= +github.com/libdns/vultr v1.0.0 h1:W8B4+k2bm9ro3bZLSZV9hMOQI+uO6Svu+GmD+Olz7ZI= +github.com/libdns/vultr v1.0.0/go.mod h1:8K1HJExcbeHS4YPkFHRZpqpXZzZ+DZAA0m0VikJgEqk= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mdp/qrterminal/v3 v3.1.1 h1:cIPwg3QU0OIm9+ce/lRfWXhPwEjOSKwk3HBwL3HBTyc= github.com/mdp/qrterminal/v3 v3.1.1/go.mod h1:5lJlXe7Jdr8wlPDdcsJttv1/knsRgzXASyr4dcGZqNU= github.com/mholt/acmez v1.0.4 h1:N3cE4Pek+dSolbsofIkAYz6H1d3pE+2G0os7QHslf80= github.com/mholt/acmez v1.0.4/go.mod h1:qFGLZ4u+ehWINeJZjzPlsnjJBCPAADWTcIqE/7DAYQY= +github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.51/go.mod h1:2Z9d3CP1LQWihRZUf29mQ19yDThaI4DAYzte2CaQW5c= -github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= -github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= +github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= @@ -187,6 +215,7 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -197,8 +226,9 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= @@ -220,8 +250,9 @@ github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -231,8 +262,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf h1:7PflaKRtU4np/epFxRXlFhlzLXZzKFrH5/I4so5Ove0= @@ -241,6 +272,8 @@ github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 h1:d/Wr/Vl/wiJHc github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301/go.mod h1:ntmMHL/xPq1WLeKiw8p/eRATaae6PiVRNipHFJxI8PM= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/vultr/govultr/v3 v3.6.4 h1:unvY9eXlBw667ECQZDbBDOIaWB8wkk6Bx+yB0IMKXJ4= +github.com/vultr/govultr/v3 v3.6.4/go.mod h1:rt9v2x114jZmmLAE/h5N5jnxTmsK9ewwS2oQZ0UBQzM= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -331,6 +364,7 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/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-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -365,6 +399,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= +golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -389,6 +425,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -462,6 +499,7 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -586,12 +624,12 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/go.work.sum b/go.work.sum index eb36f69..4370b0e 100644 --- a/go.work.sum +++ b/go.work.sum @@ -7,6 +7,8 @@ cloud.google.com/go/compute v1.14.0 h1:hfm2+FfxVmnRlh6LpB7cg1ZNU+5edAHmW679JePzt cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ= cloud.google.com/go/firestore v1.9.0 h1:IBlRyxgGySXu5VuW0RgGFlTtLukSnNkpDiEOMkQkmpA= cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= @@ -53,6 +55,8 @@ github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzA github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= +github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415 h1:q1oJaUPdmpDm/VyXosjgPgr6wS7c5iV2p0PwJD73bUI= @@ -83,7 +87,9 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4er github.com/golang/lint v0.0.0-20180702182130-06c8688daad7 h1:2hRPrmiwPrp3fQX967rNJIhQPtiGXdlQWAxKbKw3VHA= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= @@ -108,8 +114,6 @@ github.com/grpc-ecosystem/grpc-gateway v1.5.0 h1:WcmKMm43DR7RdtlkEXQJyo5ws8iTp98 github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/hashicorp/consul/api v1.18.0 h1:R7PPNzTCeN6VuQNDwwhZWJvzCtGSrNpJqfb22h3yH9g= github.com/hashicorp/consul/api v1.18.0/go.mod h1:owRRGJ9M5xReDC5nfT8FTJrNAPbT4NM6p/k+d03q2v4= -github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= @@ -123,6 +127,8 @@ github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfE github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1 h1:ujPKutqRlJtcfWk6toYVYagwra7HQHbXOaS171b4Tg8= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= +github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= @@ -136,8 +142,6 @@ github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+L github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe h1:W/GaMY0y69G4cFlmsC6B9sbuo2fP8OFP1ABjt4kPz+w= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= @@ -151,12 +155,17 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5 h1:8Q0qkMVC/MmWkpIdlvZgcv2o2jrlF6zqVOh7W5YHdMA= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= +github.com/montanaflynn/stats v0.7.0 h1:r3y12KyNxj/Sb/iOE46ws+3mS1+MZca1wlHQFPsY/JU= +github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86 h1:D6paGObi5Wud7xg83MaEFyjxQB1W5bz5d0IFppr+ymk= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab h1:eFXv9Nu1lGbrNbj619aWwZfVF5HBrm9Plte8aNptuTI= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/openzipkin/zipkin-go v0.1.1 h1:A/ADD6HaPnAKj3yS7HjGHRK77qi41Hi0DirOOIQAeIw= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= github.com/pkg/sftp v1.13.1 h1:I2qBYMChEhIjOgazfJmV3/mZM256btk6wkCDRmW7JYs= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -252,8 +261,10 @@ golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -263,6 +274,7 @@ golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk= @@ -270,6 +282,7 @@ golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852 h1:xYq6+9AtI+xP3M4r0N1hCkHrInHDBohhquRgx9Kk6gI= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -278,6 +291,7 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2 h1:IRJeR9r1pYWsHKTRe/IInb7lYvbBVIqOgsX/u0mbOWY= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= @@ -305,6 +319,7 @@ golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= @@ -326,13 +341,15 @@ google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9M google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.52.0 h1:kd48UiU7EHsV4rnLyOJRuP/Il/UHE7gdDAQ+SZI7nZk= google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919 h1:tmXTu+dfa+d9Evp8NpJdgOy6+rt8/x4yG7qPBrtNfLY= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From 3c22e5967fb5794ef36fc29866d303e742cf8d62 Mon Sep 17 00:00:00 2001 From: kovacs Date: Mon, 27 May 2024 12:45:50 +0800 Subject: [PATCH 090/161] fix(acme): fix config name fix config name Signed-off-by: kovacs --- app/cmd/server.go | 20 ++++++++++---------- app/cmd/server_test.go | 6 ++++++ app/cmd/server_test.yaml | 4 ++++ 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/app/cmd/server.go b/app/cmd/server.go index f86e97b..840613b 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -98,7 +98,7 @@ type serverConfigACME struct { } type serverConfigACMEDNSProvider struct { - Name string `mapstructure:"provider"` + Name string `mapstructure:"name"` Config map[string]string `mapstructure:"config"` } @@ -333,40 +333,40 @@ func (c *serverConfig) fillTLSConfig(hyConfig *server.Config) error { case "cloudflare": cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ DNSProvider: &cloudflare.Provider{ - APIToken: c.ACME.DNSProvider.Config["CLOUDFLARE_API_TOKEN"], + APIToken: c.ACME.DNSProvider.Config["cloudflare_api_token"], }, } case "duckdns": cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ DNSProvider: &duckdns.Provider{ - APIToken: c.ACME.DNSProvider.Config["DUCKDNS_API_TOKEN"], - OverrideDomain: c.ACME.DNSProvider.Config["DUCKDNS_OVERRIDE_DOMAIN"], + APIToken: c.ACME.DNSProvider.Config["duckdns_api_token"], + OverrideDomain: c.ACME.DNSProvider.Config["duckdns_override_domain"], }, } case "gandi": cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ DNSProvider: &gandi.Provider{ - BearerToken: c.ACME.DNSProvider.Config["GANDI_API_TOKEN"], + BearerToken: c.ACME.DNSProvider.Config["gandi_api_token"], }, } case "godaddy": cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ DNSProvider: &godaddy.Provider{ - APIToken: c.ACME.DNSProvider.Config["GODADDY_API_TOKEN"], + APIToken: c.ACME.DNSProvider.Config["godaddy_api_token"], }, } case "namedotcom": cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ DNSProvider: &namedotcom.Provider{ - Token: c.ACME.DNSProvider.Config["NAMEDOTCOM_TOKEN"], - User: c.ACME.DNSProvider.Config["NAMEDOTCOM_USER"], - Server: c.ACME.DNSProvider.Config["NAMEDOTCOM_SERVER"], + Token: c.ACME.DNSProvider.Config["namedotcom_token"], + User: c.ACME.DNSProvider.Config["namedotcom_user"], + Server: c.ACME.DNSProvider.Config["namedotcom_server"], }, } case "vultr": cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ DNSProvider: &vultr.Provider{ - APIToken: c.ACME.DNSProvider.Config["VULTR_API_TOKEN"], + APIToken: c.ACME.DNSProvider.Config["vultr_api_token"], }, } } diff --git a/app/cmd/server_test.go b/app/cmd/server_test.go index 935a998..17a563d 100644 --- a/app/cmd/server_test.go +++ b/app/cmd/server_test.go @@ -41,6 +41,12 @@ func TestServerConfig(t *testing.T) { AltHTTPPort: 9980, AltTLSALPNPort: 9443, Dir: "random_dir", + DNSProvider: serverConfigACMEDNSProvider{ + Name: "cloudflare", + Config: map[string]string{ + "cloudflare_api_token": "xxxxxxxxxx", + }, + }, }, QUIC: serverConfigQUIC{ InitStreamReceiveWindow: 77881, diff --git a/app/cmd/server_test.yaml b/app/cmd/server_test.yaml index 1ab5d5f..1e04e80 100644 --- a/app/cmd/server_test.yaml +++ b/app/cmd/server_test.yaml @@ -20,6 +20,10 @@ acme: altHTTPPort: 9980 altTLSALPNPort: 9443 dir: random_dir + dnsProvider: + name: "cloudflare" + config: + cloudflare_api_token: "xxxxxxxxxx" quic: initStreamReceiveWindow: 77881 From 10234e5daf82802a32753e4ec9afda69de44ecc1 Mon Sep 17 00:00:00 2001 From: Haruue Date: Wed, 29 May 2024 11:28:33 +0800 Subject: [PATCH 091/161] fix(hyperbole): missing v2 in cmdpkg --- hyperbole.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hyperbole.py b/hyperbole.py index 978ef8c..fc38eba 100755 --- a/hyperbole.py +++ b/hyperbole.py @@ -29,7 +29,7 @@ BUILD_DIR = "build" CORE_SRC_DIR = "./core" EXTRAS_SRC_DIR = "./extras" APP_SRC_DIR = "./app" -APP_SRC_CMD_PKG = "github.com/apernet/hysteria/app/cmd" +APP_SRC_CMD_PKG = "github.com/apernet/hysteria/app/v2/cmd" MODULE_SRC_DIRS = [CORE_SRC_DIR, EXTRAS_SRC_DIR, APP_SRC_DIR] From e1ac7c88ab3b9c60bfb3c3ee244133a347e5f50f Mon Sep 17 00:00:00 2001 From: Haruue Date: Thu, 30 May 2024 22:55:39 +0800 Subject: [PATCH 092/161] fix(client/http): ffmpeg not works with proxy Go's resp.Write() adds a "Content-Length: 0" header and it seems that ffmpeg doesn't like this and immediately closes the proxy connection. close: #1109 --- app/internal/http/server.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/internal/http/server.go b/app/internal/http/server.go index e5761ce..760e9e1 100644 --- a/app/internal/http/server.go +++ b/app/internal/http/server.go @@ -278,6 +278,9 @@ func sendSimpleResponse(conn net.Conn, req *http.Request, statusCode int) error ProtoMinor: req.ProtoMinor, Header: http.Header{}, } + // Remove the "Content-Length: 0" header, some clients (e.g. ffmpeg) may not like it. + // NOTE: This will also cause go/http to add a "Connection: close" header, but seems to be fine. + resp.ContentLength = -1 return resp.Write(conn) } From 23b79688fbd5a777c9ee7a38d5084263d2f3d859 Mon Sep 17 00:00:00 2001 From: Haruue Date: Thu, 30 May 2024 23:12:27 +0800 Subject: [PATCH 093/161] chore(client/http): rm "Connection: close" header Magic of undocumented features. --- app/internal/http/server.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/internal/http/server.go b/app/internal/http/server.go index 760e9e1..0b5e411 100644 --- a/app/internal/http/server.go +++ b/app/internal/http/server.go @@ -279,8 +279,10 @@ func sendSimpleResponse(conn net.Conn, req *http.Request, statusCode int) error Header: http.Header{}, } // Remove the "Content-Length: 0" header, some clients (e.g. ffmpeg) may not like it. - // NOTE: This will also cause go/http to add a "Connection: close" header, but seems to be fine. resp.ContentLength = -1 + // Also, prevent the "Connection: close" header. + resp.Close = false + resp.Uncompressed = true return resp.Write(conn) } From 18d075cc079d468b74bc5f160c733b694ce9c6a3 Mon Sep 17 00:00:00 2001 From: Toby Date: Mon, 10 Jun 2024 16:28:21 -0700 Subject: [PATCH 094/161] feat: rework acme config format --- app/cmd/server.go | 116 ++++++++++++++++++++++++++------------- app/cmd/server_test.go | 29 ++++++---- app/cmd/server_test.yaml | 21 ++++--- 3 files changed, 112 insertions(+), 54 deletions(-) diff --git a/app/cmd/server.go b/app/cmd/server.go index 840613b..d2c9f4c 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -6,12 +6,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/libdns/cloudflare" - "github.com/libdns/duckdns" - "github.com/libdns/gandi" - "github.com/libdns/godaddy" - "github.com/libdns/namedotcom" - "github.com/libdns/vultr" "net" "net/http" "net/http/httputil" @@ -22,6 +16,12 @@ import ( "time" "github.com/caddyserver/certmagic" + "github.com/libdns/cloudflare" + "github.com/libdns/duckdns" + "github.com/libdns/gandi" + "github.com/libdns/godaddy" + "github.com/libdns/namedotcom" + "github.com/libdns/vultr" "github.com/mholt/acmez/acme" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -85,19 +85,36 @@ type serverConfigTLS struct { } type serverConfigACME struct { - Domains []string `mapstructure:"domains"` - Email string `mapstructure:"email"` - CA string `mapstructure:"ca"` - DisableHTTP bool `mapstructure:"disableHTTP"` - DisableTLSALPN bool `mapstructure:"disableTLSALPN"` - ListenHost string `mapstructure:"listenHost"` - AltHTTPPort int `mapstructure:"altHTTPPort"` - AltTLSALPNPort int `mapstructure:"altTLSALPNPort"` - DNSProvider serverConfigACMEDNSProvider `mapstructure:"dnsProvider"` - Dir string `mapstructure:"dir"` + // Common fields + Domains []string `mapstructure:"domains"` + Email string `mapstructure:"email"` + CA string `mapstructure:"ca"` + ListenHost string `mapstructure:"listenHost"` + Dir string `mapstructure:"dir"` + + // Type selection + Type string `mapstructure:"type"` + HTTP serverConfigACMEHTTP `mapstructure:"http"` + TLS serverConfigACMETLS `mapstructure:"tls"` + DNS serverConfigACMEDNS `mapstructure:"dns"` + + // Legacy fields for backwards compatibility + // Only applicable when Type is empty + DisableHTTP bool `mapstructure:"disableHTTP"` + DisableTLSALPN bool `mapstructure:"disableTLSALPN"` + AltHTTPPort int `mapstructure:"altHTTPPort"` + AltTLSALPNPort int `mapstructure:"altTLSALPNPort"` } -type serverConfigACMEDNSProvider struct { +type serverConfigACMEHTTP struct { + AltPort int `mapstructure:"altPort"` +} + +type serverConfigACMETLS struct { + AltPort int `mapstructure:"altPort"` +} + +type serverConfigACMEDNS struct { Name string `mapstructure:"name"` Config map[string]string `mapstructure:"config"` } @@ -304,14 +321,10 @@ func (c *serverConfig) fillTLSConfig(hyConfig *server.Config) error { Logger: logger, } cmIssuer := certmagic.NewACMEIssuer(cmCfg, certmagic.ACMEIssuer{ - Email: c.ACME.Email, - Agreed: true, - DisableHTTPChallenge: c.ACME.DisableHTTP, - DisableTLSALPNChallenge: c.ACME.DisableTLSALPN, - ListenHost: c.ACME.ListenHost, - AltHTTPPort: c.ACME.AltHTTPPort, - AltTLSALPNPort: c.ACME.AltTLSALPNPort, - Logger: logger, + Email: c.ACME.Email, + Agreed: true, + ListenHost: c.ACME.ListenHost, + Logger: logger, }) switch strings.ToLower(c.ACME.CA) { case "letsencrypt", "le", "": @@ -325,51 +338,80 @@ func (c *serverConfig) fillTLSConfig(hyConfig *server.Config) error { } cmIssuer.ExternalAccount = eab default: - return configError{Field: "acme.ca", Err: errors.New("unknown CA")} + return configError{Field: "acme.ca", Err: errors.New("unsupported CA")} } - if c.ACME.DNSProvider.Name != "" && c.ACME.DNSProvider.Config != nil { - switch strings.ToLower(c.ACME.DNSProvider.Name) { + switch strings.ToLower(c.ACME.Type) { + case "http": + cmIssuer.DisableHTTPChallenge = false + cmIssuer.DisableTLSALPNChallenge = true + cmIssuer.DNS01Solver = nil + cmIssuer.AltHTTPPort = c.ACME.HTTP.AltPort + case "tls": + cmIssuer.DisableHTTPChallenge = true + cmIssuer.DisableTLSALPNChallenge = false + cmIssuer.DNS01Solver = nil + cmIssuer.AltTLSALPNPort = c.ACME.TLS.AltPort + case "dns": + cmIssuer.DisableHTTPChallenge = true + cmIssuer.DisableTLSALPNChallenge = true + if c.ACME.DNS.Name == "" { + return configError{Field: "acme.dns.name", Err: errors.New("empty DNS provider name")} + } + if c.ACME.DNS.Config == nil { + return configError{Field: "acme.dns.config", Err: errors.New("empty DNS provider config")} + } + switch strings.ToLower(c.ACME.DNS.Name) { case "cloudflare": cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ DNSProvider: &cloudflare.Provider{ - APIToken: c.ACME.DNSProvider.Config["cloudflare_api_token"], + APIToken: c.ACME.DNS.Config["cloudflare_api_token"], }, } case "duckdns": cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ DNSProvider: &duckdns.Provider{ - APIToken: c.ACME.DNSProvider.Config["duckdns_api_token"], - OverrideDomain: c.ACME.DNSProvider.Config["duckdns_override_domain"], + APIToken: c.ACME.DNS.Config["duckdns_api_token"], + OverrideDomain: c.ACME.DNS.Config["duckdns_override_domain"], }, } case "gandi": cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ DNSProvider: &gandi.Provider{ - BearerToken: c.ACME.DNSProvider.Config["gandi_api_token"], + BearerToken: c.ACME.DNS.Config["gandi_api_token"], }, } case "godaddy": cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ DNSProvider: &godaddy.Provider{ - APIToken: c.ACME.DNSProvider.Config["godaddy_api_token"], + APIToken: c.ACME.DNS.Config["godaddy_api_token"], }, } case "namedotcom": cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ DNSProvider: &namedotcom.Provider{ - Token: c.ACME.DNSProvider.Config["namedotcom_token"], - User: c.ACME.DNSProvider.Config["namedotcom_user"], - Server: c.ACME.DNSProvider.Config["namedotcom_server"], + Token: c.ACME.DNS.Config["namedotcom_token"], + User: c.ACME.DNS.Config["namedotcom_user"], + Server: c.ACME.DNS.Config["namedotcom_server"], }, } case "vultr": cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ DNSProvider: &vultr.Provider{ - APIToken: c.ACME.DNSProvider.Config["vultr_api_token"], + APIToken: c.ACME.DNS.Config["vultr_api_token"], }, } + default: + return configError{Field: "acme.dns.name", Err: errors.New("unsupported DNS provider")} } + case "": + // Legacy compatibility mode + cmIssuer.DisableHTTPChallenge = c.ACME.DisableHTTP + cmIssuer.DisableTLSALPNChallenge = c.ACME.DisableTLSALPN + cmIssuer.AltHTTPPort = c.ACME.AltHTTPPort + cmIssuer.AltTLSALPNPort = c.ACME.AltTLSALPNPort + default: + return configError{Field: "acme.type", Err: errors.New("unsupported ACME type")} } cmCfg.Issuers = []certmagic.Issuer{cmIssuer} diff --git a/app/cmd/server_test.go b/app/cmd/server_test.go index 17a563d..d81a61a 100644 --- a/app/cmd/server_test.go +++ b/app/cmd/server_test.go @@ -34,19 +34,28 @@ func TestServerConfig(t *testing.T) { "sub1.example.com", "sub2.example.com", }, - Email: "haha@cringe.net", - CA: "zero", - DisableHTTP: true, - DisableTLSALPN: true, - AltHTTPPort: 9980, - AltTLSALPNPort: 9443, - Dir: "random_dir", - DNSProvider: serverConfigACMEDNSProvider{ - Name: "cloudflare", + Email: "haha@cringe.net", + CA: "zero", + ListenHost: "127.0.0.9", + Dir: "random_dir", + Type: "dns", + HTTP: serverConfigACMEHTTP{ + AltPort: 8888, + }, + TLS: serverConfigACMETLS{ + AltPort: 44333, + }, + DNS: serverConfigACMEDNS{ + Name: "gomommy", Config: map[string]string{ - "cloudflare_api_token": "xxxxxxxxxx", + "key1": "value1", + "key2": "value2", }, }, + DisableHTTP: true, + DisableTLSALPN: true, + AltHTTPPort: 8080, + AltTLSALPNPort: 4433, }, QUIC: serverConfigQUIC{ InitStreamReceiveWindow: 77881, diff --git a/app/cmd/server_test.yaml b/app/cmd/server_test.yaml index 1e04e80..86a2dcf 100644 --- a/app/cmd/server_test.yaml +++ b/app/cmd/server_test.yaml @@ -15,15 +15,22 @@ acme: - sub2.example.com email: haha@cringe.net ca: zero + listenHost: 127.0.0.9 + dir: random_dir + type: dns + http: + altPort: 8888 + tls: + altPort: 44333 + dns: + name: gomommy + config: + key1: value1 + key2: value2 disableHTTP: true disableTLSALPN: true - altHTTPPort: 9980 - altTLSALPNPort: 9443 - dir: random_dir - dnsProvider: - name: "cloudflare" - config: - cloudflare_api_token: "xxxxxxxxxx" + altHTTPPort: 8080 + altTLSALPNPort: 4433 quic: initStreamReceiveWindow: 77881 From d318903783c142b68af10d7831fdd409f4b67f3b Mon Sep 17 00:00:00 2001 From: Toby Date: Mon, 10 Jun 2024 16:30:22 -0700 Subject: [PATCH 095/161] chore: go mod tidy --- core/go.mod | 13 +++++++------ core/go.sum | 22 +++++++--------------- extras/go.mod | 12 ++++++++---- extras/go.sum | 25 ++++++++----------------- 4 files changed, 30 insertions(+), 42 deletions(-) diff --git a/core/go.mod b/core/go.mod index aacf34c..66a8938 100644 --- a/core/go.mod +++ b/core/go.mod @@ -4,7 +4,7 @@ go 1.21 require ( github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 go.uber.org/goleak v1.2.1 golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 golang.org/x/time v0.5.0 @@ -13,13 +13,14 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect - github.com/kr/text v0.2.0 // indirect - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/stretchr/objx v0.5.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect go.uber.org/mock v0.4.0 // indirect golang.org/x/crypto v0.23.0 // indirect golang.org/x/mod v0.17.0 // indirect @@ -27,7 +28,7 @@ require ( golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect golang.org/x/tools v0.21.0 // indirect - google.golang.org/protobuf v1.33.0 // indirect - gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect + google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/core/go.sum b/core/go.sum index d46bf43..96fd988 100644 --- a/core/go.sum +++ b/core/go.sum @@ -11,19 +11,17 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= @@ -32,15 +30,11 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= @@ -64,11 +58,9 @@ golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/extras/go.mod b/extras/go.mod index 0cfa732..efc1111 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -6,24 +6,27 @@ require ( github.com/apernet/hysteria/core/v2 v2.0.0-00010101000000-000000000000 github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 github.com/hashicorp/golang-lru/v2 v2.0.5 - github.com/miekg/dns v1.1.55 - github.com/stretchr/testify v1.8.4 + github.com/miekg/dns v1.1.59 + github.com/stretchr/testify v1.9.0 github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 golang.org/x/crypto v0.23.0 golang.org/x/net v0.25.0 - google.golang.org/protobuf v1.33.0 + google.golang.org/protobuf v1.34.1 ) require ( github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/stretchr/objx v0.5.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect go.uber.org/mock v0.4.0 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect @@ -32,6 +35,7 @@ require ( golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect golang.org/x/tools v0.21.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/extras/go.sum b/extras/go.sum index 898bff3..4ef8205 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -12,8 +12,7 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= @@ -21,13 +20,11 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4= github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/miekg/dns v1.1.51/go.mod h1:2Z9d3CP1LQWihRZUf29mQ19yDThaI4DAYzte2CaQW5c= -github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= -github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= @@ -38,15 +35,11 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf h1:7PflaKRtU4np/epFxRXlFhlzLXZzKFrH5/I4so5Ove0= github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf/go.mod h1:CLUSJbazqETbaR+i0YAhXBICV9TrKH93pziccMhmhpM= github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 h1:d/Wr/Vl/wiJHc3AHYbYs5I3PucJvRuw3SvbmlIRf+oM= @@ -104,11 +97,9 @@ golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From feacb1f85e29769c2e0ff18e3705ef2bfc55556c Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 15 Jun 2024 14:15:56 -0700 Subject: [PATCH 096/161] feat(core): server RequestHook support --- app/go.mod | 4 - app/go.sum | 4 - core/go.sum | 10 ++ core/internal/integration_tests/.mockery.yaml | 3 + core/internal/integration_tests/hook_test.go | 144 ++++++++++++++++++ .../mocks/mock_RequestHook.go | 141 +++++++++++++++++ core/server/config.go | 13 ++ core/server/mock_udpIO.go | 47 ++++++ core/server/server.go | 24 ++- core/server/udp.go | 30 +++- core/server/udp_test.go | 1 + extras/go.mod | 5 +- extras/go.sum | 9 ++ 13 files changed, 416 insertions(+), 19 deletions(-) create mode 100644 core/internal/integration_tests/hook_test.go create mode 100644 core/internal/integration_tests/mocks/mock_RequestHook.go diff --git a/app/go.mod b/app/go.mod index 46cf92c..bfc20b0 100644 --- a/app/go.mod +++ b/app/go.mod @@ -33,7 +33,6 @@ require ( github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect - github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect @@ -42,7 +41,6 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/cpuid/v2 v2.1.1 // indirect - github.com/kr/pretty v0.3.1 // indirect github.com/libdns/libdns v0.2.2 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/miekg/dns v1.1.59 // indirect @@ -53,7 +51,6 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect github.com/spf13/afero v1.9.3 // indirect @@ -77,7 +74,6 @@ require ( golang.org/x/text v0.15.0 // indirect golang.org/x/tools v0.21.0 // indirect google.golang.org/protobuf v1.34.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect rsc.io/qr v0.2.0 // indirect diff --git a/app/go.sum b/app/go.sum index 8f07498..550394f 100644 --- a/app/go.sum +++ b/app/go.sum @@ -59,7 +59,6 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -169,7 +168,6 @@ github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdy github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -215,7 +213,6 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -226,7 +223,6 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= diff --git a/core/go.sum b/core/go.sum index 96fd988..a8e10c4 100644 --- a/core/go.sum +++ b/core/go.sum @@ -12,12 +12,15 @@ github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -26,15 +29,20 @@ github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= @@ -59,8 +67,10 @@ golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/core/internal/integration_tests/.mockery.yaml b/core/internal/integration_tests/.mockery.yaml index ad233ba..550a725 100644 --- a/core/internal/integration_tests/.mockery.yaml +++ b/core/internal/integration_tests/.mockery.yaml @@ -24,3 +24,6 @@ packages: TrafficLogger: config: mockname: MockTrafficLogger + RequestHook: + config: + mockname: MockRequestHook \ No newline at end of file diff --git a/core/internal/integration_tests/hook_test.go b/core/internal/integration_tests/hook_test.go new file mode 100644 index 0000000..14f67bc --- /dev/null +++ b/core/internal/integration_tests/hook_test.go @@ -0,0 +1,144 @@ +package integration_tests + +import ( + "io" + "net" + "testing" + + "github.com/apernet/hysteria/core/v2/client" + "github.com/apernet/hysteria/core/v2/internal/integration_tests/mocks" + "github.com/apernet/hysteria/core/v2/server" + "github.com/apernet/quic-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestClientServerHookTCP(t *testing.T) { + fakeEchoAddr := "hahanope:6666" + realEchoAddr := "127.0.0.1:22333" + + // Create server + udpConn, udpAddr, err := serverConn() + assert.NoError(t, err) + auth := mocks.NewMockAuthenticator(t) + auth.EXPECT().Authenticate(mock.Anything, mock.Anything, mock.Anything).Return(true, "nobody") + hook := mocks.NewMockRequestHook(t) + hook.EXPECT().TCP(mock.Anything, mock.Anything).RunAndReturn(func(stream quic.Stream, s *string) ([]byte, error) { + assert.Equal(t, fakeEchoAddr, *s) + // Change the address + *s = realEchoAddr + // Read the first 5 bytes and replace them with "byeee" + data := make([]byte, 5) + _, err := io.ReadFull(stream, data) + if err != nil { + return nil, err + } + assert.Equal(t, []byte("hello"), data) + return []byte("byeee"), nil + }).Once() + s, err := server.NewServer(&server.Config{ + TLSConfig: serverTLSConfig(), + Conn: udpConn, + RequestHook: hook, + Authenticator: auth, + }) + assert.NoError(t, err) + defer s.Close() + go s.Serve() + + // Create TCP echo server + echoListener, err := net.Listen("tcp", realEchoAddr) + assert.NoError(t, err) + echoServer := &tcpEchoServer{Listener: echoListener} + defer echoServer.Close() + go echoServer.Serve() + + // Create client + c, _, err := client.NewClient(&client.Config{ + ServerAddr: udpAddr, + TLSConfig: client.TLSConfig{InsecureSkipVerify: true}, + FastOpen: true, // Client MUST have FastOpen for this + }) + assert.NoError(t, err) + defer c.Close() + + // Dial TCP + conn, err := c.TCP(fakeEchoAddr) + assert.NoError(t, err) + defer conn.Close() + + // Send and receive data + sData := []byte("hello world") + _, err = conn.Write(sData) + assert.NoError(t, err) + rData := make([]byte, len(sData)) + _, err = io.ReadFull(conn, rData) + assert.NoError(t, err) + assert.Equal(t, []byte("byeee world"), rData) +} + +func TestClientServerHookUDP(t *testing.T) { + fakeEchoAddr := "hahanope:6666" + realEchoAddr := "127.0.0.1:22333" + + // Create server + udpConn, udpAddr, err := serverConn() + assert.NoError(t, err) + auth := mocks.NewMockAuthenticator(t) + auth.EXPECT().Authenticate(mock.Anything, mock.Anything, mock.Anything).Return(true, "nobody") + hook := mocks.NewMockRequestHook(t) + hook.EXPECT().UDP(mock.Anything, mock.Anything).RunAndReturn(func(bytes []byte, s *string) error { + assert.Equal(t, fakeEchoAddr, *s) + assert.Equal(t, []byte("hello world"), bytes) + // Change the address + *s = realEchoAddr + return nil + }).Once() + s, err := server.NewServer(&server.Config{ + TLSConfig: serverTLSConfig(), + Conn: udpConn, + RequestHook: hook, + Authenticator: auth, + }) + assert.NoError(t, err) + defer s.Close() + go s.Serve() + + // Create UDP echo server + echoConn, err := net.ListenPacket("udp", realEchoAddr) + assert.NoError(t, err) + echoServer := &udpEchoServer{Conn: echoConn} + defer echoServer.Close() + go echoServer.Serve() + + // Create client + c, _, err := client.NewClient(&client.Config{ + ServerAddr: udpAddr, + TLSConfig: client.TLSConfig{InsecureSkipVerify: true}, + }) + assert.NoError(t, err) + defer c.Close() + + // Listen UDP + conn, err := c.UDP() + assert.NoError(t, err) + defer conn.Close() + + // Send and receive data + sData := []byte("hello world") + err = conn.Send(sData, fakeEchoAddr) + assert.NoError(t, err) + rData, rAddr, err := conn.Receive() + assert.NoError(t, err) + assert.Equal(t, sData, rData) + assert.Equal(t, realEchoAddr, rAddr) + + // Subsequent packets should also be sent to the real echo server + sData = []byte("never stop fighting") + err = conn.Send(sData, fakeEchoAddr) + assert.NoError(t, err) + rData, rAddr, err = conn.Receive() + assert.NoError(t, err) + assert.Equal(t, sData, rData) + assert.Equal(t, realEchoAddr, rAddr) +} diff --git a/core/internal/integration_tests/mocks/mock_RequestHook.go b/core/internal/integration_tests/mocks/mock_RequestHook.go new file mode 100644 index 0000000..a7a6cc5 --- /dev/null +++ b/core/internal/integration_tests/mocks/mock_RequestHook.go @@ -0,0 +1,141 @@ +// Code generated by mockery v2.43.0. DO NOT EDIT. + +package mocks + +import ( + quic "github.com/apernet/quic-go" + mock "github.com/stretchr/testify/mock" +) + +// MockRequestHook is an autogenerated mock type for the RequestHook type +type MockRequestHook struct { + mock.Mock +} + +type MockRequestHook_Expecter struct { + mock *mock.Mock +} + +func (_m *MockRequestHook) EXPECT() *MockRequestHook_Expecter { + return &MockRequestHook_Expecter{mock: &_m.Mock} +} + +// TCP provides a mock function with given fields: stream, reqAddr +func (_m *MockRequestHook) TCP(stream quic.Stream, reqAddr *string) ([]byte, error) { + ret := _m.Called(stream, reqAddr) + + if len(ret) == 0 { + panic("no return value specified for TCP") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(quic.Stream, *string) ([]byte, error)); ok { + return rf(stream, reqAddr) + } + if rf, ok := ret.Get(0).(func(quic.Stream, *string) []byte); ok { + r0 = rf(stream, reqAddr) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(quic.Stream, *string) error); ok { + r1 = rf(stream, reqAddr) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockRequestHook_TCP_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TCP' +type MockRequestHook_TCP_Call struct { + *mock.Call +} + +// TCP is a helper method to define mock.On call +// - stream quic.Stream +// - reqAddr *string +func (_e *MockRequestHook_Expecter) TCP(stream interface{}, reqAddr interface{}) *MockRequestHook_TCP_Call { + return &MockRequestHook_TCP_Call{Call: _e.mock.On("TCP", stream, reqAddr)} +} + +func (_c *MockRequestHook_TCP_Call) Run(run func(stream quic.Stream, reqAddr *string)) *MockRequestHook_TCP_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(quic.Stream), args[1].(*string)) + }) + return _c +} + +func (_c *MockRequestHook_TCP_Call) Return(_a0 []byte, _a1 error) *MockRequestHook_TCP_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockRequestHook_TCP_Call) RunAndReturn(run func(quic.Stream, *string) ([]byte, error)) *MockRequestHook_TCP_Call { + _c.Call.Return(run) + return _c +} + +// UDP provides a mock function with given fields: data, reqAddr +func (_m *MockRequestHook) UDP(data []byte, reqAddr *string) error { + ret := _m.Called(data, reqAddr) + + if len(ret) == 0 { + panic("no return value specified for UDP") + } + + var r0 error + if rf, ok := ret.Get(0).(func([]byte, *string) error); ok { + r0 = rf(data, reqAddr) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockRequestHook_UDP_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UDP' +type MockRequestHook_UDP_Call struct { + *mock.Call +} + +// UDP is a helper method to define mock.On call +// - data []byte +// - reqAddr *string +func (_e *MockRequestHook_Expecter) UDP(data interface{}, reqAddr interface{}) *MockRequestHook_UDP_Call { + return &MockRequestHook_UDP_Call{Call: _e.mock.On("UDP", data, reqAddr)} +} + +func (_c *MockRequestHook_UDP_Call) Run(run func(data []byte, reqAddr *string)) *MockRequestHook_UDP_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].([]byte), args[1].(*string)) + }) + return _c +} + +func (_c *MockRequestHook_UDP_Call) Return(_a0 error) *MockRequestHook_UDP_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockRequestHook_UDP_Call) RunAndReturn(run func([]byte, *string) error) *MockRequestHook_UDP_Call { + _c.Call.Return(run) + return _c +} + +// NewMockRequestHook creates a new instance of MockRequestHook. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockRequestHook(t interface { + mock.TestingT + Cleanup(func()) +}) *MockRequestHook { + mock := &MockRequestHook{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/server/config.go b/core/server/config.go index c6bf733..bd8b7ca 100644 --- a/core/server/config.go +++ b/core/server/config.go @@ -8,6 +8,7 @@ import ( "github.com/apernet/hysteria/core/v2/errors" "github.com/apernet/hysteria/core/v2/internal/pmtud" + "github.com/apernet/quic-go" ) const ( @@ -22,6 +23,7 @@ type Config struct { TLSConfig TLSConfig QUICConfig QUICConfig Conn net.PacketConn + RequestHook RequestHook Outbound Outbound BandwidthConfig BandwidthConfig IgnoreClientBandwidth bool @@ -110,6 +112,17 @@ type QUICConfig struct { DisablePathMTUDiscovery bool // The server may still override this to true on unsupported platforms. } +// RequestHook allows filtering and modifying requests before the server connects to the remote. +// The returned byte slice, if not empty, will be sent to the remote before proxying - this is +// mainly for "putting back" the content read from the client for sniffing, etc. +// Return a non-nil error to abort the connection. +// Note that due to the current architectural limitations, it can only inspect the first packet +// of a UDP connection. It also cannot put back any data as the first packet is always sent as-is. +type RequestHook interface { + TCP(stream quic.Stream, reqAddr *string) ([]byte, error) + UDP(data []byte, reqAddr *string) error +} + // Outbound provides the implementation of how the server should connect to remote servers. // Although UDP includes a reqAddr, the implementation does not necessarily have to use it // to make a "connected" UDP connection that does not accept packets from other addresses. diff --git a/core/server/mock_udpIO.go b/core/server/mock_udpIO.go index 3950cb9..bbdcf94 100644 --- a/core/server/mock_udpIO.go +++ b/core/server/mock_udpIO.go @@ -20,6 +20,53 @@ func (_m *mockUDPIO) EXPECT() *mockUDPIO_Expecter { return &mockUDPIO_Expecter{mock: &_m.Mock} } +// Hook provides a mock function with given fields: data, reqAddr +func (_m *mockUDPIO) Hook(data []byte, reqAddr *string) error { + ret := _m.Called(data, reqAddr) + + if len(ret) == 0 { + panic("no return value specified for Hook") + } + + var r0 error + if rf, ok := ret.Get(0).(func([]byte, *string) error); ok { + r0 = rf(data, reqAddr) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// mockUDPIO_Hook_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Hook' +type mockUDPIO_Hook_Call struct { + *mock.Call +} + +// Hook is a helper method to define mock.On call +// - data []byte +// - reqAddr *string +func (_e *mockUDPIO_Expecter) Hook(data interface{}, reqAddr interface{}) *mockUDPIO_Hook_Call { + return &mockUDPIO_Hook_Call{Call: _e.mock.On("Hook", data, reqAddr)} +} + +func (_c *mockUDPIO_Hook_Call) Run(run func(data []byte, reqAddr *string)) *mockUDPIO_Hook_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].([]byte), args[1].(*string)) + }) + return _c +} + +func (_c *mockUDPIO_Hook_Call) Return(_a0 error) *mockUDPIO_Hook_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockUDPIO_Hook_Call) RunAndReturn(run func([]byte, *string) error) *mockUDPIO_Hook_Call { + _c.Call.Return(run) + return _c +} + // ReceiveMessage provides a mock function with given fields: func (_m *mockUDPIO) ReceiveMessage() (*protocol.UDPMessage, error) { ret := _m.Called() diff --git a/core/server/server.go b/core/server/server.go index bd61db0..7ebb387 100644 --- a/core/server/server.go +++ b/core/server/server.go @@ -170,7 +170,7 @@ func (h *h3sHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if !h.config.DisableUDP { go func() { sm := newUDPSessionManager( - &udpIOImpl{h.conn, id, h.config.TrafficLogger, h.config.Outbound}, + &udpIOImpl{h.conn, id, h.config.TrafficLogger, h.config.RequestHook, h.config.Outbound}, &udpEventLoggerImpl{h.conn, id, h.config.EventLogger}, h.config.UDPIdleTimeout) h.udpSM = sm @@ -211,6 +211,16 @@ func (h *h3sHandler) handleTCPRequest(stream quic.Stream) { _ = stream.Close() return } + // Call the hook if set + var putback []byte + if h.config.RequestHook != nil { + putback, err = h.config.RequestHook.TCP(stream, &reqAddr) + if err != nil { + _ = protocol.WriteTCPResponse(stream, false, err.Error()) + _ = stream.Close() + return + } + } // Log the event if h.config.EventLogger != nil { h.config.EventLogger.TCPRequest(h.conn.RemoteAddr(), h.authID, reqAddr) @@ -227,6 +237,10 @@ func (h *h3sHandler) handleTCPRequest(stream quic.Stream) { return } _ = protocol.WriteTCPResponse(stream, true, "") + // Put back the data if the hook requested + if len(putback) > 0 { + _, _ = tConn.Write(putback) + } // Start proxying if h.config.TrafficLogger != nil { err = copyTwoWayWithLogger(h.authID, stream, tConn, h.config.TrafficLogger) @@ -260,6 +274,7 @@ type udpIOImpl struct { Conn quic.Connection AuthID string TrafficLogger TrafficLogger + RequestHook RequestHook Outbound Outbound } @@ -304,6 +319,13 @@ func (io *udpIOImpl) SendMessage(buf []byte, msg *protocol.UDPMessage) error { return io.Conn.SendDatagram(buf[:msgN]) } +func (io *udpIOImpl) Hook(data []byte, reqAddr *string) error { + if io.RequestHook != nil { + return io.RequestHook.UDP(data, reqAddr) + } + return nil +} + func (io *udpIOImpl) UDP(reqAddr string) (UDPConn, error) { return io.Outbound.UDP(reqAddr) } diff --git a/core/server/udp.go b/core/server/udp.go index 320dc00..ecaee29 100644 --- a/core/server/udp.go +++ b/core/server/udp.go @@ -20,6 +20,7 @@ const ( type udpIO interface { ReceiveMessage() (*protocol.UDPMessage, error) SendMessage([]byte, *protocol.UDPMessage) error + Hook(data []byte, reqAddr *string) error UDP(reqAddr string) (UDPConn, error) } @@ -29,11 +30,12 @@ type udpEventLogger interface { } type udpSessionEntry struct { - ID uint32 - Conn UDPConn - D *frag.Defragger - Last *utils.AtomicTime - Timeout bool // true if the session is closed due to timeout + ID uint32 + Conn UDPConn + OverrideAddr string // Ignore the address in the UDP message, always use this if not empty + D *frag.Defragger + Last *utils.AtomicTime + Timeout bool // true if the session is closed due to timeout } // Feed feeds a UDP message to the session. @@ -47,7 +49,11 @@ func (e *udpSessionEntry) Feed(msg *protocol.UDPMessage) (int, error) { if dfMsg == nil { return 0, nil } - return e.Conn.WriteTo(dfMsg.Data, dfMsg.Addr) + if e.OverrideAddr != "" { + return e.Conn.WriteTo(dfMsg.Data, e.OverrideAddr) + } else { + return e.Conn.WriteTo(dfMsg.Data, dfMsg.Addr) + } } // ReceiveLoop receives incoming UDP packets, packs them into UDP messages, @@ -177,7 +183,15 @@ func (m *udpSessionManager) feed(msg *protocol.UDPMessage) { // Create a new session if not exists if entry == nil { + // Call the hook + origMsgAddr := msg.Addr + err := m.io.Hook(msg.Data, &msg.Addr) + if err != nil { + return + } + // Log the event m.eventLogger.New(msg.SessionID, msg.Addr) + // Dial target & create a new session entry conn, err := m.io.UDP(msg.Addr) if err != nil { m.eventLogger.Close(msg.SessionID, err) @@ -189,6 +203,10 @@ func (m *udpSessionManager) feed(msg *protocol.UDPMessage) { D: &frag.Defragger{}, Last: utils.NewAtomicTime(time.Now()), } + if origMsgAddr != msg.Addr { + // Hook changed the address, enable address override + entry.OverrideAddr = msg.Addr + } // Start the receive loop for this session go func() { err := entry.ReceiveLoop(m.io) diff --git a/core/server/udp_test.go b/core/server/udp_test.go index a0169e3..aa78327 100644 --- a/core/server/udp_test.go +++ b/core/server/udp_test.go @@ -25,6 +25,7 @@ func TestUDPSessionManager(t *testing.T) { } return m, nil }) + io.EXPECT().Hook(mock.Anything, mock.Anything).Return(nil, nil) go sm.Run() diff --git a/extras/go.mod b/extras/go.mod index efc1111..5830418 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -18,14 +18,12 @@ require ( github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect - github.com/golang/protobuf v1.5.4 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect - github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect go.uber.org/mock v0.4.0 // indirect @@ -35,7 +33,6 @@ require ( golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect golang.org/x/tools v0.21.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/extras/go.sum b/extras/go.sum index 4ef8205..5d931d7 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -5,6 +5,7 @@ github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -13,6 +14,7 @@ github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= @@ -21,10 +23,12 @@ github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvH github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/miekg/dns v1.1.51/go.mod h1:2Z9d3CP1LQWihRZUf29mQ19yDThaI4DAYzte2CaQW5c= github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= +github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= @@ -36,10 +40,13 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf h1:7PflaKRtU4np/epFxRXlFhlzLXZzKFrH5/I4so5Ove0= github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf/go.mod h1:CLUSJbazqETbaR+i0YAhXBICV9TrKH93pziccMhmhpM= github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 h1:d/Wr/Vl/wiJHc3AHYbYs5I3PucJvRuw3SvbmlIRf+oM= @@ -98,8 +105,10 @@ golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From a852febc1f87bb1d7924c360748e3379eb9a993c Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 15 Jun 2024 15:42:39 -0700 Subject: [PATCH 097/161] fix: incorrect speed conversion base --- app/cmd/speedtest.go | 4 ++-- core/internal/congestion/bbr/bbr_sender.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/cmd/speedtest.go b/app/cmd/speedtest.go index 6195c72..12d3a83 100644 --- a/app/cmd/speedtest.go +++ b/app/cmd/speedtest.go @@ -152,8 +152,8 @@ func formatSpeed(bytes uint32, duration time.Duration, useBytes bool) string { speed *= 8 } unitIndex := 0 - for speed > 1024 && unitIndex < len(units)-1 { - speed /= 1024 + for speed > 1000 && unitIndex < len(units)-1 { + speed /= 1000 unitIndex++ } return fmt.Sprintf("%.2f %s", speed, units[unitIndex]) diff --git a/core/internal/congestion/bbr/bbr_sender.go b/core/internal/congestion/bbr/bbr_sender.go index 62868ec..8f58e1f 100644 --- a/core/internal/congestion/bbr/bbr_sender.go +++ b/core/internal/congestion/bbr/bbr_sender.go @@ -976,8 +976,8 @@ func formatSpeed(bw Bandwidth) string { bwf := float64(bw) units := []string{"bps", "Kbps", "Mbps", "Gbps"} unitIndex := 0 - for bwf > 1024 && unitIndex < len(units)-1 { - bwf /= 1024 + for bwf > 1000 && unitIndex < len(units)-1 { + bwf /= 1000 unitIndex++ } return fmt.Sprintf("%.2f %s", bwf, units[unitIndex]) From 2c62a1a1b47e15226a6781bda0521aa14256f755 Mon Sep 17 00:00:00 2001 From: Toby Date: Sun, 16 Jun 2024 13:26:02 -0700 Subject: [PATCH 098/161] fix: do not require client-side fast open --- core/internal/integration_tests/hook_test.go | 1 - core/server/server.go | 13 ++++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/core/internal/integration_tests/hook_test.go b/core/internal/integration_tests/hook_test.go index 14f67bc..43baf4f 100644 --- a/core/internal/integration_tests/hook_test.go +++ b/core/internal/integration_tests/hook_test.go @@ -57,7 +57,6 @@ func TestClientServerHookTCP(t *testing.T) { c, _, err := client.NewClient(&client.Config{ ServerAddr: udpAddr, TLSConfig: client.TLSConfig{InsecureSkipVerify: true}, - FastOpen: true, // Client MUST have FastOpen for this }) assert.NoError(t, err) defer c.Close() diff --git a/core/server/server.go b/core/server/server.go index 7ebb387..4fc9d41 100644 --- a/core/server/server.go +++ b/core/server/server.go @@ -214,9 +214,12 @@ func (h *h3sHandler) handleTCPRequest(stream quic.Stream) { // Call the hook if set var putback []byte if h.config.RequestHook != nil { + // When RequestHook is enabled, the server should always accept a connection + // so that the client will send whatever request the hook wants to see. + // This is essentially a server-side fast-open. + _ = protocol.WriteTCPResponse(stream, true, "RequestHook enabled") putback, err = h.config.RequestHook.TCP(stream, &reqAddr) if err != nil { - _ = protocol.WriteTCPResponse(stream, false, err.Error()) _ = stream.Close() return } @@ -228,7 +231,9 @@ func (h *h3sHandler) handleTCPRequest(stream quic.Stream) { // Dial target tConn, err := h.config.Outbound.TCP(reqAddr) if err != nil { - _ = protocol.WriteTCPResponse(stream, false, err.Error()) + if h.config.RequestHook == nil { + _ = protocol.WriteTCPResponse(stream, false, err.Error()) + } _ = stream.Close() // Log the error if h.config.EventLogger != nil { @@ -236,7 +241,9 @@ func (h *h3sHandler) handleTCPRequest(stream quic.Stream) { } return } - _ = protocol.WriteTCPResponse(stream, true, "") + if h.config.RequestHook == nil { + _ = protocol.WriteTCPResponse(stream, true, "Connected") + } // Put back the data if the hook requested if len(putback) > 0 { _, _ = tConn.Write(putback) From c78dbb38a14fc228b9958b6527cbf583b07b061d Mon Sep 17 00:00:00 2001 From: Toby Date: Tue, 18 Jun 2024 21:46:25 -0700 Subject: [PATCH 099/161] feat: add a Check method to let the implementation decide whether to hook a request --- core/internal/integration_tests/hook_test.go | 2 + .../mocks/mock_RequestHook.go | 47 +++++++++++++++++++ core/server/config.go | 2 + core/server/server.go | 25 ++++++---- 4 files changed, 66 insertions(+), 10 deletions(-) diff --git a/core/internal/integration_tests/hook_test.go b/core/internal/integration_tests/hook_test.go index 43baf4f..1121d13 100644 --- a/core/internal/integration_tests/hook_test.go +++ b/core/internal/integration_tests/hook_test.go @@ -23,6 +23,7 @@ func TestClientServerHookTCP(t *testing.T) { auth := mocks.NewMockAuthenticator(t) auth.EXPECT().Authenticate(mock.Anything, mock.Anything, mock.Anything).Return(true, "nobody") hook := mocks.NewMockRequestHook(t) + hook.EXPECT().Check(false, fakeEchoAddr).Return(true).Once() hook.EXPECT().TCP(mock.Anything, mock.Anything).RunAndReturn(func(stream quic.Stream, s *string) ([]byte, error) { assert.Equal(t, fakeEchoAddr, *s) // Change the address @@ -86,6 +87,7 @@ func TestClientServerHookUDP(t *testing.T) { auth := mocks.NewMockAuthenticator(t) auth.EXPECT().Authenticate(mock.Anything, mock.Anything, mock.Anything).Return(true, "nobody") hook := mocks.NewMockRequestHook(t) + hook.EXPECT().Check(true, fakeEchoAddr).Return(true).Once() hook.EXPECT().UDP(mock.Anything, mock.Anything).RunAndReturn(func(bytes []byte, s *string) error { assert.Equal(t, fakeEchoAddr, *s) assert.Equal(t, []byte("hello world"), bytes) diff --git a/core/internal/integration_tests/mocks/mock_RequestHook.go b/core/internal/integration_tests/mocks/mock_RequestHook.go index a7a6cc5..5418eaf 100644 --- a/core/internal/integration_tests/mocks/mock_RequestHook.go +++ b/core/internal/integration_tests/mocks/mock_RequestHook.go @@ -20,6 +20,53 @@ func (_m *MockRequestHook) EXPECT() *MockRequestHook_Expecter { return &MockRequestHook_Expecter{mock: &_m.Mock} } +// Check provides a mock function with given fields: isUDP, reqAddr +func (_m *MockRequestHook) Check(isUDP bool, reqAddr string) bool { + ret := _m.Called(isUDP, reqAddr) + + if len(ret) == 0 { + panic("no return value specified for Check") + } + + var r0 bool + if rf, ok := ret.Get(0).(func(bool, string) bool); ok { + r0 = rf(isUDP, reqAddr) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// MockRequestHook_Check_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Check' +type MockRequestHook_Check_Call struct { + *mock.Call +} + +// Check is a helper method to define mock.On call +// - isUDP bool +// - reqAddr string +func (_e *MockRequestHook_Expecter) Check(isUDP interface{}, reqAddr interface{}) *MockRequestHook_Check_Call { + return &MockRequestHook_Check_Call{Call: _e.mock.On("Check", isUDP, reqAddr)} +} + +func (_c *MockRequestHook_Check_Call) Run(run func(isUDP bool, reqAddr string)) *MockRequestHook_Check_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(bool), args[1].(string)) + }) + return _c +} + +func (_c *MockRequestHook_Check_Call) Return(_a0 bool) *MockRequestHook_Check_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockRequestHook_Check_Call) RunAndReturn(run func(bool, string) bool) *MockRequestHook_Check_Call { + _c.Call.Return(run) + return _c +} + // TCP provides a mock function with given fields: stream, reqAddr func (_m *MockRequestHook) TCP(stream quic.Stream, reqAddr *string) ([]byte, error) { ret := _m.Called(stream, reqAddr) diff --git a/core/server/config.go b/core/server/config.go index bd8b7ca..f90c820 100644 --- a/core/server/config.go +++ b/core/server/config.go @@ -113,12 +113,14 @@ type QUICConfig struct { } // RequestHook allows filtering and modifying requests before the server connects to the remote. +// A request will only be hooked if Check returns true. // The returned byte slice, if not empty, will be sent to the remote before proxying - this is // mainly for "putting back" the content read from the client for sniffing, etc. // Return a non-nil error to abort the connection. // Note that due to the current architectural limitations, it can only inspect the first packet // of a UDP connection. It also cannot put back any data as the first packet is always sent as-is. type RequestHook interface { + Check(isUDP bool, reqAddr string) bool TCP(stream quic.Stream, reqAddr *string) ([]byte, error) UDP(data []byte, reqAddr *string) error } diff --git a/core/server/server.go b/core/server/server.go index 4fc9d41..ba55b31 100644 --- a/core/server/server.go +++ b/core/server/server.go @@ -213,15 +213,19 @@ func (h *h3sHandler) handleTCPRequest(stream quic.Stream) { } // Call the hook if set var putback []byte + var hooked bool if h.config.RequestHook != nil { - // When RequestHook is enabled, the server should always accept a connection + hooked = h.config.RequestHook.Check(false, reqAddr) + // When the hook is enabled, the server should always accept a connection // so that the client will send whatever request the hook wants to see. // This is essentially a server-side fast-open. - _ = protocol.WriteTCPResponse(stream, true, "RequestHook enabled") - putback, err = h.config.RequestHook.TCP(stream, &reqAddr) - if err != nil { - _ = stream.Close() - return + if hooked { + _ = protocol.WriteTCPResponse(stream, true, "RequestHook enabled") + putback, err = h.config.RequestHook.TCP(stream, &reqAddr) + if err != nil { + _ = stream.Close() + return + } } } // Log the event @@ -231,7 +235,7 @@ func (h *h3sHandler) handleTCPRequest(stream quic.Stream) { // Dial target tConn, err := h.config.Outbound.TCP(reqAddr) if err != nil { - if h.config.RequestHook == nil { + if !hooked { _ = protocol.WriteTCPResponse(stream, false, err.Error()) } _ = stream.Close() @@ -241,7 +245,7 @@ func (h *h3sHandler) handleTCPRequest(stream quic.Stream) { } return } - if h.config.RequestHook == nil { + if !hooked { _ = protocol.WriteTCPResponse(stream, true, "Connected") } // Put back the data if the hook requested @@ -327,10 +331,11 @@ func (io *udpIOImpl) SendMessage(buf []byte, msg *protocol.UDPMessage) error { } func (io *udpIOImpl) Hook(data []byte, reqAddr *string) error { - if io.RequestHook != nil { + if io.RequestHook != nil && io.RequestHook.Check(true, *reqAddr) { return io.RequestHook.UDP(data, reqAddr) + } else { + return nil } - return nil } func (io *udpIOImpl) UDP(reqAddr string) (UDPConn, error) { From 8aab73502981938dd707a12d9122746683e01c19 Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 29 Jun 2024 13:40:52 -0700 Subject: [PATCH 100/161] feat: experimental HTTP/TLS sniffing implementation (no QUIC yet) --- app/cmd/server.go | 7 ++ extras/go.mod | 2 +- extras/sniff/sniff.go | 165 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 extras/sniff/sniff.go diff --git a/app/cmd/server.go b/app/cmd/server.go index d2c9f4c..1a1ee6d 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -15,6 +15,8 @@ import ( "strings" "time" + "github.com/apernet/hysteria/extras/v2/sniff" + "github.com/caddyserver/certmagic" "github.com/libdns/cloudflare" "github.com/libdns/duckdns" @@ -855,6 +857,11 @@ func runServer(cmd *cobra.Command, args []string) { logger.Fatal("failed to load server config", zap.Error(err)) } + hyConfig.RequestHook = &sniff.Sniffer{ + Timeout: 4 * time.Second, + RewriteDomain: false, + } + s, err := server.NewServer(hyConfig) if err != nil { logger.Fatal("failed to initialize server", zap.Error(err)) diff --git a/extras/go.mod b/extras/go.mod index 5830418..21cc989 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -4,6 +4,7 @@ go 1.21 require ( github.com/apernet/hysteria/core/v2 v2.0.0-00010101000000-000000000000 + github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023 github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 github.com/hashicorp/golang-lru/v2 v2.0.5 github.com/miekg/dns v1.1.59 @@ -15,7 +16,6 @@ require ( ) require ( - github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect diff --git a/extras/sniff/sniff.go b/extras/sniff/sniff.go new file mode 100644 index 0000000..61f80c2 --- /dev/null +++ b/extras/sniff/sniff.go @@ -0,0 +1,165 @@ +package sniff + +import ( + "bufio" + "context" + "crypto/tls" + "io" + "net" + "net/http" + "strings" + "time" + + "github.com/apernet/hysteria/core/v2/server" + "github.com/apernet/quic-go" +) + +var _ server.RequestHook = (*Sniffer)(nil) + +// Sniffer is a server core RequestHook that performs packet inspection and possibly +// rewrites the request address based on what's in the protocol header. +// This is mainly for inbounds that inherently cannot get domain information (e.g. TUN), +// in which case sniffing can restore the domains and apply ACLs correctly. +// Currently supports HTTP, HTTPS (TLS) and QUIC. +type Sniffer struct { + Timeout time.Duration + RewriteDomain bool // Whether to rewrite the address even when it's already a domain +} + +func (h *Sniffer) isDomain(addr string) bool { + host, _, err := net.SplitHostPort(addr) + if err != nil { + return false + } + return net.ParseIP(host) == nil +} + +func (h *Sniffer) isHTTP(buf []byte) bool { + if len(buf) < 3 { + return false + } + // First 3 bytes should be English letters (whatever HTTP method) + for _, b := range buf[:3] { + if (b < 'A' || b > 'Z') && (b < 'a' || b > 'z') { + return false + } + } + return true +} + +func (h *Sniffer) isTLS(buf []byte) bool { + if len(buf) < 3 { + return false + } + return buf[0] >= 0x16 && buf[0] <= 0x17 && + buf[1] == 0x03 && buf[2] <= 0x09 +} + +func (h *Sniffer) Check(isUDP bool, reqAddr string) bool { + // @ means it's internal (e.g. speed test) + return !strings.HasPrefix(reqAddr, "@") && !isUDP && (h.RewriteDomain || !h.isDomain(reqAddr)) +} + +func (h *Sniffer) TCP(stream quic.Stream, reqAddr *string) ([]byte, error) { + err := stream.SetReadDeadline(time.Now().Add(h.Timeout)) + if err != nil { + return nil, err + } + // Make sure to reset the deadline after sniffing + defer stream.SetReadDeadline(time.Time{}) + // Read 3 bytes to determine the protocol + pre := make([]byte, 3) + n, err := io.ReadFull(stream, pre) + if err != nil { + // Not enough within the timeout, just return what we have + return pre[:n], nil + } + if h.isHTTP(pre) { + fConn := &fakeConn{Stream: stream, Pre: pre} + req, _ := http.ReadRequest(bufio.NewReader(fConn)) + if req != nil && req.Host != "" { + _, port, err := net.SplitHostPort(*reqAddr) + if err != nil { + return nil, err + } + *reqAddr = net.JoinHostPort(req.Host, port) + } + return fConn.Buffer, nil + } else if h.isTLS(pre) { + fConn := &fakeConn{Stream: stream, Pre: pre} + var clientHello *tls.ClientHelloInfo + _ = tls.Server(fConn, &tls.Config{ + GetConfigForClient: func(info *tls.ClientHelloInfo) (*tls.Config, error) { + clientHello = info + return nil, nil + }, + }).HandshakeContext(context.Background()) + if clientHello != nil && clientHello.ServerName != "" { + _, port, err := net.SplitHostPort(*reqAddr) + if err != nil { + return nil, err + } + *reqAddr = net.JoinHostPort(clientHello.ServerName, port) + } + return fConn.Buffer, nil + } else { + // Unrecognized protocol, just return what we have + return pre, nil + } +} + +func (h *Sniffer) UDP(data []byte, reqAddr *string) error { + return nil +} + +type fakeConn struct { + Stream quic.Stream + Pre []byte + Buffer []byte +} + +func (c *fakeConn) Read(b []byte) (n int, err error) { + if len(c.Pre) > 0 { + n = copy(b, c.Pre) + c.Pre = c.Pre[n:] + c.Buffer = append(c.Buffer, b[:n]...) + return n, nil + } + n, err = c.Stream.Read(b) + if n > 0 { + c.Buffer = append(c.Buffer, b[:n]...) + } + return n, err +} + +func (c *fakeConn) Write(b []byte) (n int, err error) { + // Do not write anything, pretend it's successful + return len(b), nil +} + +func (c *fakeConn) Close() error { + // Do not close the stream + return nil +} + +func (c *fakeConn) LocalAddr() net.Addr { + // Doesn't matter + return nil +} + +func (c *fakeConn) RemoteAddr() net.Addr { + // Doesn't matter + return nil +} + +func (c *fakeConn) SetDeadline(t time.Time) error { + return c.Stream.SetReadDeadline(t) +} + +func (c *fakeConn) SetReadDeadline(t time.Time) error { + return c.Stream.SetReadDeadline(t) +} + +func (c *fakeConn) SetWriteDeadline(t time.Time) error { + return c.Stream.SetWriteDeadline(t) +} From 16bfdc7720bab6f0fb11ea56e81c53b92ae5f0dd Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 29 Jun 2024 15:52:56 -0700 Subject: [PATCH 101/161] feat: QUIC sniffing --- app/go.mod | 12 +- app/go.sum | 24 ++- core/go.mod | 8 +- core/go.sum | 16 +- extras/go.mod | 12 +- extras/go.sum | 24 ++- extras/sniff/internal/quic/LICENSE | 31 +++ extras/sniff/internal/quic/README.md | 1 + extras/sniff/internal/quic/header.go | 105 ++++++++++ .../sniff/internal/quic/packet_protector.go | 193 ++++++++++++++++++ .../internal/quic/packet_protector_test.go | 94 +++++++++ extras/sniff/internal/quic/payload.go | 122 +++++++++++ extras/sniff/internal/quic/quic.go | 59 ++++++ extras/sniff/sniff.go | 98 +++++---- go.work.sum | 4 + 15 files changed, 716 insertions(+), 87 deletions(-) create mode 100644 extras/sniff/internal/quic/LICENSE create mode 100644 extras/sniff/internal/quic/README.md create mode 100644 extras/sniff/internal/quic/header.go create mode 100644 extras/sniff/internal/quic/packet_protector.go create mode 100644 extras/sniff/internal/quic/packet_protector_test.go create mode 100644 extras/sniff/internal/quic/payload.go create mode 100644 extras/sniff/internal/quic/quic.go diff --git a/app/go.mod b/app/go.mod index bfc20b0..dfbea42 100644 --- a/app/go.mod +++ b/app/go.mod @@ -23,12 +23,14 @@ require ( github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 go.uber.org/zap v1.24.0 golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 - golang.org/x/sys v0.20.0 + golang.org/x/sys v0.21.0 ) require ( + github.com/andybalholm/brotli v1.1.0 // indirect github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023 // indirect github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect + github.com/cloudflare/circl v1.3.9 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -40,6 +42,7 @@ require ( github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/klauspost/compress v1.17.9 // indirect github.com/klauspost/cpuid/v2 v2.1.1 // indirect github.com/libdns/libdns v0.2.2 // indirect github.com/magiconair/properties v1.8.7 // indirect @@ -51,6 +54,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect + github.com/refraction-networking/utls v1.6.6 // indirect github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect github.com/spf13/afero v1.9.3 // indirect @@ -66,13 +70,13 @@ require ( go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect - golang.org/x/crypto v0.23.0 // indirect + golang.org/x/crypto v0.24.0 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.25.0 // indirect golang.org/x/oauth2 v0.20.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/text v0.15.0 // indirect - golang.org/x/tools v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/protobuf v1.34.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/app/go.sum b/app/go.sum index 550394f..e1b80a5 100644 --- a/app/go.sum +++ b/app/go.sum @@ -38,6 +38,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f h1:uVh0qpEslrWjgzx9vOcyCqsOY3c9kofDZ1n+qaw35ZY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f/go.mod h1:xkkq9D4ygcldQQhKS/w9CadiCKwCngU7K9E3DaKahpM= github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023 h1:UTrvVPt+GfeOeli9/3gvpCDz2Jd5UEn3YotfP0u/pok= @@ -55,6 +57,8 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE= +github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -164,6 +168,8 @@ github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLf github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdydm0= github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= @@ -222,6 +228,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/refraction-networking/utls v1.6.6 h1:igFsYBUJPYM8Rno9xUuDoM5GQrVEqY4llzEXOkL43Ig= +github.com/refraction-networking/utls v1.6.6/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= @@ -306,8 +314,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -455,8 +463,8 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -468,8 +476,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -526,8 +534,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= -golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/core/go.mod b/core/go.mod index 66a8938..f35cc54 100644 --- a/core/go.mod +++ b/core/go.mod @@ -22,12 +22,12 @@ require ( github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/stretchr/objx v0.5.2 // indirect go.uber.org/mock v0.4.0 // indirect - golang.org/x/crypto v0.23.0 // indirect + golang.org/x/crypto v0.24.0 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.25.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect - golang.org/x/tools v0.21.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/protobuf v1.34.1 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/core/go.sum b/core/go.sum index a8e10c4..1a133cb 100644 --- a/core/go.sum +++ b/core/go.sum @@ -47,8 +47,8 @@ go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= @@ -58,14 +58,14 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/extras/go.mod b/extras/go.mod index 21cc989..2d9fae7 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -8,17 +8,21 @@ require ( github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 github.com/hashicorp/golang-lru/v2 v2.0.5 github.com/miekg/dns v1.1.59 + github.com/refraction-networking/utls v1.6.6 github.com/stretchr/testify v1.9.0 github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 - golang.org/x/crypto v0.23.0 + golang.org/x/crypto v0.24.0 golang.org/x/net v0.25.0 google.golang.org/protobuf v1.34.1 ) require ( + github.com/andybalholm/brotli v1.1.0 // indirect + github.com/cloudflare/circl v1.3.9 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect + github.com/klauspost/compress v1.17.9 // indirect github.com/kr/text v0.2.0 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect @@ -30,9 +34,9 @@ require ( golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect - golang.org/x/tools v0.21.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/extras/go.sum b/extras/go.sum index 5d931d7..74dceb5 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -1,3 +1,5 @@ +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023 h1:UTrvVPt+GfeOeli9/3gvpCDz2Jd5UEn3YotfP0u/pok= github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023/go.mod h1:UkcG7+34BM+bbH2RFVKtHQp3mR7h8yJHx4z95lZ7sx4= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= @@ -5,6 +7,8 @@ github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE= +github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -22,6 +26,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4= github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -39,6 +45,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/refraction-networking/utls v1.6.6 h1:igFsYBUJPYM8Rno9xUuDoM5GQrVEqY4llzEXOkL43Ig= +github.com/refraction-networking/utls v1.6.6/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -58,8 +66,8 @@ go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -84,8 +92,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -93,16 +101,16 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= -golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= diff --git a/extras/sniff/internal/quic/LICENSE b/extras/sniff/internal/quic/LICENSE new file mode 100644 index 0000000..43970c4 --- /dev/null +++ b/extras/sniff/internal/quic/LICENSE @@ -0,0 +1,31 @@ +Author:: Cuong Manh Le +Copyright:: Copyright (c) 2023, Cuong Manh Le +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of the @organization@ nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LE MANH CUONG +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/extras/sniff/internal/quic/README.md b/extras/sniff/internal/quic/README.md new file mode 100644 index 0000000..8f3a5e2 --- /dev/null +++ b/extras/sniff/internal/quic/README.md @@ -0,0 +1 @@ +The code here is from https://github.com/cuonglm/quicsni with various modifications. \ No newline at end of file diff --git a/extras/sniff/internal/quic/header.go b/extras/sniff/internal/quic/header.go new file mode 100644 index 0000000..c1a5e7c --- /dev/null +++ b/extras/sniff/internal/quic/header.go @@ -0,0 +1,105 @@ +package quic + +import ( + "bytes" + "encoding/binary" + "errors" + "io" + + "github.com/apernet/quic-go/quicvarint" +) + +// The Header represents a QUIC header. +type Header struct { + Type uint8 + Version uint32 + SrcConnectionID []byte + DestConnectionID []byte + Length int64 + Token []byte +} + +// ParseInitialHeader parses the initial packet of a QUIC connection, +// return the initial header and number of bytes read so far. +func ParseInitialHeader(data []byte) (*Header, int64, error) { + br := bytes.NewReader(data) + hdr, err := parseLongHeader(br) + if err != nil { + return nil, 0, err + } + n := int64(len(data) - br.Len()) + return hdr, n, nil +} + +func parseLongHeader(b *bytes.Reader) (*Header, error) { + typeByte, err := b.ReadByte() + if err != nil { + return nil, err + } + h := &Header{} + ver, err := beUint32(b) + if err != nil { + return nil, err + } + h.Version = ver + if h.Version != 0 && typeByte&0x40 == 0 { + return nil, errors.New("not a QUIC packet") + } + destConnIDLen, err := b.ReadByte() + if err != nil { + return nil, err + } + h.DestConnectionID = make([]byte, int(destConnIDLen)) + if err := readConnectionID(b, h.DestConnectionID); err != nil { + return nil, err + } + srcConnIDLen, err := b.ReadByte() + if err != nil { + return nil, err + } + h.SrcConnectionID = make([]byte, int(srcConnIDLen)) + if err := readConnectionID(b, h.SrcConnectionID); err != nil { + return nil, err + } + + initialPacketType := byte(0b00) + if h.Version == V2 { + initialPacketType = 0b01 + } + if (typeByte >> 4 & 0b11) == initialPacketType { + tokenLen, err := quicvarint.Read(b) + if err != nil { + return nil, err + } + if tokenLen > uint64(b.Len()) { + return nil, io.EOF + } + h.Token = make([]byte, tokenLen) + if _, err := io.ReadFull(b, h.Token); err != nil { + return nil, err + } + } + + pl, err := quicvarint.Read(b) + if err != nil { + return nil, err + } + h.Length = int64(pl) + return h, err +} + +func readConnectionID(r io.Reader, cid []byte) error { + _, err := io.ReadFull(r, cid) + if err == io.ErrUnexpectedEOF { + return io.EOF + } + return nil +} + +func beUint32(r io.Reader) (uint32, error) { + b := make([]byte, 4) + if _, err := io.ReadFull(r, b); err != nil { + return 0, err + } + return binary.BigEndian.Uint32(b), nil +} diff --git a/extras/sniff/internal/quic/packet_protector.go b/extras/sniff/internal/quic/packet_protector.go new file mode 100644 index 0000000..42de841 --- /dev/null +++ b/extras/sniff/internal/quic/packet_protector.go @@ -0,0 +1,193 @@ +package quic + +import ( + "crypto" + "crypto/aes" + "crypto/cipher" + "crypto/sha256" + "crypto/tls" + "encoding/binary" + "errors" + "fmt" + "hash" + + "golang.org/x/crypto/chacha20" + "golang.org/x/crypto/chacha20poly1305" + "golang.org/x/crypto/cryptobyte" + "golang.org/x/crypto/hkdf" +) + +// NewProtectionKey creates a new ProtectionKey. +func NewProtectionKey(suite uint16, secret []byte, v uint32) (*ProtectionKey, error) { + return newProtectionKey(suite, secret, v) +} + +// NewInitialProtectionKey is like NewProtectionKey, but the returned protection key +// is used for encrypt/decrypt Initial Packet only. +// +// See: https://datatracker.ietf.org/doc/html/draft-ietf-quic-tls-32#name-initial-secrets +func NewInitialProtectionKey(secret []byte, v uint32) (*ProtectionKey, error) { + return NewProtectionKey(tls.TLS_AES_128_GCM_SHA256, secret, v) +} + +// NewPacketProtector creates a new PacketProtector. +func NewPacketProtector(key *ProtectionKey) *PacketProtector { + return &PacketProtector{key: key} +} + +// PacketProtector is used for protecting a QUIC packet. +// +// See: https://www.rfc-editor.org/rfc/rfc9001.html#name-packet-protection +type PacketProtector struct { + key *ProtectionKey +} + +// UnProtect decrypts a QUIC packet. +func (pp *PacketProtector) UnProtect(packet []byte, pnOffset, pnMax int64) ([]byte, error) { + if isLongHeader(packet[0]) && int64(len(packet)) < pnOffset+4+16 { + return nil, errors.New("packet with long header is too small") + } + + // https://www.rfc-editor.org/rfc/rfc9001.html#name-header-protection-sample + sampleOffset := pnOffset + 4 + sample := packet[sampleOffset : sampleOffset+16] + + // https://www.rfc-editor.org/rfc/rfc9001.html#name-header-protection-applicati + mask := pp.key.headerProtection(sample) + if isLongHeader(packet[0]) { + // Long header: 4 bits masked + packet[0] ^= mask[0] & 0x0f + } else { + // Short header: 5 bits masked + packet[0] ^= mask[0] & 0x1f + } + + pnLen := packet[0]&0x3 + 1 + pn := int64(0) + for i := uint8(0); i < pnLen; i++ { + packet[pnOffset:][i] ^= mask[1+i] + pn = (pn << 8) | int64(packet[pnOffset:][i]) + } + pn = decodePacketNumber(pnMax, pn, pnLen) + hdr := packet[:pnOffset+int64(pnLen)] + payload := packet[pnOffset:][pnLen:] + dec, err := pp.key.aead.Open(payload[:0], pp.key.nonce(pn), payload, hdr) + if err != nil { + return nil, fmt.Errorf("decryption failed: %w", err) + } + return dec, nil +} + +// ProtectionKey is the key used to protect a QUIC packet. +type ProtectionKey struct { + aead cipher.AEAD + headerProtection func(sample []byte) (mask []byte) + iv []byte +} + +// https://datatracker.ietf.org/doc/html/draft-ietf-quic-tls-32#name-aead-usage +// +// "The 62 bits of the reconstructed QUIC packet number in network byte order are +// left-padded with zeros to the size of the IV. The exclusive OR of the padded +// packet number and the IV forms the AEAD nonce." +func (pk *ProtectionKey) nonce(pn int64) []byte { + nonce := make([]byte, len(pk.iv)) + binary.BigEndian.PutUint64(nonce[len(nonce)-8:], uint64(pn)) + for i := range pk.iv { + nonce[i] ^= pk.iv[i] + } + return nonce +} + +func newProtectionKey(suite uint16, secret []byte, v uint32) (*ProtectionKey, error) { + switch suite { + case tls.TLS_AES_128_GCM_SHA256: + key := hkdfExpandLabel(crypto.SHA256.New, secret, keyLabel(v), nil, 16) + c, err := aes.NewCipher(key) + if err != nil { + panic(err) + } + aead, err := cipher.NewGCM(c) + if err != nil { + panic(err) + } + iv := hkdfExpandLabel(crypto.SHA256.New, secret, ivLabel(v), nil, aead.NonceSize()) + hpKey := hkdfExpandLabel(crypto.SHA256.New, secret, headerProtectionLabel(v), nil, 16) + hp, err := aes.NewCipher(hpKey) + if err != nil { + panic(err) + } + k := &ProtectionKey{} + k.aead = aead + // https://datatracker.ietf.org/doc/html/draft-ietf-quic-tls-32#name-aes-based-header-protection + k.headerProtection = func(sample []byte) []byte { + mask := make([]byte, hp.BlockSize()) + hp.Encrypt(mask, sample) + return mask + } + k.iv = iv + return k, nil + case tls.TLS_CHACHA20_POLY1305_SHA256: + key := hkdfExpandLabel(crypto.SHA256.New, secret, keyLabel(v), nil, chacha20poly1305.KeySize) + aead, err := chacha20poly1305.New(key) + if err != nil { + return nil, err + } + iv := hkdfExpandLabel(crypto.SHA256.New, secret, ivLabel(v), nil, aead.NonceSize()) + hpKey := hkdfExpandLabel(sha256.New, secret, headerProtectionLabel(v), nil, chacha20.KeySize) + k := &ProtectionKey{} + k.aead = aead + // https://datatracker.ietf.org/doc/html/draft-ietf-quic-tls-32#name-chacha20-based-header-prote + k.headerProtection = func(sample []byte) []byte { + nonce := sample[4:16] + c, err := chacha20.NewUnauthenticatedCipher(hpKey, nonce) + if err != nil { + panic(err) + } + c.SetCounter(binary.LittleEndian.Uint32(sample[:4])) + mask := make([]byte, 5) + c.XORKeyStream(mask, mask) + return mask + } + k.iv = iv + return k, nil + } + return nil, errors.New("not supported cipher suite") +} + +// decodePacketNumber decode the packet number after header protection removed. +// +// See: https://datatracker.ietf.org/doc/html/draft-ietf-quic-transport-32#section-appendix.a +func decodePacketNumber(largest, truncated int64, nbits uint8) int64 { + expected := largest + 1 + win := int64(1 << (nbits * 8)) + hwin := win / 2 + mask := win - 1 + candidate := (expected &^ mask) | truncated + switch { + case candidate <= expected-hwin && candidate < (1<<62)-win: + return candidate + win + case candidate > expected+hwin && candidate >= win: + return candidate - win + } + return candidate +} + +// Copied from crypto/tls/key_schedule.go. +func hkdfExpandLabel(hash func() hash.Hash, secret []byte, label string, context []byte, length int) []byte { + var hkdfLabel cryptobyte.Builder + hkdfLabel.AddUint16(uint16(length)) + hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes([]byte("tls13 ")) + b.AddBytes([]byte(label)) + }) + hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(context) + }) + out := make([]byte, length) + n, err := hkdf.Expand(hash, secret, hkdfLabel.BytesOrPanic()).Read(out) + if err != nil || n != length { + panic("quic: HKDF-Expand-Label invocation failed unexpectedly") + } + return out +} diff --git a/extras/sniff/internal/quic/packet_protector_test.go b/extras/sniff/internal/quic/packet_protector_test.go new file mode 100644 index 0000000..bc355d2 --- /dev/null +++ b/extras/sniff/internal/quic/packet_protector_test.go @@ -0,0 +1,94 @@ +package quic + +import ( + "bytes" + "crypto" + "crypto/tls" + "encoding/hex" + "strings" + "testing" + "unicode" + + "golang.org/x/crypto/hkdf" +) + +func TestInitialPacketProtector_UnProtect(t *testing.T) { + // https://datatracker.ietf.org/doc/html/draft-ietf-quic-tls-32#name-server-initial + protect := mustHexDecodeString(` + c7ff0000200008f067a5502a4262b500 4075fb12ff07823a5d24534d906ce4c7 + 6782a2167e3479c0f7f6395dc2c91676 302fe6d70bb7cbeb117b4ddb7d173498 + 44fd61dae200b8338e1b932976b61d91 e64a02e9e0ee72e3a6f63aba4ceeeec5 + be2f24f2d86027572943533846caa13e 6f163fb257473d0eda5047360fd4a47e + fd8142fafc0f76 + `) + unProtect := mustHexDecodeString(` + 02000000000600405a020000560303ee fce7f7b37ba1d1632e96677825ddf739 + 88cfc79825df566dc5430b9a045a1200 130100002e00330024001d00209d3c94 + 0d89690b84d08a60993c144eca684d10 81287c834d5311bcf32bb9da1a002b00 + 020304 + `) + + connID := mustHexDecodeString(`8394c8f03e515708`) + + packet := append([]byte{}, protect...) + hdr, offset, err := ParseInitialHeader(packet) + if err != nil { + t.Fatal(err) + } + + initialSecret := hkdf.Extract(crypto.SHA256.New, connID, getSalt(hdr.Version)) + serverSecret := hkdfExpandLabel(crypto.SHA256.New, initialSecret, "server in", []byte{}, crypto.SHA256.Size()) + key, err := NewInitialProtectionKey(serverSecret, hdr.Version) + if err != nil { + t.Fatal(err) + } + pp := NewPacketProtector(key) + got, err := pp.UnProtect(protect, offset, 1) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(got, unProtect) { + t.Error("UnProtect returns wrong result") + } +} + +func TestPacketProtectorShortHeader_UnProtect(t *testing.T) { + // https://datatracker.ietf.org/doc/html/draft-ietf-quic-tls-32#name-chacha20-poly1305-short-hea + protect := mustHexDecodeString(`4cfe4189655e5cd55c41f69080575d7999c25a5bfb`) + unProtect := mustHexDecodeString(`01`) + hdr := mustHexDecodeString(`4200bff4`) + + secret := mustHexDecodeString(`9ac312a7f877468ebe69422748ad00a1 5443f18203a07d6060f688f30f21632b`) + k, err := NewProtectionKey(tls.TLS_CHACHA20_POLY1305_SHA256, secret, V1) + if err != nil { + t.Fatal(err) + } + + pnLen := int(hdr[0]&0x03) + 1 + offset := len(hdr) - pnLen + pp := NewPacketProtector(k) + got, err := pp.UnProtect(protect, int64(offset), 654360564) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(got, unProtect) { + t.Error("UnProtect returns wrong result") + } +} + +func mustHexDecodeString(s string) []byte { + b, err := hex.DecodeString(normalizeHex(s)) + if err != nil { + panic(err) + } + return b +} + +func normalizeHex(s string) string { + return strings.Map(func(c rune) rune { + if unicode.IsSpace(c) { + return -1 + } + return c + }, s) +} diff --git a/extras/sniff/internal/quic/payload.go b/extras/sniff/internal/quic/payload.go new file mode 100644 index 0000000..453b714 --- /dev/null +++ b/extras/sniff/internal/quic/payload.go @@ -0,0 +1,122 @@ +package quic + +import ( + "bytes" + "crypto" + "errors" + "fmt" + "io" + "sort" + + "github.com/apernet/quic-go/quicvarint" + "golang.org/x/crypto/hkdf" +) + +func ReadCryptoPayload(packet []byte) ([]byte, error) { + hdr, offset, err := ParseInitialHeader(packet) + if err != nil { + return nil, err + } + // Some sanity checks + if hdr.Version != V1 && hdr.Version != V2 { + return nil, fmt.Errorf("unsupported version: %x", hdr.Version) + } + if offset == 0 || hdr.Length == 0 { + return nil, errors.New("invalid packet") + } + + initialSecret := hkdf.Extract(crypto.SHA256.New, hdr.DestConnectionID, getSalt(hdr.Version)) + clientSecret := hkdfExpandLabel(crypto.SHA256.New, initialSecret, "client in", []byte{}, crypto.SHA256.Size()) + key, err := NewInitialProtectionKey(clientSecret, hdr.Version) + if err != nil { + return nil, fmt.Errorf("NewInitialProtectionKey: %w", err) + } + pp := NewPacketProtector(key) + // https://datatracker.ietf.org/doc/html/draft-ietf-quic-tls-32#name-client-initial + // + // "The unprotected header includes the connection ID and a 4-byte packet number encoding for a packet number of 2" + if int64(len(packet)) < offset+hdr.Length { + return nil, fmt.Errorf("packet is too short: %d < %d", len(packet), offset+hdr.Length) + } + unProtectedPayload, err := pp.UnProtect(packet[:offset+hdr.Length], offset, 2) + if err != nil { + return nil, err + } + frs, err := extractCryptoFrames(bytes.NewReader(unProtectedPayload)) + if err != nil { + return nil, err + } + data := assembleCryptoFrames(frs) + if data == nil { + return nil, errors.New("unable to assemble crypto frames") + } + return data, nil +} + +const ( + paddingFrameType = 0x00 + pingFrameType = 0x01 + cryptoFrameType = 0x06 +) + +type cryptoFrame struct { + Offset int64 + Data []byte +} + +func extractCryptoFrames(r *bytes.Reader) ([]cryptoFrame, error) { + var frames []cryptoFrame + for r.Len() > 0 { + typ, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + if typ == paddingFrameType || typ == pingFrameType { + continue + } + if typ != cryptoFrameType { + return nil, fmt.Errorf("encountered unexpected frame type: %d", typ) + } + var frame cryptoFrame + offset, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + frame.Offset = int64(offset) + dataLen, err := quicvarint.Read(r) + if err != nil { + return nil, err + } + frame.Data = make([]byte, dataLen) + if _, err := io.ReadFull(r, frame.Data); err != nil { + return nil, err + } + frames = append(frames, frame) + } + return frames, nil +} + +// assembleCryptoFrames assembles multiple crypto frames into a single slice (if possible). +// It returns an error if the frames cannot be assembled. This can happen if the frames are not contiguous. +func assembleCryptoFrames(frames []cryptoFrame) []byte { + if len(frames) == 0 { + return nil + } + if len(frames) == 1 { + return frames[0].Data + } + // sort the frames by offset + sort.Slice(frames, func(i, j int) bool { return frames[i].Offset < frames[j].Offset }) + // check if the frames are contiguous + for i := 1; i < len(frames); i++ { + if frames[i].Offset != frames[i-1].Offset+int64(len(frames[i-1].Data)) { + return nil + } + } + // concatenate the frames + data := make([]byte, frames[len(frames)-1].Offset+int64(len(frames[len(frames)-1].Data))) + for _, frame := range frames { + copy(data[frame.Offset:], frame.Data) + } + return data +} diff --git a/extras/sniff/internal/quic/quic.go b/extras/sniff/internal/quic/quic.go new file mode 100644 index 0000000..1cfa103 --- /dev/null +++ b/extras/sniff/internal/quic/quic.go @@ -0,0 +1,59 @@ +package quic + +const ( + V1 uint32 = 0x1 + V2 uint32 = 0x6b3343cf + + hkdfLabelKeyV1 = "quic key" + hkdfLabelKeyV2 = "quicv2 key" + hkdfLabelIVV1 = "quic iv" + hkdfLabelIVV2 = "quicv2 iv" + hkdfLabelHPV1 = "quic hp" + hkdfLabelHPV2 = "quicv2 hp" +) + +var ( + quicSaltOld = []byte{0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99} + // https://www.rfc-editor.org/rfc/rfc9001.html#name-initial-secrets + quicSaltV1 = []byte{0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a} + // https://www.ietf.org/archive/id/draft-ietf-quic-v2-10.html#name-initial-salt-2 + quicSaltV2 = []byte{0x0d, 0xed, 0xe3, 0xde, 0xf7, 0x00, 0xa6, 0xdb, 0x81, 0x93, 0x81, 0xbe, 0x6e, 0x26, 0x9d, 0xcb, 0xf9, 0xbd, 0x2e, 0xd9} +) + +// isLongHeader reports whether b is the first byte of a long header packet. +func isLongHeader(b byte) bool { + return b&0x80 > 0 +} + +func getSalt(v uint32) []byte { + switch v { + case V1: + return quicSaltV1 + case V2: + return quicSaltV2 + } + return quicSaltOld +} + +func keyLabel(v uint32) string { + kl := hkdfLabelKeyV1 + if v == V2 { + kl = hkdfLabelKeyV2 + } + return kl +} + +func ivLabel(v uint32) string { + ivl := hkdfLabelIVV1 + if v == V2 { + ivl = hkdfLabelIVV2 + } + return ivl +} + +func headerProtectionLabel(v uint32) string { + if v == V2 { + return hkdfLabelHPV2 + } + return hkdfLabelHPV1 +} diff --git a/extras/sniff/sniff.go b/extras/sniff/sniff.go index 61f80c2..79e35f6 100644 --- a/extras/sniff/sniff.go +++ b/extras/sniff/sniff.go @@ -2,16 +2,17 @@ package sniff import ( "bufio" - "context" - "crypto/tls" "io" "net" "net/http" "strings" "time" - "github.com/apernet/hysteria/core/v2/server" "github.com/apernet/quic-go" + utls "github.com/refraction-networking/utls" + + "github.com/apernet/hysteria/core/v2/server" + quicInternal "github.com/apernet/hysteria/extras/v2/sniff/internal/quic" ) var _ server.RequestHook = (*Sniffer)(nil) @@ -57,7 +58,7 @@ func (h *Sniffer) isTLS(buf []byte) bool { func (h *Sniffer) Check(isUDP bool, reqAddr string) bool { // @ means it's internal (e.g. speed test) - return !strings.HasPrefix(reqAddr, "@") && !isUDP && (h.RewriteDomain || !h.isDomain(reqAddr)) + return !strings.HasPrefix(reqAddr, "@") && (h.RewriteDomain || !h.isDomain(reqAddr)) } func (h *Sniffer) TCP(stream quic.Stream, reqAddr *string) ([]byte, error) { @@ -75,8 +76,9 @@ func (h *Sniffer) TCP(stream quic.Stream, reqAddr *string) ([]byte, error) { return pre[:n], nil } if h.isHTTP(pre) { - fConn := &fakeConn{Stream: stream, Pre: pre} - req, _ := http.ReadRequest(bufio.NewReader(fConn)) + // HTTP + tr := &teeReader{Stream: stream, Pre: pre} + req, _ := http.ReadRequest(bufio.NewReader(tr)) if req != nil && req.Host != "" { _, port, err := net.SplitHostPort(*reqAddr) if err != nil { @@ -84,16 +86,24 @@ func (h *Sniffer) TCP(stream quic.Stream, reqAddr *string) ([]byte, error) { } *reqAddr = net.JoinHostPort(req.Host, port) } - return fConn.Buffer, nil + return tr.Buffer(), nil } else if h.isTLS(pre) { - fConn := &fakeConn{Stream: stream, Pre: pre} - var clientHello *tls.ClientHelloInfo - _ = tls.Server(fConn, &tls.Config{ - GetConfigForClient: func(info *tls.ClientHelloInfo) (*tls.Config, error) { - clientHello = info - return nil, nil - }, - }).HandshakeContext(context.Background()) + // TLS + // Need to read 2 more bytes (content length) + pre = append(pre, make([]byte, 2)...) + n, err = io.ReadFull(stream, pre[3:]) + if err != nil { + // Not enough within the timeout, just return what we have + return pre[:3+n], nil + } + contentLength := int(pre[3])<<8 | int(pre[4]) + pre = append(pre, make([]byte, contentLength)...) + n, err = io.ReadFull(stream, pre[5:]) + if err != nil { + // Not enough within the timeout, just return what we have + return pre[:5+n], nil + } + clientHello := utls.UnmarshalClientHello(pre[5:]) if clientHello != nil && clientHello.ServerName != "" { _, port, err := net.SplitHostPort(*reqAddr) if err != nil { @@ -101,7 +111,7 @@ func (h *Sniffer) TCP(stream quic.Stream, reqAddr *string) ([]byte, error) { } *reqAddr = net.JoinHostPort(clientHello.ServerName, port) } - return fConn.Buffer, nil + return pre, nil } else { // Unrecognized protocol, just return what we have return pre, nil @@ -109,57 +119,43 @@ func (h *Sniffer) TCP(stream quic.Stream, reqAddr *string) ([]byte, error) { } func (h *Sniffer) UDP(data []byte, reqAddr *string) error { + pl, err := quicInternal.ReadCryptoPayload(data) + if err != nil || len(pl) < 4 || pl[0] != 0x01 { + // Unrecognized protocol, incomplete payload or not a client hello + return nil + } + clientHello := utls.UnmarshalClientHello(pl) + if clientHello != nil && clientHello.ServerName != "" { + _, port, err := net.SplitHostPort(*reqAddr) + if err != nil { + return err + } + *reqAddr = net.JoinHostPort(clientHello.ServerName, port) + } return nil } -type fakeConn struct { +type teeReader struct { Stream quic.Stream Pre []byte - Buffer []byte + + buf []byte } -func (c *fakeConn) Read(b []byte) (n int, err error) { +func (c *teeReader) Read(b []byte) (n int, err error) { if len(c.Pre) > 0 { n = copy(b, c.Pre) c.Pre = c.Pre[n:] - c.Buffer = append(c.Buffer, b[:n]...) + c.buf = append(c.buf, b[:n]...) return n, nil } n, err = c.Stream.Read(b) if n > 0 { - c.Buffer = append(c.Buffer, b[:n]...) + c.buf = append(c.buf, b[:n]...) } return n, err } -func (c *fakeConn) Write(b []byte) (n int, err error) { - // Do not write anything, pretend it's successful - return len(b), nil -} - -func (c *fakeConn) Close() error { - // Do not close the stream - return nil -} - -func (c *fakeConn) LocalAddr() net.Addr { - // Doesn't matter - return nil -} - -func (c *fakeConn) RemoteAddr() net.Addr { - // Doesn't matter - return nil -} - -func (c *fakeConn) SetDeadline(t time.Time) error { - return c.Stream.SetReadDeadline(t) -} - -func (c *fakeConn) SetReadDeadline(t time.Time) error { - return c.Stream.SetReadDeadline(t) -} - -func (c *fakeConn) SetWriteDeadline(t time.Time) error { - return c.Stream.SetWriteDeadline(t) +func (c *teeReader) Buffer() []byte { + return append(c.Pre, c.buf...) } diff --git a/go.work.sum b/go.work.sum index 4370b0e..f0551e7 100644 --- a/go.work.sum +++ b/go.work.sum @@ -41,6 +41,8 @@ github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625 h1:ckJgFhFWywOx+ github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 h1:D21IyuvjDCshj1/qq+pCNd3VZOAEI9jy6Bi131YlXgI= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/bwesterb/go-ristretto v1.2.3 h1:1w53tCkGhCQ5djbat3+MH0BAQ5Kfgbt56UZQ/JMzngw= +github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= @@ -309,6 +311,8 @@ golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= From 3412368d206dc93ed5130c2a7ede334d352ddd4e Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 29 Jun 2024 16:27:57 -0700 Subject: [PATCH 102/161] feat: app sniff options --- app/cmd/server.go | 23 ++++++++++++++++++----- app/cmd/server_test.go | 5 +++++ app/cmd/server_test.yaml | 5 +++++ extras/sniff/sniff.go | 11 ++++++++++- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/app/cmd/server.go b/app/cmd/server.go index 1a1ee6d..bca4c80 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -66,6 +66,7 @@ type serverConfig struct { UDPIdleTimeout time.Duration `mapstructure:"udpIdleTimeout"` Auth serverConfigAuth `mapstructure:"auth"` Resolver serverConfigResolver `mapstructure:"resolver"` + Sniff serverConfigSniff `mapstructure:"sniff"` ACL serverConfigACL `mapstructure:"acl"` Outbounds []serverConfigOutboundEntry `mapstructure:"outbounds"` TrafficStats serverConfigTrafficStats `mapstructure:"trafficStats"` @@ -181,6 +182,12 @@ type serverConfigResolver struct { HTTPS serverConfigResolverHTTPS `mapstructure:"https"` } +type serverConfigSniff struct { + Enable bool `mapstructure:"enable"` + Timeout time.Duration `mapstructure:"timeout"` + RewriteDomain bool `mapstructure:"rewriteDomain"` +} + type serverConfigACL struct { File string `mapstructure:"file"` Inline []string `mapstructure:"inline"` @@ -543,6 +550,16 @@ func serverConfigOutboundHTTPToOutbound(c serverConfigOutboundHTTP) (outbounds.P return outbounds.NewHTTPOutbound(c.URL, c.Insecure) } +func (c *serverConfig) fillRequestHook(hyConfig *server.Config) error { + if c.Sniff.Enable { + hyConfig.RequestHook = &sniff.Sniffer{ + Timeout: c.Sniff.Timeout, + RewriteDomain: c.Sniff.RewriteDomain, + } + } + return nil +} + func (c *serverConfig) fillOutboundConfig(hyConfig *server.Config) error { // Resolver, ACL, actual outbound are all implemented through the Outbound interface. // Depending on the config, we build a chain like this: @@ -823,6 +840,7 @@ func (c *serverConfig) Config() (*server.Config, error) { c.fillConn, c.fillTLSConfig, c.fillQUICConfig, + c.fillRequestHook, c.fillOutboundConfig, c.fillBandwidthConfig, c.fillIgnoreClientBandwidth, @@ -857,11 +875,6 @@ func runServer(cmd *cobra.Command, args []string) { logger.Fatal("failed to load server config", zap.Error(err)) } - hyConfig.RequestHook = &sniff.Sniffer{ - Timeout: 4 * time.Second, - RewriteDomain: false, - } - s, err := server.NewServer(hyConfig) if err != nil { logger.Fatal("failed to initialize server", zap.Error(err)) diff --git a/app/cmd/server_test.go b/app/cmd/server_test.go index d81a61a..bd46681 100644 --- a/app/cmd/server_test.go +++ b/app/cmd/server_test.go @@ -111,6 +111,11 @@ func TestServerConfig(t *testing.T) { Insecure: true, }, }, + Sniff: serverConfigSniff{ + Enable: true, + Timeout: 1 * time.Second, + RewriteDomain: true, + }, ACL: serverConfigACL{ File: "chnroute.txt", Inline: []string{ diff --git a/app/cmd/server_test.yaml b/app/cmd/server_test.yaml index 86a2dcf..343b0a9 100644 --- a/app/cmd/server_test.yaml +++ b/app/cmd/server_test.yaml @@ -83,6 +83,11 @@ resolver: sni: real.stuff.net insecure: true +sniff: + enable: true + timeout: 1s + rewriteDomain: true + acl: file: chnroute.txt inline: diff --git a/extras/sniff/sniff.go b/extras/sniff/sniff.go index 79e35f6..68b3fbc 100644 --- a/extras/sniff/sniff.go +++ b/extras/sniff/sniff.go @@ -15,6 +15,10 @@ import ( quicInternal "github.com/apernet/hysteria/extras/v2/sniff/internal/quic" ) +const ( + sniffDefaultTimeout = 4 * time.Second +) + var _ server.RequestHook = (*Sniffer)(nil) // Sniffer is a server core RequestHook that performs packet inspection and possibly @@ -62,7 +66,12 @@ func (h *Sniffer) Check(isUDP bool, reqAddr string) bool { } func (h *Sniffer) TCP(stream quic.Stream, reqAddr *string) ([]byte, error) { - err := stream.SetReadDeadline(time.Now().Add(h.Timeout)) + var err error + if h.Timeout == 0 { + err = stream.SetReadDeadline(time.Now().Add(sniffDefaultTimeout)) + } else { + err = stream.SetReadDeadline(time.Now().Add(h.Timeout)) + } if err != nil { return nil, err } From 7b4def4c35b549555cfb05b141c97dca7574dfec Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 29 Jun 2024 17:42:30 -0700 Subject: [PATCH 103/161] chore: add sniff test cases --- extras/sniff/.mockery.yaml | 12 + extras/sniff/mock_Stream.go | 492 ++++++++++++++++++++++++++++++++++++ extras/sniff/sniff_test.go | 126 +++++++++ 3 files changed, 630 insertions(+) create mode 100644 extras/sniff/.mockery.yaml create mode 100644 extras/sniff/mock_Stream.go create mode 100644 extras/sniff/sniff_test.go diff --git a/extras/sniff/.mockery.yaml b/extras/sniff/.mockery.yaml new file mode 100644 index 0000000..c866d1d --- /dev/null +++ b/extras/sniff/.mockery.yaml @@ -0,0 +1,12 @@ +with-expecter: true +dir: . +outpkg: sniff +packages: + github.com/apernet/quic-go: + interfaces: + Stream: + config: + mockname: mockStream + replace-type: # internal package alias dirty fix + - github.com/apernet/quic-go/internal/protocol=github.com/apernet/quic-go + - github.com/apernet/quic-go/internal/qerr=github.com/apernet/quic-go diff --git a/extras/sniff/mock_Stream.go b/extras/sniff/mock_Stream.go new file mode 100644 index 0000000..8b21e95 --- /dev/null +++ b/extras/sniff/mock_Stream.go @@ -0,0 +1,492 @@ +// Code generated by mockery v2.43.0. DO NOT EDIT. + +package sniff + +import ( + context "context" + + qerr "github.com/apernet/quic-go" + mock "github.com/stretchr/testify/mock" + + time "time" +) + +// mockStream is an autogenerated mock type for the Stream type +type mockStream struct { + mock.Mock +} + +type mockStream_Expecter struct { + mock *mock.Mock +} + +func (_m *mockStream) EXPECT() *mockStream_Expecter { + return &mockStream_Expecter{mock: &_m.Mock} +} + +// CancelRead provides a mock function with given fields: _a0 +func (_m *mockStream) CancelRead(_a0 qerr.StreamErrorCode) { + _m.Called(_a0) +} + +// mockStream_CancelRead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CancelRead' +type mockStream_CancelRead_Call struct { + *mock.Call +} + +// CancelRead is a helper method to define mock.On call +// - _a0 qerr.StreamErrorCode +func (_e *mockStream_Expecter) CancelRead(_a0 interface{}) *mockStream_CancelRead_Call { + return &mockStream_CancelRead_Call{Call: _e.mock.On("CancelRead", _a0)} +} + +func (_c *mockStream_CancelRead_Call) Run(run func(_a0 qerr.StreamErrorCode)) *mockStream_CancelRead_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(qerr.StreamErrorCode)) + }) + return _c +} + +func (_c *mockStream_CancelRead_Call) Return() *mockStream_CancelRead_Call { + _c.Call.Return() + return _c +} + +func (_c *mockStream_CancelRead_Call) RunAndReturn(run func(qerr.StreamErrorCode)) *mockStream_CancelRead_Call { + _c.Call.Return(run) + return _c +} + +// CancelWrite provides a mock function with given fields: _a0 +func (_m *mockStream) CancelWrite(_a0 qerr.StreamErrorCode) { + _m.Called(_a0) +} + +// mockStream_CancelWrite_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CancelWrite' +type mockStream_CancelWrite_Call struct { + *mock.Call +} + +// CancelWrite is a helper method to define mock.On call +// - _a0 qerr.StreamErrorCode +func (_e *mockStream_Expecter) CancelWrite(_a0 interface{}) *mockStream_CancelWrite_Call { + return &mockStream_CancelWrite_Call{Call: _e.mock.On("CancelWrite", _a0)} +} + +func (_c *mockStream_CancelWrite_Call) Run(run func(_a0 qerr.StreamErrorCode)) *mockStream_CancelWrite_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(qerr.StreamErrorCode)) + }) + return _c +} + +func (_c *mockStream_CancelWrite_Call) Return() *mockStream_CancelWrite_Call { + _c.Call.Return() + return _c +} + +func (_c *mockStream_CancelWrite_Call) RunAndReturn(run func(qerr.StreamErrorCode)) *mockStream_CancelWrite_Call { + _c.Call.Return(run) + return _c +} + +// Close provides a mock function with given fields: +func (_m *mockStream) Close() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Close") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// mockStream_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' +type mockStream_Close_Call struct { + *mock.Call +} + +// Close is a helper method to define mock.On call +func (_e *mockStream_Expecter) Close() *mockStream_Close_Call { + return &mockStream_Close_Call{Call: _e.mock.On("Close")} +} + +func (_c *mockStream_Close_Call) Run(run func()) *mockStream_Close_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockStream_Close_Call) Return(_a0 error) *mockStream_Close_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockStream_Close_Call) RunAndReturn(run func() error) *mockStream_Close_Call { + _c.Call.Return(run) + return _c +} + +// Context provides a mock function with given fields: +func (_m *mockStream) Context() context.Context { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Context") + } + + var r0 context.Context + if rf, ok := ret.Get(0).(func() context.Context); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(context.Context) + } + } + + return r0 +} + +// mockStream_Context_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Context' +type mockStream_Context_Call struct { + *mock.Call +} + +// Context is a helper method to define mock.On call +func (_e *mockStream_Expecter) Context() *mockStream_Context_Call { + return &mockStream_Context_Call{Call: _e.mock.On("Context")} +} + +func (_c *mockStream_Context_Call) Run(run func()) *mockStream_Context_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockStream_Context_Call) Return(_a0 context.Context) *mockStream_Context_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockStream_Context_Call) RunAndReturn(run func() context.Context) *mockStream_Context_Call { + _c.Call.Return(run) + return _c +} + +// Read provides a mock function with given fields: p +func (_m *mockStream) Read(p []byte) (int, error) { + ret := _m.Called(p) + + if len(ret) == 0 { + panic("no return value specified for Read") + } + + var r0 int + var r1 error + if rf, ok := ret.Get(0).(func([]byte) (int, error)); ok { + return rf(p) + } + if rf, ok := ret.Get(0).(func([]byte) int); ok { + r0 = rf(p) + } else { + r0 = ret.Get(0).(int) + } + + if rf, ok := ret.Get(1).(func([]byte) error); ok { + r1 = rf(p) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockStream_Read_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Read' +type mockStream_Read_Call struct { + *mock.Call +} + +// Read is a helper method to define mock.On call +// - p []byte +func (_e *mockStream_Expecter) Read(p interface{}) *mockStream_Read_Call { + return &mockStream_Read_Call{Call: _e.mock.On("Read", p)} +} + +func (_c *mockStream_Read_Call) Run(run func(p []byte)) *mockStream_Read_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].([]byte)) + }) + return _c +} + +func (_c *mockStream_Read_Call) Return(n int, err error) *mockStream_Read_Call { + _c.Call.Return(n, err) + return _c +} + +func (_c *mockStream_Read_Call) RunAndReturn(run func([]byte) (int, error)) *mockStream_Read_Call { + _c.Call.Return(run) + return _c +} + +// SetDeadline provides a mock function with given fields: t +func (_m *mockStream) SetDeadline(t time.Time) error { + ret := _m.Called(t) + + if len(ret) == 0 { + panic("no return value specified for SetDeadline") + } + + var r0 error + if rf, ok := ret.Get(0).(func(time.Time) error); ok { + r0 = rf(t) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// mockStream_SetDeadline_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetDeadline' +type mockStream_SetDeadline_Call struct { + *mock.Call +} + +// SetDeadline is a helper method to define mock.On call +// - t time.Time +func (_e *mockStream_Expecter) SetDeadline(t interface{}) *mockStream_SetDeadline_Call { + return &mockStream_SetDeadline_Call{Call: _e.mock.On("SetDeadline", t)} +} + +func (_c *mockStream_SetDeadline_Call) Run(run func(t time.Time)) *mockStream_SetDeadline_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(time.Time)) + }) + return _c +} + +func (_c *mockStream_SetDeadline_Call) Return(_a0 error) *mockStream_SetDeadline_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockStream_SetDeadline_Call) RunAndReturn(run func(time.Time) error) *mockStream_SetDeadline_Call { + _c.Call.Return(run) + return _c +} + +// SetReadDeadline provides a mock function with given fields: t +func (_m *mockStream) SetReadDeadline(t time.Time) error { + ret := _m.Called(t) + + if len(ret) == 0 { + panic("no return value specified for SetReadDeadline") + } + + var r0 error + if rf, ok := ret.Get(0).(func(time.Time) error); ok { + r0 = rf(t) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// mockStream_SetReadDeadline_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetReadDeadline' +type mockStream_SetReadDeadline_Call struct { + *mock.Call +} + +// SetReadDeadline is a helper method to define mock.On call +// - t time.Time +func (_e *mockStream_Expecter) SetReadDeadline(t interface{}) *mockStream_SetReadDeadline_Call { + return &mockStream_SetReadDeadline_Call{Call: _e.mock.On("SetReadDeadline", t)} +} + +func (_c *mockStream_SetReadDeadline_Call) Run(run func(t time.Time)) *mockStream_SetReadDeadline_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(time.Time)) + }) + return _c +} + +func (_c *mockStream_SetReadDeadline_Call) Return(_a0 error) *mockStream_SetReadDeadline_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockStream_SetReadDeadline_Call) RunAndReturn(run func(time.Time) error) *mockStream_SetReadDeadline_Call { + _c.Call.Return(run) + return _c +} + +// SetWriteDeadline provides a mock function with given fields: t +func (_m *mockStream) SetWriteDeadline(t time.Time) error { + ret := _m.Called(t) + + if len(ret) == 0 { + panic("no return value specified for SetWriteDeadline") + } + + var r0 error + if rf, ok := ret.Get(0).(func(time.Time) error); ok { + r0 = rf(t) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// mockStream_SetWriteDeadline_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetWriteDeadline' +type mockStream_SetWriteDeadline_Call struct { + *mock.Call +} + +// SetWriteDeadline is a helper method to define mock.On call +// - t time.Time +func (_e *mockStream_Expecter) SetWriteDeadline(t interface{}) *mockStream_SetWriteDeadline_Call { + return &mockStream_SetWriteDeadline_Call{Call: _e.mock.On("SetWriteDeadline", t)} +} + +func (_c *mockStream_SetWriteDeadline_Call) Run(run func(t time.Time)) *mockStream_SetWriteDeadline_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(time.Time)) + }) + return _c +} + +func (_c *mockStream_SetWriteDeadline_Call) Return(_a0 error) *mockStream_SetWriteDeadline_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockStream_SetWriteDeadline_Call) RunAndReturn(run func(time.Time) error) *mockStream_SetWriteDeadline_Call { + _c.Call.Return(run) + return _c +} + +// StreamID provides a mock function with given fields: +func (_m *mockStream) StreamID() qerr.StreamID { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for StreamID") + } + + var r0 qerr.StreamID + if rf, ok := ret.Get(0).(func() qerr.StreamID); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(qerr.StreamID) + } + + return r0 +} + +// mockStream_StreamID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StreamID' +type mockStream_StreamID_Call struct { + *mock.Call +} + +// StreamID is a helper method to define mock.On call +func (_e *mockStream_Expecter) StreamID() *mockStream_StreamID_Call { + return &mockStream_StreamID_Call{Call: _e.mock.On("StreamID")} +} + +func (_c *mockStream_StreamID_Call) Run(run func()) *mockStream_StreamID_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockStream_StreamID_Call) Return(_a0 qerr.StreamID) *mockStream_StreamID_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockStream_StreamID_Call) RunAndReturn(run func() qerr.StreamID) *mockStream_StreamID_Call { + _c.Call.Return(run) + return _c +} + +// Write provides a mock function with given fields: p +func (_m *mockStream) Write(p []byte) (int, error) { + ret := _m.Called(p) + + if len(ret) == 0 { + panic("no return value specified for Write") + } + + var r0 int + var r1 error + if rf, ok := ret.Get(0).(func([]byte) (int, error)); ok { + return rf(p) + } + if rf, ok := ret.Get(0).(func([]byte) int); ok { + r0 = rf(p) + } else { + r0 = ret.Get(0).(int) + } + + if rf, ok := ret.Get(1).(func([]byte) error); ok { + r1 = rf(p) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockStream_Write_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Write' +type mockStream_Write_Call struct { + *mock.Call +} + +// Write is a helper method to define mock.On call +// - p []byte +func (_e *mockStream_Expecter) Write(p interface{}) *mockStream_Write_Call { + return &mockStream_Write_Call{Call: _e.mock.On("Write", p)} +} + +func (_c *mockStream_Write_Call) Run(run func(p []byte)) *mockStream_Write_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].([]byte)) + }) + return _c +} + +func (_c *mockStream_Write_Call) Return(n int, err error) *mockStream_Write_Call { + _c.Call.Return(n, err) + return _c +} + +func (_c *mockStream_Write_Call) RunAndReturn(run func([]byte) (int, error)) *mockStream_Write_Call { + _c.Call.Return(run) + return _c +} + +// newMockStream creates a new instance of mockStream. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockStream(t interface { + mock.TestingT + Cleanup(func()) +}) *mockStream { + mock := &mockStream{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/extras/sniff/sniff_test.go b/extras/sniff/sniff_test.go new file mode 100644 index 0000000..fb86c3b --- /dev/null +++ b/extras/sniff/sniff_test.go @@ -0,0 +1,126 @@ +package sniff + +import ( + "encoding/base64" + "io" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestSnifferTCP(t *testing.T) { + sniffer := &Sniffer{ + Timeout: 1 * time.Second, + RewriteDomain: false, + } + + buf := &[]byte{} + + // Test HTTP + *buf = []byte("POST /hello HTTP/1.1\r\n" + + "Host: example.com\r\n" + + "User-Agent: mamamiya\r\n" + + "Content-Length: 27\r\n" + + "Connection: keep-alive\r\n\r\n" + + "param1=value1¶m2=value2") + index := 0 + stream := &mockStream{} + stream.EXPECT().SetReadDeadline(mock.Anything).Return(nil) + stream.EXPECT().Read(mock.Anything).RunAndReturn(func(bs []byte) (int, error) { + if index < len(*buf) { + n := copy(bs, (*buf)[index:]) + index += n + return n, nil + } else { + return 0, io.EOF + } + }) + + // Rewrite IP to domain + reqAddr := "111.111.111.111:80" + assert.True(t, sniffer.Check(false, reqAddr)) + putback, err := sniffer.TCP(stream, &reqAddr) + assert.NoError(t, err) + assert.Equal(t, *buf, putback) + assert.Equal(t, "example.com:80", reqAddr) + + // Do not rewrite if it's already a domain + index = 0 + reqAddr = "gulag.cc:443" + assert.False(t, sniffer.Check(false, reqAddr)) + + // Turn on rewrite and now it should rewrite + sniffer.RewriteDomain = true + assert.True(t, sniffer.Check(false, reqAddr)) + + // Test TLS + *buf, err = base64.StdEncoding.DecodeString("FgMBARcBAAETAwPJL2jlt1OAo+Rslkjv/aqKiTthKMaCKg2Gvd+uALDbDCDdY+UIk8ouadEB9fC3j52Y1i7SJZqGIgBRIS6kKieYrAAoEwITAcAswCvAMMAvwCTAI8AowCfACsAJwBTAEwCdAJwAPQA8ADUALwEAAKIAAAAOAAwAAAlpcGluZm8uaW8ABQAFAQAAAAAAKwAJCAMEAwMDAgMBAA0AGgAYCAQIBQgGBAEFAQIBBAMFAwIDAgIGAQYDACMAAAAKAAgABgAdABcAGAAQAAsACQhodHRwLzEuMQAzACYAJAAdACBguQbqNJNyamYxYcrBFpBP7pWv5TgZsP9gwGtMYNKVBQAxAAAAFwAA/wEAAQAALQACAQE=") + assert.NoError(t, err) + index = 0 + reqAddr = "222.222.222.222:443" + assert.True(t, sniffer.Check(false, reqAddr)) + putback, err = sniffer.TCP(stream, &reqAddr) + assert.NoError(t, err) + assert.Equal(t, *buf, putback) + assert.Equal(t, "ipinfo.io:443", reqAddr) + + // Test unrecognized 1 + *buf = []byte("Wait It's All Ohio? Always Has Been.") + index = 0 + reqAddr = "123.123.123.123:123" + assert.True(t, sniffer.Check(false, reqAddr)) + putback, err = sniffer.TCP(stream, &reqAddr) + assert.NoError(t, err) + assert.Equal(t, *buf, putback) + assert.Equal(t, "123.123.123.123:123", reqAddr) + + // Test unrecognized 2 + *buf = []byte("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a") + index = 0 + reqAddr = "45.45.45.45:45" + assert.True(t, sniffer.Check(false, reqAddr)) + putback, err = sniffer.TCP(stream, &reqAddr) + assert.NoError(t, err) + assert.Equal(t, []byte("\x01\x02\x03"), putback) + assert.Equal(t, "45.45.45.45:45", reqAddr) + + // Test timeout + blockStream := &mockStream{} + blockStream.EXPECT().SetReadDeadline(mock.Anything).Return(nil) + blockStream.EXPECT().Read(mock.Anything).RunAndReturn(func(bs []byte) (int, error) { + time.Sleep(2 * time.Second) + return 0, io.EOF + }) + reqAddr = "66.66.66.66:66" + assert.True(t, sniffer.Check(false, reqAddr)) + putback, err = sniffer.TCP(blockStream, &reqAddr) + assert.NoError(t, err) + assert.Equal(t, []byte{}, putback) + assert.Equal(t, "66.66.66.66:66", reqAddr) +} + +func TestSnifferUDP(t *testing.T) { + sniffer := &Sniffer{ + Timeout: 1 * time.Second, + RewriteDomain: false, + } + + // Test QUIC + reqAddr := "2.3.4.5:443" + assert.True(t, sniffer.Check(true, reqAddr)) + pkt, err := base64.StdEncoding.DecodeString("ygAAAAEIwugWgPS7ulYAAES8hY891uwgGE9GG4CPOLd+nsDe28raso24lCSFmlFwYQG1uF39ikbL13/R9ZTghYmTl+jEbr6F9TxxRiOgpTmKRmh6aKZiIiVfy5pVRckovaI8lq0WRoW9xoFNTyYtQP8TVJ3bLCK+zUqpquEQSyWf7CE43ywayyMpE9UlIoPXFWCoopXLM1SvzdQ+17P51N9KR7m4emti4DWWTBLMQOvrwd2HEEkbiZdRO1wf6ZXJlIat5dN0R/6uod60OFPO+u+awvq67MoMReC7+5I/xWI+xx6o4JpnZNn6YPG8Gqi8hS6doNcAAdtD8h5eMLuHCCgkpX3QVjjfWtcOhtw9xKjU43HhUPwzUTv+JDLgwuTQCTmlfYlb3B+pk4b2I9si0tJ0SBuYaZ2VQPtZbj2hpGXw3gn11pbN8xsbKkQL50+Scd4dGJxWQlGaJHeaU5WOCkxLXc635z8m5XO/CBHVYPGp4pfwfwNUgbe5WF+3MaUIlDB8dMfsnrO0BmZPo379jVx0SFLTAiS8wAdHib1WNEY8qKYnTWuiyxYg1GZEhJt0nXmI+8f0eJq42DgHBWC+Rf5rRBr/Sf25o3mFAmTUaul0Woo9/CIrpT73B63N91xd9A77i4ru995YG8l9Hen+eLtpDU9Q9376nwMDYBzeYG9U/Rn0Urbm6q4hmAgV/xlNJ2rAyDS+yLnwqD6I0PRy8bZJEttcidb/SkOyrpgMiAzWeT+SO+c/k+Y8H0UTRa05faZUrhuUaym9wAcaIVRA6nFI+fejfjVp+7afFv+kWn3vCqQEij+CRHuxkltrixZMD2rfYj6NUW7TTYBtPRtuV/V0ZIDjRR26vr4K+0D84+l3c0mA/l6nmpP5kkco3nmpdjtQN6sGXL7+5o0nnsftX5d6/n5mLyEpP+AEDl1zk3iqkS62RsITwql6DMMoGbSDdUpMclCIeM0vlo3CkxGMO7QA9ruVeNddkL3EWMivl+uxO43sXEEqYQHVl4N75y63t05GOf7/gm9Kb/BJ8MpG9ViEkVYaskQCzi3D8bVpzo8FfTj8te8B6c3ikc/cm7r8k0ZcZpr+YiLGDYq+0ilHxpqJfmq8dPkSvxdzLcUSvy7+LMQ/TTobRSF7L4JhtDKck0+00vl9H35Tkh9N+MsVtpKdWyoqZ4XaK2Nx1M6AieczXpdFc0y7lYPoUfF4IeW8WzeVUclol5ElYjkyFz/lDOGAe1bF2g5AYaGWCPiGleVZknNdD5ihB8W8Mfkt1pEwq2S97AHrppqkf/VoIfZzeqH8wUFw8fDDrZIpnoa0rW7HfwIQaqJhPCyB9Z6TVbV4x9UWmaHfVAcinCK/7o10dtaj3rvEqcUC/iPceGq3Tqv/p9GGNJ+Ci2JBjXqNxYr893Llk75VdPD9pM6y1SM0P80oXNy32VMtafkFFST8GpvvqWcxUJ93kzaY8RmU1g3XFOImSU2utU6+FUQ2Pn5uLwcfT2cTYfTpPGh+WXjSbZ6trqdEMEsLHybuPo2UN4WpVLXVQma3kSaHQggcLlEip8GhEUAy/xCb2eKqhI4HkDpDjwDnDVKufWlnRaOHf58cc8Woi+WT8JTOkHC+nBEG6fKRPHDG08U5yayIQIjI") + assert.NoError(t, err) + err = sniffer.UDP(pkt, &reqAddr) + assert.NoError(t, err) + assert.Equal(t, "www.notion.so:443", reqAddr) + + // Test unrecognized + pkt = []byte("oh my sweet summer child") + reqAddr = "90.90.90.90:90" + assert.True(t, sniffer.Check(true, reqAddr)) + err = sniffer.UDP(pkt, &reqAddr) + assert.NoError(t, err) + assert.Equal(t, "90.90.90.90:90", reqAddr) +} From b481b49a28f69cc17dd9c0857567fd80d5a567fb Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 29 Jun 2024 17:46:04 -0700 Subject: [PATCH 104/161] chore: import format fix --- app/cmd/server.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/cmd/server.go b/app/cmd/server.go index bca4c80..fdd9a54 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -15,8 +15,6 @@ import ( "strings" "time" - "github.com/apernet/hysteria/extras/v2/sniff" - "github.com/caddyserver/certmagic" "github.com/libdns/cloudflare" "github.com/libdns/duckdns" @@ -36,6 +34,7 @@ import ( "github.com/apernet/hysteria/extras/v2/masq" "github.com/apernet/hysteria/extras/v2/obfs" "github.com/apernet/hysteria/extras/v2/outbounds" + "github.com/apernet/hysteria/extras/v2/sniff" "github.com/apernet/hysteria/extras/v2/trafficlogger" ) From deeeafd8d7726760c66390b48e70dff9805b18e9 Mon Sep 17 00:00:00 2001 From: Toby Date: Sun, 30 Jun 2024 12:04:59 -0700 Subject: [PATCH 105/161] feat: allow specifying port ranges for sniffing --- app/cmd/server.go | 18 +++- app/cmd/server_test.go | 2 + app/cmd/server_test.yaml | 2 + extras/sniff/sniff.go | 25 ++++- extras/sniff/sniff_test.go | 41 +++++---- extras/transport/udphop/addr.go | 39 ++------ extras/transport/udphop/addr_test.go | 132 --------------------------- extras/utils/portunion.go | 107 ++++++++++++++++++++++ extras/utils/portunion_test.go | 92 +++++++++++++++++++ 9 files changed, 276 insertions(+), 182 deletions(-) delete mode 100644 extras/transport/udphop/addr_test.go create mode 100644 extras/utils/portunion.go create mode 100644 extras/utils/portunion_test.go diff --git a/app/cmd/server.go b/app/cmd/server.go index fdd9a54..b45fb15 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -36,6 +36,7 @@ import ( "github.com/apernet/hysteria/extras/v2/outbounds" "github.com/apernet/hysteria/extras/v2/sniff" "github.com/apernet/hysteria/extras/v2/trafficlogger" + eUtils "github.com/apernet/hysteria/extras/v2/utils" ) const ( @@ -185,6 +186,8 @@ type serverConfigSniff struct { Enable bool `mapstructure:"enable"` Timeout time.Duration `mapstructure:"timeout"` RewriteDomain bool `mapstructure:"rewriteDomain"` + TCPPorts string `mapstructure:"tcpPorts"` + UDPPorts string `mapstructure:"udpPorts"` } type serverConfigACL struct { @@ -551,10 +554,23 @@ func serverConfigOutboundHTTPToOutbound(c serverConfigOutboundHTTP) (outbounds.P func (c *serverConfig) fillRequestHook(hyConfig *server.Config) error { if c.Sniff.Enable { - hyConfig.RequestHook = &sniff.Sniffer{ + s := &sniff.Sniffer{ Timeout: c.Sniff.Timeout, RewriteDomain: c.Sniff.RewriteDomain, } + if c.Sniff.TCPPorts != "" { + s.TCPPorts = eUtils.ParsePortUnion(c.Sniff.TCPPorts) + if s.TCPPorts == nil { + return configError{Field: "sniff.tcpPorts", Err: errors.New("invalid port union")} + } + } + if c.Sniff.UDPPorts != "" { + s.UDPPorts = eUtils.ParsePortUnion(c.Sniff.UDPPorts) + if s.UDPPorts == nil { + return configError{Field: "sniff.udpPorts", Err: errors.New("invalid port union")} + } + } + hyConfig.RequestHook = s } return nil } diff --git a/app/cmd/server_test.go b/app/cmd/server_test.go index bd46681..bb2d12a 100644 --- a/app/cmd/server_test.go +++ b/app/cmd/server_test.go @@ -115,6 +115,8 @@ func TestServerConfig(t *testing.T) { Enable: true, Timeout: 1 * time.Second, RewriteDomain: true, + TCPPorts: "80,443,1000-2000", + UDPPorts: "443", }, ACL: serverConfigACL{ File: "chnroute.txt", diff --git a/app/cmd/server_test.yaml b/app/cmd/server_test.yaml index 343b0a9..ff0bf52 100644 --- a/app/cmd/server_test.yaml +++ b/app/cmd/server_test.yaml @@ -87,6 +87,8 @@ sniff: enable: true timeout: 1s rewriteDomain: true + tcpPorts: 80,443,1000-2000 + udpPorts: 443 acl: file: chnroute.txt diff --git a/extras/sniff/sniff.go b/extras/sniff/sniff.go index 68b3fbc..e0c94d4 100644 --- a/extras/sniff/sniff.go +++ b/extras/sniff/sniff.go @@ -5,6 +5,7 @@ import ( "io" "net" "net/http" + "strconv" "strings" "time" @@ -13,6 +14,7 @@ import ( "github.com/apernet/hysteria/core/v2/server" quicInternal "github.com/apernet/hysteria/extras/v2/sniff/internal/quic" + "github.com/apernet/hysteria/extras/v2/utils" ) const ( @@ -29,6 +31,8 @@ var _ server.RequestHook = (*Sniffer)(nil) type Sniffer struct { Timeout time.Duration RewriteDomain bool // Whether to rewrite the address even when it's already a domain + TCPPorts utils.PortUnion + UDPPorts utils.PortUnion } func (h *Sniffer) isDomain(addr string) bool { @@ -62,7 +66,26 @@ func (h *Sniffer) isTLS(buf []byte) bool { func (h *Sniffer) Check(isUDP bool, reqAddr string) bool { // @ means it's internal (e.g. speed test) - return !strings.HasPrefix(reqAddr, "@") && (h.RewriteDomain || !h.isDomain(reqAddr)) + if strings.HasPrefix(reqAddr, "@") { + return false + } + host, port, err := net.SplitHostPort(reqAddr) + if err != nil { + return false + } + if !h.RewriteDomain && net.ParseIP(host) == nil { + // Is a domain and domain rewriting is disabled + return false + } + portNum, err := strconv.Atoi(port) + if err != nil { + return false + } + if isUDP { + return h.UDPPorts == nil || h.UDPPorts.Contains(uint16(portNum)) + } else { + return h.TCPPorts == nil || h.TCPPorts.Contains(uint16(portNum)) + } } func (h *Sniffer) TCP(stream quic.Stream, reqAddr *string) ([]byte, error) { diff --git a/extras/sniff/sniff_test.go b/extras/sniff/sniff_test.go index fb86c3b..a22784e 100644 --- a/extras/sniff/sniff_test.go +++ b/extras/sniff/sniff_test.go @@ -6,10 +6,35 @@ import ( "testing" "time" + "github.com/apernet/hysteria/extras/v2/utils" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) +func TestSnifferCheck(t *testing.T) { + sniffer := &Sniffer{ + Timeout: 1 * time.Second, + RewriteDomain: false, + TCPPorts: nil, // nil = all + UDPPorts: nil, // nil = all + } + + assert.True(t, sniffer.Check(false, "1.1.1.1:80")) + assert.False(t, sniffer.Check(false, "example.com:443")) + + sniffer.RewriteDomain = true + assert.True(t, sniffer.Check(false, "example.com:443")) + + sniffer.TCPPorts = []utils.PortRange{{80, 80}} + assert.True(t, sniffer.Check(false, "google.com:80")) + assert.False(t, sniffer.Check(false, "google.com:443")) + + sniffer.UDPPorts = []utils.PortRange{{443, 443}} + assert.True(t, sniffer.Check(true, "google.com:443")) + assert.False(t, sniffer.Check(true, "google.com:80")) +} + func TestSnifferTCP(t *testing.T) { sniffer := &Sniffer{ Timeout: 1 * time.Second, @@ -40,27 +65,16 @@ func TestSnifferTCP(t *testing.T) { // Rewrite IP to domain reqAddr := "111.111.111.111:80" - assert.True(t, sniffer.Check(false, reqAddr)) putback, err := sniffer.TCP(stream, &reqAddr) assert.NoError(t, err) assert.Equal(t, *buf, putback) assert.Equal(t, "example.com:80", reqAddr) - // Do not rewrite if it's already a domain - index = 0 - reqAddr = "gulag.cc:443" - assert.False(t, sniffer.Check(false, reqAddr)) - - // Turn on rewrite and now it should rewrite - sniffer.RewriteDomain = true - assert.True(t, sniffer.Check(false, reqAddr)) - // Test TLS *buf, err = base64.StdEncoding.DecodeString("FgMBARcBAAETAwPJL2jlt1OAo+Rslkjv/aqKiTthKMaCKg2Gvd+uALDbDCDdY+UIk8ouadEB9fC3j52Y1i7SJZqGIgBRIS6kKieYrAAoEwITAcAswCvAMMAvwCTAI8AowCfACsAJwBTAEwCdAJwAPQA8ADUALwEAAKIAAAAOAAwAAAlpcGluZm8uaW8ABQAFAQAAAAAAKwAJCAMEAwMDAgMBAA0AGgAYCAQIBQgGBAEFAQIBBAMFAwIDAgIGAQYDACMAAAAKAAgABgAdABcAGAAQAAsACQhodHRwLzEuMQAzACYAJAAdACBguQbqNJNyamYxYcrBFpBP7pWv5TgZsP9gwGtMYNKVBQAxAAAAFwAA/wEAAQAALQACAQE=") assert.NoError(t, err) index = 0 reqAddr = "222.222.222.222:443" - assert.True(t, sniffer.Check(false, reqAddr)) putback, err = sniffer.TCP(stream, &reqAddr) assert.NoError(t, err) assert.Equal(t, *buf, putback) @@ -70,7 +84,6 @@ func TestSnifferTCP(t *testing.T) { *buf = []byte("Wait It's All Ohio? Always Has Been.") index = 0 reqAddr = "123.123.123.123:123" - assert.True(t, sniffer.Check(false, reqAddr)) putback, err = sniffer.TCP(stream, &reqAddr) assert.NoError(t, err) assert.Equal(t, *buf, putback) @@ -80,7 +93,6 @@ func TestSnifferTCP(t *testing.T) { *buf = []byte("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a") index = 0 reqAddr = "45.45.45.45:45" - assert.True(t, sniffer.Check(false, reqAddr)) putback, err = sniffer.TCP(stream, &reqAddr) assert.NoError(t, err) assert.Equal(t, []byte("\x01\x02\x03"), putback) @@ -94,7 +106,6 @@ func TestSnifferTCP(t *testing.T) { return 0, io.EOF }) reqAddr = "66.66.66.66:66" - assert.True(t, sniffer.Check(false, reqAddr)) putback, err = sniffer.TCP(blockStream, &reqAddr) assert.NoError(t, err) assert.Equal(t, []byte{}, putback) @@ -109,7 +120,6 @@ func TestSnifferUDP(t *testing.T) { // Test QUIC reqAddr := "2.3.4.5:443" - assert.True(t, sniffer.Check(true, reqAddr)) pkt, err := base64.StdEncoding.DecodeString("ygAAAAEIwugWgPS7ulYAAES8hY891uwgGE9GG4CPOLd+nsDe28raso24lCSFmlFwYQG1uF39ikbL13/R9ZTghYmTl+jEbr6F9TxxRiOgpTmKRmh6aKZiIiVfy5pVRckovaI8lq0WRoW9xoFNTyYtQP8TVJ3bLCK+zUqpquEQSyWf7CE43ywayyMpE9UlIoPXFWCoopXLM1SvzdQ+17P51N9KR7m4emti4DWWTBLMQOvrwd2HEEkbiZdRO1wf6ZXJlIat5dN0R/6uod60OFPO+u+awvq67MoMReC7+5I/xWI+xx6o4JpnZNn6YPG8Gqi8hS6doNcAAdtD8h5eMLuHCCgkpX3QVjjfWtcOhtw9xKjU43HhUPwzUTv+JDLgwuTQCTmlfYlb3B+pk4b2I9si0tJ0SBuYaZ2VQPtZbj2hpGXw3gn11pbN8xsbKkQL50+Scd4dGJxWQlGaJHeaU5WOCkxLXc635z8m5XO/CBHVYPGp4pfwfwNUgbe5WF+3MaUIlDB8dMfsnrO0BmZPo379jVx0SFLTAiS8wAdHib1WNEY8qKYnTWuiyxYg1GZEhJt0nXmI+8f0eJq42DgHBWC+Rf5rRBr/Sf25o3mFAmTUaul0Woo9/CIrpT73B63N91xd9A77i4ru995YG8l9Hen+eLtpDU9Q9376nwMDYBzeYG9U/Rn0Urbm6q4hmAgV/xlNJ2rAyDS+yLnwqD6I0PRy8bZJEttcidb/SkOyrpgMiAzWeT+SO+c/k+Y8H0UTRa05faZUrhuUaym9wAcaIVRA6nFI+fejfjVp+7afFv+kWn3vCqQEij+CRHuxkltrixZMD2rfYj6NUW7TTYBtPRtuV/V0ZIDjRR26vr4K+0D84+l3c0mA/l6nmpP5kkco3nmpdjtQN6sGXL7+5o0nnsftX5d6/n5mLyEpP+AEDl1zk3iqkS62RsITwql6DMMoGbSDdUpMclCIeM0vlo3CkxGMO7QA9ruVeNddkL3EWMivl+uxO43sXEEqYQHVl4N75y63t05GOf7/gm9Kb/BJ8MpG9ViEkVYaskQCzi3D8bVpzo8FfTj8te8B6c3ikc/cm7r8k0ZcZpr+YiLGDYq+0ilHxpqJfmq8dPkSvxdzLcUSvy7+LMQ/TTobRSF7L4JhtDKck0+00vl9H35Tkh9N+MsVtpKdWyoqZ4XaK2Nx1M6AieczXpdFc0y7lYPoUfF4IeW8WzeVUclol5ElYjkyFz/lDOGAe1bF2g5AYaGWCPiGleVZknNdD5ihB8W8Mfkt1pEwq2S97AHrppqkf/VoIfZzeqH8wUFw8fDDrZIpnoa0rW7HfwIQaqJhPCyB9Z6TVbV4x9UWmaHfVAcinCK/7o10dtaj3rvEqcUC/iPceGq3Tqv/p9GGNJ+Ci2JBjXqNxYr893Llk75VdPD9pM6y1SM0P80oXNy32VMtafkFFST8GpvvqWcxUJ93kzaY8RmU1g3XFOImSU2utU6+FUQ2Pn5uLwcfT2cTYfTpPGh+WXjSbZ6trqdEMEsLHybuPo2UN4WpVLXVQma3kSaHQggcLlEip8GhEUAy/xCb2eKqhI4HkDpDjwDnDVKufWlnRaOHf58cc8Woi+WT8JTOkHC+nBEG6fKRPHDG08U5yayIQIjI") assert.NoError(t, err) err = sniffer.UDP(pkt, &reqAddr) @@ -119,7 +129,6 @@ func TestSnifferUDP(t *testing.T) { // Test unrecognized pkt = []byte("oh my sweet summer child") reqAddr = "90.90.90.90:90" - assert.True(t, sniffer.Check(true, reqAddr)) err = sniffer.UDP(pkt, &reqAddr) assert.NoError(t, err) assert.Equal(t, "90.90.90.90:90", reqAddr) diff --git a/extras/transport/udphop/addr.go b/extras/transport/udphop/addr.go index 3c70472..afde26a 100644 --- a/extras/transport/udphop/addr.go +++ b/extras/transport/udphop/addr.go @@ -3,8 +3,8 @@ package udphop import ( "fmt" "net" - "strconv" - "strings" + + "github.com/apernet/hysteria/extras/v2/utils" ) type InvalidPortError struct { @@ -57,36 +57,11 @@ func ResolveUDPHopAddr(addr string) (*UDPHopAddr, error) { PortStr: portStr, } - portStrs := strings.Split(portStr, ",") - for _, portStr := range portStrs { - if strings.Contains(portStr, "-") { - // Port range - portRange := strings.Split(portStr, "-") - if len(portRange) != 2 { - return nil, InvalidPortError{portStr} - } - start, err := strconv.ParseUint(portRange[0], 10, 16) - if err != nil { - return nil, InvalidPortError{portStr} - } - end, err := strconv.ParseUint(portRange[1], 10, 16) - if err != nil { - return nil, InvalidPortError{portStr} - } - if start > end { - start, end = end, start - } - for i := start; i <= end; i++ { - result.Ports = append(result.Ports, uint16(i)) - } - } else { - // Single port - port, err := strconv.ParseUint(portStr, 10, 16) - if err != nil { - return nil, InvalidPortError{portStr} - } - result.Ports = append(result.Ports, uint16(port)) - } + pu := utils.ParsePortUnion(portStr) + if pu == nil { + return nil, InvalidPortError{portStr} } + result.Ports = pu.Ports() + return result, nil } diff --git a/extras/transport/udphop/addr_test.go b/extras/transport/udphop/addr_test.go deleted file mode 100644 index 94a1016..0000000 --- a/extras/transport/udphop/addr_test.go +++ /dev/null @@ -1,132 +0,0 @@ -package udphop - -import ( - "net" - "reflect" - "testing" -) - -func TestResolveUDPHopAddr(t *testing.T) { - type args struct { - addr string - } - tests := []struct { - name string - args args - want *UDPHopAddr - wantErr bool - }{ - { - name: "empty", - args: args{ - addr: "", - }, - want: nil, - wantErr: true, - }, - { - name: "no port", - args: args{ - addr: "8.8.8.8", - }, - want: nil, - wantErr: true, - }, - { - name: "single port", - args: args{ - addr: "8.8.4.4:1234", - }, - want: &UDPHopAddr{ - IP: net.ParseIP("8.8.4.4"), - Ports: []uint16{1234}, - PortStr: "1234", - }, - wantErr: false, - }, - { - name: "multiple ports", - args: args{ - addr: "8.8.3.3:1234,5678,9012", - }, - want: &UDPHopAddr{ - IP: net.ParseIP("8.8.3.3"), - Ports: []uint16{1234, 5678, 9012}, - PortStr: "1234,5678,9012", - }, - wantErr: false, - }, - { - name: "port range", - args: args{ - addr: "1.2.3.4:1234-1240", - }, - want: &UDPHopAddr{ - IP: net.ParseIP("1.2.3.4"), - Ports: []uint16{1234, 1235, 1236, 1237, 1238, 1239, 1240}, - PortStr: "1234-1240", - }, - wantErr: false, - }, - { - name: "port range reversed", - args: args{ - addr: "123.123.123.123:9990-9980", - }, - want: &UDPHopAddr{ - IP: net.ParseIP("123.123.123.123"), - Ports: []uint16{9980, 9981, 9982, 9983, 9984, 9985, 9986, 9987, 9988, 9989, 9990}, - PortStr: "9990-9980", - }, - wantErr: false, - }, - { - name: "port range & port list", - args: args{ - addr: "9.9.9.9:1234-1236,5678,9012", - }, - want: &UDPHopAddr{ - IP: net.ParseIP("9.9.9.9"), - Ports: []uint16{1234, 1235, 1236, 5678, 9012}, - PortStr: "1234-1236,5678,9012", - }, - wantErr: false, - }, - { - name: "invalid port", - args: args{ - addr: "5.5.5.5:1234,bs", - }, - want: nil, - wantErr: true, - }, - { - name: "invalid port range 1", - args: args{ - addr: "6.6.6.6:7788-bbss", - }, - want: nil, - wantErr: true, - }, - { - name: "invalid port range 2", - args: args{ - addr: "1.0.0.1:8899-9002-9005", - }, - want: nil, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := ResolveUDPHopAddr(tt.args.addr) - if (err != nil) != tt.wantErr { - t.Errorf("ParseUDPHopAddr() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("ParseUDPHopAddr() got = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/extras/utils/portunion.go b/extras/utils/portunion.go new file mode 100644 index 0000000..20a31d0 --- /dev/null +++ b/extras/utils/portunion.go @@ -0,0 +1,107 @@ +package utils + +import ( + "sort" + "strconv" + "strings" +) + +// PortUnion is a collection of multiple port ranges. +type PortUnion []PortRange + +// PortRange represents a range of ports. +// Start and End are inclusive. [Start, End] +type PortRange struct { + Start, End uint16 +} + +// ParsePortUnion parses a string of comma-separated port ranges (or single ports) into a PortUnion. +// Returns nil if the input is invalid. +// The returned PortUnion is guaranteed to be normalized. +func ParsePortUnion(s string) PortUnion { + if s == "all" || s == "*" { + // Wildcard special case + return PortUnion{PortRange{0, 65535}} + } + var result PortUnion + portStrs := strings.Split(s, ",") + for _, portStr := range portStrs { + if strings.Contains(portStr, "-") { + // Port range + portRange := strings.Split(portStr, "-") + if len(portRange) != 2 { + return nil + } + start, err := strconv.ParseUint(portRange[0], 10, 16) + if err != nil { + return nil + } + end, err := strconv.ParseUint(portRange[1], 10, 16) + if err != nil { + return nil + } + if start > end { + start, end = end, start + } + result = append(result, PortRange{uint16(start), uint16(end)}) + } else { + // Single port + port, err := strconv.ParseUint(portStr, 10, 16) + if err != nil { + return nil + } + result = append(result, PortRange{uint16(port), uint16(port)}) + } + } + if result == nil { + return nil + } + return result.Normalize() +} + +// Normalize normalizes a PortUnion. +// No overlapping ranges, ranges are sorted from low to high. +func (u PortUnion) Normalize() PortUnion { + if len(u) == 0 { + return u + } + sort.Slice(u, func(i, j int) bool { + if u[i].Start == u[j].Start { + return u[i].End < u[j].End + } + return u[i].Start < u[j].Start + }) + normalized := PortUnion{u[0]} + for _, current := range u[1:] { + last := &normalized[len(normalized)-1] + if current.Start <= last.End+1 { + if current.End > last.End { + last.End = current.End + } + } else { + normalized = append(normalized, current) + } + } + return normalized +} + +// Ports returns all ports in the PortUnion as a slice. +func (u PortUnion) Ports() []uint16 { + var ports []uint16 + for _, r := range u { + for i := r.Start; i <= r.End; i++ { + ports = append(ports, i) + } + } + return ports +} + +// Contains returns true if the PortUnion contains the given port. +func (u PortUnion) Contains(port uint16) bool { + for _, r := range u { + if port >= r.Start && port <= r.End { + return true + } + } + return false +} diff --git a/extras/utils/portunion_test.go b/extras/utils/portunion_test.go new file mode 100644 index 0000000..551bae1 --- /dev/null +++ b/extras/utils/portunion_test.go @@ -0,0 +1,92 @@ +package utils + +import ( + "reflect" + "testing" +) + +func TestParsePortUnion(t *testing.T) { + tests := []struct { + name string + s string + want PortUnion + }{ + { + name: "empty", + s: "", + want: nil, + }, + { + name: "all 1", + s: "all", + want: PortUnion{{0, 65535}}, + }, + { + name: "all 2", + s: "*", + want: PortUnion{{0, 65535}}, + }, + { + name: "single port", + s: "1234", + want: PortUnion{{1234, 1234}}, + }, + { + name: "multiple ports (unsorted)", + s: "5678,1234,9012", + want: PortUnion{{1234, 1234}, {5678, 5678}, {9012, 9012}}, + }, + { + name: "one range", + s: "1234-1240", + want: PortUnion{{1234, 1240}}, + }, + { + name: "one range (reversed)", + s: "1240-1234", + want: PortUnion{{1234, 1240}}, + }, + { + name: "multiple ports and ranges (reversed, unsorted, overlapping)", + s: "5678,1200-1236,9100-9012,1234-1240", + want: PortUnion{{1200, 1240}, {5678, 5678}, {9012, 9100}}, + }, + { + name: "invalid 1", + s: "1234-", + want: nil, + }, + { + name: "invalid 2", + s: "1234-ggez", + want: nil, + }, + { + name: "invalid 3", + s: "233,", + want: nil, + }, + { + name: "invalid 4", + s: "1234-1240-1250", + want: nil, + }, + { + name: "invalid 5", + s: "-,,", + want: nil, + }, + { + name: "invalid 6", + s: "http", + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := ParsePortUnion(tt.s); !reflect.DeepEqual(got, tt.want) { + t.Errorf("ParsePortUnion() = %v, want %v", got, tt.want) + } + }) + } +} From 6a90fe18ee397404d25427e64b75c4eb48739885 Mon Sep 17 00:00:00 2001 From: Toby Date: Sun, 30 Jun 2024 20:16:55 -0700 Subject: [PATCH 106/161] feat: graceful speed test shutdown --- app/cmd/speedtest.go | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/app/cmd/speedtest.go b/app/cmd/speedtest.go index 12d3a83..f5c37fb 100644 --- a/app/cmd/speedtest.go +++ b/app/cmd/speedtest.go @@ -3,6 +3,9 @@ package cmd import ( "errors" "fmt" + "os" + "os/signal" + "syscall" "time" "github.com/spf13/cobra" @@ -68,11 +71,26 @@ func runSpeedtest(cmd *cobra.Command, args []string) { zap.Bool("udpEnabled", info.UDPEnabled), zap.Uint64("tx", info.Tx)) - if !skipDownload { - runDownloadTest(c) - } - if !skipUpload { - runUploadTest(c) + signalChan := make(chan os.Signal, 1) + signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM) + defer signal.Stop(signalChan) + + runChan := make(chan struct{}, 1) + go func() { + if !skipDownload { + runDownloadTest(c) + } + if !skipUpload { + runUploadTest(c) + } + runChan <- struct{}{} + }() + + select { + case <-signalChan: + logger.Info("received signal, shutting down gracefully") + case <-runChan: + logger.Info("speed test complete") } } From 8d9c7fa04c7e2e2cea8c52102d16e1590fcbadfd Mon Sep 17 00:00:00 2001 From: Toby Date: Tue, 2 Jul 2024 15:24:48 -0700 Subject: [PATCH 107/161] feat: quic-go v0.45.1 --- app/go.mod | 2 +- app/go.sum | 4 ++-- core/go.mod | 2 +- core/go.sum | 4 ++-- extras/go.mod | 2 +- extras/go.sum | 4 ++-- go.work.sum | 11 +++++++++++ 7 files changed, 20 insertions(+), 9 deletions(-) diff --git a/app/go.mod b/app/go.mod index dfbea42..b4bd8dd 100644 --- a/app/go.mod +++ b/app/go.mod @@ -28,7 +28,7 @@ require ( require ( github.com/andybalholm/brotli v1.1.0 // indirect - github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023 // indirect + github.com/apernet/quic-go v0.45.2-0.20240702221538-ed74cfbe8b6e // indirect github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect github.com/cloudflare/circl v1.3.9 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/app/go.sum b/app/go.sum index e1b80a5..86841ce 100644 --- a/app/go.sum +++ b/app/go.sum @@ -42,8 +42,8 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1 github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f h1:uVh0qpEslrWjgzx9vOcyCqsOY3c9kofDZ1n+qaw35ZY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f/go.mod h1:xkkq9D4ygcldQQhKS/w9CadiCKwCngU7K9E3DaKahpM= -github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023 h1:UTrvVPt+GfeOeli9/3gvpCDz2Jd5UEn3YotfP0u/pok= -github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023/go.mod h1:UkcG7+34BM+bbH2RFVKtHQp3mR7h8yJHx4z95lZ7sx4= +github.com/apernet/quic-go v0.45.2-0.20240702221538-ed74cfbe8b6e h1:KBs8aBfKl5AKPKGpfn3bl0joDJXDq5fnH+AjFODiU+A= +github.com/apernet/quic-go v0.45.2-0.20240702221538-ed74cfbe8b6e/go.mod h1:MjGWpXA31DZZWESdX3/PjIpSWIT1fOm8FNCqyXXFZFU= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad h1:QzQ2sKpc9o42HNRR8ukM5uMC/RzR2HgZd/Nvaqol2C0= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= diff --git a/core/go.mod b/core/go.mod index f35cc54..708b1dc 100644 --- a/core/go.mod +++ b/core/go.mod @@ -3,7 +3,7 @@ module github.com/apernet/hysteria/core/v2 go 1.21 require ( - github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023 + github.com/apernet/quic-go v0.45.2-0.20240702221538-ed74cfbe8b6e github.com/stretchr/testify v1.9.0 go.uber.org/goleak v1.2.1 golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 diff --git a/core/go.sum b/core/go.sum index 1a133cb..b492093 100644 --- a/core/go.sum +++ b/core/go.sum @@ -1,5 +1,5 @@ -github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023 h1:UTrvVPt+GfeOeli9/3gvpCDz2Jd5UEn3YotfP0u/pok= -github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023/go.mod h1:UkcG7+34BM+bbH2RFVKtHQp3mR7h8yJHx4z95lZ7sx4= +github.com/apernet/quic-go v0.45.2-0.20240702221538-ed74cfbe8b6e h1:KBs8aBfKl5AKPKGpfn3bl0joDJXDq5fnH+AjFODiU+A= +github.com/apernet/quic-go v0.45.2-0.20240702221538-ed74cfbe8b6e/go.mod h1:MjGWpXA31DZZWESdX3/PjIpSWIT1fOm8FNCqyXXFZFU= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= diff --git a/extras/go.mod b/extras/go.mod index 2d9fae7..f1f2bdc 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -4,7 +4,7 @@ go 1.21 require ( github.com/apernet/hysteria/core/v2 v2.0.0-00010101000000-000000000000 - github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023 + github.com/apernet/quic-go v0.45.2-0.20240702221538-ed74cfbe8b6e github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 github.com/hashicorp/golang-lru/v2 v2.0.5 github.com/miekg/dns v1.1.59 diff --git a/extras/go.sum b/extras/go.sum index 74dceb5..5517d6d 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -1,7 +1,7 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023 h1:UTrvVPt+GfeOeli9/3gvpCDz2Jd5UEn3YotfP0u/pok= -github.com/apernet/quic-go v0.44.1-0.20240520215222-bb2e53664023/go.mod h1:UkcG7+34BM+bbH2RFVKtHQp3mR7h8yJHx4z95lZ7sx4= +github.com/apernet/quic-go v0.45.2-0.20240702221538-ed74cfbe8b6e h1:KBs8aBfKl5AKPKGpfn3bl0joDJXDq5fnH+AjFODiU+A= +github.com/apernet/quic-go v0.45.2-0.20240702221538-ed74cfbe8b6e/go.mod h1:MjGWpXA31DZZWESdX3/PjIpSWIT1fOm8FNCqyXXFZFU= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= diff --git a/go.work.sum b/go.work.sum index f0551e7..a870be4 100644 --- a/go.work.sum +++ b/go.work.sum @@ -37,6 +37,7 @@ github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8 github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625 h1:ckJgFhFWywOx+YLEMIJsTb+NV6NexWICk5+AMSuz3ss= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 h1:D21IyuvjDCshj1/qq+pCNd3VZOAEI9jy6Bi131YlXgI= @@ -44,6 +45,7 @@ github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7 github.com/bwesterb/go-ristretto v1.2.3 h1:1w53tCkGhCQ5djbat3+MH0BAQ5Kfgbt56UZQ/JMzngw= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= @@ -170,10 +172,14 @@ github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTm github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= github.com/pkg/sftp v1.13.1 h1:I2qBYMChEhIjOgazfJmV3/mZM256btk6wkCDRmW7JYs= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -256,6 +262,7 @@ golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a h1:Jw5wfR+h9mnIYH+OtGT2im5wV1YGGDora5vTv/aa5bE= golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= @@ -294,6 +301,7 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2 h1:IRJeR9r1pYWsHKTRe/IInb7lYvbBVIqOgsX/u0mbOWY= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= @@ -314,6 +322,7 @@ golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -324,6 +333,7 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= @@ -347,6 +357,7 @@ google.golang.org/grpc v1.52.0 h1:kd48UiU7EHsV4rnLyOJRuP/Il/UHE7gdDAQ+SZI7nZk= google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= From a7ecd0804694c173ebb6ec321fd450537a4ba081 Mon Sep 17 00:00:00 2001 From: yiguous <147401898+yiguous@users.noreply.github.com> Date: Fri, 5 Jul 2024 18:34:54 +0800 Subject: [PATCH 108/161] fix escaped auth --- app/cmd/client.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/cmd/client.go b/app/cmd/client.go index 8f05c1e..65bc7e0 100644 --- a/app/cmd/client.go +++ b/app/cmd/client.go @@ -393,7 +393,11 @@ func (c *clientConfig) parseURI() bool { return false } if u.User != nil { - c.Auth = u.User.String() + auth, err := url.QueryUnescape(u.User.String()) + if err != nil { + return false + } + c.Auth = auth } c.Server = u.Host q := u.Query() From c62c8c5513f39e18f2856795788e0ff375d90c3d Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 3 Aug 2024 13:14:34 -0700 Subject: [PATCH 109/161] feat: quic-go v0.45.2 --- app/go.mod | 2 +- app/go.sum | 4 ++-- core/go.mod | 2 +- core/go.sum | 4 ++-- extras/go.mod | 2 +- extras/go.sum | 4 ++-- go.work.sum | 6 ++++++ 7 files changed, 15 insertions(+), 9 deletions(-) diff --git a/app/go.mod b/app/go.mod index b4bd8dd..84c7b46 100644 --- a/app/go.mod +++ b/app/go.mod @@ -28,7 +28,7 @@ require ( require ( github.com/andybalholm/brotli v1.1.0 // indirect - github.com/apernet/quic-go v0.45.2-0.20240702221538-ed74cfbe8b6e // indirect + github.com/apernet/quic-go v0.45.3-0.20240803200022-e3824dfb4b89 // indirect github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect github.com/cloudflare/circl v1.3.9 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/app/go.sum b/app/go.sum index 86841ce..b475fbb 100644 --- a/app/go.sum +++ b/app/go.sum @@ -42,8 +42,8 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1 github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f h1:uVh0qpEslrWjgzx9vOcyCqsOY3c9kofDZ1n+qaw35ZY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f/go.mod h1:xkkq9D4ygcldQQhKS/w9CadiCKwCngU7K9E3DaKahpM= -github.com/apernet/quic-go v0.45.2-0.20240702221538-ed74cfbe8b6e h1:KBs8aBfKl5AKPKGpfn3bl0joDJXDq5fnH+AjFODiU+A= -github.com/apernet/quic-go v0.45.2-0.20240702221538-ed74cfbe8b6e/go.mod h1:MjGWpXA31DZZWESdX3/PjIpSWIT1fOm8FNCqyXXFZFU= +github.com/apernet/quic-go v0.45.3-0.20240803200022-e3824dfb4b89 h1:UKwGA1Lto7xQ3AGeTmBU7Y3WznMRTt9atOJ05xKNOSU= +github.com/apernet/quic-go v0.45.3-0.20240803200022-e3824dfb4b89/go.mod h1:MjGWpXA31DZZWESdX3/PjIpSWIT1fOm8FNCqyXXFZFU= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad h1:QzQ2sKpc9o42HNRR8ukM5uMC/RzR2HgZd/Nvaqol2C0= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= diff --git a/core/go.mod b/core/go.mod index 708b1dc..390a9f1 100644 --- a/core/go.mod +++ b/core/go.mod @@ -3,7 +3,7 @@ module github.com/apernet/hysteria/core/v2 go 1.21 require ( - github.com/apernet/quic-go v0.45.2-0.20240702221538-ed74cfbe8b6e + github.com/apernet/quic-go v0.45.3-0.20240803200022-e3824dfb4b89 github.com/stretchr/testify v1.9.0 go.uber.org/goleak v1.2.1 golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 diff --git a/core/go.sum b/core/go.sum index b492093..08b1fa3 100644 --- a/core/go.sum +++ b/core/go.sum @@ -1,5 +1,5 @@ -github.com/apernet/quic-go v0.45.2-0.20240702221538-ed74cfbe8b6e h1:KBs8aBfKl5AKPKGpfn3bl0joDJXDq5fnH+AjFODiU+A= -github.com/apernet/quic-go v0.45.2-0.20240702221538-ed74cfbe8b6e/go.mod h1:MjGWpXA31DZZWESdX3/PjIpSWIT1fOm8FNCqyXXFZFU= +github.com/apernet/quic-go v0.45.3-0.20240803200022-e3824dfb4b89 h1:UKwGA1Lto7xQ3AGeTmBU7Y3WznMRTt9atOJ05xKNOSU= +github.com/apernet/quic-go v0.45.3-0.20240803200022-e3824dfb4b89/go.mod h1:MjGWpXA31DZZWESdX3/PjIpSWIT1fOm8FNCqyXXFZFU= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= diff --git a/extras/go.mod b/extras/go.mod index f1f2bdc..0da032b 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -4,7 +4,7 @@ go 1.21 require ( github.com/apernet/hysteria/core/v2 v2.0.0-00010101000000-000000000000 - github.com/apernet/quic-go v0.45.2-0.20240702221538-ed74cfbe8b6e + github.com/apernet/quic-go v0.45.3-0.20240803200022-e3824dfb4b89 github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 github.com/hashicorp/golang-lru/v2 v2.0.5 github.com/miekg/dns v1.1.59 diff --git a/extras/go.sum b/extras/go.sum index 5517d6d..d8658b3 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -1,7 +1,7 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/apernet/quic-go v0.45.2-0.20240702221538-ed74cfbe8b6e h1:KBs8aBfKl5AKPKGpfn3bl0joDJXDq5fnH+AjFODiU+A= -github.com/apernet/quic-go v0.45.2-0.20240702221538-ed74cfbe8b6e/go.mod h1:MjGWpXA31DZZWESdX3/PjIpSWIT1fOm8FNCqyXXFZFU= +github.com/apernet/quic-go v0.45.3-0.20240803200022-e3824dfb4b89 h1:UKwGA1Lto7xQ3AGeTmBU7Y3WznMRTt9atOJ05xKNOSU= +github.com/apernet/quic-go v0.45.3-0.20240803200022-e3824dfb4b89/go.mod h1:MjGWpXA31DZZWESdX3/PjIpSWIT1fOm8FNCqyXXFZFU= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= diff --git a/go.work.sum b/go.work.sum index a870be4..44dac15 100644 --- a/go.work.sum +++ b/go.work.sum @@ -37,6 +37,7 @@ github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8 github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625 h1:ckJgFhFWywOx+YLEMIJsTb+NV6NexWICk5+AMSuz3ss= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= @@ -45,6 +46,7 @@ github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7 github.com/bwesterb/go-ristretto v1.2.3 h1:1w53tCkGhCQ5djbat3+MH0BAQ5Kfgbt56UZQ/JMzngw= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= @@ -172,13 +174,17 @@ github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTm github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= github.com/pkg/sftp v1.13.1 h1:I2qBYMChEhIjOgazfJmV3/mZM256btk6wkCDRmW7JYs= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= From 604132f8d0e4e8beb53f4d439da2c4a0ea4de91a Mon Sep 17 00:00:00 2001 From: Haruue Date: Sun, 4 Aug 2024 14:34:57 +0800 Subject: [PATCH 110/161] fix(test): signature mismatch of udpIO.Hook ref: #1125 --- core/server/udp_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/server/udp_test.go b/core/server/udp_test.go index aa78327..045edbd 100644 --- a/core/server/udp_test.go +++ b/core/server/udp_test.go @@ -25,7 +25,7 @@ func TestUDPSessionManager(t *testing.T) { } return m, nil }) - io.EXPECT().Hook(mock.Anything, mock.Anything).Return(nil, nil) + io.EXPECT().Hook(mock.Anything, mock.Anything).Return(nil) go sm.Run() From 48bf9b964a63c43eef65f9c364d0d27e9e08eb4b Mon Sep 17 00:00:00 2001 From: Toby Date: Fri, 16 Aug 2024 15:46:30 -0700 Subject: [PATCH 111/161] fix: sniffing handled HTTP host header incorrectly --- extras/sniff/sniff.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/extras/sniff/sniff.go b/extras/sniff/sniff.go index e0c94d4..b2d7419 100644 --- a/extras/sniff/sniff.go +++ b/extras/sniff/sniff.go @@ -112,11 +112,21 @@ func (h *Sniffer) TCP(stream quic.Stream, reqAddr *string) ([]byte, error) { tr := &teeReader{Stream: stream, Pre: pre} req, _ := http.ReadRequest(bufio.NewReader(tr)) if req != nil && req.Host != "" { - _, port, err := net.SplitHostPort(*reqAddr) + // req.Host may already contain the port. + // If it does, just overwrite the whole address with req.Host. + // Otherwise, use the port in reqAddr. + _, _, err := net.SplitHostPort(req.Host) if err != nil { - return nil, err + // Not host:port format, append the port from reqAddr + _, port, err := net.SplitHostPort(*reqAddr) + if err != nil { + return nil, err + } + *reqAddr = net.JoinHostPort(req.Host, port) + } else { + // Already host:port format + *reqAddr = req.Host } - *reqAddr = net.JoinHostPort(req.Host, port) } return tr.Buffer(), nil } else if h.isTLS(pre) { From f014c005463cb5a77696485703d25c1d9566c287 Mon Sep 17 00:00:00 2001 From: Toby Date: Fri, 16 Aug 2024 15:51:42 -0700 Subject: [PATCH 112/161] fix: add a test case --- extras/sniff/sniff_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/extras/sniff/sniff_test.go b/extras/sniff/sniff_test.go index a22784e..731cc6b 100644 --- a/extras/sniff/sniff_test.go +++ b/extras/sniff/sniff_test.go @@ -70,6 +70,18 @@ func TestSnifferTCP(t *testing.T) { assert.Equal(t, *buf, putback) assert.Equal(t, "example.com:80", reqAddr) + // Test HTTP with Host as host:port + *buf = []byte("GET / HTTP/1.1\r\n" + + "Host: example.com:8080\r\n" + + "User-Agent: test-agent\r\n" + + "Accept: */*\r\n\r\n") + index = 0 + reqAddr = "222.222.222.222:10086" + putback, err = sniffer.TCP(stream, &reqAddr) + assert.NoError(t, err) + assert.Equal(t, *buf, putback) + assert.Equal(t, "example.com:8080", reqAddr) + // Test TLS *buf, err = base64.StdEncoding.DecodeString("FgMBARcBAAETAwPJL2jlt1OAo+Rslkjv/aqKiTthKMaCKg2Gvd+uALDbDCDdY+UIk8ouadEB9fC3j52Y1i7SJZqGIgBRIS6kKieYrAAoEwITAcAswCvAMMAvwCTAI8AowCfACsAJwBTAEwCdAJwAPQA8ADUALwEAAKIAAAAOAAwAAAlpcGluZm8uaW8ABQAFAQAAAAAAKwAJCAMEAwMDAgMBAA0AGgAYCAQIBQgGBAEFAQIBBAMFAwIDAgIGAQYDACMAAAAKAAgABgAdABcAGAAQAAsACQhodHRwLzEuMQAzACYAJAAdACBguQbqNJNyamYxYcrBFpBP7pWv5TgZsP9gwGtMYNKVBQAxAAAAFwAA/wEAAQAALQACAQE=") assert.NoError(t, err) From 7e70547dbdf1346d98c5f1da4a607502db7288e2 Mon Sep 17 00:00:00 2001 From: Toby Date: Fri, 16 Aug 2024 16:16:05 -0700 Subject: [PATCH 113/161] feat: quic-go v0.46.0 --- app/go.mod | 2 +- app/go.sum | 4 ++-- core/go.mod | 2 +- core/go.sum | 4 ++-- extras/go.mod | 2 +- extras/go.sum | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/go.mod b/app/go.mod index 84c7b46..f995c78 100644 --- a/app/go.mod +++ b/app/go.mod @@ -28,7 +28,7 @@ require ( require ( github.com/andybalholm/brotli v1.1.0 // indirect - github.com/apernet/quic-go v0.45.3-0.20240803200022-e3824dfb4b89 // indirect + github.com/apernet/quic-go v0.46.1-0.20240816230517-268ed2476167 // indirect github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect github.com/cloudflare/circl v1.3.9 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/app/go.sum b/app/go.sum index b475fbb..5b4bce8 100644 --- a/app/go.sum +++ b/app/go.sum @@ -42,8 +42,8 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1 github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f h1:uVh0qpEslrWjgzx9vOcyCqsOY3c9kofDZ1n+qaw35ZY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f/go.mod h1:xkkq9D4ygcldQQhKS/w9CadiCKwCngU7K9E3DaKahpM= -github.com/apernet/quic-go v0.45.3-0.20240803200022-e3824dfb4b89 h1:UKwGA1Lto7xQ3AGeTmBU7Y3WznMRTt9atOJ05xKNOSU= -github.com/apernet/quic-go v0.45.3-0.20240803200022-e3824dfb4b89/go.mod h1:MjGWpXA31DZZWESdX3/PjIpSWIT1fOm8FNCqyXXFZFU= +github.com/apernet/quic-go v0.46.1-0.20240816230517-268ed2476167 h1:+jKV1EuDJiUoa4XgRyle5w7wIo+0hil+YyUmwhd4ttk= +github.com/apernet/quic-go v0.46.1-0.20240816230517-268ed2476167/go.mod h1:MjGWpXA31DZZWESdX3/PjIpSWIT1fOm8FNCqyXXFZFU= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad h1:QzQ2sKpc9o42HNRR8ukM5uMC/RzR2HgZd/Nvaqol2C0= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= diff --git a/core/go.mod b/core/go.mod index 390a9f1..77513a9 100644 --- a/core/go.mod +++ b/core/go.mod @@ -3,7 +3,7 @@ module github.com/apernet/hysteria/core/v2 go 1.21 require ( - github.com/apernet/quic-go v0.45.3-0.20240803200022-e3824dfb4b89 + github.com/apernet/quic-go v0.46.1-0.20240816230517-268ed2476167 github.com/stretchr/testify v1.9.0 go.uber.org/goleak v1.2.1 golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 diff --git a/core/go.sum b/core/go.sum index 08b1fa3..c36f51e 100644 --- a/core/go.sum +++ b/core/go.sum @@ -1,5 +1,5 @@ -github.com/apernet/quic-go v0.45.3-0.20240803200022-e3824dfb4b89 h1:UKwGA1Lto7xQ3AGeTmBU7Y3WznMRTt9atOJ05xKNOSU= -github.com/apernet/quic-go v0.45.3-0.20240803200022-e3824dfb4b89/go.mod h1:MjGWpXA31DZZWESdX3/PjIpSWIT1fOm8FNCqyXXFZFU= +github.com/apernet/quic-go v0.46.1-0.20240816230517-268ed2476167 h1:+jKV1EuDJiUoa4XgRyle5w7wIo+0hil+YyUmwhd4ttk= +github.com/apernet/quic-go v0.46.1-0.20240816230517-268ed2476167/go.mod h1:MjGWpXA31DZZWESdX3/PjIpSWIT1fOm8FNCqyXXFZFU= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= diff --git a/extras/go.mod b/extras/go.mod index 0da032b..358ae7d 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -4,7 +4,7 @@ go 1.21 require ( github.com/apernet/hysteria/core/v2 v2.0.0-00010101000000-000000000000 - github.com/apernet/quic-go v0.45.3-0.20240803200022-e3824dfb4b89 + github.com/apernet/quic-go v0.46.1-0.20240816230517-268ed2476167 github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 github.com/hashicorp/golang-lru/v2 v2.0.5 github.com/miekg/dns v1.1.59 diff --git a/extras/go.sum b/extras/go.sum index d8658b3..e8be6d6 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -1,7 +1,7 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/apernet/quic-go v0.45.3-0.20240803200022-e3824dfb4b89 h1:UKwGA1Lto7xQ3AGeTmBU7Y3WznMRTt9atOJ05xKNOSU= -github.com/apernet/quic-go v0.45.3-0.20240803200022-e3824dfb4b89/go.mod h1:MjGWpXA31DZZWESdX3/PjIpSWIT1fOm8FNCqyXXFZFU= +github.com/apernet/quic-go v0.46.1-0.20240816230517-268ed2476167 h1:+jKV1EuDJiUoa4XgRyle5w7wIo+0hil+YyUmwhd4ttk= +github.com/apernet/quic-go v0.46.1-0.20240816230517-268ed2476167/go.mod h1:MjGWpXA31DZZWESdX3/PjIpSWIT1fOm8FNCqyXXFZFU= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= From 55c3a064cca4a8e2bb83cf054cd6e901d681302c Mon Sep 17 00:00:00 2001 From: Toby Date: Fri, 16 Aug 2024 20:48:14 -0700 Subject: [PATCH 114/161] fix: never overwrite the port --- extras/sniff/sniff.go | 22 +++++++++------------- extras/sniff/sniff_test.go | 2 +- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/extras/sniff/sniff.go b/extras/sniff/sniff.go index b2d7419..9994b8a 100644 --- a/extras/sniff/sniff.go +++ b/extras/sniff/sniff.go @@ -112,21 +112,17 @@ func (h *Sniffer) TCP(stream quic.Stream, reqAddr *string) ([]byte, error) { tr := &teeReader{Stream: stream, Pre: pre} req, _ := http.ReadRequest(bufio.NewReader(tr)) if req != nil && req.Host != "" { - // req.Host may already contain the port. - // If it does, just overwrite the whole address with req.Host. - // Otherwise, use the port in reqAddr. - _, _, err := net.SplitHostPort(req.Host) + // req.Host can be host:port, in which case we need to extract the host part + host, _, err := net.SplitHostPort(req.Host) if err != nil { - // Not host:port format, append the port from reqAddr - _, port, err := net.SplitHostPort(*reqAddr) - if err != nil { - return nil, err - } - *reqAddr = net.JoinHostPort(req.Host, port) - } else { - // Already host:port format - *reqAddr = req.Host + // No port, just use the whole string + host = req.Host } + _, port, err := net.SplitHostPort(*reqAddr) + if err != nil { + return nil, err + } + *reqAddr = net.JoinHostPort(host, port) } return tr.Buffer(), nil } else if h.isTLS(pre) { diff --git a/extras/sniff/sniff_test.go b/extras/sniff/sniff_test.go index 731cc6b..445660b 100644 --- a/extras/sniff/sniff_test.go +++ b/extras/sniff/sniff_test.go @@ -80,7 +80,7 @@ func TestSnifferTCP(t *testing.T) { putback, err = sniffer.TCP(stream, &reqAddr) assert.NoError(t, err) assert.Equal(t, *buf, putback) - assert.Equal(t, "example.com:8080", reqAddr) + assert.Equal(t, "example.com:10086", reqAddr) // Test TLS *buf, err = base64.StdEncoding.DecodeString("FgMBARcBAAETAwPJL2jlt1OAo+Rslkjv/aqKiTthKMaCKg2Gvd+uALDbDCDdY+UIk8ouadEB9fC3j52Y1i7SJZqGIgBRIS6kKieYrAAoEwITAcAswCvAMMAvwCTAI8AowCfACsAJwBTAEwCdAJwAPQA8ADUALwEAAKIAAAAOAAwAAAlpcGluZm8uaW8ABQAFAQAAAAAAKwAJCAMEAwMDAgMBAA0AGgAYCAQIBQgGBAEFAQIBBAMFAwIDAgIGAQYDACMAAAAKAAgABgAdABcAGAAQAAsACQhodHRwLzEuMQAzACYAJAAdACBguQbqNJNyamYxYcrBFpBP7pWv5TgZsP9gwGtMYNKVBQAxAAAAFwAA/wEAAQAALQACAQE=") From 00df1cab0f0c121a99a54ef5077d302ed49651c7 Mon Sep 17 00:00:00 2001 From: Haruue Date: Wed, 21 Aug 2024 18:18:41 +0800 Subject: [PATCH 115/161] fix(scripts): detect selinux with getenforce chcon is widely available in coreutils, even if the system doesn't support selinux. --- scripts/install_server.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install_server.sh b/scripts/install_server.sh index d222f33..0c85dc7 100644 --- a/scripts/install_server.sh +++ b/scripts/install_server.sh @@ -465,7 +465,7 @@ check_environment_systemd() { } check_environment_selinux() { - if ! has_command chcon; then + if ! has_command getenforce; then return fi From fd2d20a46a801e18289a99fe02625f6835c42969 Mon Sep 17 00:00:00 2001 From: Haruue Date: Sat, 24 Aug 2024 00:27:57 +0800 Subject: [PATCH 116/161] feat: local cert loader & sni guard --- app/cmd/server.go | 48 +++++--- app/internal/utils/certloader.go | 189 +++++++++++++++++++++++++++++++ 2 files changed, 221 insertions(+), 16 deletions(-) create mode 100644 app/internal/utils/certloader.go diff --git a/app/cmd/server.go b/app/cmd/server.go index b45fb15..3d37c09 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -83,8 +83,9 @@ type serverConfigObfs struct { } type serverConfigTLS struct { - Cert string `mapstructure:"cert"` - Key string `mapstructure:"key"` + Cert string `mapstructure:"cert"` + Key string `mapstructure:"key"` + SNIGuard string `mapstructure:"sniGuard"` // "disable", "dns-san", "strict" } type serverConfigACME struct { @@ -290,31 +291,46 @@ func (c *serverConfig) fillTLSConfig(hyConfig *server.Config) error { if c.TLS != nil && c.ACME != nil { return configError{Field: "tls", Err: errors.New("cannot set both tls and acme")} } + // SNI guard + var sniGuard utils.SNIGuardFunc + switch strings.ToLower(c.TLS.SNIGuard) { + case "", "dns-san": + sniGuard = utils.SNIGuardDNSSAN + case "strict": + sniGuard = utils.SNIGuardStrict + case "disable": + sniGuard = nil + default: + return configError{Field: "tls.sniGuard", Err: errors.New("unsupported SNI guard")} + } if c.TLS != nil { // Local TLS cert if c.TLS.Cert == "" || c.TLS.Key == "" { return configError{Field: "tls", Err: errors.New("empty cert or key path")} } + certLoader := &utils.LocalCertificateLoader{ + CertFile: c.TLS.Cert, + KeyFile: c.TLS.Key, + SNIGuard: sniGuard, + } // Try loading the cert-key pair here to catch errors early // (e.g. invalid files or insufficient permissions) - certPEMBlock, err := os.ReadFile(c.TLS.Cert) + err := certLoader.InitializeCache() if err != nil { - return configError{Field: "tls.cert", Err: err} - } - keyPEMBlock, err := os.ReadFile(c.TLS.Key) - if err != nil { - return configError{Field: "tls.key", Err: err} - } - _, err = tls.X509KeyPair(certPEMBlock, keyPEMBlock) - if err != nil { - return configError{Field: "tls", Err: fmt.Errorf("invalid cert-key pair: %w", err)} + var pathErr *os.PathError + if errors.As(err, &pathErr) { + if pathErr.Path == c.TLS.Cert { + return configError{Field: "tls.cert", Err: pathErr} + } + if pathErr.Path == c.TLS.Key { + return configError{Field: "tls.key", Err: pathErr} + } + } + return configError{Field: "tls", Err: err} } // Use GetCertificate instead of Certificates so that // users can update the cert without restarting the server. - hyConfig.TLSConfig.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { - cert, err := tls.LoadX509KeyPair(c.TLS.Cert, c.TLS.Key) - return &cert, err - } + hyConfig.TLSConfig.GetCertificate = certLoader.GetCertificate } else { // ACME dataDir := c.ACME.Dir diff --git a/app/internal/utils/certloader.go b/app/internal/utils/certloader.go new file mode 100644 index 0000000..390548e --- /dev/null +++ b/app/internal/utils/certloader.go @@ -0,0 +1,189 @@ +package utils + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "net" + "os" + "strings" + "sync" + "time" +) + +type LocalCertificateLoader struct { + CertFile string + KeyFile string + SNIGuard SNIGuardFunc + + lock sync.RWMutex + cache *localCertificateCache +} + +type SNIGuardFunc func(info *tls.ClientHelloInfo, cert *tls.Certificate) error + +// localCertificateCache holds the certificate and its mod times. +// this struct is designed to be read-only. +// +// to update the cache, use LocalCertificateLoader.makeCache and +// update the LocalCertificateLoader.cache field. +type localCertificateCache struct { + certificate *tls.Certificate + certModTime time.Time + keyModTime time.Time +} + +func (l *LocalCertificateLoader) InitializeCache() error { + cache, err := l.makeCache() + if err != nil { + return err + } + + l.lock.Lock() + defer l.lock.Unlock() + l.cache = cache + return nil +} + +func (l *LocalCertificateLoader) GetCertificate(info *tls.ClientHelloInfo) (*tls.Certificate, error) { + cert, err := l.getCertificateWithCache() + if err != nil { + return nil, err + } + + if l.SNIGuard == nil { + return cert, nil + } + err = l.SNIGuard(info, cert) + if err != nil { + return nil, err + } + + return cert, nil +} + +func (l *LocalCertificateLoader) checkModTime() (certModTime, keyModTime time.Time, err error) { + if fi, ferr := os.Stat(l.CertFile); ferr != nil { + err = fmt.Errorf("failed to stat certificate file: %w", ferr) + return + } else { + certModTime = fi.ModTime() + } + if fi, ferr := os.Stat(l.KeyFile); ferr != nil { + err = fmt.Errorf("failed to stat key file: %w", ferr) + return + } else { + keyModTime = fi.ModTime() + } + return +} + +func (l *LocalCertificateLoader) makeCache() (cache *localCertificateCache, err error) { + c := &localCertificateCache{} + + c.certModTime, c.keyModTime, err = l.checkModTime() + if err != nil { + return + } + + cert, err := tls.LoadX509KeyPair(l.CertFile, l.KeyFile) + if err != nil { + return + } + c.certificate = &cert + c.certificate.Leaf, err = x509.ParseCertificate(cert.Certificate[0]) + if err != nil { + return + } + + cache = c + return +} + +func (l *LocalCertificateLoader) getCertificateWithCache() (*tls.Certificate, error) { + l.lock.RLock() + cache := l.cache + l.lock.RUnlock() + + certModTime, keyModTime, terr := l.checkModTime() + if terr != nil { + if cache != nil { + // use cache when file is temporarily unavailable + return cache.certificate, nil + } + return nil, terr + } + + if cache != nil && cache.certModTime.Equal(certModTime) && cache.keyModTime.Equal(keyModTime) { + // cache is up-to-date + return cache.certificate, nil + } + + if cache != nil { + if !l.lock.TryLock() { + // another goroutine is updating the cache + return cache.certificate, nil + } + } else { + l.lock.Lock() + } + defer l.lock.Unlock() + + newCache, err := l.makeCache() + if err != nil { + if cache != nil { + // use cache when loading failed + return cache.certificate, nil + } + return nil, err + } + + l.cache = newCache + return newCache.certificate, nil +} + +// getNameFromClientHello returns a normalized form of hello.ServerName. +// If hello.ServerName is empty (i.e. client did not use SNI), then the +// associated connection's local address is used to extract an IP address. +// +// ref: https://github.com/caddyserver/certmagic/blob/3bad5b6bb595b09c14bd86ff0b365d302faaf5e2/handshake.go#L838 +func getNameFromClientHello(hello *tls.ClientHelloInfo) string { + normalizedName := func(serverName string) string { + return strings.ToLower(strings.TrimSpace(serverName)) + } + localIPFromConn := func(c net.Conn) string { + if c == nil { + return "" + } + localAddr := c.LocalAddr().String() + ip, _, err := net.SplitHostPort(localAddr) + if err != nil { + ip = localAddr + } + if scopeIDStart := strings.Index(ip, "%"); scopeIDStart > -1 { + ip = ip[:scopeIDStart] + } + return ip + } + + if name := normalizedName(hello.ServerName); name != "" { + return name + } + return localIPFromConn(hello.Conn) +} + +func SNIGuardDNSSAN(info *tls.ClientHelloInfo, cert *tls.Certificate) error { + if len(cert.Leaf.DNSNames) == 0 { + return nil + } + return SNIGuardStrict(info, cert) +} + +func SNIGuardStrict(info *tls.ClientHelloInfo, cert *tls.Certificate) error { + hostname := getNameFromClientHello(info) + err := cert.Leaf.VerifyHostname(hostname) + if err != nil { + return fmt.Errorf("sni guard: %w", err) + } + return nil +} From 57a48a674be1e731dcafb6580da1bb406106c270 Mon Sep 17 00:00:00 2001 From: Haruue Date: Sat, 24 Aug 2024 10:37:08 +0800 Subject: [PATCH 117/161] chore: replace rwlock with atomic pointer --- app/internal/utils/certloader.go | 40 ++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/app/internal/utils/certloader.go b/app/internal/utils/certloader.go index 390548e..6e4a7be 100644 --- a/app/internal/utils/certloader.go +++ b/app/internal/utils/certloader.go @@ -8,6 +8,7 @@ import ( "os" "strings" "sync" + "sync/atomic" "time" ) @@ -16,8 +17,8 @@ type LocalCertificateLoader struct { KeyFile string SNIGuard SNIGuardFunc - lock sync.RWMutex - cache *localCertificateCache + lock sync.Mutex + cache atomic.Pointer[localCertificateCache] } type SNIGuardFunc func(info *tls.ClientHelloInfo, cert *tls.Certificate) error @@ -34,14 +35,15 @@ type localCertificateCache struct { } func (l *LocalCertificateLoader) InitializeCache() error { + l.lock.Lock() + defer l.lock.Unlock() + cache, err := l.makeCache() if err != nil { return err } - l.lock.Lock() - defer l.lock.Unlock() - l.cache = cache + l.cache.Store(cache) return nil } @@ -63,18 +65,19 @@ func (l *LocalCertificateLoader) GetCertificate(info *tls.ClientHelloInfo) (*tls } func (l *LocalCertificateLoader) checkModTime() (certModTime, keyModTime time.Time, err error) { - if fi, ferr := os.Stat(l.CertFile); ferr != nil { - err = fmt.Errorf("failed to stat certificate file: %w", ferr) + fi, err := os.Stat(l.CertFile) + if err != nil { + err = fmt.Errorf("failed to stat certificate file: %w", err) return - } else { - certModTime = fi.ModTime() } - if fi, ferr := os.Stat(l.KeyFile); ferr != nil { - err = fmt.Errorf("failed to stat key file: %w", ferr) + certModTime = fi.ModTime() + + fi, err = os.Stat(l.KeyFile) + if err != nil { + err = fmt.Errorf("failed to stat key file: %w", err) return - } else { - keyModTime = fi.ModTime() } + keyModTime = fi.ModTime() return } @@ -101,9 +104,7 @@ func (l *LocalCertificateLoader) makeCache() (cache *localCertificateCache, err } func (l *LocalCertificateLoader) getCertificateWithCache() (*tls.Certificate, error) { - l.lock.RLock() - cache := l.cache - l.lock.RUnlock() + cache := l.cache.Load() certModTime, keyModTime, terr := l.checkModTime() if terr != nil { @@ -129,6 +130,11 @@ func (l *LocalCertificateLoader) getCertificateWithCache() (*tls.Certificate, er } defer l.lock.Unlock() + if l.cache.Load() != cache { + // another goroutine updated the cache + return l.cache.Load().certificate, nil + } + newCache, err := l.makeCache() if err != nil { if cache != nil { @@ -138,7 +144,7 @@ func (l *LocalCertificateLoader) getCertificateWithCache() (*tls.Certificate, er return nil, err } - l.cache = newCache + l.cache.Store(newCache) return newCache.certificate, nil } From 45893b5d1e5c69b1519051b24a46425b519f31a0 Mon Sep 17 00:00:00 2001 From: Haruue Date: Sat, 24 Aug 2024 13:40:42 +0800 Subject: [PATCH 118/161] test: update server_test for sniGuard --- app/cmd/server_test.go | 5 +++-- app/cmd/server_test.yaml | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/cmd/server_test.go b/app/cmd/server_test.go index bb2d12a..f35edfb 100644 --- a/app/cmd/server_test.go +++ b/app/cmd/server_test.go @@ -26,8 +26,9 @@ func TestServerConfig(t *testing.T) { }, }, TLS: &serverConfigTLS{ - Cert: "some.crt", - Key: "some.key", + Cert: "some.crt", + Key: "some.key", + SNIGuard: "strict", }, ACME: &serverConfigACME{ Domains: []string{ diff --git a/app/cmd/server_test.yaml b/app/cmd/server_test.yaml index ff0bf52..b7d1a3e 100644 --- a/app/cmd/server_test.yaml +++ b/app/cmd/server_test.yaml @@ -8,6 +8,7 @@ obfs: tls: cert: some.crt key: some.key + sniGuard: strict acme: domains: From bcf830c29aa2b73a04f0108cd22ae420e095de4c Mon Sep 17 00:00:00 2001 From: Haruue Date: Sat, 24 Aug 2024 13:41:46 +0800 Subject: [PATCH 119/161] chore: only init cert.Leaf when not populated since Go 1.23, cert.Leaf will be populated after loaded. see doc of tls.LoadX509KeyPair for details --- app/internal/utils/certloader.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/internal/utils/certloader.go b/app/internal/utils/certloader.go index 6e4a7be..fb41a3c 100644 --- a/app/internal/utils/certloader.go +++ b/app/internal/utils/certloader.go @@ -94,9 +94,12 @@ func (l *LocalCertificateLoader) makeCache() (cache *localCertificateCache, err return } c.certificate = &cert - c.certificate.Leaf, err = x509.ParseCertificate(cert.Certificate[0]) - if err != nil { - return + if c.certificate.Leaf == nil { + // certificate.Leaf was left nil by tls.LoadX509KeyPair before Go 1.23 + c.certificate.Leaf, err = x509.ParseCertificate(cert.Certificate[0]) + if err != nil { + return + } } cache = c From 667b08ec3e49e1a19a30046293bdf576768e8979 Mon Sep 17 00:00:00 2001 From: Haruue Date: Sat, 24 Aug 2024 17:25:31 +0800 Subject: [PATCH 120/161] test: add tests for certloader --- app/internal/utils/certloader_test.go | 139 ++++++++++++++++++ app/internal/utils/certloader_test_gencert.py | 134 +++++++++++++++++ .../utils/certloader_test_tlsclient.py | 60 ++++++++ app/internal/utils/testcerts/.gitignore | 3 + 4 files changed, 336 insertions(+) create mode 100644 app/internal/utils/certloader_test.go create mode 100644 app/internal/utils/certloader_test_gencert.py create mode 100644 app/internal/utils/certloader_test_tlsclient.py create mode 100644 app/internal/utils/testcerts/.gitignore diff --git a/app/internal/utils/certloader_test.go b/app/internal/utils/certloader_test.go new file mode 100644 index 0000000..7c5875c --- /dev/null +++ b/app/internal/utils/certloader_test.go @@ -0,0 +1,139 @@ +package utils + +import ( + "crypto/tls" + "log" + "net/http" + "os" + "os/exec" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +const ( + testListen = "127.82.39.147:12947" + testCAFile = "./testcerts/ca" + testCertFile = "./testcerts/cert" + testKeyFile = "./testcerts/key" +) + +func TestCertificateLoaderPathError(t *testing.T) { + assert.NoError(t, os.RemoveAll(testCertFile)) + assert.NoError(t, os.RemoveAll(testKeyFile)) + loader := LocalCertificateLoader{ + CertFile: testCertFile, + KeyFile: testKeyFile, + SNIGuard: SNIGuardStrict, + } + err := loader.InitializeCache() + var pathErr *os.PathError + assert.ErrorAs(t, err, &pathErr) +} + +func TestCertificateLoaderFullChain(t *testing.T) { + assert.NoError(t, generateTestCertificate([]string{"example.com"}, "fullchain")) + + loader := LocalCertificateLoader{ + CertFile: testCertFile, + KeyFile: testKeyFile, + SNIGuard: SNIGuardStrict, + } + assert.NoError(t, loader.InitializeCache()) + + lis, err := tls.Listen("tcp", testListen, &tls.Config{ + GetCertificate: loader.GetCertificate, + }) + assert.NoError(t, err) + defer lis.Close() + go http.Serve(lis, nil) + + assert.Error(t, runTestTLSClient("unmatched-sni.example.com")) + assert.Error(t, runTestTLSClient("")) + assert.NoError(t, runTestTLSClient("example.com")) +} + +func TestCertificateLoaderNoSAN(t *testing.T) { + assert.NoError(t, generateTestCertificate(nil, "selfsign")) + + loader := LocalCertificateLoader{ + CertFile: testCertFile, + KeyFile: testKeyFile, + SNIGuard: SNIGuardDNSSAN, + } + assert.NoError(t, loader.InitializeCache()) + + lis, err := tls.Listen("tcp", testListen, &tls.Config{ + GetCertificate: loader.GetCertificate, + }) + assert.NoError(t, err) + defer lis.Close() + go http.Serve(lis, nil) + + assert.NoError(t, runTestTLSClient("")) +} + +func TestCertificateLoaderReplaceCertificate(t *testing.T) { + assert.NoError(t, generateTestCertificate([]string{"example.com"}, "fullchain")) + + loader := LocalCertificateLoader{ + CertFile: testCertFile, + KeyFile: testKeyFile, + SNIGuard: SNIGuardStrict, + } + assert.NoError(t, loader.InitializeCache()) + + lis, err := tls.Listen("tcp", testListen, &tls.Config{ + GetCertificate: loader.GetCertificate, + }) + assert.NoError(t, err) + defer lis.Close() + go http.Serve(lis, nil) + + assert.NoError(t, runTestTLSClient("example.com")) + assert.Error(t, runTestTLSClient("2.example.com")) + + assert.NoError(t, generateTestCertificate([]string{"2.example.com"}, "fullchain")) + + assert.Error(t, runTestTLSClient("example.com")) + assert.NoError(t, runTestTLSClient("2.example.com")) +} + +func generateTestCertificate(dnssan []string, certType string) error { + args := []string{ + "certloader_test_gencert.py", + "--ca", testCAFile, + "--cert", testCertFile, + "--key", testKeyFile, + "--type", certType, + } + if len(dnssan) > 0 { + args = append(args, "--dnssan", strings.Join(dnssan, ",")) + } + cmd := exec.Command("python3", args...) + out, err := cmd.CombinedOutput() + if err != nil { + log.Printf("Failed to generate test certificate: %s", out) + return err + } + return nil +} + +func runTestTLSClient(sni string) error { + args := []string{ + "certloader_test_tlsclient.py", + "--server", testListen, + "--ca", testCAFile, + } + if sni != "" { + args = append(args, "--sni", sni) + } + cmd := exec.Command("python3", args...) + out, err := cmd.CombinedOutput() + if err != nil { + log.Printf("Failed to run test TLS client: %s", out) + return err + } + return nil +} diff --git a/app/internal/utils/certloader_test_gencert.py b/app/internal/utils/certloader_test_gencert.py new file mode 100644 index 0000000..d4d5695 --- /dev/null +++ b/app/internal/utils/certloader_test_gencert.py @@ -0,0 +1,134 @@ +import argparse +import datetime +from cryptography import x509 +from cryptography.x509.oid import NameOID +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption + + +def create_key(): + return ec.generate_private_key(ec.SECP256R1()) + + +def create_certificate(cert_type, subject, issuer, private_key, public_key, dns_san=None): + serial_number = x509.random_serial_number() + not_valid_before = datetime.datetime.now(datetime.UTC) + not_valid_after = not_valid_before + datetime.timedelta(days=365) + + subject_name = x509.Name([ + x509.NameAttribute(NameOID.COUNTRY_NAME, subject.get('C', 'ZZ')), + x509.NameAttribute(NameOID.ORGANIZATION_NAME, subject.get('O', 'No Organization')), + x509.NameAttribute(NameOID.COMMON_NAME, subject.get('CN', 'No CommonName')), + ]) + issuer_name = x509.Name([ + x509.NameAttribute(NameOID.COUNTRY_NAME, issuer.get('C', 'ZZ')), + x509.NameAttribute(NameOID.ORGANIZATION_NAME, issuer.get('O', 'No Organization')), + x509.NameAttribute(NameOID.COMMON_NAME, issuer.get('CN', 'No CommonName')), + ]) + builder = x509.CertificateBuilder() + builder = builder.subject_name(subject_name) + builder = builder.issuer_name(issuer_name) + builder = builder.public_key(public_key) + builder = builder.serial_number(serial_number) + builder = builder.not_valid_before(not_valid_before) + builder = builder.not_valid_after(not_valid_after) + if cert_type == 'root': + builder = builder.add_extension( + x509.BasicConstraints(ca=True, path_length=None), critical=True + ) + elif cert_type == 'intermediate': + builder = builder.add_extension( + x509.BasicConstraints(ca=True, path_length=0), critical=True + ) + elif cert_type == 'leaf': + builder = builder.add_extension( + x509.BasicConstraints(ca=False, path_length=None), critical=True + ) + else: + raise ValueError(f'Invalid cert_type: {cert_type}') + if dns_san: + builder = builder.add_extension( + x509.SubjectAlternativeName([x509.DNSName(d) for d in dns_san.split(',')]), + critical=False + ) + return builder.sign(private_key=private_key, algorithm=hashes.SHA256()) + + +def main(): + parser = argparse.ArgumentParser(description='Generate HTTPS server certificate.') + parser.add_argument('--ca', required=True, + help='Path to write the X509 CA certificate in PEM format') + parser.add_argument('--cert', required=True, + help='Path to write the X509 certificate in PEM format') + parser.add_argument('--key', required=True, + help='Path to write the private key in PEM format') + parser.add_argument('--dnssan', required=False, default=None, + help='Comma-separated list of DNS SANs') + parser.add_argument('--type', required=True, choices=['selfsign', 'fullchain'], + help='Type of certificate to generate') + + args = parser.parse_args() + + key = create_key() + public_key = key.public_key() + + if args.type == 'selfsign': + subject = {"C": "ZZ", "O": "Certificate", "CN": "Certificate"} + cert = create_certificate( + cert_type='root', + subject=subject, + issuer=subject, + private_key=key, + public_key=public_key, + dns_san=args.dnssan) + with open(args.ca, 'wb') as f: + f.write(cert.public_bytes(Encoding.PEM)) + with open(args.cert, 'wb') as f: + f.write(cert.public_bytes(Encoding.PEM)) + with open(args.key, 'wb') as f: + f.write( + key.private_bytes(Encoding.PEM, PrivateFormat.TraditionalOpenSSL, NoEncryption())) + + elif args.type == 'fullchain': + ca_key = create_key() + ca_public_key = ca_key.public_key() + ca_subject = {"C": "ZZ", "O": "Root CA", "CN": "Root CA"} + ca_cert = create_certificate( + cert_type='root', + subject=ca_subject, + issuer=ca_subject, + private_key=ca_key, + public_key=ca_public_key) + + intermediate_key = create_key() + intermediate_public_key = intermediate_key.public_key() + intermediate_subject = {"C": "ZZ", "O": "Intermediate CA", "CN": "Intermediate CA"} + intermediate_cert = create_certificate( + cert_type='intermediate', + subject=intermediate_subject, + issuer=ca_subject, + private_key=ca_key, + public_key=intermediate_public_key) + + leaf_subject = {"C": "ZZ", "O": "Leaf Certificate", "CN": "Leaf Certificate"} + cert = create_certificate( + cert_type='leaf', + subject=leaf_subject, + issuer=intermediate_subject, + private_key=intermediate_key, + public_key=public_key, + dns_san=args.dnssan) + + with open(args.ca, 'wb') as f: + f.write(ca_cert.public_bytes(Encoding.PEM)) + with open(args.cert, 'wb') as f: + f.write(cert.public_bytes(Encoding.PEM)) + f.write(intermediate_cert.public_bytes(Encoding.PEM)) + with open(args.key, 'wb') as f: + f.write( + key.private_bytes(Encoding.PEM, PrivateFormat.TraditionalOpenSSL, NoEncryption())) + + +if __name__ == "__main__": + main() diff --git a/app/internal/utils/certloader_test_tlsclient.py b/app/internal/utils/certloader_test_tlsclient.py new file mode 100644 index 0000000..3b7efd6 --- /dev/null +++ b/app/internal/utils/certloader_test_tlsclient.py @@ -0,0 +1,60 @@ +import argparse +import ssl +import socket +import sys + + +def check_tls(server, ca_cert, sni, alpn): + try: + host, port = server.split(":") + port = int(port) + + if ca_cert: + context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=ca_cert) + context.check_hostname = sni is not None + context.verify_mode = ssl.CERT_REQUIRED + else: + context = ssl.create_default_context() + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + + if alpn: + context.set_alpn_protocols([p for p in alpn.split(",")]) + + with socket.create_connection((host, port)) as sock: + with context.wrap_socket(sock, server_hostname=sni) as ssock: + # Verify handshake and certificate + print(f'Connected to {ssock.version()} using {ssock.cipher()}') + print(f'Server certificate validated and details: {ssock.getpeercert()}') + print("OK") + return 0 + except Exception as e: + print(f"Error: {e}") + return 1 + + +def main(): + parser = argparse.ArgumentParser(description="Test TLS Server") + parser.add_argument("--server", required=True, + help="Server address to test (e.g., 127.1.2.3:8443)") + parser.add_argument("--ca", required=False, default=None, + help="CA certificate file used to validate the server certificate" + "Omit to use insecure connection") + parser.add_argument("--sni", required=False, default=None, + help="SNI to send in ClientHello") + parser.add_argument("--alpn", required=False, default='h2', + help="ALPN to send in ClientHello") + + args = parser.parse_args() + + exit_status = check_tls( + server=args.server, + ca_cert=args.ca, + sni=args.sni, + alpn=args.alpn) + + sys.exit(exit_status) + + +if __name__ == "__main__": + main() diff --git a/app/internal/utils/testcerts/.gitignore b/app/internal/utils/testcerts/.gitignore new file mode 100644 index 0000000..082821a --- /dev/null +++ b/app/internal/utils/testcerts/.gitignore @@ -0,0 +1,3 @@ +# This directory is used for certificate generation in certloader_test.go +/* +!/.gitignore From 4ed3f21d7293dd6ac3bf3a9dc4c1fa0e28247379 Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 24 Aug 2024 17:07:45 -0700 Subject: [PATCH 121/161] fix: crash when the tls option is not used & change from python3 to python --- app/cmd/server.go | 24 ++++++++++++------------ app/internal/utils/certloader_test.go | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/cmd/server.go b/app/cmd/server.go index 3d37c09..3da748d 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -291,19 +291,19 @@ func (c *serverConfig) fillTLSConfig(hyConfig *server.Config) error { if c.TLS != nil && c.ACME != nil { return configError{Field: "tls", Err: errors.New("cannot set both tls and acme")} } - // SNI guard - var sniGuard utils.SNIGuardFunc - switch strings.ToLower(c.TLS.SNIGuard) { - case "", "dns-san": - sniGuard = utils.SNIGuardDNSSAN - case "strict": - sniGuard = utils.SNIGuardStrict - case "disable": - sniGuard = nil - default: - return configError{Field: "tls.sniGuard", Err: errors.New("unsupported SNI guard")} - } if c.TLS != nil { + // SNI guard + var sniGuard utils.SNIGuardFunc + switch strings.ToLower(c.TLS.SNIGuard) { + case "", "dns-san": + sniGuard = utils.SNIGuardDNSSAN + case "strict": + sniGuard = utils.SNIGuardStrict + case "disable": + sniGuard = nil + default: + return configError{Field: "tls.sniGuard", Err: errors.New("unsupported SNI guard")} + } // Local TLS cert if c.TLS.Cert == "" || c.TLS.Key == "" { return configError{Field: "tls", Err: errors.New("empty cert or key path")} diff --git a/app/internal/utils/certloader_test.go b/app/internal/utils/certloader_test.go index 7c5875c..3a8e26b 100644 --- a/app/internal/utils/certloader_test.go +++ b/app/internal/utils/certloader_test.go @@ -111,7 +111,7 @@ func generateTestCertificate(dnssan []string, certType string) error { if len(dnssan) > 0 { args = append(args, "--dnssan", strings.Join(dnssan, ",")) } - cmd := exec.Command("python3", args...) + cmd := exec.Command("python", args...) out, err := cmd.CombinedOutput() if err != nil { log.Printf("Failed to generate test certificate: %s", out) @@ -129,7 +129,7 @@ func runTestTLSClient(sni string) error { if sni != "" { args = append(args, "--sni", sni) } - cmd := exec.Command("python3", args...) + cmd := exec.Command("python", args...) out, err := cmd.CombinedOutput() if err != nil { log.Printf("Failed to run test TLS client: %s", out) From d4b9c5a822d17d3e2ae59a7696e4ffbab0c67dbc Mon Sep 17 00:00:00 2001 From: Haruue Date: Sun, 25 Aug 2024 13:36:45 +0800 Subject: [PATCH 122/161] test: add requirements.txt for ut scripts --- requirements.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..44ee651 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,11 @@ +blinker==1.8.2 +cffi==1.17.0 +click==8.1.7 +cryptography==43.0.0 +Flask==3.0.3 +itsdangerous==2.2.0 +Jinja2==3.1.4 +MarkupSafe==2.1.5 +pycparser==2.22 +PySocks==1.7.1 +Werkzeug==3.0.4 From 4ecbd572947b3dc41d089831612d7dd67140e91d Mon Sep 17 00:00:00 2001 From: Haruue Date: Sun, 22 Sep 2024 22:48:06 +0800 Subject: [PATCH 123/161] fix: quic sniff not work if udp msg fragmentated --- core/server/udp.go | 198 ++++++++++++++++++++++++++++++++------------- 1 file changed, 143 insertions(+), 55 deletions(-) diff --git a/core/server/udp.go b/core/server/udp.go index ecaee29..0ec0d5e 100644 --- a/core/server/udp.go +++ b/core/server/udp.go @@ -31,11 +31,62 @@ type udpEventLogger interface { type udpSessionEntry struct { ID uint32 - Conn UDPConn OverrideAddr string // Ignore the address in the UDP message, always use this if not empty + OriginalAddr string // The original address in the UDP message D *frag.Defragger Last *utils.AtomicTime - Timeout bool // true if the session is closed due to timeout + IO udpIO + + DialFunc func(addr string, firstMsgData []byte) (conn UDPConn, actualAddr string, err error) + ExitFunc func(err error) + + timeoutChan chan struct{} + exitChan chan error + + conn UDPConn + connLock sync.Mutex + closed bool +} + +func newUDPSessionEntry( + id uint32, io udpIO, + dialFunc func(string, []byte) (UDPConn, string, error), + exitFunc func(error), +) (e *udpSessionEntry) { + e = &udpSessionEntry{ + ID: id, + D: &frag.Defragger{}, + Last: utils.NewAtomicTime(time.Now()), + IO: io, + + DialFunc: dialFunc, + ExitFunc: exitFunc, + + timeoutChan: make(chan struct{}), + exitChan: make(chan error, 2), + } + + go func() { + // Guard routine + var err error + select { + case <-e.timeoutChan: + // Use nil error to indicate timeout. + case err = <-e.exitChan: + } + + // We need this lock to ensure not to create conn after session exit + e.connLock.Lock() + e.closed = true + if e.conn != nil { + _ = e.conn.Close() + } + e.connLock.Unlock() + + e.ExitFunc(err) + }() + + return } // Feed feeds a UDP message to the session. @@ -49,27 +100,72 @@ func (e *udpSessionEntry) Feed(msg *protocol.UDPMessage) (int, error) { if dfMsg == nil { return 0, nil } - if e.OverrideAddr != "" { - return e.Conn.WriteTo(dfMsg.Data, e.OverrideAddr) - } else { - return e.Conn.WriteTo(dfMsg.Data, dfMsg.Addr) + + if e.conn == nil { + err := e.initConn(dfMsg) + if err != nil { + return 0, err + } } + + addr := dfMsg.Addr + if e.OverrideAddr != "" { + addr = e.OverrideAddr + } + + return e.conn.WriteTo(dfMsg.Data, addr) } -// ReceiveLoop receives incoming UDP packets, packs them into UDP messages, -// and sends using the provided io. -// Exit and returns error when either the underlying UDP connection returns -// error (e.g. closed), or the provided io returns error when sending. -func (e *udpSessionEntry) ReceiveLoop(io udpIO) error { +// initConn initializes the UDP connection of the session. +// If no error is returned, the e.conn is set to the new connection. +func (e *udpSessionEntry) initConn(firstMsg *protocol.UDPMessage) error { + // We need this lock to ensure not to create conn after session exit + e.connLock.Lock() + defer e.connLock.Unlock() + + if e.closed { + return errors.New("session is closed") + } + + conn, actualAddr, err := e.DialFunc(firstMsg.Addr, firstMsg.Data) + if err != nil { + // Fail fast if DailFunc failed + // (usually indicates the connection has been rejected by the ACL) + e.exitChan <- err + return err + } + + e.conn = conn + if firstMsg.Addr != actualAddr { + e.OverrideAddr = actualAddr + e.OriginalAddr = firstMsg.Addr + } + go e.receiveLoop() + return nil +} + +// receiveLoop receives incoming UDP packets, packs them into UDP messages, +// and sends using the IO. +// Exit when either the underlying UDP connection returns error (e.g. closed), +// or the IO returns error when sending. +func (e *udpSessionEntry) receiveLoop() { udpBuf := make([]byte, protocol.MaxUDPSize) msgBuf := make([]byte, protocol.MaxUDPSize) for { - udpN, rAddr, err := e.Conn.ReadFrom(udpBuf) + udpN, rAddr, err := e.conn.ReadFrom(udpBuf) if err != nil { - return err + e.exitChan <- err + return } e.Last.Set(time.Now()) + if e.OriginalAddr != "" { + // Use the original address in the opposite direction, + // otherwise the QUIC clients or NAT on the client side + // may not treat it as the same UDP session. + rAddr = e.OriginalAddr + } + msg := &protocol.UDPMessage{ SessionID: e.ID, PacketID: 0, @@ -78,13 +174,23 @@ func (e *udpSessionEntry) ReceiveLoop(io udpIO) error { Addr: rAddr, Data: udpBuf[:udpN], } - err = sendMessageAutoFrag(io, msgBuf, msg) + err = sendMessageAutoFrag(e.IO, msgBuf, msg) if err != nil { - return err + e.exitChan <- err + return } } } +// MarkTimeout marks the session to be cleaned up due to timeout. +// Should only be called by the cleanup routine of the session manager. +func (e *udpSessionEntry) MarkTimeout() { + select { + case e.timeoutChan <- struct{}{}: + default: + } +} + // sendMessageAutoFrag tries to send a UDP message as a whole first, // but if it fails due to quic.ErrMessageTooLarge, it tries again by // fragmenting the message. @@ -168,10 +274,8 @@ func (m *udpSessionManager) cleanup(idleOnly bool) { now := time.Now() for _, entry := range m.m { if !idleOnly || now.Sub(entry.Last.Get()) > m.idleTimeout { - entry.Timeout = true - _ = entry.Conn.Close() - // Closing the connection here will cause the ReceiveLoop to exit, - // and the session will be removed from the map there. + entry.MarkTimeout() + // Entry will be removed by its ExitFunc. } } } @@ -183,47 +287,31 @@ func (m *udpSessionManager) feed(msg *protocol.UDPMessage) { // Create a new session if not exists if entry == nil { - // Call the hook - origMsgAddr := msg.Addr - err := m.io.Hook(msg.Data, &msg.Addr) - if err != nil { - return - } - // Log the event - m.eventLogger.New(msg.SessionID, msg.Addr) - // Dial target & create a new session entry - conn, err := m.io.UDP(msg.Addr) - if err != nil { - m.eventLogger.Close(msg.SessionID, err) - return - } - entry = &udpSessionEntry{ - ID: msg.SessionID, - Conn: conn, - D: &frag.Defragger{}, - Last: utils.NewAtomicTime(time.Now()), - } - if origMsgAddr != msg.Addr { - // Hook changed the address, enable address override - entry.OverrideAddr = msg.Addr - } - // Start the receive loop for this session - go func() { - err := entry.ReceiveLoop(m.io) - if !entry.Timeout { - _ = entry.Conn.Close() - m.eventLogger.Close(entry.ID, err) - } else { - // Connection already closed by timeout cleanup, - // no need to close again here. - // Use nil error to indicate timeout. - m.eventLogger.Close(entry.ID, nil) + dialFunc := func(addr string, firstMsgData []byte) (conn UDPConn, actualAddr string, err error) { + // Call the hook + err = m.io.Hook(firstMsgData, &addr) + if err != nil { + return } + actualAddr = addr + // Log the event + m.eventLogger.New(msg.SessionID, addr) + // Dial target + conn, err = m.io.UDP(addr) + return + } + exitFunc := func(err error) { + // Log the event + m.eventLogger.Close(entry.ID, err) + // Remove the session from the map m.mutex.Lock() delete(m.m, entry.ID) m.mutex.Unlock() - }() + } + + entry = newUDPSessionEntry(msg.SessionID, m.io, dialFunc, exitFunc) + // Insert the session into the map m.mutex.Lock() m.m[msg.SessionID] = entry From 931fc2fdb2bbbba0e78f131e020898cc597abb0b Mon Sep 17 00:00:00 2001 From: Haruue Date: Fri, 4 Oct 2024 11:27:36 +0800 Subject: [PATCH 124/161] chore: replace guard routine with CloseWithErr() --- core/server/udp.go | 64 +++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 35 deletions(-) diff --git a/core/server/udp.go b/core/server/udp.go index 0ec0d5e..3d7cd71 100644 --- a/core/server/udp.go +++ b/core/server/udp.go @@ -40,9 +40,6 @@ type udpSessionEntry struct { DialFunc func(addr string, firstMsgData []byte) (conn UDPConn, actualAddr string, err error) ExitFunc func(err error) - timeoutChan chan struct{} - exitChan chan error - conn UDPConn connLock sync.Mutex closed bool @@ -61,34 +58,30 @@ func newUDPSessionEntry( DialFunc: dialFunc, ExitFunc: exitFunc, - - timeoutChan: make(chan struct{}), - exitChan: make(chan error, 2), } - go func() { - // Guard routine - var err error - select { - case <-e.timeoutChan: - // Use nil error to indicate timeout. - case err = <-e.exitChan: - } - - // We need this lock to ensure not to create conn after session exit - e.connLock.Lock() - e.closed = true - if e.conn != nil { - _ = e.conn.Close() - } - e.connLock.Unlock() - - e.ExitFunc(err) - }() - return } +func (e *udpSessionEntry) CloseWithErr(err error) { + // We need this lock to ensure not to create conn after session exit + e.connLock.Lock() + + if e.closed { + // Already closed + e.connLock.Unlock() + return + } + + e.closed = true + if e.conn != nil { + _ = e.conn.Close() + } + e.connLock.Unlock() + + e.ExitFunc(err) +} + // Feed feeds a UDP message to the session. // If the message itself is a complete message, or it completes a fragmented message, // the message is written to the session's UDP connection, and the number of bytes @@ -121,17 +114,18 @@ func (e *udpSessionEntry) Feed(msg *protocol.UDPMessage) (int, error) { func (e *udpSessionEntry) initConn(firstMsg *protocol.UDPMessage) error { // We need this lock to ensure not to create conn after session exit e.connLock.Lock() - defer e.connLock.Unlock() if e.closed { + e.connLock.Unlock() return errors.New("session is closed") } conn, actualAddr, err := e.DialFunc(firstMsg.Addr, firstMsg.Data) if err != nil { - // Fail fast if DailFunc failed + // Fail fast if DialFunc failed // (usually indicates the connection has been rejected by the ACL) - e.exitChan <- err + e.connLock.Unlock() + e.CloseWithErr(err) return err } @@ -141,6 +135,8 @@ func (e *udpSessionEntry) initConn(firstMsg *protocol.UDPMessage) error { e.OriginalAddr = firstMsg.Addr } go e.receiveLoop() + + e.connLock.Unlock() return nil } @@ -154,7 +150,7 @@ func (e *udpSessionEntry) receiveLoop() { for { udpN, rAddr, err := e.conn.ReadFrom(udpBuf) if err != nil { - e.exitChan <- err + e.CloseWithErr(err) return } e.Last.Set(time.Now()) @@ -176,7 +172,7 @@ func (e *udpSessionEntry) receiveLoop() { } err = sendMessageAutoFrag(e.IO, msgBuf, msg) if err != nil { - e.exitChan <- err + e.CloseWithErr(err) return } } @@ -185,10 +181,8 @@ func (e *udpSessionEntry) receiveLoop() { // MarkTimeout marks the session to be cleaned up due to timeout. // Should only be called by the cleanup routine of the session manager. func (e *udpSessionEntry) MarkTimeout() { - select { - case e.timeoutChan <- struct{}{}: - default: - } + // nil error indicates timeout. + e.CloseWithErr(nil) } // sendMessageAutoFrag tries to send a UDP message as a whole first, From dc023ae13a2ed16c260ab566f9086cc56e8685bd Mon Sep 17 00:00:00 2001 From: Haruue Date: Fri, 4 Oct 2024 16:33:41 +0800 Subject: [PATCH 125/161] fix: udpSessionManager.mutex reentrant by cleanup --- core/server/udp.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/core/server/udp.go b/core/server/udp.go index 3d7cd71..ec470fc 100644 --- a/core/server/udp.go +++ b/core/server/udp.go @@ -262,16 +262,21 @@ func (m *udpSessionManager) idleCleanupLoop(stopCh <-chan struct{}) { func (m *udpSessionManager) cleanup(idleOnly bool) { // We use RLock here as we are only scanning the map, not deleting from it. - m.mutex.RLock() - defer m.mutex.RUnlock() + timeoutEntry := make([]*udpSessionEntry, 0, len(m.m)) + m.mutex.RLock() now := time.Now() for _, entry := range m.m { if !idleOnly || now.Sub(entry.Last.Get()) > m.idleTimeout { - entry.MarkTimeout() - // Entry will be removed by its ExitFunc. + timeoutEntry = append(timeoutEntry, entry) } } + m.mutex.RUnlock() + + for _, entry := range timeoutEntry { + entry.MarkTimeout() + // Entry will be removed by its ExitFunc. + } } func (m *udpSessionManager) feed(msg *protocol.UDPMessage) { From 4e2f138008c7fa91f4c84624f074207389ddd11f Mon Sep 17 00:00:00 2001 From: Haruue Date: Fri, 4 Oct 2024 16:40:15 +0800 Subject: [PATCH 126/161] chore: fix comments --- core/server/udp.go | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/core/server/udp.go b/core/server/udp.go index ec470fc..eb10b19 100644 --- a/core/server/udp.go +++ b/core/server/udp.go @@ -63,6 +63,8 @@ func newUDPSessionEntry( return } +// CloseWithErr closes the session and calls ExitFunc with the given error. +// A nil error indicates the session is cleaned up due to timeout. func (e *udpSessionEntry) CloseWithErr(err error) { // We need this lock to ensure not to create conn after session exit e.connLock.Lock() @@ -125,6 +127,7 @@ func (e *udpSessionEntry) initConn(firstMsg *protocol.UDPMessage) error { // Fail fast if DialFunc failed // (usually indicates the connection has been rejected by the ACL) e.connLock.Unlock() + // CloseWithErr acquires the connLock again e.CloseWithErr(err) return err } @@ -178,13 +181,6 @@ func (e *udpSessionEntry) receiveLoop() { } } -// MarkTimeout marks the session to be cleaned up due to timeout. -// Should only be called by the cleanup routine of the session manager. -func (e *udpSessionEntry) MarkTimeout() { - // nil error indicates timeout. - e.CloseWithErr(nil) -} - // sendMessageAutoFrag tries to send a UDP message as a whole first, // but if it fails due to quic.ErrMessageTooLarge, it tries again by // fragmenting the message. @@ -261,9 +257,9 @@ func (m *udpSessionManager) idleCleanupLoop(stopCh <-chan struct{}) { } func (m *udpSessionManager) cleanup(idleOnly bool) { - // We use RLock here as we are only scanning the map, not deleting from it. timeoutEntry := make([]*udpSessionEntry, 0, len(m.m)) + // We use RLock here as we are only scanning the map, not deleting from it. m.mutex.RLock() now := time.Now() for _, entry := range m.m { @@ -274,8 +270,9 @@ func (m *udpSessionManager) cleanup(idleOnly bool) { m.mutex.RUnlock() for _, entry := range timeoutEntry { - entry.MarkTimeout() - // Entry will be removed by its ExitFunc. + // This eventually calls entry.ExitFunc, + // where the m.mutex will be locked again to remove the entry from the map. + entry.CloseWithErr(nil) } } From 947701897b0562230f133b6cc81674f6380bb898 Mon Sep 17 00:00:00 2001 From: Toby Date: Fri, 4 Oct 2024 10:29:25 -0700 Subject: [PATCH 127/161] fix: TestClientServerHookUDP --- core/internal/integration_tests/hook_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/internal/integration_tests/hook_test.go b/core/internal/integration_tests/hook_test.go index 1121d13..64affe8 100644 --- a/core/internal/integration_tests/hook_test.go +++ b/core/internal/integration_tests/hook_test.go @@ -132,7 +132,9 @@ func TestClientServerHookUDP(t *testing.T) { rData, rAddr, err := conn.Receive() assert.NoError(t, err) assert.Equal(t, sData, rData) - assert.Equal(t, realEchoAddr, rAddr) + // Hook address change is transparent, + // the client should still see the fake echo address it sent packets to + assert.Equal(t, fakeEchoAddr, rAddr) // Subsequent packets should also be sent to the real echo server sData = []byte("never stop fighting") @@ -141,5 +143,5 @@ func TestClientServerHookUDP(t *testing.T) { rData, rAddr, err = conn.Receive() assert.NoError(t, err) assert.Equal(t, sData, rData) - assert.Equal(t, realEchoAddr, rAddr) + assert.Equal(t, fakeEchoAddr, rAddr) } From b3116c62682871a3a15234c1493a79aad805b196 Mon Sep 17 00:00:00 2001 From: Toby Date: Fri, 4 Oct 2024 10:47:41 -0700 Subject: [PATCH 128/161] feat: update TestUDPSessionManager to cover the fragmented msg hook --- core/server/udp_test.go | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/core/server/udp_test.go b/core/server/udp_test.go index 045edbd..8aa899f 100644 --- a/core/server/udp_test.go +++ b/core/server/udp_test.go @@ -25,7 +25,6 @@ func TestUDPSessionManager(t *testing.T) { } return m, nil }) - io.EXPECT().Hook(mock.Anything, mock.Anything).Return(nil) go sm.Run() @@ -50,6 +49,7 @@ func TestUDPSessionManager(t *testing.T) { eventLogger.EXPECT().New(msg1.SessionID, msg1.Addr).Return().Once() udpConn1 := newMockUDPConn(t) udpConn1Ch := make(chan []byte, 1) + io.EXPECT().Hook(msg1.Data, &msg1.Addr).Return(nil).Once() io.EXPECT().UDP(msg1.Addr).Return(udpConn1, nil).Once() udpConn1.EXPECT().WriteTo(msg1.Data, msg1.Addr).Return(5, nil).Once() udpConn1.EXPECT().ReadFrom(mock.Anything).RunAndReturn(func(b []byte) (int, string, error) { @@ -66,31 +66,44 @@ func TestUDPSessionManager(t *testing.T) { msgCh <- msg1 udpConn1Ch <- []byte("hi back") - msg2 := &protocol.UDPMessage{ + msg2data := []byte("how are you doing?") + msg2_1 := &protocol.UDPMessage{ SessionID: 5678, PacketID: 0, FragID: 0, - FragCount: 1, + FragCount: 2, Addr: "address2.net:12450", - Data: []byte("how are you"), + Data: msg2data[:6], } - eventLogger.EXPECT().New(msg2.SessionID, msg2.Addr).Return().Once() + msg2_2 := &protocol.UDPMessage{ + SessionID: 5678, + PacketID: 0, + FragID: 1, + FragCount: 2, + Addr: "address2.net:12450", + Data: msg2data[6:], + } + + eventLogger.EXPECT().New(msg2_1.SessionID, msg2_1.Addr).Return().Once() udpConn2 := newMockUDPConn(t) udpConn2Ch := make(chan []byte, 1) - io.EXPECT().UDP(msg2.Addr).Return(udpConn2, nil).Once() - udpConn2.EXPECT().WriteTo(msg2.Data, msg2.Addr).Return(11, nil).Once() + // On fragmentation, make sure hook gets the whole message + io.EXPECT().Hook(msg2data, &msg2_1.Addr).Return(nil).Once() + io.EXPECT().UDP(msg2_1.Addr).Return(udpConn2, nil).Once() + udpConn2.EXPECT().WriteTo(msg2data, msg2_1.Addr).Return(11, nil).Once() udpConn2.EXPECT().ReadFrom(mock.Anything).RunAndReturn(func(b []byte) (int, string, error) { - return udpReadFunc(msg2.Addr, udpConn2Ch, b) + return udpReadFunc(msg2_1.Addr, udpConn2Ch, b) }) io.EXPECT().SendMessage(mock.Anything, &protocol.UDPMessage{ - SessionID: msg2.SessionID, + SessionID: msg2_1.SessionID, PacketID: 0, FragID: 0, FragCount: 1, - Addr: msg2.Addr, + Addr: msg2_1.Addr, Data: []byte("im fine"), }).Return(nil).Once() - msgCh <- msg2 + msgCh <- msg2_1 + msgCh <- msg2_2 udpConn2Ch <- []byte("im fine") msg3 := &protocol.UDPMessage{ @@ -123,7 +136,7 @@ func TestUDPSessionManager(t *testing.T) { return nil }).Once() eventLogger.EXPECT().Close(msg1.SessionID, nil).Once() - eventLogger.EXPECT().Close(msg2.SessionID, nil).Once() + eventLogger.EXPECT().Close(msg2_1.SessionID, nil).Once() time.Sleep(3 * time.Second) // Wait for timeout mock.AssertExpectationsForObjects(t, io, eventLogger, udpConn1, udpConn2) @@ -140,6 +153,7 @@ func TestUDPSessionManager(t *testing.T) { } eventLogger.EXPECT().New(msg4.SessionID, msg4.Addr).Return().Once() udpConn4 := newMockUDPConn(t) + io.EXPECT().Hook(msg4.Data, &msg4.Addr).Return(nil).Once() io.EXPECT().UDP(msg4.Addr).Return(udpConn4, nil).Once() udpConn4.EXPECT().WriteTo(msg4.Data, msg4.Addr).Return(12, nil).Once() udpConn4.EXPECT().ReadFrom(mock.Anything).Return(0, "", errUDPClosed).Once() @@ -161,6 +175,7 @@ func TestUDPSessionManager(t *testing.T) { Data: []byte("babe i miss you"), } eventLogger.EXPECT().New(msg5.SessionID, msg5.Addr).Return().Once() + io.EXPECT().Hook(msg5.Data, &msg5.Addr).Return(nil).Once() io.EXPECT().UDP(msg5.Addr).Return(nil, errUDPIO).Once() eventLogger.EXPECT().Close(msg5.SessionID, errUDPIO).Once() msgCh <- msg5 From ef6da94927a6631cbed074dc1c96062b16aa0930 Mon Sep 17 00:00:00 2001 From: Toby Date: Fri, 4 Oct 2024 11:21:30 -0700 Subject: [PATCH 129/161] feat: quic-go v0.47.0 --- .github/workflows/master.yml | 2 +- .github/workflows/release.yml | 2 +- app/go.mod | 18 ++++++++++-------- app/go.sum | 28 ++++++++++++++-------------- core/go.mod | 16 +++++++++------- core/go.sum | 28 ++++++++++++++-------------- extras/go.mod | 18 ++++++++++-------- extras/go.sum | 28 ++++++++++++++-------------- go.work | 4 +++- go.work.sum | 2 ++ 10 files changed, 78 insertions(+), 68 deletions(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 7a7c523..2b852fc 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -19,7 +19,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.22" + go-version: "1.23" - name: Setup Python # This is for the build script uses: actions/setup-python@v5 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 70710c0..d7a2433 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,7 +23,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.22" + go-version: "1.23" - name: Setup Python # This is for the build script uses: actions/setup-python@v5 diff --git a/app/go.mod b/app/go.mod index f995c78..7353f6a 100644 --- a/app/go.mod +++ b/app/go.mod @@ -1,6 +1,8 @@ module github.com/apernet/hysteria/app/v2 -go 1.21 +go 1.22 + +toolchain go1.23.2 require ( github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f @@ -23,12 +25,12 @@ require ( github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 go.uber.org/zap v1.24.0 golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 - golang.org/x/sys v0.21.0 + golang.org/x/sys v0.23.0 ) require ( github.com/andybalholm/brotli v1.1.0 // indirect - github.com/apernet/quic-go v0.46.1-0.20240816230517-268ed2476167 // indirect + github.com/apernet/quic-go v0.47.1-0.20241004180137-a80d14e2080d // indirect github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect github.com/cloudflare/circl v1.3.9 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -53,7 +55,7 @@ require ( github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/quic-go/qpack v0.4.0 // indirect + github.com/quic-go/qpack v0.5.1 // indirect github.com/refraction-networking/utls v1.6.6 // indirect github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect @@ -70,12 +72,12 @@ require ( go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect - golang.org/x/crypto v0.24.0 // indirect + golang.org/x/crypto v0.26.0 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.25.0 // indirect + golang.org/x/net v0.28.0 // indirect golang.org/x/oauth2 v0.20.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/text v0.17.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/protobuf v1.34.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/app/go.sum b/app/go.sum index 5b4bce8..73ee963 100644 --- a/app/go.sum +++ b/app/go.sum @@ -42,8 +42,8 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1 github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f h1:uVh0qpEslrWjgzx9vOcyCqsOY3c9kofDZ1n+qaw35ZY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f/go.mod h1:xkkq9D4ygcldQQhKS/w9CadiCKwCngU7K9E3DaKahpM= -github.com/apernet/quic-go v0.46.1-0.20240816230517-268ed2476167 h1:+jKV1EuDJiUoa4XgRyle5w7wIo+0hil+YyUmwhd4ttk= -github.com/apernet/quic-go v0.46.1-0.20240816230517-268ed2476167/go.mod h1:MjGWpXA31DZZWESdX3/PjIpSWIT1fOm8FNCqyXXFZFU= +github.com/apernet/quic-go v0.47.1-0.20241004180137-a80d14e2080d h1:KWRCWISqJOgY9/0hhH8Bevjw/k4tCQ7oJlXLyFv8u9s= +github.com/apernet/quic-go v0.47.1-0.20241004180137-a80d14e2080d/go.mod h1:x0paLlmCzNOUDDQIgmgFWmnpWQIEuH1GNfA6NdgSTuM= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad h1:QzQ2sKpc9o42HNRR8ukM5uMC/RzR2HgZd/Nvaqol2C0= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= @@ -226,8 +226,8 @@ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qR github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= -github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= github.com/refraction-networking/utls v1.6.6 h1:igFsYBUJPYM8Rno9xUuDoM5GQrVEqY4llzEXOkL43Ig= github.com/refraction-networking/utls v1.6.6/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -314,8 +314,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -392,8 +392,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -418,8 +418,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -463,8 +463,8 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -476,8 +476,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/core/go.mod b/core/go.mod index 77513a9..8d4cec0 100644 --- a/core/go.mod +++ b/core/go.mod @@ -1,9 +1,11 @@ module github.com/apernet/hysteria/core/v2 -go 1.21 +go 1.22 + +toolchain go1.23.2 require ( - github.com/apernet/quic-go v0.46.1-0.20240816230517-268ed2476167 + github.com/apernet/quic-go v0.47.1-0.20241004180137-a80d14e2080d github.com/stretchr/testify v1.9.0 go.uber.org/goleak v1.2.1 golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 @@ -18,15 +20,15 @@ require ( github.com/kr/pretty v0.3.1 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/quic-go/qpack v0.4.0 // indirect + github.com/quic-go/qpack v0.5.1 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/stretchr/objx v0.5.2 // indirect go.uber.org/mock v0.4.0 // indirect - golang.org/x/crypto v0.24.0 // indirect + golang.org/x/crypto v0.26.0 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/net v0.28.0 // indirect + golang.org/x/sys v0.23.0 // indirect + golang.org/x/text v0.17.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/protobuf v1.34.1 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect diff --git a/core/go.sum b/core/go.sum index c36f51e..4af12ad 100644 --- a/core/go.sum +++ b/core/go.sum @@ -1,5 +1,5 @@ -github.com/apernet/quic-go v0.46.1-0.20240816230517-268ed2476167 h1:+jKV1EuDJiUoa4XgRyle5w7wIo+0hil+YyUmwhd4ttk= -github.com/apernet/quic-go v0.46.1-0.20240816230517-268ed2476167/go.mod h1:MjGWpXA31DZZWESdX3/PjIpSWIT1fOm8FNCqyXXFZFU= +github.com/apernet/quic-go v0.47.1-0.20241004180137-a80d14e2080d h1:KWRCWISqJOgY9/0hhH8Bevjw/k4tCQ7oJlXLyFv8u9s= +github.com/apernet/quic-go v0.47.1-0.20241004180137-a80d14e2080d/go.mod h1:x0paLlmCzNOUDDQIgmgFWmnpWQIEuH1GNfA6NdgSTuM= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -32,8 +32,8 @@ github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+q github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= -github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= @@ -47,21 +47,21 @@ go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= diff --git a/extras/go.mod b/extras/go.mod index 358ae7d..be48ce6 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -1,18 +1,20 @@ module github.com/apernet/hysteria/extras/v2 -go 1.21 +go 1.22 + +toolchain go1.23.2 require ( github.com/apernet/hysteria/core/v2 v2.0.0-00010101000000-000000000000 - github.com/apernet/quic-go v0.46.1-0.20240816230517-268ed2476167 + github.com/apernet/quic-go v0.47.1-0.20241004180137-a80d14e2080d github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 github.com/hashicorp/golang-lru/v2 v2.0.5 github.com/miekg/dns v1.1.59 github.com/refraction-networking/utls v1.6.6 github.com/stretchr/testify v1.9.0 github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 - golang.org/x/crypto v0.24.0 - golang.org/x/net v0.25.0 + golang.org/x/crypto v0.26.0 + golang.org/x/net v0.28.0 google.golang.org/protobuf v1.34.1 ) @@ -27,15 +29,15 @@ require ( github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/quic-go/qpack v0.4.0 // indirect + github.com/quic-go/qpack v0.5.1 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect go.uber.org/mock v0.4.0 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.23.0 // indirect + golang.org/x/text v0.17.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/extras/go.sum b/extras/go.sum index e8be6d6..d1f42ea 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -1,7 +1,7 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/apernet/quic-go v0.46.1-0.20240816230517-268ed2476167 h1:+jKV1EuDJiUoa4XgRyle5w7wIo+0hil+YyUmwhd4ttk= -github.com/apernet/quic-go v0.46.1-0.20240816230517-268ed2476167/go.mod h1:MjGWpXA31DZZWESdX3/PjIpSWIT1fOm8FNCqyXXFZFU= +github.com/apernet/quic-go v0.47.1-0.20241004180137-a80d14e2080d h1:KWRCWISqJOgY9/0hhH8Bevjw/k4tCQ7oJlXLyFv8u9s= +github.com/apernet/quic-go v0.47.1-0.20241004180137-a80d14e2080d/go.mod h1:x0paLlmCzNOUDDQIgmgFWmnpWQIEuH1GNfA6NdgSTuM= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -43,8 +43,8 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= -github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= github.com/refraction-networking/utls v1.6.6 h1:igFsYBUJPYM8Rno9xUuDoM5GQrVEqY4llzEXOkL43Ig= github.com/refraction-networking/utls v1.6.6/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= @@ -66,8 +66,8 @@ go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -78,13 +78,13 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -92,8 +92,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -101,8 +101,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/go.work b/go.work index bde9a9c..1ff4f48 100644 --- a/go.work +++ b/go.work @@ -1,4 +1,6 @@ -go 1.21 +go 1.22 + +toolchain go1.23.2 use ( ./app diff --git a/go.work.sum b/go.work.sum index 44dac15..b00f726 100644 --- a/go.work.sum +++ b/go.work.sum @@ -327,6 +327,8 @@ golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 1001b2b1adb5eb21e7f3659e58b353aed42239d3 Mon Sep 17 00:00:00 2001 From: Haruue Date: Sat, 5 Oct 2024 10:23:43 +0800 Subject: [PATCH 130/161] chore: fix comments --- core/server/udp.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/server/udp.go b/core/server/udp.go index eb10b19..14efc9e 100644 --- a/core/server/udp.go +++ b/core/server/udp.go @@ -133,7 +133,9 @@ func (e *udpSessionEntry) initConn(firstMsg *protocol.UDPMessage) error { } e.conn = conn + if firstMsg.Addr != actualAddr { + // Hook changed the address, enable address override e.OverrideAddr = actualAddr e.OriginalAddr = firstMsg.Addr } From af2d75d1d008d29dd8305a393c60a389cac19ff8 Mon Sep 17 00:00:00 2001 From: "Haruue Icymoon (usamimi-wsl)" Date: Sat, 19 Oct 2024 16:27:16 +0800 Subject: [PATCH 131/161] fix: check masq url scheme in server cfg parsing Check the url scheme of masquerade.proxy.url when parsing server config and fail fast if it is not "http" or "https". ref: #1227 The user assigned the URL with a naked hostname and got errors until the request was handled. --- app/cmd/server.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/cmd/server.go b/app/cmd/server.go index 3da748d..a4b8470 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -804,6 +804,9 @@ func (c *serverConfig) fillMasqHandler(hyConfig *server.Config) error { if err != nil { return configError{Field: "masquerade.proxy.url", Err: err} } + if u.Scheme != "http" && u.Scheme != "https" { + return configError{Field: "masquerade.proxy.url", Err: fmt.Errorf("unsupported protocol scheme \"%s\"", u.Scheme)} + } handler = &httputil.ReverseProxy{ Rewrite: func(r *httputil.ProxyRequest) { r.SetURL(u) From 99e959f8c96276038b500635efa81bfa79917874 Mon Sep 17 00:00:00 2001 From: "Haruue Icymoon (usamimi-wsl)" Date: Sat, 19 Oct 2024 17:24:52 +0800 Subject: [PATCH 132/161] feat: share subcommand Useful for third-party scripts/clients that just want to generate the sharing URI without starting the client. --- app/cmd/share.go | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 app/cmd/share.go diff --git a/app/cmd/share.go b/app/cmd/share.go new file mode 100644 index 0000000..ad96e80 --- /dev/null +++ b/app/cmd/share.go @@ -0,0 +1,55 @@ +package cmd + +import ( + "fmt" + + "github.com/apernet/hysteria/app/v2/internal/utils" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "go.uber.org/zap" +) + +var ( + noText bool + withQR bool +) + +// shareCmd represents the share command +var shareCmd = &cobra.Command{ + Use: "share", + Short: "Generate share URI", + Long: "Generate a hysteria2:// URI from a client config for sharing", + Run: runShare, +} + +func init() { + initShareFlags() + rootCmd.AddCommand(shareCmd) +} + +func initShareFlags() { + shareCmd.Flags().BoolVar(&noText, "notext", false, "do not show URI as text") + shareCmd.Flags().BoolVar(&withQR, "qr", false, "show URI as QR code") +} + +func runShare(cmd *cobra.Command, args []string) { + if err := viper.ReadInConfig(); err != nil { + logger.Fatal("failed to read client config", zap.Error(err)) + } + var config clientConfig + if err := viper.Unmarshal(&config); err != nil { + logger.Fatal("failed to parse client config", zap.Error(err)) + } + if _, err := config.Config(); err != nil { + logger.Fatal("failed to load client config", zap.Error(err)) + } + + u := config.URI() + + if !noText { + fmt.Println(u) + } + if withQR { + utils.PrintQR(u) + } +} From d65997c02b1312cf1950ba0d42ea3973d91e3b36 Mon Sep 17 00:00:00 2001 From: Haruue Date: Tue, 5 Nov 2024 00:44:04 +0900 Subject: [PATCH 133/161] fix: inf loop in PortUnion.Ports() when end=65535 fix: #1240 Any uint16 value is less than or equal to 65535. --- extras/utils/portunion.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extras/utils/portunion.go b/extras/utils/portunion.go index 20a31d0..07326e4 100644 --- a/extras/utils/portunion.go +++ b/extras/utils/portunion.go @@ -1,6 +1,7 @@ package utils import ( + "math" "sort" "strconv" "strings" @@ -91,6 +92,9 @@ func (u PortUnion) Ports() []uint16 { for _, r := range u { for i := r.Start; i <= r.End; i++ { ports = append(ports, i) + if i == math.MaxUint16 { + break + } } } return ports From a9422e63be8f55c9e097523e98ee8169ca12f54f Mon Sep 17 00:00:00 2001 From: Haruue Date: Tue, 5 Nov 2024 00:46:16 +0900 Subject: [PATCH 134/161] test: add ut for PortUnion.Ports() --- extras/utils/portunion_test.go | 48 ++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/extras/utils/portunion_test.go b/extras/utils/portunion_test.go index 551bae1..0a9bd0c 100644 --- a/extras/utils/portunion_test.go +++ b/extras/utils/portunion_test.go @@ -2,6 +2,7 @@ package utils import ( "reflect" + "slices" "testing" ) @@ -90,3 +91,50 @@ func TestParsePortUnion(t *testing.T) { }) } } + +func TestPortUnion_Ports(t *testing.T) { + tests := []struct { + name string + pu PortUnion + want []uint16 + }{ + { + name: "single port", + pu: PortUnion{{1234, 1234}}, + want: []uint16{1234}, + }, + { + name: "multiple ports", + pu: PortUnion{{1234, 1236}}, + want: []uint16{1234, 1235, 1236}, + }, + { + name: "multiple ports and ranges", + pu: PortUnion{{1234, 1236}, {5678, 5680}, {9000, 9002}}, + want: []uint16{1234, 1235, 1236, 5678, 5679, 5680, 9000, 9001, 9002}, + }, + { + name: "single port 65535", + pu: PortUnion{{65535, 65535}}, + want: []uint16{65535}, + }, + { + name: "port range with 65535", + pu: PortUnion{{65530, 65535}}, + want: []uint16{65530, 65531, 65532, 65533, 65534, 65535}, + }, + { + name: "multiple ports and ranges with 65535", + pu: PortUnion{{65530, 65535}, {1234, 1236}}, + want: []uint16{65530, 65531, 65532, 65533, 65534, 65535, 1234, 1235, 1236}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.pu.Ports(); !slices.Equal(got, tt.want) { + t.Errorf("PortUnion.Ports() = %v, want %v", got, tt.want) + } + }) + } +} From 9a21e2e8c661642c9cb664b214f70f147f78aa49 Mon Sep 17 00:00:00 2001 From: Haruue Date: Tue, 5 Nov 2024 01:30:44 +0900 Subject: [PATCH 135/161] chore: a better fix to portunion --- extras/utils/portunion.go | 10 +++------- extras/utils/portunion_test.go | 10 ++++++++++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/extras/utils/portunion.go b/extras/utils/portunion.go index 07326e4..f76a6fd 100644 --- a/extras/utils/portunion.go +++ b/extras/utils/portunion.go @@ -1,7 +1,6 @@ package utils import ( - "math" "sort" "strconv" "strings" @@ -75,7 +74,7 @@ func (u PortUnion) Normalize() PortUnion { normalized := PortUnion{u[0]} for _, current := range u[1:] { last := &normalized[len(normalized)-1] - if current.Start <= last.End+1 { + if uint32(current.Start) <= uint32(last.End)+1 { if current.End > last.End { last.End = current.End } @@ -90,11 +89,8 @@ func (u PortUnion) Normalize() PortUnion { func (u PortUnion) Ports() []uint16 { var ports []uint16 for _, r := range u { - for i := r.Start; i <= r.End; i++ { - ports = append(ports, i) - if i == math.MaxUint16 { - break - } + for i := uint32(r.Start); i <= uint32(r.End); i++ { + ports = append(ports, uint16(i)) } } return ports diff --git a/extras/utils/portunion_test.go b/extras/utils/portunion_test.go index 0a9bd0c..ba056a3 100644 --- a/extras/utils/portunion_test.go +++ b/extras/utils/portunion_test.go @@ -52,6 +52,16 @@ func TestParsePortUnion(t *testing.T) { s: "5678,1200-1236,9100-9012,1234-1240", want: PortUnion{{1200, 1240}, {5678, 5678}, {9012, 9100}}, }, + { + name: "multiple ports and ranges with 65535 (reversed, unsorted, overlapping)", + s: "5678,1200-1236,65531-65535,65532-65534,9100-9012,1234-1240", + want: PortUnion{{1200, 1240}, {5678, 5678}, {9012, 9100}, {65531, 65535}}, + }, + { + name: "multiple ports and ranges with 65535 (reversed, unsorted, overlapping) 2", + s: "5678,1200-1236,65532-65535,65531-65534,9100-9012,1234-1240", + want: PortUnion{{1200, 1240}, {5678, 5678}, {9012, 9100}, {65531, 65535}}, + }, { name: "invalid 1", s: "1234-", From 04cf6f2e1ad56b0de791d42220273288b6cd9429 Mon Sep 17 00:00:00 2001 From: Toby Date: Mon, 4 Nov 2024 11:32:58 -0800 Subject: [PATCH 136/161] feat: quic-go v0.48.1 --- app/go.mod | 2 +- app/go.sum | 4 ++-- core/go.mod | 2 +- core/go.sum | 4 ++-- extras/go.mod | 2 +- extras/go.sum | 4 ++-- go.work.sum | 2 ++ 7 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/go.mod b/app/go.mod index 7353f6a..dab7e51 100644 --- a/app/go.mod +++ b/app/go.mod @@ -30,7 +30,7 @@ require ( require ( github.com/andybalholm/brotli v1.1.0 // indirect - github.com/apernet/quic-go v0.47.1-0.20241004180137-a80d14e2080d // indirect + github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7 // indirect github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect github.com/cloudflare/circl v1.3.9 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/app/go.sum b/app/go.sum index 73ee963..dec519b 100644 --- a/app/go.sum +++ b/app/go.sum @@ -42,8 +42,8 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1 github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f h1:uVh0qpEslrWjgzx9vOcyCqsOY3c9kofDZ1n+qaw35ZY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f/go.mod h1:xkkq9D4ygcldQQhKS/w9CadiCKwCngU7K9E3DaKahpM= -github.com/apernet/quic-go v0.47.1-0.20241004180137-a80d14e2080d h1:KWRCWISqJOgY9/0hhH8Bevjw/k4tCQ7oJlXLyFv8u9s= -github.com/apernet/quic-go v0.47.1-0.20241004180137-a80d14e2080d/go.mod h1:x0paLlmCzNOUDDQIgmgFWmnpWQIEuH1GNfA6NdgSTuM= +github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7 h1:zO38yBOvQ1dLHbSuaU5BFZ8zalnSDQslj+i/9AGOk9s= +github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7/go.mod h1:LoSUY2chVqNQCDyi4IZGqPpXLy1FuCkE37PKwtJvNGg= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad h1:QzQ2sKpc9o42HNRR8ukM5uMC/RzR2HgZd/Nvaqol2C0= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= diff --git a/core/go.mod b/core/go.mod index 8d4cec0..beb0372 100644 --- a/core/go.mod +++ b/core/go.mod @@ -5,7 +5,7 @@ go 1.22 toolchain go1.23.2 require ( - github.com/apernet/quic-go v0.47.1-0.20241004180137-a80d14e2080d + github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7 github.com/stretchr/testify v1.9.0 go.uber.org/goleak v1.2.1 golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 diff --git a/core/go.sum b/core/go.sum index 4af12ad..d65498b 100644 --- a/core/go.sum +++ b/core/go.sum @@ -1,5 +1,5 @@ -github.com/apernet/quic-go v0.47.1-0.20241004180137-a80d14e2080d h1:KWRCWISqJOgY9/0hhH8Bevjw/k4tCQ7oJlXLyFv8u9s= -github.com/apernet/quic-go v0.47.1-0.20241004180137-a80d14e2080d/go.mod h1:x0paLlmCzNOUDDQIgmgFWmnpWQIEuH1GNfA6NdgSTuM= +github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7 h1:zO38yBOvQ1dLHbSuaU5BFZ8zalnSDQslj+i/9AGOk9s= +github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7/go.mod h1:LoSUY2chVqNQCDyi4IZGqPpXLy1FuCkE37PKwtJvNGg= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= diff --git a/extras/go.mod b/extras/go.mod index be48ce6..3da331a 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -6,7 +6,7 @@ toolchain go1.23.2 require ( github.com/apernet/hysteria/core/v2 v2.0.0-00010101000000-000000000000 - github.com/apernet/quic-go v0.47.1-0.20241004180137-a80d14e2080d + github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7 github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 github.com/hashicorp/golang-lru/v2 v2.0.5 github.com/miekg/dns v1.1.59 diff --git a/extras/go.sum b/extras/go.sum index d1f42ea..d07ba7c 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -1,7 +1,7 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/apernet/quic-go v0.47.1-0.20241004180137-a80d14e2080d h1:KWRCWISqJOgY9/0hhH8Bevjw/k4tCQ7oJlXLyFv8u9s= -github.com/apernet/quic-go v0.47.1-0.20241004180137-a80d14e2080d/go.mod h1:x0paLlmCzNOUDDQIgmgFWmnpWQIEuH1GNfA6NdgSTuM= +github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7 h1:zO38yBOvQ1dLHbSuaU5BFZ8zalnSDQslj+i/9AGOk9s= +github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7/go.mod h1:LoSUY2chVqNQCDyi4IZGqPpXLy1FuCkE37PKwtJvNGg= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= diff --git a/go.work.sum b/go.work.sum index b00f726..79da3fa 100644 --- a/go.work.sum +++ b/go.work.sum @@ -290,6 +290,7 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk= @@ -298,6 +299,7 @@ golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852 h1:xYq6+9AtI+xP3M4r0N1hCkHr golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= From 685cd3663b4f1df0659f072fa08919ee95f254c9 Mon Sep 17 00:00:00 2001 From: Toby Date: Mon, 4 Nov 2024 12:01:00 -0800 Subject: [PATCH 137/161] feat: add toolchain & quic-go to version info --- app/cmd/root.go | 20 ++++++++++++-------- hyperbole.py | 41 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/app/cmd/root.go b/app/cmd/root.go index cc7f39f..a58e2bc 100644 --- a/app/cmd/root.go +++ b/app/cmd/root.go @@ -29,20 +29,24 @@ const ( var ( // These values will be injected by the build system - appVersion = "Unknown" - appDate = "Unknown" - appType = "Unknown" // aka channel - appCommit = "Unknown" - appPlatform = "Unknown" - appArch = "Unknown" + appVersion = "Unknown" + appDate = "Unknown" + appType = "Unknown" // aka channel + appToolchain = "Unknown" + appCommit = "Unknown" + appPlatform = "Unknown" + appArch = "Unknown" + libVersion = "Unknown" appVersionLong = fmt.Sprintf("Version:\t%s\n"+ "BuildDate:\t%s\n"+ "BuildType:\t%s\n"+ + "Toolchain:\t%s\n"+ "CommitHash:\t%s\n"+ "Platform:\t%s\n"+ - "Architecture:\t%s", - appVersion, appDate, appType, appCommit, appPlatform, appArch) + "Architecture:\t%s\n"+ + "LibVersion:\t%s", + appVersion, appDate, appType, appToolchain, appCommit, appPlatform, appArch, libVersion) appAboutLong = fmt.Sprintf("%s\n%s\n%s\n\n%s", appLogo, appDesc, appAuthors, appVersionLong) ) diff --git a/hyperbole.py b/hyperbole.py index fc38eba..ecc248d 100755 --- a/hyperbole.py +++ b/hyperbole.py @@ -145,12 +145,33 @@ def get_app_commit(): return app_commit +def get_toolchain(): + try: + output = subprocess.check_output(["go", "version"]).decode().strip() + if output.startswith("go version "): + output = output[11:] + return output + except Exception: + return "Unknown" + + def get_current_os_arch(): d_os = subprocess.check_output(["go", "env", "GOOS"]).decode().strip() d_arch = subprocess.check_output(["go", "env", "GOARCH"]).decode().strip() return (d_os, d_arch) +def get_lib_version(): + try: + with open(CORE_SRC_DIR + "/go.mod") as f: + for line in f: + line = line.strip() + if line.startswith("github.com/apernet/quic-go"): + return line.split(" ")[1].strip() + except Exception: + return "Unknown" + + def get_app_platforms(): platforms = os.environ.get("HY_APP_PLATFORMS") if not platforms: @@ -176,8 +197,12 @@ def cmd_build(pprof=False, release=False, race=False): os.makedirs(BUILD_DIR, exist_ok=True) app_version = get_app_version() - app_date = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ") + app_date = datetime.datetime.now(datetime.timezone.utc).strftime( + "%Y-%m-%dT%H:%M:%SZ" + ) + app_toolchain = get_toolchain() app_commit = get_app_commit() + lib_version = get_lib_version() ldflags = [ "-X", @@ -190,7 +215,11 @@ def cmd_build(pprof=False, release=False, race=False): + ("release" if release else "dev") + ("-pprof" if pprof else ""), "-X", + '"' + APP_SRC_CMD_PKG + ".appToolchain=" + app_toolchain + '"', + "-X", APP_SRC_CMD_PKG + ".appCommit=" + app_commit, + "-X", + APP_SRC_CMD_PKG + ".libVersion=" + lib_version, ] if release: ldflags.append("-s") @@ -267,8 +296,12 @@ def cmd_run(args, pprof=False, race=False): return app_version = get_app_version() - app_date = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ") + app_date = datetime.datetime.now(datetime.timezone.utc).strftime( + "%Y-%m-%dT%H:%M:%SZ" + ) + app_toolchain = get_toolchain() app_commit = get_app_commit() + lib_version = get_lib_version() current_os, current_arch = get_current_os_arch() @@ -280,11 +313,15 @@ def cmd_run(args, pprof=False, race=False): "-X", APP_SRC_CMD_PKG + ".appType=dev-run", "-X", + '"' + APP_SRC_CMD_PKG + ".appToolchain=" + app_toolchain + '"', + "-X", APP_SRC_CMD_PKG + ".appCommit=" + app_commit, "-X", APP_SRC_CMD_PKG + ".appPlatform=" + current_os, "-X", APP_SRC_CMD_PKG + ".appArch=" + current_arch, + "-X", + APP_SRC_CMD_PKG + ".libVersion=" + lib_version, ] cmd = ["go", "run", "-ldflags", " ".join(ldflags)] From d4a1c2b58067e9c9ff13071a028615beb97d722e Mon Sep 17 00:00:00 2001 From: Haruue Date: Tue, 5 Nov 2024 10:00:19 +0900 Subject: [PATCH 138/161] fix(scripts): extra line in installed version Checking for installed version ... v2.5.2 v0.47.1 Checking for latest version ... v2.5.2 --- scripts/install_server.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install_server.sh b/scripts/install_server.sh index 0c85dc7..4277ab9 100644 --- a/scripts/install_server.sh +++ b/scripts/install_server.sh @@ -872,7 +872,7 @@ is_hysteria1_version() { get_installed_version() { if is_hysteria_installed; then if "$EXECUTABLE_INSTALL_PATH" version > /dev/null 2>&1; then - "$EXECUTABLE_INSTALL_PATH" version | grep Version | grep -o 'v[.0-9]*' + "$EXECUTABLE_INSTALL_PATH" version | grep '^Version' | grep -o 'v[.0-9]*' elif "$EXECUTABLE_INSTALL_PATH" -v > /dev/null 2>&1; then # hysteria 1 "$EXECUTABLE_INSTALL_PATH" -v | cut -d ' ' -f 3 From 068163856833891a6e46a745197044a84ec6a727 Mon Sep 17 00:00:00 2001 From: Haruue Date: Fri, 8 Nov 2024 15:28:50 +0900 Subject: [PATCH 139/161] feat(trafficlogger): dump streams stats --- core/internal/utils/atomic.go | 30 +++++++ core/server/config.go | 65 +++++++++++++++ core/server/copy.go | 7 +- core/server/server.go | 32 +++++++- extras/trafficlogger/http.go | 147 ++++++++++++++++++++++++++++++++++ 5 files changed, 277 insertions(+), 4 deletions(-) diff --git a/core/internal/utils/atomic.go b/core/internal/utils/atomic.go index e3c3d97..7739013 100644 --- a/core/internal/utils/atomic.go +++ b/core/internal/utils/atomic.go @@ -22,3 +22,33 @@ func (t *AtomicTime) Set(new time.Time) { func (t *AtomicTime) Get() time.Time { return t.v.Load().(time.Time) } + +type Atomic[T any] struct { + v atomic.Value +} + +func (a *Atomic[T]) Load() T { + value := a.v.Load() + if value == nil { + var zero T + return zero + } + return value.(T) +} + +func (a *Atomic[T]) Store(value T) { + a.v.Store(value) +} + +func (a *Atomic[T]) Swap(new T) T { + old := a.v.Swap(new) + if old == nil { + var zero T + return zero + } + return old.(T) +} + +func (a *Atomic[T]) CompareAndSwap(old, new T) bool { + return a.v.CompareAndSwap(old, new) +} diff --git a/core/server/config.go b/core/server/config.go index f90c820..19aae53 100644 --- a/core/server/config.go +++ b/core/server/config.go @@ -4,8 +4,11 @@ import ( "crypto/tls" "net" "net/http" + "sync/atomic" "time" + "github.com/apernet/hysteria/core/v2/internal/utils" + "github.com/apernet/hysteria/core/v2/errors" "github.com/apernet/hysteria/core/v2/internal/pmtud" "github.com/apernet/quic-go" @@ -212,4 +215,66 @@ type EventLogger interface { type TrafficLogger interface { LogTraffic(id string, tx, rx uint64) (ok bool) LogOnlineState(id string, online bool) + TraceStream(stream quic.Stream, stats *StreamStats) + UntraceStream(stream quic.Stream) +} + +type StreamState int + +const ( + // StreamStateInitial indicates the initial state of a stream. + // Client has opened the stream, but we have not received the proxy request yet. + StreamStateInitial StreamState = iota + + // StreamStateHooking indicates that the hook (usually sniff) is processing. + // Client has sent the proxy request, but sniff requires more data to complete. + StreamStateHooking + + // StreamStateConnecting indicates that we are connecting to the proxy target. + StreamStateConnecting + + // StreamStateEstablished indicates the proxy is established. + StreamStateEstablished + + // StreamStateClosed indicates the stream is closed. + StreamStateClosed +) + +func (s StreamState) String() string { + switch s { + case StreamStateInitial: + return "init" + case StreamStateHooking: + return "hook" + case StreamStateConnecting: + return "connect" + case StreamStateEstablished: + return "estab" + case StreamStateClosed: + return "closed" + default: + return "unknown" + } +} + +type StreamStats struct { + State utils.Atomic[StreamState] + + AuthID string + ConnID uint32 + InitialTime time.Time + + ReqAddr utils.Atomic[string] + HookedReqAddr utils.Atomic[string] + + Tx atomic.Uint64 + Rx atomic.Uint64 + + LastActiveTime utils.Atomic[time.Time] +} + +func (s *StreamStats) setHookedReqAddr(addr string) { + if addr != s.ReqAddr.Load() { + s.HookedReqAddr.Store(addr) + } } diff --git a/core/server/copy.go b/core/server/copy.go index d55dcef..2f99ae4 100644 --- a/core/server/copy.go +++ b/core/server/copy.go @@ -3,6 +3,7 @@ package server import ( "errors" "io" + "time" ) var errDisconnect = errors.New("traffic logger requested disconnect") @@ -31,15 +32,19 @@ func copyBufferLog(dst io.Writer, src io.Reader, log func(n uint64) bool) error } } -func copyTwoWayWithLogger(id string, serverRw, remoteRw io.ReadWriter, l TrafficLogger) error { +func copyTwoWayWithLogger(id string, serverRw, remoteRw io.ReadWriter, l TrafficLogger, stats *StreamStats) error { errChan := make(chan error, 2) go func() { errChan <- copyBufferLog(serverRw, remoteRw, func(n uint64) bool { + stats.LastActiveTime.Store(time.Now()) + stats.Rx.Add(n) return l.LogTraffic(id, 0, n) }) }() go func() { errChan <- copyBufferLog(remoteRw, serverRw, func(n uint64) bool { + stats.LastActiveTime.Store(time.Now()) + stats.Tx.Add(n) return l.LogTraffic(id, n, 0) }) }() diff --git a/core/server/server.go b/core/server/server.go index ba55b31..f7ad957 100644 --- a/core/server/server.go +++ b/core/server/server.go @@ -3,8 +3,10 @@ package server import ( "context" "crypto/tls" + "math/rand" "net/http" "sync" + "time" "github.com/apernet/quic-go" "github.com/apernet/quic-go/http3" @@ -100,6 +102,7 @@ type h3sHandler struct { authenticated bool authMutex sync.Mutex authID string + connID uint32 // a random id for dump streams udpSM *udpSessionManager // Only set after authentication } @@ -108,6 +111,7 @@ func newH3sHandler(config *Config, conn quic.Connection) *h3sHandler { return &h3sHandler{ config: config, conn: conn, + connID: rand.Uint32(), } } @@ -205,12 +209,29 @@ func (h *h3sHandler) ProxyStreamHijacker(ft http3.FrameType, id quic.ConnectionT } func (h *h3sHandler) handleTCPRequest(stream quic.Stream) { + trafficLogger := h.config.TrafficLogger + streamStats := &StreamStats{ + AuthID: h.authID, + ConnID: h.connID, + InitialTime: time.Now(), + } + streamStats.State.Store(StreamStateInitial) + streamStats.LastActiveTime.Store(time.Now()) + defer func() { + streamStats.State.Store(StreamStateClosed) + }() + if trafficLogger != nil { + trafficLogger.TraceStream(stream, streamStats) + defer trafficLogger.UntraceStream(stream) + } + // Read request reqAddr, err := protocol.ReadTCPRequest(stream) if err != nil { _ = stream.Close() return } + streamStats.ReqAddr.Store(reqAddr) // Call the hook if set var putback []byte var hooked bool @@ -220,12 +241,14 @@ func (h *h3sHandler) handleTCPRequest(stream quic.Stream) { // so that the client will send whatever request the hook wants to see. // This is essentially a server-side fast-open. if hooked { + streamStats.State.Store(StreamStateHooking) _ = protocol.WriteTCPResponse(stream, true, "RequestHook enabled") putback, err = h.config.RequestHook.TCP(stream, &reqAddr) if err != nil { _ = stream.Close() return } + streamStats.setHookedReqAddr(reqAddr) } } // Log the event @@ -233,6 +256,7 @@ func (h *h3sHandler) handleTCPRequest(stream quic.Stream) { h.config.EventLogger.TCPRequest(h.conn.RemoteAddr(), h.authID, reqAddr) } // Dial target + streamStats.State.Store(StreamStateConnecting) tConn, err := h.config.Outbound.TCP(reqAddr) if err != nil { if !hooked { @@ -248,13 +272,15 @@ func (h *h3sHandler) handleTCPRequest(stream quic.Stream) { if !hooked { _ = protocol.WriteTCPResponse(stream, true, "Connected") } + streamStats.State.Store(StreamStateEstablished) // Put back the data if the hook requested if len(putback) > 0 { - _, _ = tConn.Write(putback) + n, _ := tConn.Write(putback) + streamStats.Tx.Add(uint64(n)) } // Start proxying - if h.config.TrafficLogger != nil { - err = copyTwoWayWithLogger(h.authID, stream, tConn, h.config.TrafficLogger) + if trafficLogger != nil { + err = copyTwoWayWithLogger(h.authID, stream, tConn, trafficLogger, streamStats) } else { // Use the fast path if no traffic logger is set err = copyTwoWay(stream, tConn) diff --git a/extras/trafficlogger/http.go b/extras/trafficlogger/http.go index 9ab943a..87eb919 100644 --- a/extras/trafficlogger/http.go +++ b/extras/trafficlogger/http.go @@ -1,10 +1,17 @@ package trafficlogger import ( + "cmp" "encoding/json" + "fmt" "net/http" + "slices" "strconv" + "strings" "sync" + "time" + + "github.com/apernet/quic-go" "github.com/apernet/hysteria/core/v2/server" ) @@ -25,6 +32,7 @@ func NewTrafficStatsServer(secret string) TrafficStatsServer { StatsMap: make(map[string]*trafficStatsEntry), KickMap: make(map[string]struct{}), OnlineMap: make(map[string]int), + StreamMap: make(map[quic.Stream]*server.StreamStats), Secret: secret, } } @@ -33,6 +41,7 @@ type trafficStatsServerImpl struct { Mutex sync.RWMutex StatsMap map[string]*trafficStatsEntry OnlineMap map[string]int + StreamMap map[quic.Stream]*server.StreamStats KickMap map[string]struct{} Secret string } @@ -78,6 +87,20 @@ func (s *trafficStatsServerImpl) LogOnlineState(id string, online bool) { } } +func (s *trafficStatsServerImpl) TraceStream(stream quic.Stream, stats *server.StreamStats) { + s.Mutex.Lock() + defer s.Mutex.Unlock() + + s.StreamMap[stream] = stats +} + +func (s *trafficStatsServerImpl) UntraceStream(stream quic.Stream) { + s.Mutex.Lock() + defer s.Mutex.Unlock() + + delete(s.StreamMap, stream) +} + func (s *trafficStatsServerImpl) ServeHTTP(w http.ResponseWriter, r *http.Request) { if s.Secret != "" && r.Header.Get("Authorization") != s.Secret { http.Error(w, "unauthorized", http.StatusUnauthorized) @@ -99,6 +122,10 @@ func (s *trafficStatsServerImpl) ServeHTTP(w http.ResponseWriter, r *http.Reques s.getOnline(w, r) return } + if r.Method == http.MethodGet && r.URL.Path == "/dump/streams" { + s.getDumpStreams(w, r) + return + } http.NotFound(w, r) } @@ -137,6 +164,126 @@ func (s *trafficStatsServerImpl) getOnline(w http.ResponseWriter, r *http.Reques _, _ = w.Write(jb) } +type dumpStreamEntry struct { + State string `json:"state"` + + Auth string `json:"auth"` + Connection uint32 `json:"connection"` + Stream uint64 `json:"stream"` + + ReqAddr string `json:"req_addr"` + HookedReqAddr string `json:"hooked_req_addr"` + + Tx uint64 `json:"tx"` + Rx uint64 `json:"rx"` + + InitialAt string `json:"initial_at"` + LastActiveAt string `json:"last_active_at"` + + // for text/plain output + initialTime time.Time + lastActiveTime time.Time +} + +func (e *dumpStreamEntry) fromStreamStats(stream quic.Stream, s *server.StreamStats) { + e.State = s.State.Load().String() + e.Auth = s.AuthID + e.Connection = s.ConnID + e.Stream = uint64(stream.StreamID()) + e.ReqAddr = s.ReqAddr.Load() + e.HookedReqAddr = s.HookedReqAddr.Load() + e.Tx = s.Tx.Load() + e.Rx = s.Rx.Load() + e.initialTime = s.InitialTime + e.lastActiveTime = s.LastActiveTime.Load() + e.InitialAt = e.initialTime.Format(time.RFC3339Nano) + e.LastActiveAt = e.lastActiveTime.Format(time.RFC3339Nano) +} + +func formatDumpStreamLine(state, auth, connection, stream, reqAddr, hookedReqAddr, tx, rx, lifetime, lastActive string) string { + return fmt.Sprintf("%-8s %-12s %12s %8s %12s %12s %12s %12s %-16s %s", state, auth, connection, stream, tx, rx, lifetime, lastActive, reqAddr, hookedReqAddr) +} + +func (e *dumpStreamEntry) String() string { + stateText := strings.ToUpper(e.State) + connectionText := fmt.Sprintf("%08X", e.Connection) + streamText := strconv.FormatUint(e.Stream, 10) + reqAddrText := e.ReqAddr + if reqAddrText == "" { + reqAddrText = "-" + } + hookedReqAddrText := e.HookedReqAddr + if hookedReqAddrText == "" { + hookedReqAddrText = "-" + } + txText := strconv.FormatUint(e.Tx, 10) + rxText := strconv.FormatUint(e.Rx, 10) + lifetime := time.Now().Sub(e.initialTime) + if lifetime < 10*time.Minute { + lifetime = lifetime.Round(time.Millisecond) + } else { + lifetime = lifetime.Round(time.Second) + } + lastActive := time.Now().Sub(e.lastActiveTime) + if lastActive < 10*time.Minute { + lastActive = lastActive.Round(time.Millisecond) + } else { + lastActive = lastActive.Round(time.Second) + } + + return formatDumpStreamLine(stateText, e.Auth, connectionText, streamText, reqAddrText, hookedReqAddrText, txText, rxText, lifetime.String(), lastActive.String()) +} + +func (s *trafficStatsServerImpl) getDumpStreams(w http.ResponseWriter, r *http.Request) { + var entries []dumpStreamEntry + + s.Mutex.RLock() + entries = make([]dumpStreamEntry, len(s.StreamMap)) + index := 0 + for stream, stats := range s.StreamMap { + entries[index].fromStreamStats(stream, stats) + index++ + } + s.Mutex.RUnlock() + + slices.SortFunc(entries, func(lhs, rhs dumpStreamEntry) int { + if ret := cmp.Compare(lhs.Auth, rhs.Auth); ret != 0 { + return ret + } + if ret := cmp.Compare(lhs.Connection, rhs.Connection); ret != 0 { + return ret + } + if ret := cmp.Compare(lhs.Stream, rhs.Stream); ret != 0 { + return ret + } + return 0 + }) + + accept := r.Header.Get("Accept") + + if strings.Contains(accept, "text/plain") { + // Generate netstat-like output for humans + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + + // Print table header + _, _ = fmt.Fprintln(w, formatDumpStreamLine("State", "Auth", "Connection", "Stream", "Req-Addr", "Hooked-Req-Addr", "TX-Bytes", "RX-Bytes", "Lifetime", "Last-Active")) + for _, entry := range entries { + _, _ = fmt.Fprintln(w, entry.String()) + } + return + } + + // Response with json by default + wrapper := struct { + Streams []dumpStreamEntry `json:"streams"` + }{entries} + w.Header().Set("Content-Type", "application/json; charset=utf-8") + err := json.NewEncoder(w).Encode(&wrapper) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } +} + func (s *trafficStatsServerImpl) kick(w http.ResponseWriter, r *http.Request) { var ids []string err := json.NewDecoder(r.Body).Decode(&ids) From 7ac8d87ddae55af435738e4430ce6dcffc2ae140 Mon Sep 17 00:00:00 2001 From: Haruue Date: Fri, 8 Nov 2024 16:03:48 +0900 Subject: [PATCH 140/161] test: fix integration_tests for trafficlogger --- .../mocks/mock_TrafficLogger.go | 74 ++++++++++++++++++- .../integration_tests/trafficlogger_test.go | 2 + 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/core/internal/integration_tests/mocks/mock_TrafficLogger.go b/core/internal/integration_tests/mocks/mock_TrafficLogger.go index 9de44b9..1ed977e 100644 --- a/core/internal/integration_tests/mocks/mock_TrafficLogger.go +++ b/core/internal/integration_tests/mocks/mock_TrafficLogger.go @@ -2,7 +2,12 @@ package mocks -import mock "github.com/stretchr/testify/mock" +import ( + quic "github.com/apernet/quic-go" + mock "github.com/stretchr/testify/mock" + + server "github.com/apernet/hysteria/core/v2/server" +) // MockTrafficLogger is an autogenerated mock type for the TrafficLogger type type MockTrafficLogger struct { @@ -99,6 +104,73 @@ func (_c *MockTrafficLogger_LogTraffic_Call) RunAndReturn(run func(string, uint6 return _c } +// TraceStream provides a mock function with given fields: stream, stats +func (_m *MockTrafficLogger) TraceStream(stream quic.Stream, stats *server.StreamStats) { + _m.Called(stream, stats) +} + +// MockTrafficLogger_TraceStream_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TraceStream' +type MockTrafficLogger_TraceStream_Call struct { + *mock.Call +} + +// TraceStream is a helper method to define mock.On call +// - stream quic.Stream +// - stats *server.StreamStats +func (_e *MockTrafficLogger_Expecter) TraceStream(stream interface{}, stats interface{}) *MockTrafficLogger_TraceStream_Call { + return &MockTrafficLogger_TraceStream_Call{Call: _e.mock.On("TraceStream", stream, stats)} +} + +func (_c *MockTrafficLogger_TraceStream_Call) Run(run func(stream quic.Stream, stats *server.StreamStats)) *MockTrafficLogger_TraceStream_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(quic.Stream), args[1].(*server.StreamStats)) + }) + return _c +} + +func (_c *MockTrafficLogger_TraceStream_Call) Return() *MockTrafficLogger_TraceStream_Call { + _c.Call.Return() + return _c +} + +func (_c *MockTrafficLogger_TraceStream_Call) RunAndReturn(run func(quic.Stream, *server.StreamStats)) *MockTrafficLogger_TraceStream_Call { + _c.Call.Return(run) + return _c +} + +// UntraceStream provides a mock function with given fields: stream +func (_m *MockTrafficLogger) UntraceStream(stream quic.Stream) { + _m.Called(stream) +} + +// MockTrafficLogger_UntraceStream_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UntraceStream' +type MockTrafficLogger_UntraceStream_Call struct { + *mock.Call +} + +// UntraceStream is a helper method to define mock.On call +// - stream quic.Stream +func (_e *MockTrafficLogger_Expecter) UntraceStream(stream interface{}) *MockTrafficLogger_UntraceStream_Call { + return &MockTrafficLogger_UntraceStream_Call{Call: _e.mock.On("UntraceStream", stream)} +} + +func (_c *MockTrafficLogger_UntraceStream_Call) Run(run func(stream quic.Stream)) *MockTrafficLogger_UntraceStream_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(quic.Stream)) + }) + return _c +} + +func (_c *MockTrafficLogger_UntraceStream_Call) Return() *MockTrafficLogger_UntraceStream_Call { + _c.Call.Return() + return _c +} + +func (_c *MockTrafficLogger_UntraceStream_Call) RunAndReturn(run func(quic.Stream)) *MockTrafficLogger_UntraceStream_Call { + _c.Call.Return(run) + return _c +} + // NewMockTrafficLogger creates a new instance of MockTrafficLogger. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewMockTrafficLogger(t interface { diff --git a/core/internal/integration_tests/trafficlogger_test.go b/core/internal/integration_tests/trafficlogger_test.go index b5355ff..841f4ff 100644 --- a/core/internal/integration_tests/trafficlogger_test.go +++ b/core/internal/integration_tests/trafficlogger_test.go @@ -62,6 +62,7 @@ func TestClientServerTrafficLoggerTCP(t *testing.T) { return nil }) serverOb.EXPECT().TCP(addr).Return(sobConn, nil).Once() + trafficLogger.EXPECT().TraceStream(mock.Anything, mock.Anything).Return().Once() conn, err := c.TCP(addr) assert.NoError(t, err) @@ -84,6 +85,7 @@ func TestClientServerTrafficLoggerTCP(t *testing.T) { time.Sleep(1 * time.Second) // Need some time for the server to receive the data // Client reads from server again but blocked + trafficLogger.EXPECT().UntraceStream(mock.Anything).Return().Once() trafficLogger.EXPECT().LogTraffic("nobody", uint64(0), uint64(4)).Return(false).Once() trafficLogger.EXPECT().LogOnlineState("nobody", false).Return().Once() sobConnCh <- []byte("nope") From 3e8c20518db0e97ad67b638e85cbe643b26d777a Mon Sep 17 00:00:00 2001 From: Toby Date: Fri, 8 Nov 2024 14:29:50 -0800 Subject: [PATCH 141/161] chore: minor code tweaks --- core/server/config.go | 3 +-- core/server/copy.go | 4 ++-- core/server/server.go | 2 +- extras/trafficlogger/http.go | 3 +-- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/core/server/config.go b/core/server/config.go index 19aae53..a01f478 100644 --- a/core/server/config.go +++ b/core/server/config.go @@ -7,10 +7,9 @@ import ( "sync/atomic" "time" - "github.com/apernet/hysteria/core/v2/internal/utils" - "github.com/apernet/hysteria/core/v2/errors" "github.com/apernet/hysteria/core/v2/internal/pmtud" + "github.com/apernet/hysteria/core/v2/internal/utils" "github.com/apernet/quic-go" ) diff --git a/core/server/copy.go b/core/server/copy.go index 2f99ae4..7123fc8 100644 --- a/core/server/copy.go +++ b/core/server/copy.go @@ -32,7 +32,7 @@ func copyBufferLog(dst io.Writer, src io.Reader, log func(n uint64) bool) error } } -func copyTwoWayWithLogger(id string, serverRw, remoteRw io.ReadWriter, l TrafficLogger, stats *StreamStats) error { +func copyTwoWayEx(id string, serverRw, remoteRw io.ReadWriter, l TrafficLogger, stats *StreamStats) error { errChan := make(chan error, 2) go func() { errChan <- copyBufferLog(serverRw, remoteRw, func(n uint64) bool { @@ -52,7 +52,7 @@ func copyTwoWayWithLogger(id string, serverRw, remoteRw io.ReadWriter, l Traffic return <-errChan } -// copyTwoWay is the "fast-path" version of copyTwoWayWithLogger that does not log traffic. +// copyTwoWay is the "fast-path" version of copyTwoWayEx that does not log traffic or update stream stats. // It uses the built-in io.Copy instead of our own copyBufferLog. func copyTwoWay(serverRw, remoteRw io.ReadWriter) error { errChan := make(chan error, 2) diff --git a/core/server/server.go b/core/server/server.go index f7ad957..696f1d0 100644 --- a/core/server/server.go +++ b/core/server/server.go @@ -280,7 +280,7 @@ func (h *h3sHandler) handleTCPRequest(stream quic.Stream) { } // Start proxying if trafficLogger != nil { - err = copyTwoWayWithLogger(h.authID, stream, tConn, trafficLogger, streamStats) + err = copyTwoWayEx(h.authID, stream, tConn, trafficLogger, streamStats) } else { // Use the fast path if no traffic logger is set err = copyTwoWay(stream, tConn) diff --git a/extras/trafficlogger/http.go b/extras/trafficlogger/http.go index 87eb919..d8e6ebd 100644 --- a/extras/trafficlogger/http.go +++ b/extras/trafficlogger/http.go @@ -11,9 +11,8 @@ import ( "sync" "time" - "github.com/apernet/quic-go" - "github.com/apernet/hysteria/core/v2/server" + "github.com/apernet/quic-go" ) const ( From 16c964b3e143f6b2c3384d6f02e85e635984a4fc Mon Sep 17 00:00:00 2001 From: Haruue Date: Fri, 22 Nov 2024 13:47:44 +0900 Subject: [PATCH 142/161] feat(server): tcp fast open on direct outbounds --- app/cmd/server.go | 21 +-- app/cmd/server_test.go | 1 + app/cmd/server_test.yaml | 1 + app/go.mod | 4 +- app/go.sum | 8 +- extras/go.mod | 4 +- extras/go.sum | 8 +- extras/outbounds/fastopen.go | 230 +++++++++++++++++++++++++++ extras/outbounds/ob_direct.go | 113 +++++++++---- extras/outbounds/ob_direct_linux.go | 38 ++--- extras/outbounds/ob_direct_others.go | 7 +- 11 files changed, 363 insertions(+), 72 deletions(-) create mode 100644 extras/outbounds/fastopen.go diff --git a/app/cmd/server.go b/app/cmd/server.go index a4b8470..1384dd8 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -204,6 +204,7 @@ type serverConfigOutboundDirect struct { BindIPv4 string `mapstructure:"bindIPv4"` BindIPv6 string `mapstructure:"bindIPv6"` BindDevice string `mapstructure:"bindDevice"` + FastOpen bool `mapstructure:"fastOpen"` } type serverConfigOutboundSOCKS5 struct { @@ -518,18 +519,18 @@ func (c *serverConfig) fillQUICConfig(hyConfig *server.Config) error { } func serverConfigOutboundDirectToOutbound(c serverConfigOutboundDirect) (outbounds.PluggableOutbound, error) { - var mode outbounds.DirectOutboundMode + opts := outbounds.DirectOutboundOptions{} switch strings.ToLower(c.Mode) { case "", "auto": - mode = outbounds.DirectOutboundModeAuto + opts.Mode = outbounds.DirectOutboundModeAuto case "64": - mode = outbounds.DirectOutboundMode64 + opts.Mode = outbounds.DirectOutboundMode64 case "46": - mode = outbounds.DirectOutboundMode46 + opts.Mode = outbounds.DirectOutboundMode46 case "6": - mode = outbounds.DirectOutboundMode6 + opts.Mode = outbounds.DirectOutboundMode6 case "4": - mode = outbounds.DirectOutboundMode4 + opts.Mode = outbounds.DirectOutboundMode4 default: return nil, configError{Field: "outbounds.direct.mode", Err: errors.New("unsupported mode")} } @@ -546,12 +547,14 @@ func serverConfigOutboundDirectToOutbound(c serverConfigOutboundDirect) (outboun if len(c.BindIPv6) > 0 && ip6 == nil { return nil, configError{Field: "outbounds.direct.bindIPv6", Err: errors.New("invalid IPv6 address")} } - return outbounds.NewDirectOutboundBindToIPs(mode, ip4, ip6) + opts.BindIP4 = ip4 + opts.BindIP6 = ip6 } if bindDevice { - return outbounds.NewDirectOutboundBindToDevice(mode, c.BindDevice) + opts.DeviceName = c.BindDevice } - return outbounds.NewDirectOutboundSimple(mode), nil + opts.FastOpen = c.FastOpen + return outbounds.NewDirectOutboundWithOptions(opts) } func serverConfigOutboundSOCKS5ToOutbound(c serverConfigOutboundSOCKS5) (outbounds.PluggableOutbound, error) { diff --git a/app/cmd/server_test.go b/app/cmd/server_test.go index f35edfb..bcf61c3 100644 --- a/app/cmd/server_test.go +++ b/app/cmd/server_test.go @@ -138,6 +138,7 @@ func TestServerConfig(t *testing.T) { BindIPv4: "2.4.6.8", BindIPv6: "0:0:0:0:0:ffff:0204:0608", BindDevice: "eth233", + FastOpen: true, }, }, { diff --git a/app/cmd/server_test.yaml b/app/cmd/server_test.yaml index b7d1a3e..dda6d98 100644 --- a/app/cmd/server_test.yaml +++ b/app/cmd/server_test.yaml @@ -108,6 +108,7 @@ outbounds: bindIPv4: 2.4.6.8 bindIPv6: 0:0:0:0:0:ffff:0204:0608 bindDevice: eth233 + fastOpen: true - name: badstuff type: socks5 socks5: diff --git a/app/go.mod b/app/go.mod index dab7e51..2d36411 100644 --- a/app/go.mod +++ b/app/go.mod @@ -25,7 +25,7 @@ require ( github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 go.uber.org/zap v1.24.0 golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 - golang.org/x/sys v0.23.0 + golang.org/x/sys v0.25.0 ) require ( @@ -33,6 +33,8 @@ require ( github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7 // indirect github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect github.com/cloudflare/circl v1.3.9 // indirect + github.com/database64128/netx-go v0.0.0-20240905055117-62795b8b054a // indirect + github.com/database64128/tfo-go/v2 v2.2.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect diff --git a/app/go.sum b/app/go.sum index dec519b..1b30feb 100644 --- a/app/go.sum +++ b/app/go.sum @@ -63,6 +63,10 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/database64128/netx-go v0.0.0-20240905055117-62795b8b054a h1:t4SDi0pmNkryzKdM4QF3o5vqSP4GRjeZD/6j3nyxNP0= +github.com/database64128/netx-go v0.0.0-20240905055117-62795b8b054a/go.mod h1:7K2NQKbabB5mBl41vF6YayYl5g7YpDwc4dQ5iMpP3Lg= +github.com/database64128/tfo-go/v2 v2.2.2 h1:BxynF4qGF5ct3DpPLEG62uyJZ3LQhqaf0Ken+kyy7PM= +github.com/database64128/tfo-go/v2 v2.2.2/go.mod h1:2IW8jppdBwdVMjA08uEyMNnqiAHKUlqAA+J8NrsfktY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -463,8 +467,8 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= diff --git a/extras/go.mod b/extras/go.mod index 3da331a..67ef38f 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -8,6 +8,7 @@ require ( github.com/apernet/hysteria/core/v2 v2.0.0-00010101000000-000000000000 github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7 github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 + github.com/database64128/tfo-go/v2 v2.2.2 github.com/hashicorp/golang-lru/v2 v2.0.5 github.com/miekg/dns v1.1.59 github.com/refraction-networking/utls v1.6.6 @@ -21,6 +22,7 @@ require ( require ( github.com/andybalholm/brotli v1.1.0 // indirect github.com/cloudflare/circl v1.3.9 // indirect + github.com/database64128/netx-go v0.0.0-20240905055117-62795b8b054a // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect @@ -36,7 +38,7 @@ require ( golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.23.0 // indirect + golang.org/x/sys v0.25.0 // indirect golang.org/x/text v0.17.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/extras/go.sum b/extras/go.sum index d07ba7c..98616ca 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -10,6 +10,10 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE= github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/database64128/netx-go v0.0.0-20240905055117-62795b8b054a h1:t4SDi0pmNkryzKdM4QF3o5vqSP4GRjeZD/6j3nyxNP0= +github.com/database64128/netx-go v0.0.0-20240905055117-62795b8b054a/go.mod h1:7K2NQKbabB5mBl41vF6YayYl5g7YpDwc4dQ5iMpP3Lg= +github.com/database64128/tfo-go/v2 v2.2.2 h1:BxynF4qGF5ct3DpPLEG62uyJZ3LQhqaf0Ken+kyy7PM= +github.com/database64128/tfo-go/v2 v2.2.2/go.mod h1:2IW8jppdBwdVMjA08uEyMNnqiAHKUlqAA+J8NrsfktY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -92,8 +96,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= diff --git a/extras/outbounds/fastopen.go b/extras/outbounds/fastopen.go new file mode 100644 index 0000000..a607500 --- /dev/null +++ b/extras/outbounds/fastopen.go @@ -0,0 +1,230 @@ +package outbounds + +import ( + "net" + "sync" + "time" + + "github.com/database64128/tfo-go/v2" +) + +type fastOpenDialer struct { + dialer *tfo.Dialer +} + +func newFastOpenDialer(netDialer *net.Dialer) *fastOpenDialer { + return &fastOpenDialer{ + dialer: &tfo.Dialer{ + Dialer: *netDialer, + Fallback: true, + }, + } +} + +// Dial returns immediately without actually establishing a connection. +// The connection will be established by the first Write() call. +func (d *fastOpenDialer) Dial(network, address string) (net.Conn, error) { + return &fastOpenConn{ + dialer: d.dialer, + network: network, + address: address, + readyChan: make(chan struct{}), + }, nil +} + +type fastOpenConn struct { + dialer *tfo.Dialer + network string + address string + + conn net.Conn + connLock sync.RWMutex + readyChan chan struct{} + + // States before connection ready + deadline *time.Time + readDeadline *time.Time + writeDeadline *time.Time +} + +func (c *fastOpenConn) Read(b []byte) (n int, err error) { + c.connLock.RLock() + conn := c.conn + c.connLock.RUnlock() + + if conn != nil { + return conn.Read(b) + } + + // Wait until the connection is ready or closed + <-c.readyChan + + if c.conn == nil { + // This is equivalent to isClosedBeforeReady() == true + return 0, net.ErrClosed + } + + return c.conn.Read(b) +} + +func (c *fastOpenConn) Write(b []byte) (n int, err error) { + c.connLock.RLock() + conn := c.conn + c.connLock.RUnlock() + + if conn != nil { + return conn.Write(b) + } + + c.connLock.RLock() + closed := c.isClosedBeforeReady() + c.connLock.RUnlock() + + if closed { + return 0, net.ErrClosed + } + + c.connLock.Lock() + defer c.connLock.Unlock() + + if c.isClosedBeforeReady() { + // Closed by other goroutine + return 0, net.ErrClosed + } + + conn = c.conn + if conn != nil { + // Established by other goroutine + return conn.Write(b) + } + + conn, err = c.dialer.Dial(c.network, c.address, b) + if err != nil { + close(c.readyChan) + return 0, err + } + + // Apply pre-set states + if c.deadline != nil { + _ = conn.SetDeadline(*c.deadline) + } + if c.readDeadline != nil { + _ = conn.SetReadDeadline(*c.readDeadline) + } + if c.writeDeadline != nil { + _ = conn.SetWriteDeadline(*c.writeDeadline) + } + + c.conn = conn + close(c.readyChan) + return len(b), nil +} + +func (c *fastOpenConn) Close() error { + c.connLock.RLock() + defer c.connLock.RUnlock() + + if c.isClosedBeforeReady() { + return net.ErrClosed + } + + if c.conn != nil { + return c.conn.Close() + } + + close(c.readyChan) + return nil +} + +// isClosedBeforeReady returns true if the connection is closed before the real connection is established. +// This function should be called with connLock.RLock(). +func (c *fastOpenConn) isClosedBeforeReady() bool { + select { + case <-c.readyChan: + if c.conn == nil { + return true + } + default: + } + return false +} + +func (c *fastOpenConn) LocalAddr() net.Addr { + c.connLock.RLock() + defer c.connLock.RUnlock() + + if c.conn != nil { + return c.conn.LocalAddr() + } + + return nil +} + +func (c *fastOpenConn) RemoteAddr() net.Addr { + c.connLock.RLock() + conn := c.conn + c.connLock.RUnlock() + + if conn != nil { + return conn.RemoteAddr() + } + + addr, err := net.ResolveTCPAddr(c.network, c.address) + if err != nil { + return nil + } + return addr +} + +func (c *fastOpenConn) SetDeadline(t time.Time) error { + c.connLock.RLock() + defer c.connLock.RUnlock() + + c.deadline = &t + + if c.conn != nil { + return c.conn.SetDeadline(t) + } + + if c.isClosedBeforeReady() { + return net.ErrClosed + } + + return nil +} + +func (c *fastOpenConn) SetReadDeadline(t time.Time) error { + c.connLock.RLock() + defer c.connLock.RUnlock() + + c.readDeadline = &t + + if c.conn != nil { + return c.conn.SetReadDeadline(t) + } + + if c.isClosedBeforeReady() { + return net.ErrClosed + } + + return nil +} + +func (c *fastOpenConn) SetWriteDeadline(t time.Time) error { + c.connLock.RLock() + defer c.connLock.RUnlock() + + c.writeDeadline = &t + + if c.conn != nil { + return c.conn.SetWriteDeadline(t) + } + + if c.isClosedBeforeReady() { + return net.ErrClosed + } + + return nil +} + +var _ net.Conn = (*fastOpenConn)(nil) diff --git a/extras/outbounds/ob_direct.go b/extras/outbounds/ob_direct.go index b80ac00..de7ddd2 100644 --- a/extras/outbounds/ob_direct.go +++ b/extras/outbounds/ob_direct.go @@ -35,8 +35,8 @@ type directOutbound struct { Mode DirectOutboundMode // Dialer4 and Dialer6 are used for IPv4 and IPv6 TCP connections respectively. - Dialer4 *net.Dialer - Dialer6 *net.Dialer + DialFunc4 func(network, address string) (net.Conn, error) + DialFunc6 func(network, address string) (net.Conn, error) // DeviceName & BindIPs are for UDP connections. They don't use dialers, so we // need to bind them when creating the connection. @@ -45,6 +45,16 @@ type directOutbound struct { BindIP6 net.IP } +type DirectOutboundOptions struct { + Mode DirectOutboundMode + + DeviceName string + BindIP4 net.IP + BindIP6 net.IP + + FastOpen bool +} + type noAddressError struct { IPv4 bool IPv6 bool @@ -84,6 +94,57 @@ func (e resolveError) Unwrap() error { return e.Err } +func NewDirectOutboundWithOptions(opts DirectOutboundOptions) (PluggableOutbound, error) { + dialer4 := &net.Dialer{ + Timeout: defaultDialerTimeout, + } + if opts.BindIP4 != nil { + if opts.BindIP4.To4() == nil { + return nil, errors.New("BindIP4 must be an IPv4 address") + } + dialer4.LocalAddr = &net.TCPAddr{ + IP: opts.BindIP4, + } + } + dialer6 := &net.Dialer{ + Timeout: defaultDialerTimeout, + } + if opts.BindIP6 != nil { + if opts.BindIP6.To4() != nil { + return nil, errors.New("BindIP6 must be an IPv6 address") + } + dialer6.LocalAddr = &net.TCPAddr{ + IP: opts.BindIP6, + } + } + if opts.DeviceName != "" { + err := dialerBindToDevice(dialer4, opts.DeviceName) + if err != nil { + return nil, err + } + err = dialerBindToDevice(dialer6, opts.DeviceName) + if err != nil { + return nil, err + } + } + + dialFunc4 := dialer4.Dial + dialFunc6 := dialer6.Dial + if opts.FastOpen { + dialFunc4 = newFastOpenDialer(dialer4).Dial + dialFunc6 = newFastOpenDialer(dialer6).Dial + } + + return &directOutbound{ + Mode: opts.Mode, + DialFunc4: dialFunc4, + DialFunc6: dialFunc6, + DeviceName: opts.DeviceName, + BindIP4: opts.BindIP4, + BindIP6: opts.BindIP6, + }, nil +} + // NewDirectOutboundSimple creates a new directOutbound with the given mode, // without binding to a specific device. Works on all platforms. func NewDirectOutboundSimple(mode DirectOutboundMode) PluggableOutbound { @@ -91,9 +152,9 @@ func NewDirectOutboundSimple(mode DirectOutboundMode) PluggableOutbound { Timeout: defaultDialerTimeout, } return &directOutbound{ - Mode: mode, - Dialer4: d, - Dialer6: d, + Mode: mode, + DialFunc4: d.Dial, + DialFunc6: d.Dial, } } @@ -102,34 +163,20 @@ func NewDirectOutboundSimple(mode DirectOutboundMode) PluggableOutbound { // can be nil, in which case the directOutbound will not bind to a specific address // for that family. func NewDirectOutboundBindToIPs(mode DirectOutboundMode, bindIP4, bindIP6 net.IP) (PluggableOutbound, error) { - if bindIP4 != nil && bindIP4.To4() == nil { - return nil, errors.New("bindIP4 must be an IPv4 address") - } - if bindIP6 != nil && bindIP6.To4() != nil { - return nil, errors.New("bindIP6 must be an IPv6 address") - } - ob := &directOutbound{ - Mode: mode, - Dialer4: &net.Dialer{ - Timeout: defaultDialerTimeout, - }, - Dialer6: &net.Dialer{ - Timeout: defaultDialerTimeout, - }, + return NewDirectOutboundWithOptions(DirectOutboundOptions{ + Mode: mode, BindIP4: bindIP4, BindIP6: bindIP6, - } - if bindIP4 != nil { - ob.Dialer4.LocalAddr = &net.TCPAddr{ - IP: bindIP4, - } - } - if bindIP6 != nil { - ob.Dialer6.LocalAddr = &net.TCPAddr{ - IP: bindIP6, - } - } - return ob, nil + }) +} + +// NewDirectOutboundBindToDevice creates a new directOutbound with the given mode, +// and binds to the given device. Only works on Linux. +func NewDirectOutboundBindToDevice(mode DirectOutboundMode, deviceName string) (PluggableOutbound, error) { + return NewDirectOutboundWithOptions(DirectOutboundOptions{ + Mode: mode, + DeviceName: deviceName, + }) } // resolve is our built-in DNS resolver for handling the case when @@ -201,9 +248,9 @@ func (d *directOutbound) TCP(reqAddr *AddrEx) (net.Conn, error) { func (d *directOutbound) dialTCP(ip net.IP, port uint16) (net.Conn, error) { if ip.To4() != nil { - return d.Dialer4.Dial("tcp4", net.JoinHostPort(ip.String(), strconv.Itoa(int(port)))) + return d.DialFunc4("tcp4", net.JoinHostPort(ip.String(), strconv.Itoa(int(port)))) } else { - return d.Dialer6.Dial("tcp6", net.JoinHostPort(ip.String(), strconv.Itoa(int(port)))) + return d.DialFunc6("tcp6", net.JoinHostPort(ip.String(), strconv.Itoa(int(port)))) } } diff --git a/extras/outbounds/ob_direct_linux.go b/extras/outbounds/ob_direct_linux.go index 33b7d09..5607e50 100644 --- a/extras/outbounds/ob_direct_linux.go +++ b/extras/outbounds/ob_direct_linux.go @@ -6,31 +6,31 @@ import ( "syscall" ) -// NewDirectOutboundBindToDevice creates a new directOutbound with the given mode, -// and binds to the given device. Only works on Linux. -func NewDirectOutboundBindToDevice(mode DirectOutboundMode, deviceName string) (PluggableOutbound, error) { +func dialerBindToDevice(dialer *net.Dialer, deviceName string) error { if err := verifyDeviceName(deviceName); err != nil { - return nil, err + return err } - d := &net.Dialer{ - Timeout: defaultDialerTimeout, - Control: func(network, address string, c syscall.RawConn) error { - var errBind error - err := c.Control(func(fd uintptr) { - errBind = syscall.BindToDevice(int(fd), deviceName) - }) + + originControl := dialer.Control + dialer.Control = func(network, address string, c syscall.RawConn) error { + if originControl != nil { + // Chaining other control function + err := originControl(network, address, c) if err != nil { return err } - return errBind - }, + } + + var errBind error + err := c.Control(func(fd uintptr) { + errBind = syscall.BindToDevice(int(fd), deviceName) + }) + if err != nil { + return err + } + return errBind } - return &directOutbound{ - Mode: mode, - Dialer4: d, - Dialer6: d, - DeviceName: deviceName, - }, nil + return nil } func verifyDeviceName(deviceName string) error { diff --git a/extras/outbounds/ob_direct_others.go b/extras/outbounds/ob_direct_others.go index b416c30..eeedc84 100644 --- a/extras/outbounds/ob_direct_others.go +++ b/extras/outbounds/ob_direct_others.go @@ -7,11 +7,8 @@ import ( "net" ) -// NewDirectOutboundBindToDevice creates a new directOutbound with the given mode, -// and binds to the given device. This doesn't work on non-Linux platforms, so this -// is just a stub function that always returns an error. -func NewDirectOutboundBindToDevice(mode DirectOutboundMode, deviceName string) (PluggableOutbound, error) { - return nil, errors.New("binding to device is not supported on this platform") +func dialerBindToDevice(dialer *net.Dialer, deviceName string) error { + return errors.New("binding to device is not supported on this platform") } func udpConnBindToDevice(conn *net.UDPConn, deviceName string) error { From d8c61c59d746a2c496c0a61751dc5ebf25da9beb Mon Sep 17 00:00:00 2001 From: Haruue Date: Sat, 23 Nov 2024 22:31:14 +0900 Subject: [PATCH 143/161] chore: disable fallback mode of tfo dialer tfo-go caches the "unsupported" status when fallback mode is enabled. In other words, if the hysteria server is started with net.ipv4.tcp_fastopen=0 and it fails once, the tfo will not be enabled until it is restarted, even if the user later sets sysctl net.ipv4.tcp_fastopen=3. --- extras/outbounds/fastopen.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extras/outbounds/fastopen.go b/extras/outbounds/fastopen.go index a607500..1d5d1ee 100644 --- a/extras/outbounds/fastopen.go +++ b/extras/outbounds/fastopen.go @@ -15,8 +15,7 @@ type fastOpenDialer struct { func newFastOpenDialer(netDialer *net.Dialer) *fastOpenDialer { return &fastOpenDialer{ dialer: &tfo.Dialer{ - Dialer: *netDialer, - Fallback: true, + Dialer: *netDialer, }, } } From 5e11ea18fb9d02e19d545eee2749e350640419ed Mon Sep 17 00:00:00 2001 From: Toby Date: Tue, 10 Dec 2024 22:42:25 -0800 Subject: [PATCH 144/161] chore: update core/go.mod --- core/go.mod | 2 +- core/go.sum | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/go.mod b/core/go.mod index beb0372..213ff5a 100644 --- a/core/go.mod +++ b/core/go.mod @@ -27,7 +27,7 @@ require ( golang.org/x/crypto v0.26.0 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.28.0 // indirect - golang.org/x/sys v0.23.0 // indirect + golang.org/x/sys v0.25.0 // indirect golang.org/x/text v0.17.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/protobuf v1.34.1 // indirect diff --git a/core/go.sum b/core/go.sum index d65498b..fdb5c63 100644 --- a/core/go.sum +++ b/core/go.sum @@ -58,8 +58,7 @@ golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= From 400fed3bd607d5f529487aa07d7f4d630c802ae4 Mon Sep 17 00:00:00 2001 From: Haruue Date: Wed, 11 Dec 2024 18:05:11 +0900 Subject: [PATCH 145/161] chore(version): rename LibVersion to Libraries close: #1271 A key that also contains "Version" broke the version parsing of some third-party clients. --- app/cmd/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/cmd/root.go b/app/cmd/root.go index a58e2bc..13f9705 100644 --- a/app/cmd/root.go +++ b/app/cmd/root.go @@ -45,7 +45,7 @@ var ( "CommitHash:\t%s\n"+ "Platform:\t%s\n"+ "Architecture:\t%s\n"+ - "LibVersion:\t%s", + "Libraries:\tquic-go=%s", appVersion, appDate, appType, appToolchain, appCommit, appPlatform, appArch, libVersion) appAboutLong = fmt.Sprintf("%s\n%s\n%s\n\n%s", appLogo, appDesc, appAuthors, appVersionLong) From 53a4ce2598f6b169554cf17c74500e662979a614 Mon Sep 17 00:00:00 2001 From: Haruue Date: Sun, 29 Dec 2024 13:33:32 +0900 Subject: [PATCH 146/161] fix: tun failed on linux when ipv6.disable=1 close: #1285 --- app/internal/tun/check_ipv6_others.go | 14 ++++++++++++++ app/internal/tun/check_ipv6_unix.go | 16 ++++++++++++++++ app/internal/tun/check_ipv6_windows.go | 24 ++++++++++++++++++++++++ app/internal/tun/server.go | 4 ++++ 4 files changed, 58 insertions(+) create mode 100644 app/internal/tun/check_ipv6_others.go create mode 100644 app/internal/tun/check_ipv6_unix.go create mode 100644 app/internal/tun/check_ipv6_windows.go diff --git a/app/internal/tun/check_ipv6_others.go b/app/internal/tun/check_ipv6_others.go new file mode 100644 index 0000000..7cfd9ab --- /dev/null +++ b/app/internal/tun/check_ipv6_others.go @@ -0,0 +1,14 @@ +//go:build !unix && !windows + +package tun + +import "net" + +func isIPv6Supported() bool { + lis, err := net.ListenPacket("udp6", "[::1]:0") + if err != nil { + return false + } + _ = lis.Close() + return true +} diff --git a/app/internal/tun/check_ipv6_unix.go b/app/internal/tun/check_ipv6_unix.go new file mode 100644 index 0000000..8fdffaf --- /dev/null +++ b/app/internal/tun/check_ipv6_unix.go @@ -0,0 +1,16 @@ +//go:build unix + +package tun + +import ( + "golang.org/x/sys/unix" +) + +func isIPv6Supported() bool { + sock, err := unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, unix.IPPROTO_UDP) + if err != nil { + return false + } + _ = unix.Close(sock) + return true +} diff --git a/app/internal/tun/check_ipv6_windows.go b/app/internal/tun/check_ipv6_windows.go new file mode 100644 index 0000000..d488d7e --- /dev/null +++ b/app/internal/tun/check_ipv6_windows.go @@ -0,0 +1,24 @@ +//go:build windows + +package tun + +import ( + "golang.org/x/sys/windows" +) + +func isIPv6Supported() bool { + var wsaData windows.WSAData + err := windows.WSAStartup(uint32(0x202), &wsaData) + if err != nil { + // Failing silently: it is not our duty to report such errors + return true + } + defer windows.WSACleanup() + + sock, err := windows.Socket(windows.AF_INET6, windows.SOCK_DGRAM, windows.IPPROTO_UDP) + if err != nil { + return false + } + _ = windows.Closesocket(sock) + return true +} diff --git a/app/internal/tun/server.go b/app/internal/tun/server.go index 303d4ec..a999051 100644 --- a/app/internal/tun/server.go +++ b/app/internal/tun/server.go @@ -49,6 +49,10 @@ type EventLogger interface { } func (s *Server) Serve() error { + if !isIPv6Supported() { + s.Logger.Warn("tun-pre-check", zap.String("msg", "IPv6 is not supported or enabled on this system, TUN device is created without IPv6 support.")) + s.Inet6Address = nil + } tunOpts := tun.Options{ Name: s.IfName, Inet4Address: s.Inet4Address, From 2bdaf7b46ad9fc5e7a24196400c7acf1796a1aa5 Mon Sep 17 00:00:00 2001 From: Haruue Date: Sun, 29 Dec 2024 13:58:12 +0900 Subject: [PATCH 147/161] feat: allow skip cert verify in masquerade.proxy close: #1278 masquerade.proxy.insecureSkipVerify --- app/cmd/server.go | 25 +++++++++++++++++++++++-- app/cmd/server_test.go | 5 +++-- app/cmd/server_test.yaml | 1 + 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/app/cmd/server.go b/app/cmd/server.go index 1384dd8..2368c38 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -236,8 +236,9 @@ type serverConfigMasqueradeFile struct { } type serverConfigMasqueradeProxy struct { - URL string `mapstructure:"url"` - RewriteHost bool `mapstructure:"rewriteHost"` + URL string `mapstructure:"url"` + RewriteHost bool `mapstructure:"rewriteHost"` + InsecureSkipVerify bool `mapstructure:"insecureSkipVerify"` } type serverConfigMasqueradeString struct { @@ -810,6 +811,25 @@ func (c *serverConfig) fillMasqHandler(hyConfig *server.Config) error { if u.Scheme != "http" && u.Scheme != "https" { return configError{Field: "masquerade.proxy.url", Err: fmt.Errorf("unsupported protocol scheme \"%s\"", u.Scheme)} } + transport := http.DefaultTransport + if c.Masquerade.Proxy.InsecureSkipVerify { + transport = &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + // use default configs from http.DefaultTransport + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).DialContext, + ForceAttemptHTTP2: true, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + } + } handler = &httputil.ReverseProxy{ Rewrite: func(r *httputil.ProxyRequest) { r.SetURL(u) @@ -819,6 +839,7 @@ func (c *serverConfig) fillMasqHandler(hyConfig *server.Config) error { r.Out.Host = r.In.Host } }, + Transport: transport, ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) { logger.Error("HTTP reverse proxy error", zap.Error(err)) w.WriteHeader(http.StatusBadGateway) diff --git a/app/cmd/server_test.go b/app/cmd/server_test.go index bcf61c3..dd2c909 100644 --- a/app/cmd/server_test.go +++ b/app/cmd/server_test.go @@ -169,8 +169,9 @@ func TestServerConfig(t *testing.T) { Dir: "/www/masq", }, Proxy: serverConfigMasqueradeProxy{ - URL: "https://some.site.net", - RewriteHost: true, + URL: "https://some.site.net", + RewriteHost: true, + InsecureSkipVerify: true, }, String: serverConfigMasqueradeString{ Content: "aint nothin here", diff --git a/app/cmd/server_test.yaml b/app/cmd/server_test.yaml index dda6d98..3d9a308 100644 --- a/app/cmd/server_test.yaml +++ b/app/cmd/server_test.yaml @@ -132,6 +132,7 @@ masquerade: proxy: url: https://some.site.net rewriteHost: true + insecureSkipVerify: true string: content: aint nothin here headers: From 8aa80c233e2c6b28448fc28e08559ff24fa9d887 Mon Sep 17 00:00:00 2001 From: Toby Date: Sun, 29 Dec 2024 11:25:08 -0800 Subject: [PATCH 148/161] fix: rename insecureSkipVerify to insecure for consistency --- app/cmd/server.go | 8 ++++---- app/cmd/server_test.go | 6 +++--- app/cmd/server_test.yaml | 2 +- core/go.sum | 1 + 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/cmd/server.go b/app/cmd/server.go index 2368c38..5f2d562 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -236,9 +236,9 @@ type serverConfigMasqueradeFile struct { } type serverConfigMasqueradeProxy struct { - URL string `mapstructure:"url"` - RewriteHost bool `mapstructure:"rewriteHost"` - InsecureSkipVerify bool `mapstructure:"insecureSkipVerify"` + URL string `mapstructure:"url"` + RewriteHost bool `mapstructure:"rewriteHost"` + Insecure bool `mapstructure:"insecure"` } type serverConfigMasqueradeString struct { @@ -812,7 +812,7 @@ func (c *serverConfig) fillMasqHandler(hyConfig *server.Config) error { return configError{Field: "masquerade.proxy.url", Err: fmt.Errorf("unsupported protocol scheme \"%s\"", u.Scheme)} } transport := http.DefaultTransport - if c.Masquerade.Proxy.InsecureSkipVerify { + if c.Masquerade.Proxy.Insecure { transport = &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, diff --git a/app/cmd/server_test.go b/app/cmd/server_test.go index dd2c909..5849a38 100644 --- a/app/cmd/server_test.go +++ b/app/cmd/server_test.go @@ -169,9 +169,9 @@ func TestServerConfig(t *testing.T) { Dir: "/www/masq", }, Proxy: serverConfigMasqueradeProxy{ - URL: "https://some.site.net", - RewriteHost: true, - InsecureSkipVerify: true, + URL: "https://some.site.net", + RewriteHost: true, + Insecure: true, }, String: serverConfigMasqueradeString{ Content: "aint nothin here", diff --git a/app/cmd/server_test.yaml b/app/cmd/server_test.yaml index 3d9a308..b989b97 100644 --- a/app/cmd/server_test.yaml +++ b/app/cmd/server_test.yaml @@ -132,7 +132,7 @@ masquerade: proxy: url: https://some.site.net rewriteHost: true - insecureSkipVerify: true + insecure: true string: content: aint nothin here headers: diff --git a/core/go.sum b/core/go.sum index fdb5c63..3caf57a 100644 --- a/core/go.sum +++ b/core/go.sum @@ -59,6 +59,7 @@ golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= From 817d6c9a2d0899210d848c885bf4e7a655f32c73 Mon Sep 17 00:00:00 2001 From: zyppe <734935956@qq.com> Date: Sat, 4 Jan 2025 22:45:11 +0800 Subject: [PATCH 149/161] Add support for loongarch64 Test on Loongson 3A5000 --- hyperbole.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hyperbole.py b/hyperbole.py index ecc248d..13dcd0a 100755 --- a/hyperbole.py +++ b/hyperbole.py @@ -74,6 +74,9 @@ ARCH_ALIASES = { "GOARCH": "amd64", "GOAMD64": "v3", }, + "loong64": { + "GOARCH": "loong64", + }, } From 537e8144ea5eea314dd6c5393f31f88533822054 Mon Sep 17 00:00:00 2001 From: Haruue Date: Wed, 8 Jan 2025 14:56:15 +0900 Subject: [PATCH 150/161] ci: add linux/loong64 to platforms.txt --- platforms.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/platforms.txt b/platforms.txt index ea0ddf3..9059333 100644 --- a/platforms.txt +++ b/platforms.txt @@ -22,6 +22,7 @@ linux/s390x linux/mipsle linux/mipsle-sf linux/riscv64 +linux/loong64 # Android android/386 From d86aa0b4e21ffb8b8175b29af99d2d8d612ecb06 Mon Sep 17 00:00:00 2001 From: Haruue Date: Wed, 8 Jan 2025 14:57:02 +0900 Subject: [PATCH 151/161] chore(scripts): detect arch for loong64 --- scripts/install_server.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/install_server.sh b/scripts/install_server.sh index 4277ab9..93c06d9 100644 --- a/scripts/install_server.sh +++ b/scripts/install_server.sh @@ -436,6 +436,9 @@ check_environment_architecture() { 's390x') ARCHITECTURE='s390x' ;; + 'loongarch64') + ARCHITECTURE='loong64' + ;; *) error "The architecture '$(uname -a)' is not supported." note "Specify ARCHITECTURE= to bypass this check and force this script to run on this $(uname -m)." From e1df8aa4e2aa0100cd46790afbbb0788e0d741f5 Mon Sep 17 00:00:00 2001 From: Haruue Date: Mon, 3 Feb 2025 12:27:44 +0900 Subject: [PATCH 152/161] chore: make username of userpass case insensitive close: #1297 Just a workaround for "uppercase usernames do not work". Usernames in different cases (like "Gawr" and "gawR") will now conflict. --- app/cmd/server.go | 2 +- extras/auth/userpass.go | 13 ++++++++++++- extras/auth/userpass_test.go | 20 +++++++++++++++++--- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/app/cmd/server.go b/app/cmd/server.go index 5f2d562..a2aa9a4 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -755,7 +755,7 @@ func (c *serverConfig) fillAuthenticator(hyConfig *server.Config) error { if len(c.Auth.UserPass) == 0 { return configError{Field: "auth.userpass", Err: errors.New("empty auth userpass")} } - hyConfig.Authenticator = &auth.UserPassAuthenticator{Users: c.Auth.UserPass} + hyConfig.Authenticator = auth.NewUserPassAuthenticator(c.Auth.UserPass) return nil case "http", "https": if c.Auth.HTTP.URL == "" { diff --git a/extras/auth/userpass.go b/extras/auth/userpass.go index 8faf87a..f1c0184 100644 --- a/extras/auth/userpass.go +++ b/extras/auth/userpass.go @@ -19,6 +19,16 @@ type UserPassAuthenticator struct { Users map[string]string } +func NewUserPassAuthenticator(users map[string]string) *UserPassAuthenticator { + // Usernames are case-insensitive, as they are already lowercased by viper. + // Lowercase it again on our own to make it explicit. + lcUsers := make(map[string]string, len(users)) + for user, pass := range users { + lcUsers[strings.ToLower(user)] = pass + } + return &UserPassAuthenticator{Users: lcUsers} +} + func (a *UserPassAuthenticator) Authenticate(addr net.Addr, auth string, tx uint64) (ok bool, id string) { u, p, ok := splitUserPass(auth) if !ok { @@ -36,5 +46,6 @@ func splitUserPass(auth string) (user, pass string, ok bool) { if len(rs) != 2 { return "", "", false } - return rs[0], rs[1], true + // Usernames are case-insensitive + return strings.ToLower(rs[0]), rs[1], true } diff --git a/extras/auth/userpass_test.go b/extras/auth/userpass_test.go index 05f788e..0f1b568 100644 --- a/extras/auth/userpass_test.go +++ b/extras/auth/userpass_test.go @@ -85,12 +85,26 @@ func TestUserPassAuthenticator(t *testing.T) { wantOk: false, wantId: "", }, + { + name: "case insensitive username", + fields: fields{ + Users: map[string]string{ + "gawR": "gura", + "fubuki": "shirakami", + }, + }, + args: args{ + addr: nil, + auth: "Gawr:gura", + tx: 0, + }, + wantOk: true, + wantId: "gawr", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - a := &UserPassAuthenticator{ - Users: tt.fields.Users, - } + a := NewUserPassAuthenticator(tt.fields.Users) gotOk, gotId := a.Authenticate(tt.args.addr, tt.args.auth, tt.args.tx) if gotOk != tt.wantOk { t.Errorf("Authenticate() gotOk = %v, want %v", gotOk, tt.wantOk) From 7652ddcd994caa5c0f1a6ea9025886155547af9c Mon Sep 17 00:00:00 2001 From: Haruue Date: Mon, 3 Feb 2025 12:39:52 +0900 Subject: [PATCH 153/161] chore: unexport UserPassAuthenticator.Users --- extras/auth/userpass.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extras/auth/userpass.go b/extras/auth/userpass.go index f1c0184..9d11cd9 100644 --- a/extras/auth/userpass.go +++ b/extras/auth/userpass.go @@ -16,7 +16,7 @@ var _ server.Authenticator = &UserPassAuthenticator{} // UserPassAuthenticator checks the provided auth string against a map of username/password pairs. // The format of the auth string must be "username:password". type UserPassAuthenticator struct { - Users map[string]string + users map[string]string } func NewUserPassAuthenticator(users map[string]string) *UserPassAuthenticator { @@ -26,7 +26,7 @@ func NewUserPassAuthenticator(users map[string]string) *UserPassAuthenticator { for user, pass := range users { lcUsers[strings.ToLower(user)] = pass } - return &UserPassAuthenticator{Users: lcUsers} + return &UserPassAuthenticator{users: lcUsers} } func (a *UserPassAuthenticator) Authenticate(addr net.Addr, auth string, tx uint64) (ok bool, id string) { @@ -34,7 +34,7 @@ func (a *UserPassAuthenticator) Authenticate(addr net.Addr, auth string, tx uint if !ok { return false, "" } - rp, ok := a.Users[u] + rp, ok := a.users[u] if !ok || rp != p { return false, "" } From e11ad2b93b0d950e23fcb3ab81650a9dfe5e6a4e Mon Sep 17 00:00:00 2001 From: Toby Date: Mon, 3 Feb 2025 18:04:17 -0800 Subject: [PATCH 154/161] feat: quic-go v0.49.0 --- app/go.mod | 8 ++++---- app/go.sum | 16 ++++++++-------- core/go.mod | 9 +++++---- core/go.sum | 16 ++++++++-------- extras/go.mod | 8 ++++---- extras/go.sum | 16 ++++++++-------- go.work.sum | 2 ++ 7 files changed, 39 insertions(+), 36 deletions(-) diff --git a/app/go.mod b/app/go.mod index 2d36411..8bd01dd 100644 --- a/app/go.mod +++ b/app/go.mod @@ -30,7 +30,7 @@ require ( require ( github.com/andybalholm/brotli v1.1.0 // indirect - github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7 // indirect + github.com/apernet/quic-go v0.49.1-0.20250204013113-43c72b1281a0 // indirect github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect github.com/cloudflare/circl v1.3.9 // indirect github.com/database64128/netx-go v0.0.0-20240905055117-62795b8b054a // indirect @@ -71,16 +71,16 @@ require ( github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect github.com/vultr/govultr/v3 v3.6.4 // indirect go.uber.org/atomic v1.11.0 // indirect - go.uber.org/mock v0.4.0 // indirect + go.uber.org/mock v0.5.0 // indirect go.uber.org/multierr v1.11.0 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect golang.org/x/crypto v0.26.0 // indirect - golang.org/x/mod v0.17.0 // indirect + golang.org/x/mod v0.18.0 // indirect golang.org/x/net v0.28.0 // indirect golang.org/x/oauth2 v0.20.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/text v0.17.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/tools v0.22.0 // indirect google.golang.org/protobuf v1.34.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/app/go.sum b/app/go.sum index 1b30feb..85c45a7 100644 --- a/app/go.sum +++ b/app/go.sum @@ -42,8 +42,8 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1 github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f h1:uVh0qpEslrWjgzx9vOcyCqsOY3c9kofDZ1n+qaw35ZY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f/go.mod h1:xkkq9D4ygcldQQhKS/w9CadiCKwCngU7K9E3DaKahpM= -github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7 h1:zO38yBOvQ1dLHbSuaU5BFZ8zalnSDQslj+i/9AGOk9s= -github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7/go.mod h1:LoSUY2chVqNQCDyi4IZGqPpXLy1FuCkE37PKwtJvNGg= +github.com/apernet/quic-go v0.49.1-0.20250204013113-43c72b1281a0 h1:oc6//C91pY9gGOBioHeyJrmmpKv/nS8fvTeDpKNPLnI= +github.com/apernet/quic-go v0.49.1-0.20250204013113-43c72b1281a0/go.mod h1:/mMPNt1MHqduzaVB2qFHnJwam3BR5r5b35GvYouJs/o= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad h1:QzQ2sKpc9o42HNRR8ukM5uMC/RzR2HgZd/Nvaqol2C0= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= @@ -300,8 +300,8 @@ go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= -go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= -go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= @@ -358,8 +358,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -538,8 +538,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/core/go.mod b/core/go.mod index 213ff5a..1eac2dc 100644 --- a/core/go.mod +++ b/core/go.mod @@ -5,7 +5,7 @@ go 1.22 toolchain go1.23.2 require ( - github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7 + github.com/apernet/quic-go v0.49.1-0.20250204013113-43c72b1281a0 github.com/stretchr/testify v1.9.0 go.uber.org/goleak v1.2.1 golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 @@ -23,13 +23,14 @@ require ( github.com/quic-go/qpack v0.5.1 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/stretchr/objx v0.5.2 // indirect - go.uber.org/mock v0.4.0 // indirect + go.uber.org/mock v0.5.0 // indirect golang.org/x/crypto v0.26.0 // indirect - golang.org/x/mod v0.17.0 // indirect + golang.org/x/mod v0.18.0 // indirect golang.org/x/net v0.28.0 // indirect + golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.25.0 // indirect golang.org/x/text v0.17.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/tools v0.22.0 // indirect google.golang.org/protobuf v1.34.1 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/core/go.sum b/core/go.sum index 3caf57a..33e6d03 100644 --- a/core/go.sum +++ b/core/go.sum @@ -1,5 +1,5 @@ -github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7 h1:zO38yBOvQ1dLHbSuaU5BFZ8zalnSDQslj+i/9AGOk9s= -github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7/go.mod h1:LoSUY2chVqNQCDyi4IZGqPpXLy1FuCkE37PKwtJvNGg= +github.com/apernet/quic-go v0.49.1-0.20250204013113-43c72b1281a0 h1:oc6//C91pY9gGOBioHeyJrmmpKv/nS8fvTeDpKNPLnI= +github.com/apernet/quic-go v0.49.1-0.20250204013113-43c72b1281a0/go.mod h1:/mMPNt1MHqduzaVB2qFHnJwam3BR5r5b35GvYouJs/o= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -45,14 +45,14 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= -go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= -go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= @@ -64,8 +64,8 @@ golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/extras/go.mod b/extras/go.mod index 67ef38f..1ae6146 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -6,7 +6,7 @@ toolchain go1.23.2 require ( github.com/apernet/hysteria/core/v2 v2.0.0-00010101000000-000000000000 - github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7 + github.com/apernet/quic-go v0.49.1-0.20250204013113-43c72b1281a0 github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 github.com/database64128/tfo-go/v2 v2.2.2 github.com/hashicorp/golang-lru/v2 v2.0.5 @@ -34,13 +34,13 @@ require ( github.com/quic-go/qpack v0.5.1 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect - go.uber.org/mock v0.4.0 // indirect + go.uber.org/mock v0.5.0 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect - golang.org/x/mod v0.17.0 // indirect + golang.org/x/mod v0.18.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.25.0 // indirect golang.org/x/text v0.17.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/tools v0.22.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/extras/go.sum b/extras/go.sum index 98616ca..31595de 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -1,7 +1,7 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7 h1:zO38yBOvQ1dLHbSuaU5BFZ8zalnSDQslj+i/9AGOk9s= -github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7/go.mod h1:LoSUY2chVqNQCDyi4IZGqPpXLy1FuCkE37PKwtJvNGg= +github.com/apernet/quic-go v0.49.1-0.20250204013113-43c72b1281a0 h1:oc6//C91pY9gGOBioHeyJrmmpKv/nS8fvTeDpKNPLnI= +github.com/apernet/quic-go v0.49.1-0.20250204013113-43c72b1281a0/go.mod h1:/mMPNt1MHqduzaVB2qFHnJwam3BR5r5b35GvYouJs/o= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -66,8 +66,8 @@ github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301/go.mod h1:ntmMHL github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= -go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= -go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= @@ -76,8 +76,8 @@ golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJ golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -113,8 +113,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= diff --git a/go.work.sum b/go.work.sum index 79da3fa..95d2882 100644 --- a/go.work.sum +++ b/go.work.sum @@ -312,6 +312,8 @@ golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2 h1:IRJeR9r1pYWsHKTRe/IInb7lYvbBVIqOgsX/u0mbOWY= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= +golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 h1:zf5N6UOrA487eEFacMePxjXAJctxKmyjKUsjA11Uzuk= +golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= From ffab01730a6cee644ff091a2ed09e09f9d8ea8d6 Mon Sep 17 00:00:00 2001 From: Toby Date: Tue, 18 Mar 2025 20:44:59 -0700 Subject: [PATCH 155/161] chore: add LICENSE to packages --- app/LICENSE.md | 7 +++++++ core/LICENSE.md | 7 +++++++ extras/LICENSE.md | 7 +++++++ 3 files changed, 21 insertions(+) create mode 100644 app/LICENSE.md create mode 100644 core/LICENSE.md create mode 100644 extras/LICENSE.md diff --git a/app/LICENSE.md b/app/LICENSE.md new file mode 100644 index 0000000..208e8f2 --- /dev/null +++ b/app/LICENSE.md @@ -0,0 +1,7 @@ +Copyright 2023 Toby + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/core/LICENSE.md b/core/LICENSE.md new file mode 100644 index 0000000..208e8f2 --- /dev/null +++ b/core/LICENSE.md @@ -0,0 +1,7 @@ +Copyright 2023 Toby + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/extras/LICENSE.md b/extras/LICENSE.md new file mode 100644 index 0000000..208e8f2 --- /dev/null +++ b/extras/LICENSE.md @@ -0,0 +1,7 @@ +Copyright 2023 Toby + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From 5239a23aee96c54a372cbb6bd4dd82e65e09321f Mon Sep 17 00:00:00 2001 From: dropbigfish Date: Sun, 20 Apr 2025 12:06:13 +0800 Subject: [PATCH 156/161] chore: fix function name in comment Signed-off-by: dropbigfish --- core/internal/congestion/bbr/packet_number_indexed_queue.go | 2 +- extras/trafficlogger/http.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/internal/congestion/bbr/packet_number_indexed_queue.go b/core/internal/congestion/bbr/packet_number_indexed_queue.go index 86fe52d..08b99de 100644 --- a/core/internal/congestion/bbr/packet_number_indexed_queue.go +++ b/core/internal/congestion/bbr/packet_number_indexed_queue.go @@ -152,7 +152,7 @@ func (p *packetNumberIndexedQueue[T]) EntrySlotsUsed() int { return p.entries.Len() } -// LastPacket returns packet number of the first entry in the queue. +// FirstPacket returns packet number of the first entry in the queue. func (p *packetNumberIndexedQueue[T]) FirstPacket() (packetNumber congestion.PacketNumber) { return p.firstPacket } diff --git a/extras/trafficlogger/http.go b/extras/trafficlogger/http.go index d8e6ebd..8b26217 100644 --- a/extras/trafficlogger/http.go +++ b/extras/trafficlogger/http.go @@ -71,7 +71,7 @@ func (s *trafficStatsServerImpl) LogTraffic(id string, tx, rx uint64) (ok bool) return true } -// LogOnlineStateChanged updates the online state to the online map. +// LogOnlineState updates the online state to the online map. func (s *trafficStatsServerImpl) LogOnlineState(id string, online bool) { s.Mutex.Lock() defer s.Mutex.Unlock() From 29cd04fdef147136afaaafd29c1274f1cbcd8bea Mon Sep 17 00:00:00 2001 From: Toby Date: Sun, 20 Apr 2025 14:04:53 -0700 Subject: [PATCH 157/161] feat: quic-go v0.51.0 (wip) --- app/go.mod | 6 +++--- app/go.sum | 6 ++---- core/client/client.go | 22 +++++++++++++--------- core/go.mod | 6 +++--- core/go.sum | 4 ++-- extras/go.mod | 6 +++--- extras/go.sum | 6 ++---- go.work | 4 ++-- 8 files changed, 30 insertions(+), 30 deletions(-) diff --git a/app/go.mod b/app/go.mod index 8bd01dd..be67c49 100644 --- a/app/go.mod +++ b/app/go.mod @@ -1,8 +1,8 @@ module github.com/apernet/hysteria/app/v2 -go 1.22 +go 1.23 -toolchain go1.23.2 +toolchain go1.24.2 require ( github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f @@ -30,7 +30,7 @@ require ( require ( github.com/andybalholm/brotli v1.1.0 // indirect - github.com/apernet/quic-go v0.49.1-0.20250204013113-43c72b1281a0 // indirect + github.com/apernet/quic-go v0.51.1-0.20250420202007-25ded0c7e74c // indirect github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect github.com/cloudflare/circl v1.3.9 // indirect github.com/database64128/netx-go v0.0.0-20240905055117-62795b8b054a // indirect diff --git a/app/go.sum b/app/go.sum index 85c45a7..2e988eb 100644 --- a/app/go.sum +++ b/app/go.sum @@ -42,8 +42,8 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1 github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f h1:uVh0qpEslrWjgzx9vOcyCqsOY3c9kofDZ1n+qaw35ZY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f/go.mod h1:xkkq9D4ygcldQQhKS/w9CadiCKwCngU7K9E3DaKahpM= -github.com/apernet/quic-go v0.49.1-0.20250204013113-43c72b1281a0 h1:oc6//C91pY9gGOBioHeyJrmmpKv/nS8fvTeDpKNPLnI= -github.com/apernet/quic-go v0.49.1-0.20250204013113-43c72b1281a0/go.mod h1:/mMPNt1MHqduzaVB2qFHnJwam3BR5r5b35GvYouJs/o= +github.com/apernet/quic-go v0.51.1-0.20250420202007-25ded0c7e74c h1:C3a7C/cvdMmgZ1nGc+ctPwVfWlPhcE7J2C63bmlAOD8= +github.com/apernet/quic-go v0.51.1-0.20250420202007-25ded0c7e74c/go.mod h1:I/47OIGG5H/IfAm+nz2c6hm6b/NkEhpvptAoiPcY7jQ= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad h1:QzQ2sKpc9o42HNRR8ukM5uMC/RzR2HgZd/Nvaqol2C0= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= @@ -485,8 +485,6 @@ golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= diff --git a/core/client/client.go b/core/client/client.go index 91d59fd..b9ad433 100644 --- a/core/client/client.go +++ b/core/client/client.go @@ -3,6 +3,7 @@ package client import ( "context" "crypto/tls" + "errors" "net" "net/http" "net/url" @@ -221,18 +222,21 @@ func (c *clientImpl) Close() error { return nil } +var nonPermanentErrors = []error{ + quic.StreamLimitReachedError{}, +} + // wrapIfConnectionClosed checks if the error returned by quic-go -// indicates that the QUIC connection has been permanently closed, -// and if so, wraps the error with coreErrs.ClosedError. -// PITFALL: sometimes quic-go has "internal errors" that are not net.Error, -// but we still need to treat them as ClosedError. +// is recoverable (listed in nonPermanentErrors) or permanent. +// Recoverable errors are returned as-is, +// permanent ones are wrapped as ClosedError. func wrapIfConnectionClosed(err error) error { - netErr, ok := err.(net.Error) - if !ok || !netErr.Temporary() { - return coreErrs.ClosedError{Err: err} - } else { - return err + for _, e := range nonPermanentErrors { + if errors.Is(err, e) { + return err + } } + return coreErrs.ClosedError{Err: err} } type tcpConn struct { diff --git a/core/go.mod b/core/go.mod index 1eac2dc..1cd8e99 100644 --- a/core/go.mod +++ b/core/go.mod @@ -1,11 +1,11 @@ module github.com/apernet/hysteria/core/v2 -go 1.22 +go 1.23 -toolchain go1.23.2 +toolchain go1.24.2 require ( - github.com/apernet/quic-go v0.49.1-0.20250204013113-43c72b1281a0 + github.com/apernet/quic-go v0.51.1-0.20250420202007-25ded0c7e74c github.com/stretchr/testify v1.9.0 go.uber.org/goleak v1.2.1 golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 diff --git a/core/go.sum b/core/go.sum index 33e6d03..74f0b3c 100644 --- a/core/go.sum +++ b/core/go.sum @@ -1,5 +1,5 @@ -github.com/apernet/quic-go v0.49.1-0.20250204013113-43c72b1281a0 h1:oc6//C91pY9gGOBioHeyJrmmpKv/nS8fvTeDpKNPLnI= -github.com/apernet/quic-go v0.49.1-0.20250204013113-43c72b1281a0/go.mod h1:/mMPNt1MHqduzaVB2qFHnJwam3BR5r5b35GvYouJs/o= +github.com/apernet/quic-go v0.51.1-0.20250420202007-25ded0c7e74c h1:C3a7C/cvdMmgZ1nGc+ctPwVfWlPhcE7J2C63bmlAOD8= +github.com/apernet/quic-go v0.51.1-0.20250420202007-25ded0c7e74c/go.mod h1:I/47OIGG5H/IfAm+nz2c6hm6b/NkEhpvptAoiPcY7jQ= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= diff --git a/extras/go.mod b/extras/go.mod index 1ae6146..5bda6c6 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -1,12 +1,12 @@ module github.com/apernet/hysteria/extras/v2 -go 1.22 +go 1.23 -toolchain go1.23.2 +toolchain go1.24.2 require ( github.com/apernet/hysteria/core/v2 v2.0.0-00010101000000-000000000000 - github.com/apernet/quic-go v0.49.1-0.20250204013113-43c72b1281a0 + github.com/apernet/quic-go v0.51.1-0.20250420202007-25ded0c7e74c github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 github.com/database64128/tfo-go/v2 v2.2.2 github.com/hashicorp/golang-lru/v2 v2.0.5 diff --git a/extras/go.sum b/extras/go.sum index 31595de..9bbb21a 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -1,7 +1,7 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/apernet/quic-go v0.49.1-0.20250204013113-43c72b1281a0 h1:oc6//C91pY9gGOBioHeyJrmmpKv/nS8fvTeDpKNPLnI= -github.com/apernet/quic-go v0.49.1-0.20250204013113-43c72b1281a0/go.mod h1:/mMPNt1MHqduzaVB2qFHnJwam3BR5r5b35GvYouJs/o= +github.com/apernet/quic-go v0.51.1-0.20250420202007-25ded0c7e74c h1:C3a7C/cvdMmgZ1nGc+ctPwVfWlPhcE7J2C63bmlAOD8= +github.com/apernet/quic-go v0.51.1-0.20250420202007-25ded0c7e74c/go.mod h1:I/47OIGG5H/IfAm+nz2c6hm6b/NkEhpvptAoiPcY7jQ= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -107,8 +107,6 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= diff --git a/go.work b/go.work index 1ff4f48..1d4fa8b 100644 --- a/go.work +++ b/go.work @@ -1,6 +1,6 @@ -go 1.22 +go 1.23 -toolchain go1.23.2 +toolchain go1.24.2 use ( ./app From c2c4a9545ec5801aab7ce55e19b202229e46461c Mon Sep 17 00:00:00 2001 From: Toby Date: Sun, 20 Apr 2025 14:27:45 -0700 Subject: [PATCH 158/161] fix: broken cc after connection migration --- app/go.mod | 2 +- app/go.sum | 4 ++-- core/go.mod | 2 +- core/go.sum | 4 ++-- extras/go.mod | 2 +- extras/go.sum | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/go.mod b/app/go.mod index be67c49..d8a68a2 100644 --- a/app/go.mod +++ b/app/go.mod @@ -30,7 +30,7 @@ require ( require ( github.com/andybalholm/brotli v1.1.0 // indirect - github.com/apernet/quic-go v0.51.1-0.20250420202007-25ded0c7e74c // indirect + github.com/apernet/quic-go v0.51.1-0.20250420212127-ede9b032759a // indirect github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect github.com/cloudflare/circl v1.3.9 // indirect github.com/database64128/netx-go v0.0.0-20240905055117-62795b8b054a // indirect diff --git a/app/go.sum b/app/go.sum index 2e988eb..bbacee8 100644 --- a/app/go.sum +++ b/app/go.sum @@ -42,8 +42,8 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1 github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f h1:uVh0qpEslrWjgzx9vOcyCqsOY3c9kofDZ1n+qaw35ZY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f/go.mod h1:xkkq9D4ygcldQQhKS/w9CadiCKwCngU7K9E3DaKahpM= -github.com/apernet/quic-go v0.51.1-0.20250420202007-25ded0c7e74c h1:C3a7C/cvdMmgZ1nGc+ctPwVfWlPhcE7J2C63bmlAOD8= -github.com/apernet/quic-go v0.51.1-0.20250420202007-25ded0c7e74c/go.mod h1:I/47OIGG5H/IfAm+nz2c6hm6b/NkEhpvptAoiPcY7jQ= +github.com/apernet/quic-go v0.51.1-0.20250420212127-ede9b032759a h1:3UksCgxkac5L2hSbs3rZZVIOSX0EMQEg3K0HtTCoois= +github.com/apernet/quic-go v0.51.1-0.20250420212127-ede9b032759a/go.mod h1:I/47OIGG5H/IfAm+nz2c6hm6b/NkEhpvptAoiPcY7jQ= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad h1:QzQ2sKpc9o42HNRR8ukM5uMC/RzR2HgZd/Nvaqol2C0= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= diff --git a/core/go.mod b/core/go.mod index 1cd8e99..68eed04 100644 --- a/core/go.mod +++ b/core/go.mod @@ -5,7 +5,7 @@ go 1.23 toolchain go1.24.2 require ( - github.com/apernet/quic-go v0.51.1-0.20250420202007-25ded0c7e74c + github.com/apernet/quic-go v0.51.1-0.20250420212127-ede9b032759a github.com/stretchr/testify v1.9.0 go.uber.org/goleak v1.2.1 golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 diff --git a/core/go.sum b/core/go.sum index 74f0b3c..957f4c5 100644 --- a/core/go.sum +++ b/core/go.sum @@ -1,5 +1,5 @@ -github.com/apernet/quic-go v0.51.1-0.20250420202007-25ded0c7e74c h1:C3a7C/cvdMmgZ1nGc+ctPwVfWlPhcE7J2C63bmlAOD8= -github.com/apernet/quic-go v0.51.1-0.20250420202007-25ded0c7e74c/go.mod h1:I/47OIGG5H/IfAm+nz2c6hm6b/NkEhpvptAoiPcY7jQ= +github.com/apernet/quic-go v0.51.1-0.20250420212127-ede9b032759a h1:3UksCgxkac5L2hSbs3rZZVIOSX0EMQEg3K0HtTCoois= +github.com/apernet/quic-go v0.51.1-0.20250420212127-ede9b032759a/go.mod h1:I/47OIGG5H/IfAm+nz2c6hm6b/NkEhpvptAoiPcY7jQ= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= diff --git a/extras/go.mod b/extras/go.mod index 5bda6c6..19f1ffc 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -6,7 +6,7 @@ toolchain go1.24.2 require ( github.com/apernet/hysteria/core/v2 v2.0.0-00010101000000-000000000000 - github.com/apernet/quic-go v0.51.1-0.20250420202007-25ded0c7e74c + github.com/apernet/quic-go v0.51.1-0.20250420212127-ede9b032759a github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 github.com/database64128/tfo-go/v2 v2.2.2 github.com/hashicorp/golang-lru/v2 v2.0.5 diff --git a/extras/go.sum b/extras/go.sum index 9bbb21a..8457569 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -1,7 +1,7 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/apernet/quic-go v0.51.1-0.20250420202007-25ded0c7e74c h1:C3a7C/cvdMmgZ1nGc+ctPwVfWlPhcE7J2C63bmlAOD8= -github.com/apernet/quic-go v0.51.1-0.20250420202007-25ded0c7e74c/go.mod h1:I/47OIGG5H/IfAm+nz2c6hm6b/NkEhpvptAoiPcY7jQ= +github.com/apernet/quic-go v0.51.1-0.20250420212127-ede9b032759a h1:3UksCgxkac5L2hSbs3rZZVIOSX0EMQEg3K0HtTCoois= +github.com/apernet/quic-go v0.51.1-0.20250420212127-ede9b032759a/go.mod h1:I/47OIGG5H/IfAm+nz2c6hm6b/NkEhpvptAoiPcY7jQ= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= From aa5f68a6f7c436b8891a2f2e0b185f13e8312c0d Mon Sep 17 00:00:00 2001 From: Toby Date: Sun, 20 Apr 2025 14:33:40 -0700 Subject: [PATCH 159/161] chore: update go version to 1.24 in workflows --- .github/workflows/master.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 2b852fc..e5689e7 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -19,7 +19,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.23" + go-version: "1.24" - name: Setup Python # This is for the build script uses: actions/setup-python@v5 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d7a2433..a966d86 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,7 +23,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.23" + go-version: "1.24" - name: Setup Python # This is for the build script uses: actions/setup-python@v5 From 3a9e952af088649016a595d754964a61a2296279 Mon Sep 17 00:00:00 2001 From: Haruue Date: Tue, 27 May 2025 18:27:54 +0900 Subject: [PATCH 160/161] fix(client): remove share uri in log close: #1355 Since we already have the "share" subcommand, this feature is unnecessary for the "client" subcommand. This commit disables printing the share URI after the client starts, but keeps this behavior for users who specified the `--qr` flag (who may still rely on it) and shows a deprecation warning. --- app/cmd/client.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/cmd/client.go b/app/cmd/client.go index 65bc7e0..05fec80 100644 --- a/app/cmd/client.go +++ b/app/cmd/client.go @@ -470,8 +470,10 @@ func runClient(cmd *cobra.Command, args []string) { defer c.Close() uri := config.URI() - logger.Info("use this URI to share your server", zap.String("uri", uri)) if showQR { + logger.Warn("--qr flag is deprecated and will be removed in future release, " + + "please use `share` subcommand to generate share URI and QR code") + logger.Info("use this URI to share your server", zap.String("uri", uri)) utils.PrintQR(uri) } From b5ddcb5bc49cdee6a2c58084e6afff4063748b3a Mon Sep 17 00:00:00 2001 From: Toby Date: Sat, 7 Jun 2025 11:59:30 -0700 Subject: [PATCH 161/161] feat: quic-go v0.52.0 --- app/go.mod | 2 +- app/go.sum | 4 ++-- core/client/client.go | 1 + core/go.mod | 2 +- core/go.sum | 4 ++-- core/server/server.go | 1 + extras/go.mod | 2 +- extras/go.sum | 4 ++-- 8 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/go.mod b/app/go.mod index d8a68a2..25d1fd3 100644 --- a/app/go.mod +++ b/app/go.mod @@ -30,7 +30,7 @@ require ( require ( github.com/andybalholm/brotli v1.1.0 // indirect - github.com/apernet/quic-go v0.51.1-0.20250420212127-ede9b032759a // indirect + github.com/apernet/quic-go v0.52.1-0.20250607183305-9320c9d14431 // indirect github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect github.com/cloudflare/circl v1.3.9 // indirect github.com/database64128/netx-go v0.0.0-20240905055117-62795b8b054a // indirect diff --git a/app/go.sum b/app/go.sum index bbacee8..9bc7ae7 100644 --- a/app/go.sum +++ b/app/go.sum @@ -42,8 +42,8 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1 github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f h1:uVh0qpEslrWjgzx9vOcyCqsOY3c9kofDZ1n+qaw35ZY= github.com/apernet/go-tproxy v0.0.0-20230809025308-8f4723fd742f/go.mod h1:xkkq9D4ygcldQQhKS/w9CadiCKwCngU7K9E3DaKahpM= -github.com/apernet/quic-go v0.51.1-0.20250420212127-ede9b032759a h1:3UksCgxkac5L2hSbs3rZZVIOSX0EMQEg3K0HtTCoois= -github.com/apernet/quic-go v0.51.1-0.20250420212127-ede9b032759a/go.mod h1:I/47OIGG5H/IfAm+nz2c6hm6b/NkEhpvptAoiPcY7jQ= +github.com/apernet/quic-go v0.52.1-0.20250607183305-9320c9d14431 h1:9/jM7e+kVALd7Jfu1c27dcEpT/Fd/Gzq2OsQjKjakKI= +github.com/apernet/quic-go v0.52.1-0.20250607183305-9320c9d14431/go.mod h1:I/47OIGG5H/IfAm+nz2c6hm6b/NkEhpvptAoiPcY7jQ= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad h1:QzQ2sKpc9o42HNRR8ukM5uMC/RzR2HgZd/Nvaqol2C0= github.com/apernet/sing-tun v0.2.6-0.20240323130332-b9f6511036ad/go.mod h1:S5IydyLSN/QAfvY+r2GoomPJ6hidtXWm/Ad18sJVssk= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= diff --git a/core/client/client.go b/core/client/client.go index b9ad433..3691d1e 100644 --- a/core/client/client.go +++ b/core/client/client.go @@ -84,6 +84,7 @@ func (c *clientImpl) connect() (*HandshakeInfo, error) { KeepAlivePeriod: c.config.QUICConfig.KeepAlivePeriod, DisablePathMTUDiscovery: c.config.QUICConfig.DisablePathMTUDiscovery, EnableDatagrams: true, + DisablePathManager: true, } // Prepare RoundTripper var conn quic.EarlyConnection diff --git a/core/go.mod b/core/go.mod index 68eed04..a6ba575 100644 --- a/core/go.mod +++ b/core/go.mod @@ -5,7 +5,7 @@ go 1.23 toolchain go1.24.2 require ( - github.com/apernet/quic-go v0.51.1-0.20250420212127-ede9b032759a + github.com/apernet/quic-go v0.52.1-0.20250607183305-9320c9d14431 github.com/stretchr/testify v1.9.0 go.uber.org/goleak v1.2.1 golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 diff --git a/core/go.sum b/core/go.sum index 957f4c5..f44407d 100644 --- a/core/go.sum +++ b/core/go.sum @@ -1,5 +1,5 @@ -github.com/apernet/quic-go v0.51.1-0.20250420212127-ede9b032759a h1:3UksCgxkac5L2hSbs3rZZVIOSX0EMQEg3K0HtTCoois= -github.com/apernet/quic-go v0.51.1-0.20250420212127-ede9b032759a/go.mod h1:I/47OIGG5H/IfAm+nz2c6hm6b/NkEhpvptAoiPcY7jQ= +github.com/apernet/quic-go v0.52.1-0.20250607183305-9320c9d14431 h1:9/jM7e+kVALd7Jfu1c27dcEpT/Fd/Gzq2OsQjKjakKI= +github.com/apernet/quic-go v0.52.1-0.20250607183305-9320c9d14431/go.mod h1:I/47OIGG5H/IfAm+nz2c6hm6b/NkEhpvptAoiPcY7jQ= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= diff --git a/core/server/server.go b/core/server/server.go index 696f1d0..89645e0 100644 --- a/core/server/server.go +++ b/core/server/server.go @@ -43,6 +43,7 @@ func NewServer(config *Config) (Server, error) { MaxIncomingStreams: config.QUICConfig.MaxIncomingStreams, DisablePathMTUDiscovery: config.QUICConfig.DisablePathMTUDiscovery, EnableDatagrams: true, + DisablePathManager: true, } listener, err := quic.Listen(config.Conn, tlsConfig, quicConfig) if err != nil { diff --git a/extras/go.mod b/extras/go.mod index 19f1ffc..23b060a 100644 --- a/extras/go.mod +++ b/extras/go.mod @@ -6,7 +6,7 @@ toolchain go1.24.2 require ( github.com/apernet/hysteria/core/v2 v2.0.0-00010101000000-000000000000 - github.com/apernet/quic-go v0.51.1-0.20250420212127-ede9b032759a + github.com/apernet/quic-go v0.52.1-0.20250607183305-9320c9d14431 github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 github.com/database64128/tfo-go/v2 v2.2.2 github.com/hashicorp/golang-lru/v2 v2.0.5 diff --git a/extras/go.sum b/extras/go.sum index 8457569..ac6d04a 100644 --- a/extras/go.sum +++ b/extras/go.sum @@ -1,7 +1,7 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/apernet/quic-go v0.51.1-0.20250420212127-ede9b032759a h1:3UksCgxkac5L2hSbs3rZZVIOSX0EMQEg3K0HtTCoois= -github.com/apernet/quic-go v0.51.1-0.20250420212127-ede9b032759a/go.mod h1:I/47OIGG5H/IfAm+nz2c6hm6b/NkEhpvptAoiPcY7jQ= +github.com/apernet/quic-go v0.52.1-0.20250607183305-9320c9d14431 h1:9/jM7e+kVALd7Jfu1c27dcEpT/Fd/Gzq2OsQjKjakKI= +github.com/apernet/quic-go v0.52.1-0.20250607183305-9320c9d14431/go.mod h1:I/47OIGG5H/IfAm+nz2c6hm6b/NkEhpvptAoiPcY7jQ= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=