From 3cc1870aee55c284ff6687bb374939e76ca222cc Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sun, 3 Jul 2022 18:22:56 +0800 Subject: [PATCH] chore: embed hysteria, clean irrelevant codes, code from https://github.com/HyNetwork/hysteria --- adapter/outbound/hysteria.go | 15 +- go.mod | 24 +- go.sum | 349 ---------- transport/hysteria/acl/engine.go | 100 +++ transport/hysteria/acl/engine_test.go | 154 +++++ transport/hysteria/acl/entry.go | 331 ++++++++++ transport/hysteria/acl/entry_test.go | 75 +++ transport/hysteria/congestion/brutal.go | 145 +++++ transport/hysteria/congestion/pacer.go | 85 +++ transport/hysteria/conns/faketcp/LICENSE | 1 + transport/hysteria/conns/faketcp/obfs.go | 102 +++ transport/hysteria/conns/faketcp/tcp_linux.go | 616 ++++++++++++++++++ transport/hysteria/conns/faketcp/tcp_stub.go | 21 + transport/hysteria/conns/faketcp/tcp_test.go | 196 ++++++ transport/hysteria/conns/udp/obfs.go | 89 +++ transport/hysteria/conns/wechat/obfs.go | 105 +++ transport/hysteria/core/client.go | 422 ++++++++++++ transport/hysteria/core/frag.go | 67 ++ transport/hysteria/core/frag_test.go | 346 ++++++++++ transport/hysteria/core/protocol.go | 76 +++ transport/hysteria/core/stream.go | 54 ++ transport/hysteria/obfs/obfs.go | 6 + transport/hysteria/obfs/xplus.go | 52 ++ transport/hysteria/obfs/xplus_test.go | 31 + transport/hysteria/pmtud_fix/avail.go | 8 + transport/hysteria/pmtud_fix/unavail.go | 8 + transport/hysteria/transport/client.go | 106 +++ transport/hysteria/utils/misc.go | 42 ++ 28 files changed, 3251 insertions(+), 375 deletions(-) create mode 100644 transport/hysteria/acl/engine.go create mode 100644 transport/hysteria/acl/engine_test.go create mode 100644 transport/hysteria/acl/entry.go create mode 100644 transport/hysteria/acl/entry_test.go create mode 100644 transport/hysteria/congestion/brutal.go create mode 100644 transport/hysteria/congestion/pacer.go create mode 100644 transport/hysteria/conns/faketcp/LICENSE create mode 100644 transport/hysteria/conns/faketcp/obfs.go create mode 100644 transport/hysteria/conns/faketcp/tcp_linux.go create mode 100644 transport/hysteria/conns/faketcp/tcp_stub.go create mode 100644 transport/hysteria/conns/faketcp/tcp_test.go create mode 100644 transport/hysteria/conns/udp/obfs.go create mode 100644 transport/hysteria/conns/wechat/obfs.go create mode 100644 transport/hysteria/core/client.go create mode 100644 transport/hysteria/core/frag.go create mode 100644 transport/hysteria/core/frag_test.go create mode 100644 transport/hysteria/core/protocol.go create mode 100644 transport/hysteria/core/stream.go create mode 100644 transport/hysteria/obfs/obfs.go create mode 100644 transport/hysteria/obfs/xplus.go create mode 100644 transport/hysteria/obfs/xplus_test.go create mode 100644 transport/hysteria/pmtud_fix/avail.go create mode 100644 transport/hysteria/pmtud_fix/unavail.go create mode 100644 transport/hysteria/transport/client.go create mode 100644 transport/hysteria/utils/misc.go diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index 7b4bcd78..888382c6 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -5,6 +5,10 @@ import ( "crypto/tls" "crypto/x509" "fmt" + "github.com/Dreamacro/clash/transport/hysteria/core" + "github.com/Dreamacro/clash/transport/hysteria/obfs" + "github.com/Dreamacro/clash/transport/hysteria/pmtud_fix" + "github.com/Dreamacro/clash/transport/hysteria/transport" "io/ioutil" "net" "regexp" @@ -14,14 +18,10 @@ import ( "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" + hyCongestion "github.com/Dreamacro/clash/transport/hysteria/congestion" "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/congestion" M "github.com/sagernet/sing/common/metadata" - hyCongestion "github.com/tobyxdd/hysteria/pkg/congestion" - "github.com/tobyxdd/hysteria/pkg/core" - "github.com/tobyxdd/hysteria/pkg/obfs" - "github.com/tobyxdd/hysteria/pkg/pmtud_fix" - "github.com/tobyxdd/hysteria/pkg/transport" ) const ( @@ -46,11 +46,12 @@ type Hysteria struct { func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { hdc := hyDialerWithContext{ - ctx: ctx, + ctx: context.Background(), hyDialer: func() (net.PacketConn, error) { return dialer.ListenPacket(ctx, "udp", "", h.Base.DialOptions(opts...)...) }, } + tcpConn, err := h.client.DialTCP(metadata.RemoteAddress(), &hdc) if err != nil { return nil, err @@ -61,7 +62,7 @@ func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts . func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { hdc := hyDialerWithContext{ - ctx: ctx, + ctx: context.Background(), hyDialer: func() (net.PacketConn, error) { return dialer.ListenPacket(ctx, "udp", "", h.Base.DialOptions(opts...)...) }, diff --git a/go.mod b/go.mod index 34b8d041..fa956385 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,18 @@ module github.com/Dreamacro/clash go 1.18 require ( + github.com/coreos/go-iptables v0.6.0 github.com/dlclark/regexp2 v1.4.0 github.com/go-chi/chi/v5 v5.0.7 github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.1 github.com/gofrs/uuid v4.2.0+incompatible + github.com/google/gopacket v1.1.19 github.com/gorilla/websocket v1.5.0 + github.com/hashicorp/golang-lru v0.5.4 github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f github.com/lucas-clemente/quic-go v0.27.2 + github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/miekg/dns v1.1.49 github.com/oschwald/geoip2-golang v1.7.0 github.com/sagernet/sing v0.0.0-20220627234642-a817f7084d9c @@ -18,7 +22,6 @@ require ( github.com/sagernet/sing-vmess v0.0.0-20220616051646-3d3fc5d01eec github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.7.2 - github.com/tobyxdd/hysteria v1.0.4 github.com/vishvananda/netlink v1.2.1-beta.2 github.com/xtls/go v0.0.0-20210920065950-d4af136d3672 go.etcd.io/bbolt v1.3.6 @@ -39,41 +42,23 @@ require ( replace github.com/vishvananda/netlink => github.com/MetaCubeX/netlink v1.2.0-beta.0.20220529072258-d6853f887820 -replace github.com/tobyxdd/hysteria => github.com/MetaCubeX/hysteria v1.0.5-0.20220626134949-6fa84cd3e256 - replace github.com/lucas-clemente/quic-go => github.com/tobyxdd/quic-go v0.27.1-0.20220512040129-ed2a645d9218 require ( - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cheekybits/genny v1.0.0 // indirect - github.com/coreos/go-iptables v0.6.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect - github.com/golang/protobuf v1.5.2 // indirect github.com/google/btree v1.0.1 // indirect - github.com/google/gopacket v1.1.19 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/klauspost/cpuid/v2 v2.0.12 // indirect github.com/kr/pretty v0.2.1 // indirect - github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/oschwald/maxminddb-golang v1.9.0 // indirect - github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.12.2 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.32.1 // indirect - github.com/prometheus/procfs v0.7.3 // indirect - github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect - github.com/txthinking/socks5 v0.0.0-20220212043548-414499347d4a // indirect - github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe // indirect github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect @@ -81,6 +66,7 @@ require ( golang.org/x/tools v0.1.10 // indirect golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect lukechampine.com/blake3 v1.1.7 // indirect ) diff --git a/go.sum b/go.sum index f15f8282..843a6d90 100644 --- a/go.sum +++ b/go.sum @@ -2,71 +2,21 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= 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/MetaCubeX/hysteria v1.0.5-0.20220626134949-6fa84cd3e256 h1:wm5RrQfwJS63pe5G15AKdXfrwlIYFciwCs3MrVxzxSU= -github.com/MetaCubeX/hysteria v1.0.5-0.20220626134949-6fa84cd3e256/go.mod h1:bXVjOca4Xf3JRenwuPKu02XaOiZwejrMSlgsu/U88J4= github.com/MetaCubeX/netlink v1.2.0-beta.0.20220529072258-d6853f887820 h1:fGKWZ25VApYnuPZoNeqdH/nZtHa2XMajwH6Yj/OgoVc= github.com/MetaCubeX/netlink v1.2.0-beta.0.20220529072258-d6853f887820/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -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/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= -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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk= github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -76,10 +26,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= @@ -96,122 +42,70 @@ github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vz github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8= github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -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-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -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.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/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.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= 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= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -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.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= 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/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= -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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -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 v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f h1:l1QCwn715k8nYkj4Ql50rzEog3WnMdrd4YYMMwemxEo= github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 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/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -230,7 +124,6 @@ github.com/marten-seemann/qtls-go1-17 v0.1.1 h1:DQjHPq+aOzUeh9/lixAGunn6rIOQyWCh github.com/marten-seemann/qtls-go1-17 v0.1.1/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= github.com/marten-seemann/qtls-go1-18 v0.1.1 h1:qp7p7XXUFL7fpBvSS1sWD+uSqPvzNQK43DH+/qEkj0Y= github.com/marten-seemann/qtls-go1-18 v0.1.1/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= @@ -242,13 +135,8 @@ github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZ github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.1.49 h1:qe0mQU3Z/XpFeE+AEBo2rqaS1IPBJ3anmqZ4XiZJVG8= github.com/miekg/dns v1.1.49/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -270,40 +158,14 @@ github.com/oschwald/geoip2-golang v1.7.0 h1:JW1r5AKi+vv2ujSxjKthySK3jo8w8oKWPyXs github.com/oschwald/geoip2-golang v1.7.0/go.mod h1:mdI/C7iK7NVMcIDDtf4bCKMJ7r0o7UwGeCo9eiitCMQ= github.com/oschwald/maxminddb-golang v1.9.0 h1:tIk4nv6VT9OiPyrnDAfJS1s1xKDQMZOsGojab6EjC1Y= github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm82Cp5HyvYbt8K3zLY= -github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= -github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= -github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sagernet/sing v0.0.0-20220627234642-a817f7084d9c h1:98QC0wtaD648MFPw82KaT1O9LloQgR4ZyIDtNtsno8Y= github.com/sagernet/sing v0.0.0-20220627234642-a817f7084d9c/go.mod h1:I67R/q5f67xDExL2kL3RLIP7kGJBOPkYXkpRAykgC+E= @@ -334,9 +196,6 @@ github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1l github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -344,10 +203,8 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= @@ -355,12 +212,6 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tobyxdd/quic-go v0.27.1-0.20220512040129-ed2a645d9218 h1:0DEghzcIfYe+7HTuI+zEd/5M+5c/gcepjJWGdcPPIrc= github.com/tobyxdd/quic-go v0.27.1-0.20220512040129-ed2a645d9218/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI= -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-20220212043548-414499347d4a h1:BOqgJ4jku0LHPDoR51RD8Mxmo0LHxCzJT/M9MemYdHo= -github.com/txthinking/socks5 v0.0.0-20220212043548-414499347d4a/go.mod h1:7NloQcrxaZYKURWph5HLxVDlIwMHJXCPkeWPtpftsIg= -github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe h1:gMWxZxBFRAXqoGkwkYlPX2zvyyKNWJpxOxCrjqJkm5A= -github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe/go.mod h1:WgqbSEmUYSjEV3B1qmee/PpP2NYEz4bL9/+mF1ma+s4= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 h1:hl6sK6aFgTLISijk6xIzeqnPzQcsLqqvL6vEfTPinME= github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= @@ -371,68 +222,33 @@ github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695AP github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/xtls/go v0.0.0-20210920065950-d4af136d3672 h1:4mkzGhKqt3JO1BWYjtD3iRFyAx4ow67hmSqOcGjuxqQ= github.com/xtls/go v0.0.0-20210920065950-d4af136d3672/go.mod h1:YGGVbz9cOxyKFUmhW7LGaLZaMA0cPlHJinvAmVxEMSU= -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= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/automaxprocs v1.5.1 h1:e1YG66Lrk73dn4qhg8WFSvhF0JuFQF0ERIp4rpuV8Qk= go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/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= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -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-20220608143224-64259d1afd70 h1:8uGxpY2cLF9H/NSHUiEWUIBZqIcsMzMWIMPCCUkyYgc= golang.org/x/exp v0.0.0-20220608143224-64259d1afd70/go.mod h1:yh0Ynu2b5ZUe3MQfp2nM0ecK7wsgouWTDN0FNeJuIys= -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-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/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.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= @@ -442,43 +258,24 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r 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= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-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-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191007182048-72f939374954/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= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -486,90 +283,54 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG 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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= 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= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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-20181116152217-5ac8a444bdc5/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= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -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-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/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= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -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= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -579,8 +340,6 @@ golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab h1:eHo2TTVBaAPw9lDGK2Gb9G golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -588,44 +347,10 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/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= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 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-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= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -646,98 +371,33 @@ golang.zx2c4.com/wireguard/windows v0.5.4-0.20220328111914-004c22c5647e/go.mod h google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -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.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -745,7 +405,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= @@ -758,15 +417,7 @@ gvisor.dev/gvisor v0.0.0-20220527053002-8ab279227ac8/go.mod h1:TIvkJD0sxe8pIob3p honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/transport/hysteria/acl/engine.go b/transport/hysteria/acl/engine.go new file mode 100644 index 00000000..adff5690 --- /dev/null +++ b/transport/hysteria/acl/engine.go @@ -0,0 +1,100 @@ +package acl + +import ( + "github.com/Dreamacro/clash/transport/hysteria/utils" + lru "github.com/hashicorp/golang-lru" + "github.com/oschwald/geoip2-golang" + "net" +) + +const entryCacheSize = 1024 + +type Engine struct { + DefaultAction Action + Entries []Entry + Cache *lru.ARCCache + ResolveIPAddr func(string) (*net.IPAddr, error) + GeoIPReader *geoip2.Reader +} + +type cacheKey struct { + Host string + Port uint16 + IsUDP bool +} + +type cacheValue struct { + Action Action + Arg string +} + +// action, arg, isDomain, resolvedIP, error +func (e *Engine) ResolveAndMatch(host string, port uint16, isUDP bool) (Action, string, bool, *net.IPAddr, error) { + ip, zone := utils.ParseIPZone(host) + if ip == nil { + // Domain + ipAddr, err := e.ResolveIPAddr(host) + if v, ok := e.Cache.Get(cacheKey{host, port, isUDP}); ok { + // Cache hit + ce := v.(cacheValue) + return ce.Action, ce.Arg, true, ipAddr, err + } + for _, entry := range e.Entries { + mReq := MatchRequest{ + Domain: host, + Port: port, + DB: e.GeoIPReader, + } + if ipAddr != nil { + mReq.IP = ipAddr.IP + } + if isUDP { + mReq.Protocol = ProtocolUDP + } else { + mReq.Protocol = ProtocolTCP + } + if entry.Match(mReq) { + e.Cache.Add(cacheKey{host, port, isUDP}, + cacheValue{entry.Action, entry.ActionArg}) + return entry.Action, entry.ActionArg, true, ipAddr, err + } + } + e.Cache.Add(cacheKey{host, port, isUDP}, cacheValue{e.DefaultAction, ""}) + return e.DefaultAction, "", true, ipAddr, err + } else { + // IP + if v, ok := e.Cache.Get(cacheKey{ip.String(), port, isUDP}); ok { + // Cache hit + ce := v.(cacheValue) + return ce.Action, ce.Arg, false, &net.IPAddr{ + IP: ip, + Zone: zone, + }, nil + } + for _, entry := range e.Entries { + mReq := MatchRequest{ + IP: ip, + Port: port, + DB: e.GeoIPReader, + } + if isUDP { + mReq.Protocol = ProtocolUDP + } else { + mReq.Protocol = ProtocolTCP + } + if entry.Match(mReq) { + e.Cache.Add(cacheKey{ip.String(), port, isUDP}, + cacheValue{entry.Action, entry.ActionArg}) + return entry.Action, entry.ActionArg, false, &net.IPAddr{ + IP: ip, + Zone: zone, + }, nil + } + } + e.Cache.Add(cacheKey{ip.String(), port, isUDP}, cacheValue{e.DefaultAction, ""}) + return e.DefaultAction, "", false, &net.IPAddr{ + IP: ip, + Zone: zone, + }, nil + } +} diff --git a/transport/hysteria/acl/engine_test.go b/transport/hysteria/acl/engine_test.go new file mode 100644 index 00000000..4c30884d --- /dev/null +++ b/transport/hysteria/acl/engine_test.go @@ -0,0 +1,154 @@ +package acl + +import ( + "errors" + lru "github.com/hashicorp/golang-lru" + "net" + "strings" + "testing" +) + +func TestEngine_ResolveAndMatch(t *testing.T) { + cache, _ := lru.NewARC(16) + e := &Engine{ + DefaultAction: ActionDirect, + Entries: []Entry{ + { + Action: ActionProxy, + ActionArg: "", + Matcher: &domainMatcher{ + matcherBase: matcherBase{ + Protocol: ProtocolTCP, + Port: 443, + }, + Domain: "google.com", + Suffix: false, + }, + }, + { + Action: ActionHijack, + ActionArg: "good.org", + Matcher: &domainMatcher{ + matcherBase: matcherBase{}, + Domain: "evil.corp", + Suffix: true, + }, + }, + { + Action: ActionProxy, + ActionArg: "", + Matcher: &netMatcher{ + matcherBase: matcherBase{}, + Net: &net.IPNet{ + IP: net.ParseIP("10.0.0.0"), + Mask: net.CIDRMask(8, 32), + }, + }, + }, + { + Action: ActionBlock, + ActionArg: "", + Matcher: &allMatcher{}, + }, + }, + Cache: cache, + ResolveIPAddr: func(s string) (*net.IPAddr, error) { + if strings.Contains(s, "evil.corp") { + return nil, errors.New("resolve error") + } + return net.ResolveIPAddr("ip", s) + }, + } + tests := []struct { + name string + host string + port uint16 + isUDP bool + wantAction Action + wantArg string + wantErr bool + }{ + { + name: "domain proxy", + host: "google.com", + port: 443, + isUDP: false, + wantAction: ActionProxy, + wantArg: "", + }, + { + name: "domain block", + host: "google.com", + port: 80, + isUDP: false, + wantAction: ActionBlock, + wantArg: "", + }, + { + name: "domain suffix 1", + host: "evil.corp", + port: 8899, + isUDP: true, + wantAction: ActionHijack, + wantArg: "good.org", + wantErr: true, + }, + { + name: "domain suffix 2", + host: "notevil.corp", + port: 22, + isUDP: false, + wantAction: ActionBlock, + wantArg: "", + wantErr: true, + }, + { + name: "domain suffix 3", + host: "im.real.evil.corp", + port: 443, + isUDP: true, + wantAction: ActionHijack, + wantArg: "good.org", + wantErr: true, + }, + { + name: "ip match", + host: "10.2.3.4", + port: 80, + isUDP: false, + wantAction: ActionProxy, + wantArg: "", + }, + { + name: "ip mismatch", + host: "100.5.6.0", + port: 1234, + isUDP: false, + wantAction: ActionBlock, + wantArg: "", + }, + { + name: "domain proxy cache", + host: "google.com", + port: 443, + isUDP: false, + wantAction: ActionProxy, + wantArg: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotAction, gotArg, _, _, err := e.ResolveAndMatch(tt.host, tt.port, tt.isUDP) + if (err != nil) != tt.wantErr { + t.Errorf("ResolveAndMatch() error = %v, wantErr %v", err, tt.wantErr) + return + } + if gotAction != tt.wantAction { + t.Errorf("ResolveAndMatch() gotAction = %v, wantAction %v", gotAction, tt.wantAction) + } + if gotArg != tt.wantArg { + t.Errorf("ResolveAndMatch() gotArg = %v, wantAction %v", gotArg, tt.wantArg) + } + }) + } +} diff --git a/transport/hysteria/acl/entry.go b/transport/hysteria/acl/entry.go new file mode 100644 index 00000000..bc345aa1 --- /dev/null +++ b/transport/hysteria/acl/entry.go @@ -0,0 +1,331 @@ +package acl + +import ( + "errors" + "fmt" + "github.com/oschwald/geoip2-golang" + "net" + "strconv" + "strings" +) + +type Action byte +type Protocol byte + +const ( + ActionDirect = Action(iota) + ActionProxy + ActionBlock + ActionHijack +) + +const ( + ProtocolAll = Protocol(iota) + ProtocolTCP + ProtocolUDP +) + +var protocolPortAliases = map[string]string{ + "echo": "*/7", + "ftp-data": "*/20", + "ftp": "*/21", + "ssh": "*/22", + "telnet": "*/23", + "domain": "*/53", + "dns": "*/53", + "http": "*/80", + "sftp": "*/115", + "ntp": "*/123", + "https": "*/443", + "quic": "udp/443", + "socks": "*/1080", +} + +type Entry struct { + Action Action + ActionArg string + Matcher Matcher +} + +type MatchRequest struct { + IP net.IP + Domain string + + Protocol Protocol + Port uint16 + + DB *geoip2.Reader +} + +type Matcher interface { + Match(MatchRequest) bool +} + +type matcherBase struct { + Protocol Protocol + Port uint16 // 0 for all ports +} + +func (m *matcherBase) MatchProtocolPort(p Protocol, port uint16) bool { + return (m.Protocol == ProtocolAll || m.Protocol == p) && (m.Port == 0 || m.Port == port) +} + +func parseProtocolPort(s string) (Protocol, uint16, error) { + if protocolPortAliases[s] != "" { + s = protocolPortAliases[s] + } + if len(s) == 0 || s == "*" { + return ProtocolAll, 0, nil + } + parts := strings.Split(s, "/") + if len(parts) != 2 { + return ProtocolAll, 0, errors.New("invalid protocol/port syntax") + } + protocol := ProtocolAll + switch parts[0] { + case "tcp": + protocol = ProtocolTCP + case "udp": + protocol = ProtocolUDP + case "*": + protocol = ProtocolAll + default: + return ProtocolAll, 0, errors.New("invalid protocol") + } + if parts[1] == "*" { + return protocol, 0, nil + } + port, err := strconv.ParseUint(parts[1], 10, 16) + if err != nil { + return ProtocolAll, 0, errors.New("invalid port") + } + return protocol, uint16(port), nil +} + +type netMatcher struct { + matcherBase + Net *net.IPNet +} + +func (m *netMatcher) Match(r MatchRequest) bool { + if r.IP == nil { + return false + } + return m.Net.Contains(r.IP) && m.MatchProtocolPort(r.Protocol, r.Port) +} + +type domainMatcher struct { + matcherBase + Domain string + Suffix bool +} + +func (m *domainMatcher) Match(r MatchRequest) bool { + if len(r.Domain) == 0 { + return false + } + domain := strings.ToLower(r.Domain) + return (m.Domain == domain || (m.Suffix && strings.HasSuffix(domain, "."+m.Domain))) && + m.MatchProtocolPort(r.Protocol, r.Port) +} + +type countryMatcher struct { + matcherBase + Country string // ISO 3166-1 alpha-2 country code, upper case +} + +func (m *countryMatcher) Match(r MatchRequest) bool { + if r.IP == nil || r.DB == nil { + return false + } + c, err := r.DB.Country(r.IP) + if err != nil { + return false + } + return c.Country.IsoCode == m.Country && m.MatchProtocolPort(r.Protocol, r.Port) +} + +type allMatcher struct { + matcherBase +} + +func (m *allMatcher) Match(r MatchRequest) bool { + return m.MatchProtocolPort(r.Protocol, r.Port) +} + +func (e Entry) Match(r MatchRequest) bool { + return e.Matcher.Match(r) +} + +func ParseEntry(s string) (Entry, error) { + fields := strings.Fields(s) + if len(fields) < 2 { + return Entry{}, fmt.Errorf("expected at least 2 fields, got %d", len(fields)) + } + e := Entry{} + action := fields[0] + conds := fields[1:] + switch strings.ToLower(action) { + case "direct": + e.Action = ActionDirect + case "proxy": + e.Action = ActionProxy + case "block": + e.Action = ActionBlock + case "hijack": + if len(conds) < 2 { + return Entry{}, fmt.Errorf("hijack requires at least 3 fields, got %d", len(fields)) + } + e.Action = ActionHijack + e.ActionArg = conds[len(conds)-1] + conds = conds[:len(conds)-1] + default: + return Entry{}, fmt.Errorf("invalid action %s", fields[0]) + } + m, err := condsToMatcher(conds) + if err != nil { + return Entry{}, err + } + e.Matcher = m + return e, nil +} + +func condsToMatcher(conds []string) (Matcher, error) { + if len(conds) < 1 { + return nil, errors.New("no condition specified") + } + typ, args := conds[0], conds[1:] + switch strings.ToLower(typ) { + case "domain": + // domain + if len(args) == 0 || len(args) > 2 { + return nil, fmt.Errorf("invalid number of arguments for domain: %d, expected 1 or 2", len(args)) + } + mb := matcherBase{} + if len(args) == 2 { + protocol, port, err := parseProtocolPort(args[1]) + if err != nil { + return nil, err + } + mb.Protocol = protocol + mb.Port = port + } + return &domainMatcher{ + matcherBase: mb, + Domain: args[0], + Suffix: false, + }, nil + case "domain-suffix": + // domain-suffix + if len(args) == 0 || len(args) > 2 { + return nil, fmt.Errorf("invalid number of arguments for domain-suffix: %d, expected 1 or 2", len(args)) + } + mb := matcherBase{} + if len(args) == 2 { + protocol, port, err := parseProtocolPort(args[1]) + if err != nil { + return nil, err + } + mb.Protocol = protocol + mb.Port = port + } + return &domainMatcher{ + matcherBase: mb, + Domain: args[0], + Suffix: true, + }, nil + case "cidr": + // cidr + if len(args) == 0 || len(args) > 2 { + return nil, fmt.Errorf("invalid number of arguments for cidr: %d, expected 1 or 2", len(args)) + } + mb := matcherBase{} + if len(args) == 2 { + protocol, port, err := parseProtocolPort(args[1]) + if err != nil { + return nil, err + } + mb.Protocol = protocol + mb.Port = port + } + _, ipNet, err := net.ParseCIDR(args[0]) + if err != nil { + return nil, err + } + return &netMatcher{ + matcherBase: mb, + Net: ipNet, + }, nil + case "ip": + // ip + if len(args) == 0 || len(args) > 2 { + return nil, fmt.Errorf("invalid number of arguments for ip: %d, expected 1 or 2", len(args)) + } + mb := matcherBase{} + if len(args) == 2 { + protocol, port, err := parseProtocolPort(args[1]) + if err != nil { + return nil, err + } + mb.Protocol = protocol + mb.Port = port + } + ip := net.ParseIP(args[0]) + if ip == nil { + return nil, fmt.Errorf("invalid ip: %s", args[0]) + } + var ipNet *net.IPNet + if ip.To4() != nil { + ipNet = &net.IPNet{ + IP: ip, + Mask: net.CIDRMask(32, 32), + } + } else { + ipNet = &net.IPNet{ + IP: ip, + Mask: net.CIDRMask(128, 128), + } + } + return &netMatcher{ + matcherBase: mb, + Net: ipNet, + }, nil + case "country": + // country + if len(args) == 0 || len(args) > 2 { + return nil, fmt.Errorf("invalid number of arguments for country: %d, expected 1 or 2", len(args)) + } + mb := matcherBase{} + if len(args) == 2 { + protocol, port, err := parseProtocolPort(args[1]) + if err != nil { + return nil, err + } + mb.Protocol = protocol + mb.Port = port + } + return &countryMatcher{ + matcherBase: mb, + Country: strings.ToUpper(args[0]), + }, nil + case "all": + // all + if len(args) > 1 { + return nil, fmt.Errorf("invalid number of arguments for all: %d, expected 0 or 1", len(args)) + } + mb := matcherBase{} + if len(args) == 1 { + protocol, port, err := parseProtocolPort(args[0]) + if err != nil { + return nil, err + } + mb.Protocol = protocol + mb.Port = port + } + return &allMatcher{ + matcherBase: mb, + }, nil + default: + return nil, fmt.Errorf("invalid condition type: %s", typ) + } +} diff --git a/transport/hysteria/acl/entry_test.go b/transport/hysteria/acl/entry_test.go new file mode 100644 index 00000000..37b88071 --- /dev/null +++ b/transport/hysteria/acl/entry_test.go @@ -0,0 +1,75 @@ +package acl + +import ( + "net" + "reflect" + "testing" +) + +func TestParseEntry(t *testing.T) { + _, ok3net, _ := net.ParseCIDR("8.8.8.0/24") + + type args struct { + s string + } + tests := []struct { + name string + args args + want Entry + wantErr bool + }{ + {name: "empty", args: args{""}, want: Entry{}, wantErr: true}, + {name: "ok 1", args: args{"direct domain-suffix google.com"}, + want: Entry{ActionDirect, "", &domainMatcher{ + matcherBase: matcherBase{}, + Domain: "google.com", + Suffix: true, + }}, + wantErr: false}, + {name: "ok 2", args: args{"proxy domain shithole"}, + want: Entry{ActionProxy, "", &domainMatcher{ + matcherBase: matcherBase{}, + Domain: "shithole", + Suffix: false, + }}, + wantErr: false}, + {name: "ok 3", args: args{"block cidr 8.8.8.0/24 */53"}, + want: Entry{ActionBlock, "", &netMatcher{ + matcherBase: matcherBase{ProtocolAll, 53}, + Net: ok3net, + }}, + wantErr: false}, + {name: "ok 4", args: args{"hijack all udp/* udpblackhole.net"}, + want: Entry{ActionHijack, "udpblackhole.net", &allMatcher{ + matcherBase: matcherBase{ProtocolUDP, 0}, + }}, + wantErr: false}, + {name: "err 1", args: args{"what the heck"}, + want: Entry{}, + wantErr: true}, + {name: "err 2", args: args{"proxy sucks ass"}, + want: Entry{}, + wantErr: true}, + {name: "err 3", args: args{"block ip 999.999.999.999"}, + want: Entry{}, + wantErr: true}, + {name: "err 4", args: args{"hijack domain google.com"}, + want: Entry{}, + wantErr: true}, + {name: "err 5", args: args{"hijack domain google.com bing.com 123"}, + want: Entry{}, + wantErr: true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ParseEntry(tt.args.s) + if (err != nil) != tt.wantErr { + t.Errorf("ParseEntry() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ParseEntry() got = %v, wantAction %v", got, tt.want) + } + }) + } +} diff --git a/transport/hysteria/congestion/brutal.go b/transport/hysteria/congestion/brutal.go new file mode 100644 index 00000000..d08ac346 --- /dev/null +++ b/transport/hysteria/congestion/brutal.go @@ -0,0 +1,145 @@ +package congestion + +import ( + "github.com/lucas-clemente/quic-go/congestion" + "time" +) + +const ( + initMaxDatagramSize = 1252 + + pktInfoSlotCount = 4 + minSampleCount = 50 + minAckRate = 0.8 +) + +type BrutalSender struct { + rttStats congestion.RTTStatsProvider + bps congestion.ByteCount + maxDatagramSize congestion.ByteCount + pacer *pacer + + pktInfoSlots [pktInfoSlotCount]pktInfo + ackRate float64 +} + +type pktInfo struct { + Timestamp int64 + AckCount uint64 + LossCount uint64 +} + +func NewBrutalSender(bps congestion.ByteCount) *BrutalSender { + bs := &BrutalSender{ + bps: bps, + maxDatagramSize: initMaxDatagramSize, + ackRate: 1, + } + bs.pacer = newPacer(func() congestion.ByteCount { + return congestion.ByteCount(float64(bs.bps) / bs.ackRate) + }) + return bs +} + +func (b *BrutalSender) SetRTTStatsProvider(rttStats congestion.RTTStatsProvider) { + b.rttStats = rttStats +} + +func (b *BrutalSender) TimeUntilSend(bytesInFlight congestion.ByteCount) time.Time { + return b.pacer.TimeUntilSend() +} + +func (b *BrutalSender) HasPacingBudget() bool { + return b.pacer.Budget(time.Now()) >= b.maxDatagramSize +} + +func (b *BrutalSender) CanSend(bytesInFlight congestion.ByteCount) bool { + return bytesInFlight < b.GetCongestionWindow() +} + +func (b *BrutalSender) GetCongestionWindow() congestion.ByteCount { + rtt := maxDuration(b.rttStats.LatestRTT(), b.rttStats.SmoothedRTT()) + if rtt <= 0 { + return 10240 + } + return congestion.ByteCount(float64(b.bps) * rtt.Seconds() * 1.5 / b.ackRate) +} + +func (b *BrutalSender) OnPacketSent(sentTime time.Time, bytesInFlight congestion.ByteCount, + packetNumber congestion.PacketNumber, bytes congestion.ByteCount, isRetransmittable bool) { + b.pacer.SentPacket(sentTime, bytes) +} + +func (b *BrutalSender) OnPacketAcked(number congestion.PacketNumber, ackedBytes congestion.ByteCount, + priorInFlight congestion.ByteCount, eventTime time.Time) { + currentTimestamp := eventTime.Unix() + slot := currentTimestamp % pktInfoSlotCount + if b.pktInfoSlots[slot].Timestamp == currentTimestamp { + b.pktInfoSlots[slot].AckCount++ + } else { + // uninitialized slot or too old, reset + b.pktInfoSlots[slot].Timestamp = currentTimestamp + b.pktInfoSlots[slot].AckCount = 1 + b.pktInfoSlots[slot].LossCount = 0 + } + b.updateAckRate(currentTimestamp) +} + +func (b *BrutalSender) OnPacketLost(number congestion.PacketNumber, lostBytes congestion.ByteCount, + priorInFlight congestion.ByteCount) { + currentTimestamp := time.Now().Unix() + slot := currentTimestamp % pktInfoSlotCount + if b.pktInfoSlots[slot].Timestamp == currentTimestamp { + b.pktInfoSlots[slot].LossCount++ + } else { + // uninitialized slot or too old, reset + b.pktInfoSlots[slot].Timestamp = currentTimestamp + b.pktInfoSlots[slot].AckCount = 0 + b.pktInfoSlots[slot].LossCount = 1 + } + b.updateAckRate(currentTimestamp) +} + +func (b *BrutalSender) SetMaxDatagramSize(size congestion.ByteCount) { + b.maxDatagramSize = size + b.pacer.SetMaxDatagramSize(size) +} + +func (b *BrutalSender) updateAckRate(currentTimestamp int64) { + minTimestamp := currentTimestamp - pktInfoSlotCount + var ackCount, lossCount uint64 + for _, info := range b.pktInfoSlots { + if info.Timestamp < minTimestamp { + continue + } + ackCount += info.AckCount + lossCount += info.LossCount + } + if ackCount+lossCount < minSampleCount { + b.ackRate = 1 + } + rate := float64(ackCount) / float64(ackCount+lossCount) + if rate < minAckRate { + b.ackRate = minAckRate + } + b.ackRate = rate +} + +func (b *BrutalSender) InSlowStart() bool { + return false +} + +func (b *BrutalSender) InRecovery() bool { + return false +} + +func (b *BrutalSender) MaybeExitSlowStart() {} + +func (b *BrutalSender) OnRetransmissionTimeout(packetsRetransmitted bool) {} + +func maxDuration(a, b time.Duration) time.Duration { + if a > b { + return a + } + return b +} diff --git a/transport/hysteria/congestion/pacer.go b/transport/hysteria/congestion/pacer.go new file mode 100644 index 00000000..43707108 --- /dev/null +++ b/transport/hysteria/congestion/pacer.go @@ -0,0 +1,85 @@ +package congestion + +import ( + "github.com/lucas-clemente/quic-go/congestion" + "math" + "time" +) + +const ( + maxBurstPackets = 10 + minPacingDelay = time.Millisecond +) + +// The pacer implements a token bucket pacing algorithm. +type pacer struct { + budgetAtLastSent congestion.ByteCount + maxDatagramSize congestion.ByteCount + lastSentTime time.Time + getBandwidth func() congestion.ByteCount // in bytes/s +} + +func newPacer(getBandwidth func() congestion.ByteCount) *pacer { + p := &pacer{ + budgetAtLastSent: maxBurstPackets * initMaxDatagramSize, + maxDatagramSize: initMaxDatagramSize, + getBandwidth: getBandwidth, + } + return p +} + +func (p *pacer) SentPacket(sendTime time.Time, size congestion.ByteCount) { + budget := p.Budget(sendTime) + if size > budget { + p.budgetAtLastSent = 0 + } else { + p.budgetAtLastSent = budget - size + } + p.lastSentTime = sendTime +} + +func (p *pacer) Budget(now time.Time) congestion.ByteCount { + if p.lastSentTime.IsZero() { + return p.maxBurstSize() + } + budget := p.budgetAtLastSent + (p.getBandwidth()*congestion.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9 + return minByteCount(p.maxBurstSize(), budget) +} + +func (p *pacer) maxBurstSize() congestion.ByteCount { + return maxByteCount( + congestion.ByteCount((minPacingDelay+time.Millisecond).Nanoseconds())*p.getBandwidth()/1e9, + maxBurstPackets*p.maxDatagramSize, + ) +} + +// TimeUntilSend returns when the next packet should be sent. +// It returns the zero value of time.Time if a packet can be sent immediately. +func (p *pacer) TimeUntilSend() time.Time { + if p.budgetAtLastSent >= p.maxDatagramSize { + return time.Time{} + } + return p.lastSentTime.Add(maxDuration( + minPacingDelay, + time.Duration(math.Ceil(float64(p.maxDatagramSize-p.budgetAtLastSent)*1e9/ + float64(p.getBandwidth())))*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 +} diff --git a/transport/hysteria/conns/faketcp/LICENSE b/transport/hysteria/conns/faketcp/LICENSE new file mode 100644 index 00000000..79fbecb0 --- /dev/null +++ b/transport/hysteria/conns/faketcp/LICENSE @@ -0,0 +1 @@ +Grabbed from https://github.com/xtaci/tcpraw with modifications \ No newline at end of file diff --git a/transport/hysteria/conns/faketcp/obfs.go b/transport/hysteria/conns/faketcp/obfs.go new file mode 100644 index 00000000..9897accc --- /dev/null +++ b/transport/hysteria/conns/faketcp/obfs.go @@ -0,0 +1,102 @@ +package faketcp + +import ( + "github.com/Dreamacro/clash/log" + "github.com/Dreamacro/clash/transport/hysteria/obfs" + "net" + "sync" + "syscall" + "time" +) + +const udpBufferSize = 65535 + +type ObfsFakeTCPConn struct { + orig *TCPConn + obfs obfs.Obfuscator + closed bool + readBuf []byte + readMutex sync.Mutex + writeBuf []byte + writeMutex sync.Mutex +} + +func NewObfsFakeTCPConn(orig *TCPConn, obfs obfs.Obfuscator) *ObfsFakeTCPConn { + return &ObfsFakeTCPConn{ + orig: orig, + obfs: obfs, + readBuf: make([]byte, udpBufferSize), + writeBuf: make([]byte, udpBufferSize), + } +} + +func (c *ObfsFakeTCPConn) ReadFrom(p []byte) (int, net.Addr, error) { + for { + c.readMutex.Lock() + if c.closed { + log.Infoln("read faketcp obfs before") + } + n, addr, err := c.orig.ReadFrom(c.readBuf) + if c.closed { + log.Infoln("read faketcp obfs after") + } + if n <= 0 { + c.readMutex.Unlock() + return 0, addr, err + } + newN := c.obfs.Deobfuscate(c.readBuf[:n], p) + c.readMutex.Unlock() + if newN > 0 { + // Valid packet + return newN, addr, err + } else if err != nil { + // Not valid and orig.ReadFrom had some error + return 0, addr, err + } + } +} + +func (c *ObfsFakeTCPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + c.writeMutex.Lock() + bn := c.obfs.Obfuscate(p, c.writeBuf) + _, err = c.orig.WriteTo(c.writeBuf[:bn], addr) + c.writeMutex.Unlock() + if err != nil { + return 0, err + } else { + return len(p), nil + } +} + +func (c *ObfsFakeTCPConn) Close() error { + c.closed = true + return c.orig.Close() +} + +func (c *ObfsFakeTCPConn) LocalAddr() net.Addr { + return c.orig.LocalAddr() +} + +func (c *ObfsFakeTCPConn) SetDeadline(t time.Time) error { + return c.orig.SetDeadline(t) +} + +func (c *ObfsFakeTCPConn) SetReadDeadline(t time.Time) error { + return c.orig.SetReadDeadline(t) +} + +func (c *ObfsFakeTCPConn) SetWriteDeadline(t time.Time) error { + return c.orig.SetWriteDeadline(t) +} + +func (c *ObfsFakeTCPConn) SetReadBuffer(bytes int) error { + return c.orig.SetReadBuffer(bytes) +} + +func (c *ObfsFakeTCPConn) SetWriteBuffer(bytes int) error { + return c.orig.SetWriteBuffer(bytes) +} + +func (c *ObfsFakeTCPConn) SyscallConn() (syscall.RawConn, error) { + return c.orig.SyscallConn() +} diff --git a/transport/hysteria/conns/faketcp/tcp_linux.go b/transport/hysteria/conns/faketcp/tcp_linux.go new file mode 100644 index 00000000..dadb0912 --- /dev/null +++ b/transport/hysteria/conns/faketcp/tcp_linux.go @@ -0,0 +1,616 @@ +//go:build linux +// +build linux + +package faketcp + +import ( + "crypto/rand" + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + "net" + "sync" + "sync/atomic" + "syscall" + "time" + + "github.com/coreos/go-iptables/iptables" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" +) + +var ( + errOpNotImplemented = errors.New("operation not implemented") + errTimeout = errors.New("timeout") + expire = time.Minute +) + +// a message from NIC +type message struct { + bts []byte + addr net.Addr +} + +// a tcp flow information of a connection pair +type tcpFlow struct { + conn *net.TCPConn // the related system TCP connection of this flow + handle *net.IPConn // the handle to send packets + seq uint32 // TCP sequence number + ack uint32 // TCP acknowledge number + networkLayer gopacket.SerializableLayer // network layer header for tx + ts time.Time // last packet incoming time + buf gopacket.SerializeBuffer // a buffer for write + tcpHeader layers.TCP +} + +// TCPConn defines a TCP-packet oriented connection +type TCPConn struct { + die chan struct{} + dieOnce sync.Once + + // the main golang sockets + tcpconn *net.TCPConn // from net.Dial + listener *net.TCPListener // from net.Listen + + // handles + handles []*net.IPConn + + // packets captured from all related NICs will be delivered to this channel + chMessage chan message + + // all TCP flows + flowTable map[string]*tcpFlow + flowsLock sync.Mutex + + // iptables + iptables *iptables.IPTables + iprule []string + + ip6tables *iptables.IPTables + ip6rule []string + + // deadlines + readDeadline atomic.Value + writeDeadline atomic.Value + + // serialization + opts gopacket.SerializeOptions +} + +// lockflow locks the flow table and apply function `f` to the entry, and create one if not exist +func (conn *TCPConn) lockflow(addr net.Addr, f func(e *tcpFlow)) { + key := addr.String() + conn.flowsLock.Lock() + e := conn.flowTable[key] + if e == nil { // entry first visit + e = new(tcpFlow) + e.ts = time.Now() + e.buf = gopacket.NewSerializeBuffer() + } + f(e) + conn.flowTable[key] = e + conn.flowsLock.Unlock() +} + +// clean expired flows +func (conn *TCPConn) cleaner() { + ticker := time.NewTicker(time.Minute) + select { + case <-conn.die: + return + case <-ticker.C: + conn.flowsLock.Lock() + for k, v := range conn.flowTable { + if time.Now().Sub(v.ts) > expire { + if v.conn != nil { + setTTL(v.conn, 64) + v.conn.Close() + } + delete(conn.flowTable, k) + } + } + conn.flowsLock.Unlock() + } +} + +// captureFlow capture every inbound packets based on rules of BPF +func (conn *TCPConn) captureFlow(handle *net.IPConn, port int) { + buf := make([]byte, 2048) + opt := gopacket.DecodeOptions{NoCopy: true, Lazy: true} + for { + n, addr, err := handle.ReadFromIP(buf) + if err != nil { + return + } + + // try decoding TCP frame from buf[:n] + packet := gopacket.NewPacket(buf[:n], layers.LayerTypeTCP, opt) + transport := packet.TransportLayer() + tcp, ok := transport.(*layers.TCP) + if !ok { + continue + } + + // port filtering + if int(tcp.DstPort) != port { + continue + } + + // address building + var src net.TCPAddr + src.IP = addr.IP + src.Port = int(tcp.SrcPort) + + var orphan bool + // flow maintaince + conn.lockflow(&src, func(e *tcpFlow) { + if e.conn == nil { // make sure it's related to net.TCPConn + orphan = true // mark as orphan if it's not related net.TCPConn + } + + // to keep track of TCP header related to this source + e.ts = time.Now() + if tcp.ACK { + e.seq = tcp.Ack + } + if tcp.SYN { + e.ack = tcp.Seq + 1 + } + if tcp.PSH { + if e.ack == tcp.Seq { + e.ack = tcp.Seq + uint32(len(tcp.Payload)) + } + } + e.handle = handle + }) + + // push data if it's not orphan + if !orphan && tcp.PSH { + payload := make([]byte, len(tcp.Payload)) + copy(payload, tcp.Payload) + select { + case conn.chMessage <- message{payload, &src}: + case <-conn.die: + return + } + } + } +} + +// ReadFrom implements the PacketConn ReadFrom method. +func (conn *TCPConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + var timer *time.Timer + var deadline <-chan time.Time + if d, ok := conn.readDeadline.Load().(time.Time); ok && !d.IsZero() { + timer = time.NewTimer(time.Until(d)) + defer timer.Stop() + deadline = timer.C + } + + select { + case <-deadline: + return 0, nil, errTimeout + case <-conn.die: + return 0, nil, io.EOF + case packet := <-conn.chMessage: + n = copy(p, packet.bts) + return n, packet.addr, nil + } +} + +// WriteTo implements the PacketConn WriteTo method. +func (conn *TCPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + var deadline <-chan time.Time + if d, ok := conn.writeDeadline.Load().(time.Time); ok && !d.IsZero() { + timer := time.NewTimer(time.Until(d)) + defer timer.Stop() + deadline = timer.C + } + + select { + case <-deadline: + return 0, errTimeout + case <-conn.die: + return 0, io.EOF + default: + raddr, err := net.ResolveTCPAddr("tcp", addr.String()) + if err != nil { + return 0, err + } + + var lport int + if conn.tcpconn != nil { + lport = conn.tcpconn.LocalAddr().(*net.TCPAddr).Port + } else { + lport = conn.listener.Addr().(*net.TCPAddr).Port + } + + conn.lockflow(addr, func(e *tcpFlow) { + // if the flow doesn't have handle , assume this packet has lost, without notification + if e.handle == nil { + n = len(p) + return + } + + // build tcp header with local and remote port + e.tcpHeader.SrcPort = layers.TCPPort(lport) + e.tcpHeader.DstPort = layers.TCPPort(raddr.Port) + binary.Read(rand.Reader, binary.LittleEndian, &e.tcpHeader.Window) + e.tcpHeader.Window |= 0x8000 // make sure it's larger than 32768 + e.tcpHeader.Ack = e.ack + e.tcpHeader.Seq = e.seq + e.tcpHeader.PSH = true + e.tcpHeader.ACK = true + + // build IP header with src & dst ip for TCP checksum + if raddr.IP.To4() != nil { + ip := &layers.IPv4{ + Protocol: layers.IPProtocolTCP, + SrcIP: e.handle.LocalAddr().(*net.IPAddr).IP.To4(), + DstIP: raddr.IP.To4(), + } + e.tcpHeader.SetNetworkLayerForChecksum(ip) + } else { + ip := &layers.IPv6{ + NextHeader: layers.IPProtocolTCP, + SrcIP: e.handle.LocalAddr().(*net.IPAddr).IP.To16(), + DstIP: raddr.IP.To16(), + } + e.tcpHeader.SetNetworkLayerForChecksum(ip) + } + + e.buf.Clear() + gopacket.SerializeLayers(e.buf, conn.opts, &e.tcpHeader, gopacket.Payload(p)) + if conn.tcpconn != nil { + _, err = e.handle.Write(e.buf.Bytes()) + } else { + _, err = e.handle.WriteToIP(e.buf.Bytes(), &net.IPAddr{IP: raddr.IP}) + } + // increase seq in flow + e.seq += uint32(len(p)) + n = len(p) + }) + } + return +} + +// Close closes the connection. +func (conn *TCPConn) Close() error { + var err error + conn.dieOnce.Do(func() { + // signal closing + close(conn.die) + + // close all established tcp connections + if conn.tcpconn != nil { // client + setTTL(conn.tcpconn, 64) + err = conn.tcpconn.Close() + } else if conn.listener != nil { + err = conn.listener.Close() // server + conn.flowsLock.Lock() + for k, v := range conn.flowTable { + if v.conn != nil { + setTTL(v.conn, 64) + v.conn.Close() + } + delete(conn.flowTable, k) + } + conn.flowsLock.Unlock() + } + + // close handles + for k := range conn.handles { + conn.handles[k].Close() + } + + // delete iptable + if conn.iptables != nil { + conn.iptables.Delete("filter", "OUTPUT", conn.iprule...) + } + if conn.ip6tables != nil { + conn.ip6tables.Delete("filter", "OUTPUT", conn.ip6rule...) + } + }) + return err +} + +// LocalAddr returns the local network address. +func (conn *TCPConn) LocalAddr() net.Addr { + if conn.tcpconn != nil { + return conn.tcpconn.LocalAddr() + } else if conn.listener != nil { + return conn.listener.Addr() + } + return nil +} + +// SetDeadline implements the Conn SetDeadline method. +func (conn *TCPConn) SetDeadline(t time.Time) error { + if err := conn.SetReadDeadline(t); err != nil { + return err + } + if err := conn.SetWriteDeadline(t); err != nil { + return err + } + return nil +} + +// SetReadDeadline implements the Conn SetReadDeadline method. +func (conn *TCPConn) SetReadDeadline(t time.Time) error { + conn.readDeadline.Store(t) + return nil +} + +// SetWriteDeadline implements the Conn SetWriteDeadline method. +func (conn *TCPConn) SetWriteDeadline(t time.Time) error { + conn.writeDeadline.Store(t) + return nil +} + +// SetDSCP sets the 6bit DSCP field in IPv4 header, or 8bit Traffic Class in IPv6 header. +func (conn *TCPConn) SetDSCP(dscp int) error { + for k := range conn.handles { + if err := setDSCP(conn.handles[k], dscp); err != nil { + return err + } + } + return nil +} + +// SetReadBuffer sets the size of the operating system's receive buffer associated with the connection. +func (conn *TCPConn) SetReadBuffer(bytes int) error { + var err error + for k := range conn.handles { + if err := conn.handles[k].SetReadBuffer(bytes); err != nil { + return err + } + } + return err +} + +// SetWriteBuffer sets the size of the operating system's transmit buffer associated with the connection. +func (conn *TCPConn) SetWriteBuffer(bytes int) error { + var err error + for k := range conn.handles { + if err := conn.handles[k].SetWriteBuffer(bytes); err != nil { + return err + } + } + return err +} + +func (conn *TCPConn) SyscallConn() (syscall.RawConn, error) { + if len(conn.handles) == 0 { + return nil, errors.New("no handles") + // How is it possible? + } + return conn.handles[0].SyscallConn() +} + +// Dial connects to the remote TCP port, +// and returns a single packet-oriented connection +func Dial(network, address string) (*TCPConn, error) { + // remote address resolve + raddr, err := net.ResolveTCPAddr(network, address) + if err != nil { + return nil, err + } + + // AF_INET + handle, err := net.DialIP("ip:tcp", nil, &net.IPAddr{IP: raddr.IP}) + if err != nil { + return nil, err + } + + // create an established tcp connection + // will hack this tcp connection for packet transmission + tcpconn, err := net.DialTCP(network, nil, raddr) + if err != nil { + return nil, err + } + + // fields + conn := new(TCPConn) + conn.die = make(chan struct{}) + conn.flowTable = make(map[string]*tcpFlow) + conn.tcpconn = tcpconn + conn.chMessage = make(chan message) + conn.lockflow(tcpconn.RemoteAddr(), func(e *tcpFlow) { e.conn = tcpconn }) + conn.handles = append(conn.handles, handle) + conn.opts = gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + } + go conn.captureFlow(handle, tcpconn.LocalAddr().(*net.TCPAddr).Port) + go conn.cleaner() + + // iptables + err = setTTL(tcpconn, 1) + if err != nil { + return nil, err + } + + if ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4); err == nil { + rule := []string{"-m", "ttl", "--ttl-eq", "1", "-p", "tcp", "-d", raddr.IP.String(), "--dport", fmt.Sprint(raddr.Port), "-j", "DROP"} + if exists, err := ipt.Exists("filter", "OUTPUT", rule...); err == nil { + if !exists { + if err = ipt.Append("filter", "OUTPUT", rule...); err == nil { + conn.iprule = rule + conn.iptables = ipt + } + } + } + } + if ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv6); err == nil { + rule := []string{"-m", "hl", "--hl-eq", "1", "-p", "tcp", "-d", raddr.IP.String(), "--dport", fmt.Sprint(raddr.Port), "-j", "DROP"} + if exists, err := ipt.Exists("filter", "OUTPUT", rule...); err == nil { + if !exists { + if err = ipt.Append("filter", "OUTPUT", rule...); err == nil { + conn.ip6rule = rule + conn.ip6tables = ipt + } + } + } + } + + // discard everything + go io.Copy(ioutil.Discard, tcpconn) + + return conn, nil +} + +// Listen acts like net.ListenTCP, +// and returns a single packet-oriented connection +func Listen(network, address string) (*TCPConn, error) { + // fields + conn := new(TCPConn) + conn.flowTable = make(map[string]*tcpFlow) + conn.die = make(chan struct{}) + conn.chMessage = make(chan message) + conn.opts = gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + } + + // resolve address + laddr, err := net.ResolveTCPAddr(network, address) + if err != nil { + return nil, err + } + + // AF_INET + ifaces, err := net.Interfaces() + if err != nil { + return nil, err + } + + if laddr.IP == nil || laddr.IP.IsUnspecified() { // if address is not specified, capture on all ifaces + var lasterr error + for _, iface := range ifaces { + if addrs, err := iface.Addrs(); err == nil { + for _, addr := range addrs { + if ipaddr, ok := addr.(*net.IPNet); ok { + if handle, err := net.ListenIP("ip:tcp", &net.IPAddr{IP: ipaddr.IP}); err == nil { + conn.handles = append(conn.handles, handle) + go conn.captureFlow(handle, laddr.Port) + } else { + lasterr = err + } + } + } + } + } + if len(conn.handles) == 0 { + return nil, lasterr + } + } else { + if handle, err := net.ListenIP("ip:tcp", &net.IPAddr{IP: laddr.IP}); err == nil { + conn.handles = append(conn.handles, handle) + go conn.captureFlow(handle, laddr.Port) + } else { + return nil, err + } + } + + // start listening + l, err := net.ListenTCP(network, laddr) + if err != nil { + return nil, err + } + + conn.listener = l + + // start cleaner + go conn.cleaner() + + // iptables drop packets marked with TTL = 1 + // TODO: what if iptables is not available, the next hop will send back ICMP Time Exceeded, + // is this still an acceptable behavior? + if ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4); err == nil { + rule := []string{"-m", "ttl", "--ttl-eq", "1", "-p", "tcp", "--sport", fmt.Sprint(laddr.Port), "-j", "DROP"} + if exists, err := ipt.Exists("filter", "OUTPUT", rule...); err == nil { + if !exists { + if err = ipt.Append("filter", "OUTPUT", rule...); err == nil { + conn.iprule = rule + conn.iptables = ipt + } + } + } + } + if ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv6); err == nil { + rule := []string{"-m", "hl", "--hl-eq", "1", "-p", "tcp", "--sport", fmt.Sprint(laddr.Port), "-j", "DROP"} + if exists, err := ipt.Exists("filter", "OUTPUT", rule...); err == nil { + if !exists { + if err = ipt.Append("filter", "OUTPUT", rule...); err == nil { + conn.ip6rule = rule + conn.ip6tables = ipt + } + } + } + } + + // discard everything in original connection + go func() { + for { + tcpconn, err := l.AcceptTCP() + if err != nil { + return + } + + // if we cannot set TTL = 1, the only thing reasonable is panic + if err := setTTL(tcpconn, 1); err != nil { + panic(err) + } + + // record net.Conn + conn.lockflow(tcpconn.RemoteAddr(), func(e *tcpFlow) { e.conn = tcpconn }) + + // discard everything + go io.Copy(ioutil.Discard, tcpconn) + } + }() + + return conn, nil +} + +// setTTL sets the Time-To-Live field on a given connection +func setTTL(c *net.TCPConn, ttl int) error { + raw, err := c.SyscallConn() + if err != nil { + return err + } + addr := c.LocalAddr().(*net.TCPAddr) + + if addr.IP.To4() == nil { + raw.Control(func(fd uintptr) { + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, ttl) + }) + } else { + raw.Control(func(fd uintptr) { + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TTL, ttl) + }) + } + return err +} + +// setDSCP sets the 6bit DSCP field in IPv4 header, or 8bit Traffic Class in IPv6 header. +func setDSCP(c *net.IPConn, dscp int) error { + raw, err := c.SyscallConn() + if err != nil { + return err + } + addr := c.LocalAddr().(*net.IPAddr) + + if addr.IP.To4() == nil { + raw.Control(func(fd uintptr) { + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS, dscp) + }) + } else { + raw.Control(func(fd uintptr) { + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TOS, dscp<<2) + }) + } + return err +} diff --git a/transport/hysteria/conns/faketcp/tcp_stub.go b/transport/hysteria/conns/faketcp/tcp_stub.go new file mode 100644 index 00000000..9bc55077 --- /dev/null +++ b/transport/hysteria/conns/faketcp/tcp_stub.go @@ -0,0 +1,21 @@ +//go:build !linux +// +build !linux + +package faketcp + +import ( + "errors" + "net" +) + +type TCPConn struct{ *net.UDPConn } + +// Dial connects to the remote TCP port, +// and returns a single packet-oriented connection +func Dial(network, address string) (*TCPConn, error) { + return nil, errors.New("faketcp is not supported on this platform") +} + +func Listen(network, address string) (*TCPConn, error) { + return nil, errors.New("faketcp is not supported on this platform") +} diff --git a/transport/hysteria/conns/faketcp/tcp_test.go b/transport/hysteria/conns/faketcp/tcp_test.go new file mode 100644 index 00000000..ea26c689 --- /dev/null +++ b/transport/hysteria/conns/faketcp/tcp_test.go @@ -0,0 +1,196 @@ +//go:build linux +// +build linux + +package faketcp + +import ( + "log" + "net" + "net/http" + _ "net/http/pprof" + "testing" +) + +//const testPortStream = "127.0.0.1:3456" +//const testPortPacket = "127.0.0.1:3457" + +const testPortStream = "127.0.0.1:3456" +const portServerPacket = "[::]:3457" +const portRemotePacket = "127.0.0.1:3457" + +func init() { + startTCPServer() + startTCPRawServer() + go func() { + log.Println(http.ListenAndServe("0.0.0.0:6060", nil)) + }() +} + +func startTCPServer() net.Listener { + l, err := net.Listen("tcp", testPortStream) + if err != nil { + log.Panicln(err) + } + + go func() { + defer l.Close() + for { + conn, err := l.Accept() + if err != nil { + log.Println(err) + return + } + + go handleRequest(conn) + } + }() + return l +} + +func startTCPRawServer() *TCPConn { + conn, err := Listen("tcp", portServerPacket) + if err != nil { + log.Panicln(err) + } + err = conn.SetReadBuffer(1024 * 1024) + if err != nil { + log.Println(err) + } + err = conn.SetWriteBuffer(1024 * 1024) + if err != nil { + log.Println(err) + } + + go func() { + defer conn.Close() + buf := make([]byte, 1024) + for { + n, addr, err := conn.ReadFrom(buf) + if err != nil { + log.Println("server readfrom:", err) + return + } + //echo + n, err = conn.WriteTo(buf[:n], addr) + if err != nil { + log.Println("server writeTo:", err) + return + } + } + }() + return conn +} + +func handleRequest(conn net.Conn) { + defer conn.Close() + + for { + buf := make([]byte, 1024) + size, err := conn.Read(buf) + if err != nil { + log.Println("handleRequest:", err) + return + } + data := buf[:size] + conn.Write(data) + } +} + +func TestDialTCPStream(t *testing.T) { + conn, err := Dial("tcp", testPortStream) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + + addr, err := net.ResolveTCPAddr("tcp", testPortStream) + if err != nil { + t.Fatal(err) + } + + n, err := conn.WriteTo([]byte("abc"), addr) + if err != nil { + t.Fatal(n, err) + } + + buf := make([]byte, 1024) + if n, addr, err := conn.ReadFrom(buf); err != nil { + t.Fatal(n, addr, err) + } else { + log.Println(string(buf[:n]), "from:", addr) + } +} + +func TestDialToTCPPacket(t *testing.T) { + conn, err := Dial("tcp", portRemotePacket) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + + addr, err := net.ResolveTCPAddr("tcp", portRemotePacket) + if err != nil { + t.Fatal(err) + } + + n, err := conn.WriteTo([]byte("abc"), addr) + if err != nil { + t.Fatal(n, err) + } + log.Println("written") + + buf := make([]byte, 1024) + log.Println("readfrom buf") + if n, addr, err := conn.ReadFrom(buf); err != nil { + log.Println(err) + t.Fatal(n, addr, err) + } else { + log.Println(string(buf[:n]), "from:", addr) + } + + log.Println("complete") +} + +func TestSettings(t *testing.T) { + conn, err := Dial("tcp", portRemotePacket) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + if err := conn.SetDSCP(46); err != nil { + log.Fatal("SetDSCP:", err) + } + if err := conn.SetReadBuffer(4096); err != nil { + log.Fatal("SetReaderBuffer:", err) + } + if err := conn.SetWriteBuffer(4096); err != nil { + log.Fatal("SetWriteBuffer:", err) + } +} + +func BenchmarkEcho(b *testing.B) { + conn, err := Dial("tcp", portRemotePacket) + if err != nil { + b.Fatal(err) + } + defer conn.Close() + + addr, err := net.ResolveTCPAddr("tcp", portRemotePacket) + if err != nil { + b.Fatal(err) + } + + buf := make([]byte, 1024) + b.ReportAllocs() + b.SetBytes(int64(len(buf))) + for i := 0; i < b.N; i++ { + n, err := conn.WriteTo(buf, addr) + if err != nil { + b.Fatal(n, err) + } + + if n, addr, err := conn.ReadFrom(buf); err != nil { + b.Fatal(n, addr, err) + } + } +} diff --git a/transport/hysteria/conns/udp/obfs.go b/transport/hysteria/conns/udp/obfs.go new file mode 100644 index 00000000..da637d0f --- /dev/null +++ b/transport/hysteria/conns/udp/obfs.go @@ -0,0 +1,89 @@ +package udp + +import ( + "github.com/Dreamacro/clash/log" + "github.com/Dreamacro/clash/transport/hysteria/obfs" + "net" + "sync" + "time" +) + +const udpBufferSize = 65535 + +type ObfsUDPConn struct { + orig net.PacketConn + obfs obfs.Obfuscator + readBuf []byte + readMutex sync.Mutex + writeBuf []byte + writeMutex sync.Mutex + closed bool +} + +func NewObfsUDPConn(orig net.PacketConn, obfs obfs.Obfuscator) *ObfsUDPConn { + return &ObfsUDPConn{ + orig: orig, + obfs: obfs, + readBuf: make([]byte, udpBufferSize), + writeBuf: make([]byte, udpBufferSize), + } +} + +func (c *ObfsUDPConn) ReadFrom(p []byte) (int, net.Addr, error) { + for { + c.readMutex.Lock() + if c.closed { + log.Infoln("read udp obfs before") + } + n, addr, err := c.orig.ReadFrom(c.readBuf) + if c.closed { + log.Infoln("read udp obfs after") + } + if n <= 0 { + c.readMutex.Unlock() + return 0, addr, err + } + newN := c.obfs.Deobfuscate(c.readBuf[:n], p) + c.readMutex.Unlock() + if newN > 0 { + // Valid packet + return newN, addr, err + } else if err != nil { + // Not valid and orig.ReadFrom had some error + return 0, addr, err + } + } +} + +func (c *ObfsUDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + c.writeMutex.Lock() + bn := c.obfs.Obfuscate(p, c.writeBuf) + _, err = c.orig.WriteTo(c.writeBuf[:bn], addr) + c.writeMutex.Unlock() + if err != nil { + return 0, err + } else { + return len(p), nil + } +} + +func (c *ObfsUDPConn) Close() error { + c.closed = true + return c.orig.Close() +} + +func (c *ObfsUDPConn) LocalAddr() net.Addr { + return c.orig.LocalAddr() +} + +func (c *ObfsUDPConn) SetDeadline(t time.Time) error { + return c.orig.SetDeadline(t) +} + +func (c *ObfsUDPConn) SetReadDeadline(t time.Time) error { + return c.orig.SetReadDeadline(t) +} + +func (c *ObfsUDPConn) SetWriteDeadline(t time.Time) error { + return c.orig.SetWriteDeadline(t) +} diff --git a/transport/hysteria/conns/wechat/obfs.go b/transport/hysteria/conns/wechat/obfs.go new file mode 100644 index 00000000..a63b85d4 --- /dev/null +++ b/transport/hysteria/conns/wechat/obfs.go @@ -0,0 +1,105 @@ +package wechat + +import ( + "encoding/binary" + "github.com/Dreamacro/clash/log" + "github.com/Dreamacro/clash/transport/hysteria/obfs" + "math/rand" + "net" + "sync" + "time" +) + +const udpBufferSize = 65535 + +type ObfsWeChatUDPConn struct { + orig net.PacketConn + obfs obfs.Obfuscator + closed bool + readBuf []byte + readMutex sync.Mutex + writeBuf []byte + writeMutex sync.Mutex + sn uint32 +} + +func NewObfsWeChatUDPConn(orig net.PacketConn, obfs obfs.Obfuscator) *ObfsWeChatUDPConn { + log.Infoln("new wechat") + return &ObfsWeChatUDPConn{ + orig: orig, + obfs: obfs, + readBuf: make([]byte, udpBufferSize), + writeBuf: make([]byte, udpBufferSize), + sn: rand.Uint32() & 0xFFFF, + } +} + +func (c *ObfsWeChatUDPConn) ReadFrom(p []byte) (int, net.Addr, error) { + for { + c.readMutex.Lock() + if c.closed { + log.Infoln("read wechat obfs before") + } + n, addr, err := c.orig.ReadFrom(c.readBuf) + if c.closed { + log.Infoln("read wechat obfs after") + } + if n <= 13 { + c.readMutex.Unlock() + return 0, addr, err + } + newN := c.obfs.Deobfuscate(c.readBuf[13:n], p) + c.readMutex.Unlock() + if newN > 0 { + // Valid packet + return newN, addr, err + } else if err != nil { + // Not valid and orig.ReadFrom had some error + return 0, addr, err + } + } +} + +func (c *ObfsWeChatUDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + c.writeMutex.Lock() + c.writeBuf[0] = 0xa1 + c.writeBuf[1] = 0x08 + binary.BigEndian.PutUint32(c.writeBuf[2:], c.sn) + c.sn++ + c.writeBuf[6] = 0x00 + c.writeBuf[7] = 0x10 + c.writeBuf[8] = 0x11 + c.writeBuf[9] = 0x18 + c.writeBuf[10] = 0x30 + c.writeBuf[11] = 0x22 + c.writeBuf[12] = 0x30 + bn := c.obfs.Obfuscate(p, c.writeBuf[13:]) + _, err = c.orig.WriteTo(c.writeBuf[:13+bn], addr) + c.writeMutex.Unlock() + if err != nil { + return 0, err + } else { + return len(p), nil + } +} + +func (c *ObfsWeChatUDPConn) Close() error { + c.closed = true + return c.orig.Close() +} + +func (c *ObfsWeChatUDPConn) LocalAddr() net.Addr { + return c.orig.LocalAddr() +} + +func (c *ObfsWeChatUDPConn) SetDeadline(t time.Time) error { + return c.orig.SetDeadline(t) +} + +func (c *ObfsWeChatUDPConn) SetReadDeadline(t time.Time) error { + return c.orig.SetReadDeadline(t) +} + +func (c *ObfsWeChatUDPConn) SetWriteDeadline(t time.Time) error { + return c.orig.SetWriteDeadline(t) +} diff --git a/transport/hysteria/core/client.go b/transport/hysteria/core/client.go new file mode 100644 index 00000000..bd91250d --- /dev/null +++ b/transport/hysteria/core/client.go @@ -0,0 +1,422 @@ +package core + +import ( + "bytes" + "context" + "crypto/tls" + "errors" + "fmt" + "github.com/Dreamacro/clash/transport/hysteria/obfs" + "github.com/Dreamacro/clash/transport/hysteria/pmtud_fix" + "github.com/Dreamacro/clash/transport/hysteria/transport" + "github.com/Dreamacro/clash/transport/hysteria/utils" + "github.com/lucas-clemente/quic-go" + "github.com/lucas-clemente/quic-go/congestion" + "github.com/lunixbochs/struc" + "math/rand" + "net" + "strconv" + "sync" + "time" +) + +var ( + ErrClosed = errors.New("closed") +) + +type CongestionFactory func(refBPS uint64) congestion.CongestionControl + +type Client struct { + transport *transport.ClientTransport + serverAddr string + protocol string + sendBPS, recvBPS uint64 + auth []byte + congestionFactory CongestionFactory + obfuscator obfs.Obfuscator + + tlsConfig *tls.Config + quicConfig *quic.Config + + quicSession quic.Connection + reconnectMutex sync.Mutex + closed bool + + udpSessionMutex sync.RWMutex + udpSessionMap map[uint32]chan *udpMessage + udpDefragger defragger +} + +func NewClient(serverAddr string, protocol string, auth []byte, tlsConfig *tls.Config, quicConfig *quic.Config, + transport *transport.ClientTransport, sendBPS uint64, recvBPS uint64, congestionFactory CongestionFactory, + obfuscator obfs.Obfuscator) (*Client, error) { + quicConfig.DisablePathMTUDiscovery = quicConfig.DisablePathMTUDiscovery || pmtud_fix.DisablePathMTUDiscovery + c := &Client{ + transport: transport, + serverAddr: serverAddr, + protocol: protocol, + sendBPS: sendBPS, + recvBPS: recvBPS, + auth: auth, + congestionFactory: congestionFactory, + obfuscator: obfuscator, + tlsConfig: tlsConfig, + quicConfig: quicConfig, + } + return c, nil +} + +func (c *Client) connectToServer(dialer transport.PacketDialer) error { + qs, err := c.transport.QUICDial(c.protocol, c.serverAddr, c.tlsConfig, c.quicConfig, c.obfuscator, dialer) + if err != nil { + return err + } + // Control stream + ctx, ctxCancel := context.WithTimeout(context.Background(), protocolTimeout) + stream, err := qs.OpenStreamSync(ctx) + ctxCancel() + if err != nil { + _ = qs.CloseWithError(closeErrorCodeProtocol, "protocol error") + return err + } + ok, msg, err := c.handleControlStream(qs, stream) + if err != nil { + _ = qs.CloseWithError(closeErrorCodeProtocol, "protocol error") + return err + } + if !ok { + _ = qs.CloseWithError(closeErrorCodeAuth, "auth error") + return fmt.Errorf("auth error: %s", msg) + } + // All good + c.udpSessionMap = make(map[uint32]chan *udpMessage) + go c.handleMessage(qs) + c.quicSession = qs + return nil +} + +func (c *Client) handleControlStream(qs quic.Connection, stream quic.Stream) (bool, string, error) { + // Send protocol version + _, err := stream.Write([]byte{protocolVersion}) + if err != nil { + return false, "", err + } + // Send client hello + err = struc.Pack(stream, &clientHello{ + Rate: transmissionRate{ + SendBPS: c.sendBPS, + RecvBPS: c.recvBPS, + }, + Auth: c.auth, + }) + if err != nil { + return false, "", err + } + // Receive server hello + var sh serverHello + err = struc.Unpack(stream, &sh) + if err != nil { + return false, "", err + } + // Set the congestion accordingly + if sh.OK && c.congestionFactory != nil { + qs.SetCongestionControl(c.congestionFactory(sh.Rate.RecvBPS)) + } + return sh.OK, sh.Message, nil +} + +func (c *Client) handleMessage(qs quic.Connection) { + for { + msg, err := qs.ReceiveMessage() + if err != nil { + break + } + var udpMsg udpMessage + err = struc.Unpack(bytes.NewBuffer(msg), &udpMsg) + if err != nil { + continue + } + dfMsg := c.udpDefragger.Feed(udpMsg) + if dfMsg == nil { + continue + } + c.udpSessionMutex.RLock() + ch, ok := c.udpSessionMap[dfMsg.SessionID] + if ok { + select { + case ch <- dfMsg: + // OK + default: + // Silently drop the message when the channel is full + } + } + c.udpSessionMutex.RUnlock() + } +} + +func (c *Client) openStreamWithReconnect(dialer transport.PacketDialer) (quic.Connection, quic.Stream, error) { + c.reconnectMutex.Lock() + defer c.reconnectMutex.Unlock() + if c.closed { + return nil, nil, ErrClosed + } + if c.quicSession == nil { + if err := c.connectToServer(dialer); err != nil { + // Still error, oops + return nil, nil, err + } + } + stream, err := c.quicSession.OpenStream() + if err == nil { + // All good + return c.quicSession, &wrappedQUICStream{stream}, nil + } + // Something is wrong + if nErr, ok := err.(net.Error); ok && nErr.Temporary() { + // Temporary error, just return + return nil, nil, err + } + // Permanent error, need to reconnect + if err := c.connectToServer(dialer); err != nil { + // Still error, oops + return nil, nil, err + } + // We are not going to try again even if it still fails the second time + stream, err = c.quicSession.OpenStream() + return c.quicSession, &wrappedQUICStream{stream}, err +} + +func (c *Client) DialTCP(addr string, dialer transport.PacketDialer) (net.Conn, error) { + host, port, err := utils.SplitHostPort(addr) + if err != nil { + return nil, err + } + session, stream, err := c.openStreamWithReconnect(dialer) + if err != nil { + return nil, err + } + // Send request + err = struc.Pack(stream, &clientRequest{ + UDP: false, + Host: host, + Port: port, + }) + if err != nil { + _ = stream.Close() + return nil, err + } + // Read response + var sr serverResponse + err = struc.Unpack(stream, &sr) + if err != nil { + _ = stream.Close() + return nil, err + } + if !sr.OK { + _ = stream.Close() + return nil, fmt.Errorf("connection rejected: %s", sr.Message) + } + return &quicConn{ + Orig: stream, + PseudoLocalAddr: session.LocalAddr(), + PseudoRemoteAddr: session.RemoteAddr(), + }, nil +} + +func (c *Client) DialUDP(dialer transport.PacketDialer) (UDPConn, error) { + session, stream, err := c.openStreamWithReconnect(dialer) + if err != nil { + return nil, err + } + // Send request + err = struc.Pack(stream, &clientRequest{ + UDP: true, + }) + if err != nil { + _ = stream.Close() + return nil, err + } + // Read response + var sr serverResponse + err = struc.Unpack(stream, &sr) + if err != nil { + _ = stream.Close() + return nil, err + } + if !sr.OK { + _ = stream.Close() + return nil, fmt.Errorf("connection rejected: %s", sr.Message) + } + + // Create a session in the map + c.udpSessionMutex.Lock() + nCh := make(chan *udpMessage, 1024) + // Store the current session map for CloseFunc below + // to ensures that we are adding and removing sessions on the same map, + // as reconnecting will reassign the map + sessionMap := c.udpSessionMap + sessionMap[sr.UDPSessionID] = nCh + c.udpSessionMutex.Unlock() + + pktConn := &quicPktConn{ + Session: session, + Stream: stream, + CloseFunc: func() { + c.udpSessionMutex.Lock() + if ch, ok := sessionMap[sr.UDPSessionID]; ok { + close(ch) + delete(sessionMap, sr.UDPSessionID) + } + c.udpSessionMutex.Unlock() + }, + UDPSessionID: sr.UDPSessionID, + MsgCh: nCh, + } + go pktConn.Hold() + return pktConn, nil +} + +func (c *Client) Close() error { + c.reconnectMutex.Lock() + defer c.reconnectMutex.Unlock() + err := c.quicSession.CloseWithError(closeErrorCodeGeneric, "") + c.closed = true + return err +} + +type quicConn struct { + Orig quic.Stream + PseudoLocalAddr net.Addr + PseudoRemoteAddr net.Addr +} + +func (w *quicConn) Read(b []byte) (n int, err error) { + return w.Orig.Read(b) +} + +func (w *quicConn) Write(b []byte) (n int, err error) { + return w.Orig.Write(b) +} + +func (w *quicConn) Close() error { + return w.Orig.Close() +} + +func (w *quicConn) LocalAddr() net.Addr { + return w.PseudoLocalAddr +} + +func (w *quicConn) RemoteAddr() net.Addr { + return w.PseudoRemoteAddr +} + +func (w *quicConn) SetDeadline(t time.Time) error { + return w.Orig.SetDeadline(t) +} + +func (w *quicConn) SetReadDeadline(t time.Time) error { + return w.Orig.SetReadDeadline(t) +} + +func (w *quicConn) SetWriteDeadline(t time.Time) error { + return w.Orig.SetWriteDeadline(t) +} + +type UDPConn interface { + ReadFrom() ([]byte, string, error) + WriteTo([]byte, string) error + Close() error + LocalAddr() net.Addr + SetDeadline(t time.Time) error + SetReadDeadline(t time.Time) error + SetWriteDeadline(t time.Time) error +} + +type quicPktConn struct { + Session quic.Connection + Stream quic.Stream + CloseFunc func() + UDPSessionID uint32 + MsgCh <-chan *udpMessage +} + +func (c *quicPktConn) Hold() { + // Hold the stream until it's closed + buf := make([]byte, 1024) + for { + _, err := c.Stream.Read(buf) + if err != nil { + break + } + } + _ = c.Close() +} + +func (c *quicPktConn) ReadFrom() ([]byte, string, error) { + msg := <-c.MsgCh + if msg == nil { + // Closed + return nil, "", ErrClosed + } + return msg.Data, net.JoinHostPort(msg.Host, strconv.Itoa(int(msg.Port))), nil +} + +func (c *quicPktConn) WriteTo(p []byte, addr string) error { + host, port, err := utils.SplitHostPort(addr) + if err != nil { + return err + } + msg := udpMessage{ + SessionID: c.UDPSessionID, + Host: host, + Port: port, + FragCount: 1, + Data: p, + } + // try no frag first + var msgBuf bytes.Buffer + _ = struc.Pack(&msgBuf, &msg) + err = c.Session.SendMessage(msgBuf.Bytes()) + if err != nil { + if errSize, ok := err.(quic.ErrMessageToLarge); ok { + // need to frag + msg.MsgID = uint16(rand.Intn(0xFFFF)) + 1 // msgID must be > 0 when fragCount > 1 + fragMsgs := fragUDPMessage(msg, int(errSize)) + for _, fragMsg := range fragMsgs { + msgBuf.Reset() + _ = struc.Pack(&msgBuf, &fragMsg) + err = c.Session.SendMessage(msgBuf.Bytes()) + if err != nil { + return err + } + } + return nil + } else { + // some other error + return err + } + } else { + return nil + } +} + +func (c *quicPktConn) Close() error { + c.CloseFunc() + return c.Stream.Close() +} + +func (c *quicPktConn) LocalAddr() net.Addr { + return c.Session.LocalAddr() +} + +func (c *quicPktConn) SetDeadline(t time.Time) error { + return c.Stream.SetDeadline(t) +} + +func (c *quicPktConn) SetReadDeadline(t time.Time) error { + return c.Stream.SetReadDeadline(t) +} + +func (c *quicPktConn) SetWriteDeadline(t time.Time) error { + return c.Stream.SetWriteDeadline(t) +} diff --git a/transport/hysteria/core/frag.go b/transport/hysteria/core/frag.go new file mode 100644 index 00000000..7a387747 --- /dev/null +++ b/transport/hysteria/core/frag.go @@ -0,0 +1,67 @@ +package core + +func fragUDPMessage(m udpMessage, maxSize int) []udpMessage { + if m.Size() <= maxSize { + return []udpMessage{m} + } + fullPayload := m.Data + maxPayloadSize := maxSize - m.HeaderSize() + off := 0 + fragID := uint8(0) + fragCount := uint8((len(fullPayload) + maxPayloadSize - 1) / maxPayloadSize) // round up + var frags []udpMessage + for off < len(fullPayload) { + payloadSize := len(fullPayload) - off + if payloadSize > maxPayloadSize { + payloadSize = maxPayloadSize + } + frag := m + frag.FragID = fragID + frag.FragCount = fragCount + frag.DataLen = uint16(payloadSize) + frag.Data = fullPayload[off : off+payloadSize] + frags = append(frags, frag) + off += payloadSize + fragID++ + } + return frags +} + +type defragger struct { + msgID uint16 + frags []*udpMessage + count uint8 +} + +func (d *defragger) Feed(m udpMessage) *udpMessage { + if m.FragCount <= 1 { + return &m + } + if m.FragID >= m.FragCount { + // wtf is this? + return nil + } + if m.MsgID != d.msgID { + // new message, clear previous state + d.msgID = m.MsgID + d.frags = make([]*udpMessage, m.FragCount) + d.count = 1 + d.frags[m.FragID] = &m + } else if d.frags[m.FragID] == nil { + d.frags[m.FragID] = &m + d.count++ + if int(d.count) == len(d.frags) { + // all fragments received, assemble + var data []byte + for _, frag := range d.frags { + data = append(data, frag.Data...) + } + m.DataLen = uint16(len(data)) + m.Data = data + m.FragID = 0 + m.FragCount = 1 + return &m + } + } + return nil +} diff --git a/transport/hysteria/core/frag_test.go b/transport/hysteria/core/frag_test.go new file mode 100644 index 00000000..f2f24625 --- /dev/null +++ b/transport/hysteria/core/frag_test.go @@ -0,0 +1,346 @@ +package core + +import ( + "reflect" + "testing" +) + +func Test_fragUDPMessage(t *testing.T) { + type args struct { + m udpMessage + maxSize int + } + tests := []struct { + name string + args args + want []udpMessage + }{ + { + "no frag", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 0, + FragCount: 1, + DataLen: 5, + Data: []byte("hello"), + }, + 100, + }, + []udpMessage{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 0, + FragCount: 1, + DataLen: 5, + Data: []byte("hello"), + }, + }, + }, + { + "2 frags", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 0, + FragCount: 1, + DataLen: 5, + Data: []byte("hello"), + }, + 22, + }, + []udpMessage{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 0, + FragCount: 2, + DataLen: 4, + Data: []byte("hell"), + }, + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 1, + FragCount: 2, + DataLen: 1, + Data: []byte("o"), + }, + }, + }, + { + "4 frags", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 0, + FragCount: 1, + DataLen: 20, + Data: []byte("wow wow wow lol lmao"), + }, + 23, + }, + []udpMessage{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 0, + FragCount: 4, + DataLen: 5, + Data: []byte("wow w"), + }, + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 1, + FragCount: 4, + DataLen: 5, + Data: []byte("ow wo"), + }, + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 2, + FragCount: 4, + DataLen: 5, + Data: []byte("w lol"), + }, + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 3, + FragCount: 4, + DataLen: 5, + Data: []byte(" lmao"), + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := fragUDPMessage(tt.args.m, tt.args.maxSize); !reflect.DeepEqual(got, tt.want) { + t.Errorf("fragUDPMessage() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_defragger_Feed(t *testing.T) { + d := &defragger{} + type args struct { + m udpMessage + } + tests := []struct { + name string + args args + want *udpMessage + }{ + { + "no frag", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 0, + FragCount: 1, + DataLen: 5, + Data: []byte("hello"), + }, + }, + &udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 123, + FragID: 0, + FragCount: 1, + DataLen: 5, + Data: []byte("hello"), + }, + }, + { + "frag 1 - 1/3", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 666, + FragID: 0, + FragCount: 3, + DataLen: 5, + Data: []byte("hello"), + }, + }, + nil, + }, + { + "frag 1 - 2/3", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 666, + FragID: 1, + FragCount: 3, + DataLen: 8, + Data: []byte(" shitty "), + }, + }, + nil, + }, + { + "frag 1 - 3/3", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 666, + FragID: 2, + FragCount: 3, + DataLen: 7, + Data: []byte("world!!"), + }, + }, + &udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 666, + FragID: 0, + FragCount: 1, + DataLen: 20, + Data: []byte("hello shitty world!!"), + }, + }, + { + "frag 2 - 1/2", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 777, + FragID: 0, + FragCount: 2, + DataLen: 5, + Data: []byte("hello"), + }, + }, + nil, + }, + { + "frag 3 - 2/2", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 778, + FragID: 1, + FragCount: 2, + DataLen: 5, + Data: []byte(" moto"), + }, + }, + nil, + }, + { + "frag 2 - 2/2", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 777, + FragID: 1, + FragCount: 2, + DataLen: 5, + Data: []byte(" moto"), + }, + }, + nil, + }, + { + "frag 2 - 1/2 re", + args{ + udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 777, + FragID: 0, + FragCount: 2, + DataLen: 5, + Data: []byte("hello"), + }, + }, + &udpMessage{ + SessionID: 123, + HostLen: 4, + Host: "test", + Port: 123, + MsgID: 777, + FragID: 0, + FragCount: 1, + DataLen: 10, + Data: []byte("hello moto"), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := d.Feed(tt.args.m); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Feed() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/transport/hysteria/core/protocol.go b/transport/hysteria/core/protocol.go new file mode 100644 index 00000000..7fd64d7c --- /dev/null +++ b/transport/hysteria/core/protocol.go @@ -0,0 +1,76 @@ +package core + +import ( + "time" +) + +const ( + protocolVersion = uint8(3) + protocolVersionV2 = uint8(2) + protocolTimeout = 10 * time.Second + + closeErrorCodeGeneric = 0 + closeErrorCodeProtocol = 1 + closeErrorCodeAuth = 2 +) + +type transmissionRate struct { + SendBPS uint64 + RecvBPS uint64 +} + +type clientHello struct { + Rate transmissionRate + AuthLen uint16 `struc:"sizeof=Auth"` + Auth []byte +} + +type serverHello struct { + OK bool + Rate transmissionRate + MessageLen uint16 `struc:"sizeof=Message"` + Message string +} + +type clientRequest struct { + UDP bool + HostLen uint16 `struc:"sizeof=Host"` + Host string + Port uint16 +} + +type serverResponse struct { + OK bool + UDPSessionID uint32 + MessageLen uint16 `struc:"sizeof=Message"` + Message string +} + +type udpMessage struct { + SessionID uint32 + HostLen uint16 `struc:"sizeof=Host"` + Host string + Port uint16 + MsgID uint16 // doesn't matter when not fragmented, but must not be 0 when fragmented + FragID uint8 // doesn't matter when not fragmented, starts at 0 when fragmented + FragCount uint8 // must be 1 when not fragmented + DataLen uint16 `struc:"sizeof=Data"` + Data []byte +} + +func (m udpMessage) HeaderSize() int { + return 4 + 2 + len(m.Host) + 2 + 2 + 1 + 1 + 2 +} + +func (m udpMessage) Size() int { + return m.HeaderSize() + len(m.Data) +} + +type udpMessageV2 struct { + SessionID uint32 + HostLen uint16 `struc:"sizeof=Host"` + Host string + Port uint16 + DataLen uint16 `struc:"sizeof=Data"` + Data []byte +} diff --git a/transport/hysteria/core/stream.go b/transport/hysteria/core/stream.go new file mode 100644 index 00000000..8ace4a1d --- /dev/null +++ b/transport/hysteria/core/stream.go @@ -0,0 +1,54 @@ +package core + +import ( + "context" + "github.com/lucas-clemente/quic-go" + "time" +) + +// Handle stream close properly +// Ref: https://github.com/libp2p/go-libp2p-quic-transport/blob/master/stream.go +type wrappedQUICStream struct { + Stream quic.Stream +} + +func (s *wrappedQUICStream) StreamID() quic.StreamID { + return s.Stream.StreamID() +} + +func (s *wrappedQUICStream) Read(p []byte) (n int, err error) { + return s.Stream.Read(p) +} + +func (s *wrappedQUICStream) CancelRead(code quic.StreamErrorCode) { + s.Stream.CancelRead(code) +} + +func (s *wrappedQUICStream) SetReadDeadline(t time.Time) error { + return s.Stream.SetReadDeadline(t) +} + +func (s *wrappedQUICStream) Write(p []byte) (n int, err error) { + return s.Stream.Write(p) +} + +func (s *wrappedQUICStream) Close() error { + s.Stream.CancelRead(0) + return s.Stream.Close() +} + +func (s *wrappedQUICStream) CancelWrite(code quic.StreamErrorCode) { + s.Stream.CancelWrite(code) +} + +func (s *wrappedQUICStream) Context() context.Context { + return s.Stream.Context() +} + +func (s *wrappedQUICStream) SetWriteDeadline(t time.Time) error { + return s.Stream.SetWriteDeadline(t) +} + +func (s *wrappedQUICStream) SetDeadline(t time.Time) error { + return s.Stream.SetDeadline(t) +} diff --git a/transport/hysteria/obfs/obfs.go b/transport/hysteria/obfs/obfs.go new file mode 100644 index 00000000..cb108a38 --- /dev/null +++ b/transport/hysteria/obfs/obfs.go @@ -0,0 +1,6 @@ +package obfs + +type Obfuscator interface { + Deobfuscate(in []byte, out []byte) int + Obfuscate(in []byte, out []byte) int +} diff --git a/transport/hysteria/obfs/xplus.go b/transport/hysteria/obfs/xplus.go new file mode 100644 index 00000000..dd636452 --- /dev/null +++ b/transport/hysteria/obfs/xplus.go @@ -0,0 +1,52 @@ +package obfs + +import ( + "crypto/sha256" + "math/rand" + "sync" + "time" +) + +// [salt][obfuscated payload] + +const saltLen = 16 + +type XPlusObfuscator struct { + Key []byte + RandSrc *rand.Rand + + lk sync.Mutex +} + +func NewXPlusObfuscator(key []byte) *XPlusObfuscator { + return &XPlusObfuscator{ + Key: key, + RandSrc: rand.New(rand.NewSource(time.Now().UnixNano())), + } +} + +func (x *XPlusObfuscator) Deobfuscate(in []byte, out []byte) int { + pLen := len(in) - saltLen + if pLen <= 0 || len(out) < pLen { + // Invalid + return 0 + } + key := sha256.Sum256(append(x.Key, in[:saltLen]...)) + // Deobfuscate the payload + for i, c := range in[saltLen:] { + out[i] = c ^ key[i%sha256.Size] + } + return pLen +} + +func (x *XPlusObfuscator) Obfuscate(in []byte, out []byte) int { + x.lk.Lock() + _, _ = x.RandSrc.Read(out[:saltLen]) // salt + x.lk.Unlock() + // Obfuscate the payload + key := sha256.Sum256(append(x.Key, out[:saltLen]...)) + for i, c := range in { + out[i+saltLen] = c ^ key[i%sha256.Size] + } + return len(in) + saltLen +} diff --git a/transport/hysteria/obfs/xplus_test.go b/transport/hysteria/obfs/xplus_test.go new file mode 100644 index 00000000..c1cf6295 --- /dev/null +++ b/transport/hysteria/obfs/xplus_test.go @@ -0,0 +1,31 @@ +package obfs + +import ( + "bytes" + "testing" +) + +func TestXPlusObfuscator(t *testing.T) { + x := NewXPlusObfuscator([]byte("Vaundy")) + tests := []struct { + name string + p []byte + }{ + {name: "1", p: []byte("HelloWorld")}, + {name: "2", p: []byte("Regret is just a horrible attempt at time travel that ends with you feeling like crap")}, + {name: "3", p: []byte("To be, or not to be, that is the question:\nWhether 'tis nobler in the mind to suffer\n" + + "The slings and arrows of outrageous fortune,\nOr to take arms against a sea of troubles\n" + + "And by opposing end them. To die—to sleep,\nNo more; and by a sleep to say we end")}, + {name: "empty", p: []byte("")}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := make([]byte, 10240) + n := x.Obfuscate(tt.p, buf) + n2 := x.Deobfuscate(buf[:n], buf[n:]) + if !bytes.Equal(tt.p, buf[n:n+n2]) { + t.Errorf("Inconsistent deobfuscate result: got %v, want %v", buf[n:n+n2], tt.p) + } + }) + } +} diff --git a/transport/hysteria/pmtud_fix/avail.go b/transport/hysteria/pmtud_fix/avail.go new file mode 100644 index 00000000..2f2bce83 --- /dev/null +++ b/transport/hysteria/pmtud_fix/avail.go @@ -0,0 +1,8 @@ +//go:build linux || windows +// +build linux windows + +package pmtud_fix + +const ( + DisablePathMTUDiscovery = false +) diff --git a/transport/hysteria/pmtud_fix/unavail.go b/transport/hysteria/pmtud_fix/unavail.go new file mode 100644 index 00000000..0eeb83df --- /dev/null +++ b/transport/hysteria/pmtud_fix/unavail.go @@ -0,0 +1,8 @@ +//go:build !linux && !windows +// +build !linux,!windows + +package pmtud_fix + +const ( + DisablePathMTUDiscovery = true +) diff --git a/transport/hysteria/transport/client.go b/transport/hysteria/transport/client.go new file mode 100644 index 00000000..0ec3d284 --- /dev/null +++ b/transport/hysteria/transport/client.go @@ -0,0 +1,106 @@ +package transport + +import ( + "context" + "crypto/tls" + "fmt" + "github.com/Dreamacro/clash/component/resolver" + "github.com/Dreamacro/clash/transport/hysteria/conns/faketcp" + "github.com/Dreamacro/clash/transport/hysteria/conns/udp" + "github.com/Dreamacro/clash/transport/hysteria/conns/wechat" + "github.com/Dreamacro/clash/transport/hysteria/obfs" + "github.com/Dreamacro/clash/transport/hysteria/utils" + "github.com/lucas-clemente/quic-go" + "net" +) + +type ClientTransport struct { + Dialer *net.Dialer + PrefEnabled bool + PrefIPv6 bool + PrefExclusive bool +} + +func (ct *ClientTransport) quicPacketConn(proto string, server string, obfs obfs.Obfuscator, dialer PacketDialer) (net.PacketConn, error) { + if len(proto) == 0 || proto == "udp" { + conn, err := dialer.ListenPacket() + if err != nil { + return nil, err + } + if obfs != nil { + oc := udp.NewObfsUDPConn(conn, obfs) + return oc, nil + } else { + return conn, nil + } + } else if proto == "wechat-video" { + conn, err := dialer.ListenPacket() + if err != nil { + return nil, err + } + if obfs != nil { + oc := wechat.NewObfsWeChatUDPConn(conn, obfs) + return oc, nil + } else { + return conn, nil + } + } else if proto == "faketcp" { + var conn *faketcp.TCPConn + conn, err := faketcp.Dial("tcp", server) + if err != nil { + return nil, err + } + if obfs != nil { + oc := faketcp.NewObfsFakeTCPConn(conn, obfs) + return oc, nil + } else { + return conn, nil + } + } else { + return nil, fmt.Errorf("unsupported protocol: %s", proto) + } +} + +type PacketDialer interface { + ListenPacket() (net.PacketConn, error) + Context() context.Context +} + +func (ct *ClientTransport) QUICDial(proto string, server string, tlsConfig *tls.Config, quicConfig *quic.Config, obfs obfs.Obfuscator, dialer PacketDialer) (quic.Connection, error) { + ipStr, port, err := utils.SplitHostPort(server) + if err != nil { + return nil, err + } + + ip, err := resolver.ResolveProxyServerHost(ipStr) + if err != nil { + return nil, err + } + + serverUDPAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", ip, port)) + if err != nil { + return nil, err + } + pktConn, err := ct.quicPacketConn(proto, server, obfs, dialer) + if err != nil { + return nil, err + } + qs, err := quic.DialContext(dialer.Context(), pktConn, serverUDPAddr, server, tlsConfig, quicConfig) + if err != nil { + _ = pktConn.Close() + return nil, err + } + return qs, nil +} + +func (ct *ClientTransport) DialTCP(raddr *net.TCPAddr) (*net.TCPConn, error) { + conn, err := ct.Dialer.Dial("tcp", raddr.String()) + if err != nil { + return nil, err + } + return conn.(*net.TCPConn), nil +} + +func (ct *ClientTransport) ListenUDP() (*net.UDPConn, error) { + return net.ListenUDP("udp", nil) +} diff --git a/transport/hysteria/utils/misc.go b/transport/hysteria/utils/misc.go new file mode 100644 index 00000000..29c7cf0c --- /dev/null +++ b/transport/hysteria/utils/misc.go @@ -0,0 +1,42 @@ +package utils + +import ( + "net" + "strconv" +) + +func SplitHostPort(hostport string) (string, uint16, error) { + host, port, err := net.SplitHostPort(hostport) + if err != nil { + return "", 0, err + } + portUint, err := strconv.ParseUint(port, 10, 16) + if err != nil { + return "", 0, err + } + return host, uint16(portUint), err +} + +func ParseIPZone(s string) (net.IP, string) { + s, zone := splitHostZone(s) + return net.ParseIP(s), zone +} + +func splitHostZone(s string) (host, zone string) { + if i := last(s, '%'); i > 0 { + host, zone = s[:i], s[i+1:] + } else { + host = s + } + return +} + +func last(s string, b byte) int { + i := len(s) + for i--; i >= 0; i-- { + if s[i] == b { + break + } + } + return i +}