Compare commits
1 Commits
jafar
..
pr-smtpimap
| Author | SHA1 | Date | |
|---|---|---|---|
| eca06e83d0 |
@@ -8,8 +8,6 @@ require (
|
|||||||
git.torproject.org/pluggable-transports/snowflake.git/v2 v2.3.0
|
git.torproject.org/pluggable-transports/snowflake.git/v2 v2.3.0
|
||||||
github.com/AlecAivazis/survey/v2 v2.3.5
|
github.com/AlecAivazis/survey/v2 v2.3.5
|
||||||
github.com/alecthomas/kingpin v2.2.6+incompatible
|
github.com/alecthomas/kingpin v2.2.6+incompatible
|
||||||
github.com/anacrolix/dht/v2 v2.19.1
|
|
||||||
github.com/anacrolix/torrent v1.47.0
|
|
||||||
github.com/apex/log v1.9.0
|
github.com/apex/log v1.9.0
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
|
||||||
github.com/cretz/bine v0.2.0
|
github.com/cretz/bine v0.2.0
|
||||||
@@ -46,48 +44,9 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
crawshaw.io/sqlite v0.3.3-0.20210127221821-98b1f83c5508 // indirect
|
|
||||||
github.com/RoaringBitmap/roaring v1.2.1 // indirect
|
|
||||||
github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0 // indirect
|
|
||||||
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
|
|
||||||
github.com/anacrolix/chansync v0.3.0 // indirect
|
|
||||||
github.com/anacrolix/envpprof v1.2.1 // indirect
|
|
||||||
github.com/anacrolix/generics v0.0.0-20220618083756-f99e35403a60 // indirect
|
|
||||||
github.com/anacrolix/go-libutp v1.2.0 // indirect
|
|
||||||
github.com/anacrolix/log v0.13.2-0.20220711050817-613cb738ef30 // indirect
|
|
||||||
github.com/anacrolix/missinggo v1.3.0 // indirect
|
|
||||||
github.com/anacrolix/missinggo/perf v1.0.0 // indirect
|
|
||||||
github.com/anacrolix/missinggo/v2 v2.7.0 // indirect
|
|
||||||
github.com/anacrolix/mmsg v1.0.0 // indirect
|
|
||||||
github.com/anacrolix/multiless v0.3.0 // indirect
|
|
||||||
github.com/anacrolix/stm v0.4.0 // indirect
|
|
||||||
github.com/anacrolix/sync v0.4.0 // indirect
|
|
||||||
github.com/anacrolix/upnp v0.1.3-0.20220123035249-922794e51c96 // indirect
|
|
||||||
github.com/anacrolix/utp v0.1.0 // indirect
|
|
||||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
|
||||||
github.com/benbjohnson/immutable v0.3.0 // indirect
|
|
||||||
github.com/bits-and-blooms/bitset v1.2.2 // indirect
|
|
||||||
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 // indirect
|
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
|
||||||
github.com/edsrzf/mmap-go v1.1.0 // indirect
|
|
||||||
github.com/go-logr/logr v1.2.3 // indirect
|
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
|
||||||
github.com/google/btree v1.1.2 // indirect
|
|
||||||
github.com/huandu/xstrings v1.3.2 // indirect
|
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
github.com/lispad/go-generics-tools v1.1.0 // indirect
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.14 // indirect
|
|
||||||
github.com/mschoch/smat v0.2.0 // indirect
|
|
||||||
github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417 // indirect
|
|
||||||
github.com/segmentio/fasthash v1.0.3 // indirect
|
github.com/segmentio/fasthash v1.0.3 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/tidwall/btree v1.3.1 // indirect
|
|
||||||
go.etcd.io/bbolt v1.3.6 // indirect
|
|
||||||
go.opentelemetry.io/otel v1.8.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/trace v1.8.0 // indirect
|
|
||||||
golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d // indirect
|
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
|
||||||
golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -125,6 +84,7 @@ require (
|
|||||||
github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
|
github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
|
||||||
github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect
|
github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.15 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||||
github.com/mroth/weightedrand v0.4.1 // indirect
|
github.com/mroth/weightedrand v0.4.1 // indirect
|
||||||
|
|||||||
@@ -38,11 +38,6 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
|
|||||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
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.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||||
crawshaw.io/iox v0.0.0-20181124134642-c51c3df30797 h1:yDf7ARQc637HoxDho7xjqdvO5ZA2Yb+xzv/fOnnvZzw=
|
|
||||||
crawshaw.io/iox v0.0.0-20181124134642-c51c3df30797/go.mod h1:sXBiorCo8c46JlQV3oXPKINnZ8mcqnye1EkVkqsectk=
|
|
||||||
crawshaw.io/sqlite v0.3.2/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4=
|
|
||||||
crawshaw.io/sqlite v0.3.3-0.20210127221821-98b1f83c5508 h1:fILCBBFnjnrQ0whVJlGhfv1E/QiaFDNtGFBObEVRnYg=
|
|
||||||
crawshaw.io/sqlite v0.3.3-0.20210127221821-98b1f83c5508/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4=
|
|
||||||
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
|
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/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/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
|
||||||
@@ -75,23 +70,12 @@ github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63n
|
|||||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
|
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/Psiphon-Inc/rotate-safe-writer v0.0.0-20210303140923-464a7a37606e h1:NPfqIbzmijrl0VclX2t8eO5EPBhqe47LLGKpRrcVjXk=
|
github.com/Psiphon-Inc/rotate-safe-writer v0.0.0-20210303140923-464a7a37606e h1:NPfqIbzmijrl0VclX2t8eO5EPBhqe47LLGKpRrcVjXk=
|
||||||
github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w=
|
|
||||||
github.com/RoaringBitmap/roaring v0.4.17/go.mod h1:D3qVegWTmfCaX4Bl5CrBE9hfrSrrXIr8KVNvRsDi1NI=
|
|
||||||
github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo=
|
|
||||||
github.com/RoaringBitmap/roaring v1.2.1 h1:58/LJlg/81wfEHd5L9qsHduznOIhyv4qb1yWcSvVq9A=
|
|
||||||
github.com/RoaringBitmap/roaring v1.2.1/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA=
|
|
||||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||||
github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0 h1:byYvvbfSo3+9efR4IeReh77gVs4PnNDR3AMOE9NJ7a0=
|
|
||||||
github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0/go.mod h1:q37NoqncT41qKc048STsifIt69LfUJ8SrWWcz/yam5k=
|
|
||||||
github.com/alecthomas/assert/v2 v2.0.0-alpha3 h1:pcHeMvQ3OMstAWgaeaXIAL8uzB9xMm2zlxt+/4ml8lk=
|
|
||||||
github.com/alecthomas/atomic v0.1.0-alpha2 h1:dqwXmax66gXvHhsOS4pGPZKqYOlTkapELkLb3MNdlH8=
|
|
||||||
github.com/alecthomas/atomic v0.1.0-alpha2/go.mod h1:zD6QGEyw49HIq19caJDc2NMXAy8rNi9ROrxtMXATfyI=
|
|
||||||
github.com/alecthomas/kingpin v2.2.6+incompatible h1:5svnBTFgJjZvGKyYBtMB0+m5wvrbUHiqye8wRJMlnYI=
|
github.com/alecthomas/kingpin v2.2.6+incompatible h1:5svnBTFgJjZvGKyYBtMB0+m5wvrbUHiqye8wRJMlnYI=
|
||||||
github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE=
|
github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE=
|
||||||
github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142 h1:8Uy0oSf5co/NZXje7U1z8Mpep++QJOldL2hs/sBQf48=
|
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
@@ -100,61 +84,6 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF
|
|||||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
|
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
|
||||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
||||||
github.com/anacrolix/chansync v0.3.0 h1:lRu9tbeuw3wl+PhMu/r+JJCRu5ArFXIluOgdF0ao6/U=
|
|
||||||
github.com/anacrolix/chansync v0.3.0/go.mod h1:DZsatdsdXxD0WiwcGl0nJVwyjCKMDv+knl1q2iBjA2k=
|
|
||||||
github.com/anacrolix/dht/v2 v2.19.1 h1:V/UUGBASGYqYkSnmHJwX8uQmzkyhbgwE6jqcHKnNTD8=
|
|
||||||
github.com/anacrolix/dht/v2 v2.19.1/go.mod h1:3TU93c1s/oA8I/VH4m3CNP/BeKsiOGmo6HwfZBMTKUs=
|
|
||||||
github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
|
|
||||||
github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
|
|
||||||
github.com/anacrolix/envpprof v1.1.0/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4=
|
|
||||||
github.com/anacrolix/envpprof v1.2.1 h1:25TJe6t/i0AfzzldiGFKCpD+s+dk8lONBcacJZB2rdE=
|
|
||||||
github.com/anacrolix/envpprof v1.2.1/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4=
|
|
||||||
github.com/anacrolix/generics v0.0.0-20220618083756-f99e35403a60 h1:k4/h2B1gGF+PJGyGHxs8nmHHt1pzWXZWBj6jn4OBlRc=
|
|
||||||
github.com/anacrolix/generics v0.0.0-20220618083756-f99e35403a60/go.mod h1:ff2rHB/joTV03aMSSn/AZNnaIpUw0h3njetGsaXcMy8=
|
|
||||||
github.com/anacrolix/go-libutp v1.2.0 h1:sjxoB+/ARiKUR7IK/6wLWyADIBqGmu1fm0xo+8Yy7u0=
|
|
||||||
github.com/anacrolix/go-libutp v1.2.0/go.mod h1:RrJ3KcaDcf9Jqp33YL5V/5CBEc6xMc7aJL8wXfuWL50=
|
|
||||||
github.com/anacrolix/log v0.3.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU=
|
|
||||||
github.com/anacrolix/log v0.6.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU=
|
|
||||||
github.com/anacrolix/log v0.10.0/go.mod h1:s5yBP/j046fm9odtUTbHOfDUq/zh1W8OkPpJtnX0oQI=
|
|
||||||
github.com/anacrolix/log v0.10.1-0.20220123034749-3920702c17f8/go.mod h1:GmnE2c0nvz8pOIPUSC9Rawgefy1sDXqposC2wgtBZE4=
|
|
||||||
github.com/anacrolix/log v0.13.2-0.20220711050817-613cb738ef30 h1:bAgFzUxN1K3U8KwOzqCOhiygOr5NqYO3kNlV9tvp2Rc=
|
|
||||||
github.com/anacrolix/log v0.13.2-0.20220711050817-613cb738ef30/go.mod h1:D4+CvN8SnruK6zIFS/xPoRJmtvtnxs+CSfDQ+BFxZ68=
|
|
||||||
github.com/anacrolix/lsan v0.0.0-20211126052245-807000409a62 h1:P04VG6Td13FHMgS5ZBcJX23NPC/fiC4cp9bXwYujdYM=
|
|
||||||
github.com/anacrolix/lsan v0.0.0-20211126052245-807000409a62/go.mod h1:66cFKPCO7Sl4vbFnAaSq7e4OXtdMhRSBagJGWgmpJbM=
|
|
||||||
github.com/anacrolix/missinggo v0.0.0-20180725070939-60ef2fbf63df/go.mod h1:kwGiTUTZ0+p4vAz3VbAI5a30t2YbvemcmspjKwrAz5s=
|
|
||||||
github.com/anacrolix/missinggo v1.1.0/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo=
|
|
||||||
github.com/anacrolix/missinggo v1.1.2-0.20190815015349-b888af804467/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo=
|
|
||||||
github.com/anacrolix/missinggo v1.2.1/go.mod h1:J5cMhif8jPmFoC3+Uvob3OXXNIhOUikzMt+uUjeM21Y=
|
|
||||||
github.com/anacrolix/missinggo v1.3.0 h1:06HlMsudotL7BAELRZs0yDZ4yVXsHXGi323QBjAVASw=
|
|
||||||
github.com/anacrolix/missinggo v1.3.0/go.mod h1:bqHm8cE8xr+15uVfMG3BFui/TxyB6//H5fwlq/TeqMc=
|
|
||||||
github.com/anacrolix/missinggo/perf v1.0.0 h1:7ZOGYziGEBytW49+KmYGTaNfnwUqP1HBsy6BqESAJVw=
|
|
||||||
github.com/anacrolix/missinggo/perf v1.0.0/go.mod h1:ljAFWkBuzkO12MQclXzZrosP5urunoLS0Cbvb4V0uMQ=
|
|
||||||
github.com/anacrolix/missinggo/v2 v2.2.0/go.mod h1:o0jgJoYOyaoYQ4E2ZMISVa9c88BbUBVQQW4QeRkNCGY=
|
|
||||||
github.com/anacrolix/missinggo/v2 v2.5.1/go.mod h1:WEjqh2rmKECd0t1VhQkLGTdIWXO6f6NLjp5GlMZ+6FA=
|
|
||||||
github.com/anacrolix/missinggo/v2 v2.5.2/go.mod h1:yNvsLrtZYRYCOI+KRH/JM8TodHjtIE/bjOGhQaLOWIE=
|
|
||||||
github.com/anacrolix/missinggo/v2 v2.7.0 h1:4fzOAAn/VCvfWGviLmh64MPMttrlYew81JdPO7nSHvI=
|
|
||||||
github.com/anacrolix/missinggo/v2 v2.7.0/go.mod h1:2IZIvmRTizALNYFYXsPR7ofXPzJgyBpKZ4kMqMEICkI=
|
|
||||||
github.com/anacrolix/mmsg v0.0.0-20180515031531-a4a3ba1fc8bb/go.mod h1:x2/ErsYUmT77kezS63+wzZp8E3byYB0gzirM/WMBLfw=
|
|
||||||
github.com/anacrolix/mmsg v1.0.0 h1:btC7YLjOn29aTUAExJiVUhQOuf/8rhm+/nWCMAnL3Hg=
|
|
||||||
github.com/anacrolix/mmsg v1.0.0/go.mod h1:x8kRaJY/dCrY9Al0PEcj1mb/uFHwP6GCJ9fLl4thEPc=
|
|
||||||
github.com/anacrolix/multiless v0.3.0 h1:5Bu0DZncjE4e06b9r1Ap2tUY4Au0NToBP5RpuEngSis=
|
|
||||||
github.com/anacrolix/multiless v0.3.0/go.mod h1:TrCLEZfIDbMVfLoQt5tOoiBS/uq4y8+ojuEVVvTNPX4=
|
|
||||||
github.com/anacrolix/stm v0.2.0/go.mod h1:zoVQRvSiGjGoTmbM0vSLIiaKjWtNPeTvXUSdJQA4hsg=
|
|
||||||
github.com/anacrolix/stm v0.4.0 h1:tOGvuFwaBjeu1u9X1eIh9TX8OEedEiEQ1se1FjhFnXY=
|
|
||||||
github.com/anacrolix/stm v0.4.0/go.mod h1:GCkwqWoAsP7RfLW+jw+Z0ovrt2OO7wRzcTtFYMYY5t8=
|
|
||||||
github.com/anacrolix/sync v0.0.0-20180808010631-44578de4e778/go.mod h1:s735Etp3joe/voe2sdaXLcqDdJSay1O0OPnM0ystjqk=
|
|
||||||
github.com/anacrolix/sync v0.3.0/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DCOj/I0g=
|
|
||||||
github.com/anacrolix/sync v0.4.0 h1:T+MdO/u87ir/ijWsTFsPYw5jVm0SMm4kVpg8t4KF38o=
|
|
||||||
github.com/anacrolix/sync v0.4.0/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DCOj/I0g=
|
|
||||||
github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw=
|
|
||||||
github.com/anacrolix/tagflag v1.0.0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw=
|
|
||||||
github.com/anacrolix/tagflag v1.1.0/go.mod h1:Scxs9CV10NQatSmbyjqmqmeQNwGzlNe0CMUMIxqHIG8=
|
|
||||||
github.com/anacrolix/torrent v1.47.0 h1:aDUnhQZ8+kfStLICHiXOGGYVFgDENK+kz4q96linyRg=
|
|
||||||
github.com/anacrolix/torrent v1.47.0/go.mod h1:SYPxEUjMwqhDr3kWGzyQLkFMuAb1bgJ57JRMpuD3ZzE=
|
|
||||||
github.com/anacrolix/upnp v0.1.3-0.20220123035249-922794e51c96 h1:QAVZ3pN/J4/UziniAhJR2OZ9Ox5kOY2053tBbbqUPYA=
|
|
||||||
github.com/anacrolix/upnp v0.1.3-0.20220123035249-922794e51c96/go.mod h1:Wa6n8cYIdaG35x15aH3Zy6d03f7P728QfdcDeD/IEOs=
|
|
||||||
github.com/anacrolix/utp v0.1.0 h1:FOpQOmIwYsnENnz7tAGohA+r6iXpRjrq8ssKSre2Cp4=
|
|
||||||
github.com/anacrolix/utp v0.1.0/go.mod h1:MDwc+vsGEq7RMw6lr2GKOEqjWny5hO5OZXRVNaBJ2Dk=
|
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||||
@@ -178,11 +107,6 @@ github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN
|
|||||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||||
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
|
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
|
||||||
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
|
||||||
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
|
||||||
github.com/benbjohnson/immutable v0.2.0/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI=
|
|
||||||
github.com/benbjohnson/immutable v0.3.0 h1:TVRhuZx2wG9SZ0LRdqlbs9S5BZ6Y24hJEHTCgWHZEIw=
|
|
||||||
github.com/benbjohnson/immutable v0.3.0/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI=
|
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
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.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
@@ -190,15 +114,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
|
|||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
github.com/bifurcation/mint v0.0.0-20180306135233-198357931e61 h1:BU+NxuoaYPIvvp8NNkNlLr8aA0utGyuunf4Q3LJ0bh0=
|
github.com/bifurcation/mint v0.0.0-20180306135233-198357931e61 h1:BU+NxuoaYPIvvp8NNkNlLr8aA0utGyuunf4Q3LJ0bh0=
|
||||||
github.com/bifurcation/mint v0.0.0-20180306135233-198357931e61/go.mod h1:zVt7zX3K/aDCk9Tj+VM7YymsX66ERvzCJzw8rFCX2JU=
|
github.com/bifurcation/mint v0.0.0-20180306135233-198357931e61/go.mod h1:zVt7zX3K/aDCk9Tj+VM7YymsX66ERvzCJzw8rFCX2JU=
|
||||||
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
|
||||||
github.com/bits-and-blooms/bitset v1.2.2 h1:J5gbX05GpMdBjCvQ9MteIg2KKDExr7DrgK+Yc15FvIk=
|
|
||||||
github.com/bits-and-blooms/bitset v1.2.2/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
|
||||||
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
|
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
|
||||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||||
github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
|
|
||||||
github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
|
|
||||||
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8=
|
|
||||||
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8/go.mod h1:spo1JLcs67NmW1aVLEgtA8Yy1elc+X8y5SRW1sFW4Og=
|
|
||||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||||
github.com/ccding/go-stun v0.1.5-0.20220419042218-44e89cab7805 h1:AkTX8U06UIH//16PyxKLOPjlGoqcTEYpjipeCNsASfQ=
|
github.com/ccding/go-stun v0.1.5-0.20220419042218-44e89cab7805 h1:AkTX8U06UIH//16PyxKLOPjlGoqcTEYpjipeCNsASfQ=
|
||||||
@@ -240,7 +157,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
|
|||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
|
||||||
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
|
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
|
||||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||||
github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw=
|
github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw=
|
||||||
@@ -263,20 +179,16 @@ github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu
|
|||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
|
||||||
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
|
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
|
||||||
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
|
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
|
||||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
|
||||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||||
github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=
|
|
||||||
github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
|
|
||||||
github.com/elazarl/goproxy v0.0.0-20200809112317-0581fc3aee2d h1:rtM8HsT3NG37YPjz8sYSbUSdElP9lUsQENYzJDZDUBE=
|
github.com/elazarl/goproxy v0.0.0-20200809112317-0581fc3aee2d h1:rtM8HsT3NG37YPjz8sYSbUSdElP9lUsQENYzJDZDUBE=
|
||||||
github.com/elazarl/goproxy/ext v0.0.0-20200809112317-0581fc3aee2d h1:st1tmvy+4duoRj+RaeeJoECWCWM015fBtf/4aR+hhqk=
|
github.com/elazarl/goproxy/ext v0.0.0-20200809112317-0581fc3aee2d h1:st1tmvy+4duoRj+RaeeJoECWCWM015fBtf/4aR+hhqk=
|
||||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||||
@@ -297,20 +209,12 @@ github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwU
|
|||||||
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
||||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
||||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||||
github.com/frankban/quicktest v1.9.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
|
|
||||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||||
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
|
||||||
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
|
||||||
github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
|
||||||
github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
|
||||||
github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
|
||||||
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
|
||||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
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 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-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
@@ -326,11 +230,6 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
|||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
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-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
|
||||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
|
||||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
|
||||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
@@ -394,14 +293,10 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
|
|||||||
github.com/golang/protobuf v1.5.3-0.20210916003710-5d5e8c018a13 h1:yztvEbaW/qZGubeP7+Lug7PXl7NBfilUK6mw3jq25gQ=
|
github.com/golang/protobuf v1.5.3-0.20210916003710-5d5e8c018a13 h1:yztvEbaW/qZGubeP7+Lug7PXl7NBfilUK6mw3jq25gQ=
|
||||||
github.com/golang/protobuf v1.5.3-0.20210916003710-5d5e8c018a13/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.3-0.20210916003710-5d5e8c018a13/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
|
||||||
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
|
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
|
||||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
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.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
|
||||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.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.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.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
@@ -448,11 +343,8 @@ github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk
|
|||||||
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
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.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190309154008-847fc94819f9/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
|
||||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
@@ -499,11 +391,7 @@ github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSo
|
|||||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
||||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
|
|
||||||
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
|
|
||||||
github.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
|
||||||
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
|
|
||||||
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||||
github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0=
|
github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0=
|
||||||
@@ -564,13 +452,11 @@ github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX
|
|||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
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.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
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.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/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
|
||||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=
|
github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=
|
||||||
@@ -604,15 +490,13 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
|||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
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.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|
||||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
@@ -623,8 +507,6 @@ github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
|
|||||||
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||||
github.com/lispad/go-generics-tools v1.1.0 h1:mbSgcxdFVmpoyso1X/MJHXbSbSL3dD+qhRryyxk+/XY=
|
|
||||||
github.com/lispad/go-generics-tools v1.1.0/go.mod h1:2csd1EJljo/gy5qG4khXol7ivCPptNjG5Uv2X8MgK84=
|
|
||||||
github.com/lucas-clemente/quic-go v0.28.1 h1:Uo0lvVxWg5la9gflIF9lwa39ONq85Xq2D91YNEIslzU=
|
github.com/lucas-clemente/quic-go v0.28.1 h1:Uo0lvVxWg5la9gflIF9lwa39ONq85Xq2D91YNEIslzU=
|
||||||
github.com/lucas-clemente/quic-go v0.28.1/go.mod h1:oGz5DKK41cJt5+773+BSO9BXDsREY4HLf7+0odGAPO0=
|
github.com/lucas-clemente/quic-go v0.28.1/go.mod h1:oGz5DKK41cJt5+773+BSO9BXDsREY4HLf7+0odGAPO0=
|
||||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||||
@@ -671,8 +553,9 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp
|
|||||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.14 h1:qZgc/Rwetq+MtyE18WhzjokPD93dNqLGNT3QJuLvBGw=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
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/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/mdlayher/netlink v1.4.2-0.20210930205308-a81a8c23d40a h1:yk5OmRew64lWdeNanQ3l0hDgUt1E8MfipPhh/GO9Tuw=
|
github.com/mdlayher/netlink v1.4.2-0.20210930205308-a81a8c23d40a h1:yk5OmRew64lWdeNanQ3l0hDgUt1E8MfipPhh/GO9Tuw=
|
||||||
@@ -709,9 +592,6 @@ github.com/montanaflynn/stats v0.6.6 h1:Duep6KMIDpY4Yo11iFsvyqJDyfzLF9+sndUKT+v6
|
|||||||
github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||||
github.com/mroth/weightedrand v0.4.1 h1:rHcbUBopmi/3x4nnrvwGJBhX9d0vk+KgoLUZeDP6YyI=
|
github.com/mroth/weightedrand v0.4.1 h1:rHcbUBopmi/3x4nnrvwGJBhX9d0vk+KgoLUZeDP6YyI=
|
||||||
github.com/mroth/weightedrand v0.4.1/go.mod h1:3p2SIcC8al1YMzGhAIoXD+r9olo/g/cdJgAD905gyNE=
|
github.com/mroth/weightedrand v0.4.1/go.mod h1:3p2SIcC8al1YMzGhAIoXD+r9olo/g/cdJgAD905gyNE=
|
||||||
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
|
|
||||||
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
|
|
||||||
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
|
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
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/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
|
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
|
||||||
@@ -782,7 +662,6 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9
|
|||||||
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
||||||
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
|
||||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||||
github.com/pion/datachannel v1.5.2 h1:piB93s8LGmbECrpO84DnkIVWasRMk3IimbcXkTQLE6E=
|
github.com/pion/datachannel v1.5.2 h1:piB93s8LGmbECrpO84DnkIVWasRMk3IimbcXkTQLE6E=
|
||||||
@@ -848,7 +727,6 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
|
|||||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||||
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
||||||
github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
|
||||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||||
github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU=
|
github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU=
|
||||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||||
@@ -866,7 +744,6 @@ github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7q
|
|||||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
|
||||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||||
github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||||
@@ -878,7 +755,6 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
|
|||||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||||
github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
|
||||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
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.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
@@ -895,12 +771,9 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
|
|||||||
github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
|
||||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417 h1:Lt9DzQALzHoDwMBGJ6v8ObDPR0dzr2a6sXTB1Fq7IHs=
|
|
||||||
github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417/go.mod h1:qe5TWALJ8/a1Lqznoc5BDHpYX/8HU60Hm2AwRmqzxqA=
|
|
||||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||||
@@ -911,8 +784,6 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
|
|||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 h1:7YvPJVmEeFHR1Tj9sZEYsmarJEQfMVYpd/Vyy/A8dqE=
|
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 h1:7YvPJVmEeFHR1Tj9sZEYsmarJEQfMVYpd/Vyy/A8dqE=
|
||||||
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8=
|
|
||||||
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
|
|
||||||
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||||
@@ -955,12 +826,9 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
|
|||||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
|
||||||
github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8=
|
github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8=
|
||||||
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
||||||
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
|
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
|
||||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
|
||||||
github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff/go.mod h1:KSQcGKpxUMHk3nbYzs/tIBAM2iDooCn0BmttHOJEbLs=
|
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
|
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
|
||||||
github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs=
|
github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs=
|
||||||
@@ -993,7 +861,6 @@ github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5J
|
|||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
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/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
@@ -1012,11 +879,6 @@ github.com/templexxx/cpu v0.0.9 h1:cGGLK8twbc1J1S/fHnZW7BylXYaFP+0fR2s+nzsFDiU=
|
|||||||
github.com/templexxx/cpu v0.0.9/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
|
github.com/templexxx/cpu v0.0.9/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
|
||||||
github.com/templexxx/xorsimd v0.4.1 h1:iUZcywbOYDRAZUasAs2eSCUW8eobuZDy0I9FJiORkVg=
|
github.com/templexxx/xorsimd v0.4.1 h1:iUZcywbOYDRAZUasAs2eSCUW8eobuZDy0I9FJiORkVg=
|
||||||
github.com/templexxx/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo=
|
github.com/templexxx/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo=
|
||||||
github.com/tidwall/btree v1.3.1 h1:636+tdVDs8Hjcf35Di260W2xCW4KuoXOKyk9QWOvCpA=
|
|
||||||
github.com/tidwall/btree v1.3.1/go.mod h1:LGm8L/DZjPLmeWGjv5kFrY8dL4uVhMmzmmLYmsObdKE=
|
|
||||||
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
|
||||||
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
|
||||||
github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
|
||||||
github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
|
github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
|
||||||
github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk=
|
github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk=
|
||||||
github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk=
|
github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk=
|
||||||
@@ -1038,8 +900,6 @@ github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49u
|
|||||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||||
github.com/wader/filtertransport v0.0.0-20200316221534-bdd9e61eee78 h1:9sreu9e9KOihf2Y0NbpyfWhd1XFDcL4GTkPYL4IvMrg=
|
github.com/wader/filtertransport v0.0.0-20200316221534-bdd9e61eee78 h1:9sreu9e9KOihf2Y0NbpyfWhd1XFDcL4GTkPYL4IvMrg=
|
||||||
github.com/wader/filtertransport v0.0.0-20200316221534-bdd9e61eee78/go.mod h1:HazXTRLhXFyq80TQp7PUXi6BKE6mS+ydEdzEqNBKopQ=
|
github.com/wader/filtertransport v0.0.0-20200316221534-bdd9e61eee78/go.mod h1:HazXTRLhXFyq80TQp7PUXi6BKE6mS+ydEdzEqNBKopQ=
|
||||||
github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
|
||||||
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
github.com/xtaci/kcp-go/v5 v5.6.1 h1:Pwn0aoeNSPF9dTS7IgiPXn0HEtaIlVb6y5UKWPsx8bI=
|
github.com/xtaci/kcp-go/v5 v5.6.1 h1:Pwn0aoeNSPF9dTS7IgiPXn0HEtaIlVb6y5UKWPsx8bI=
|
||||||
@@ -1072,8 +932,6 @@ gitlab.com/yawning/utls.git v0.0.12-1 h1:RL6O0MP2YI0KghuEU/uGN6+8b4183eqNWoYgx7C
|
|||||||
gitlab.com/yawning/utls.git v0.0.12-1/go.mod h1:3ONKiSFR9Im/c3t5RKmMJTVdmZN496FNyk3mjrY1dyo=
|
gitlab.com/yawning/utls.git v0.0.12-1/go.mod h1:3ONKiSFR9Im/c3t5RKmMJTVdmZN496FNyk3mjrY1dyo=
|
||||||
gitlab.torproject.org/tpo/anti-censorship/geoip v0.0.0-20210928150955-7ce4b3d98d01/go.mod h1:K3LOI4H8fa6j+7E10ViHeGEQV10304FG4j94ypmKLjY=
|
gitlab.torproject.org/tpo/anti-censorship/geoip v0.0.0-20210928150955-7ce4b3d98d01/go.mod h1:K3LOI4H8fa6j+7E10ViHeGEQV10304FG4j94ypmKLjY=
|
||||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
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.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||||
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||||
@@ -1088,10 +946,6 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
|||||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||||
go.opentelemetry.io/otel v1.8.0 h1:zcvBFizPbpa1q7FehvFiHbQwGzmPILebO0tyqIR5Djg=
|
|
||||||
go.opentelemetry.io/otel v1.8.0/go.mod h1:2pkj+iMj0o03Y+cW6/m8Y4WkRdYN3AvCXCnzRMp9yvM=
|
|
||||||
go.opentelemetry.io/otel/trace v1.8.0 h1:cSy0DF9eGI5WIfNwZ1q2iUyGj00tGzP24dE1lOlHrfY=
|
|
||||||
go.opentelemetry.io/otel/trace v1.8.0/go.mod h1:0Bt3PXY8w+3pheS3hQUt+wow8b1ojPaTBoTCh2zIFI4=
|
|
||||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
@@ -1154,8 +1008,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
|||||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
golang.org/x/exp v0.0.0-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-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-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||||
golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d h1:vtUKgx8dahOomfFzLREU8nSv25YHnTgLBn4rDnWZdU0=
|
|
||||||
golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
|
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
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/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-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
@@ -1184,7 +1036,6 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/net v0.0.0-20180524181706-dfa909b99c79/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@@ -1278,7 +1129,6 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@@ -1323,7 +1173,6 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/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-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-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/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-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-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -1334,7 +1183,6 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -1388,8 +1236,6 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb
|
|||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/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-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-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U=
|
|
||||||
golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
@@ -1543,8 +1389,8 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D
|
|||||||
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||||
|
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0=
|
||||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||||
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 h1:b9mVrqYfq3P4bCdaLg1qtBnPzUYgglsIdjZkL/fQVOE=
|
|
||||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
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.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.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
@@ -1572,8 +1418,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
|
|||||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||||
|
google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
|
||||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||||
google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ=
|
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
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-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
|||||||
@@ -1,210 +0,0 @@
|
|||||||
// Package tlsproxy contains a censoring TLS proxy. Most traffic is passed
|
|
||||||
// through using the SNI to choose the hostname to connect to. Specific offending
|
|
||||||
// SNIs are censored by returning a TLS alert to the client.
|
|
||||||
package tlsproxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/apex/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Dialer establishes network connections
|
|
||||||
type Dialer interface {
|
|
||||||
DialContext(ctx context.Context, network, address string) (net.Conn, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CensoringProxy is a censoring TLS proxy
|
|
||||||
type CensoringProxy struct {
|
|
||||||
keywords []string
|
|
||||||
dial func(network, address string) (net.Conn, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCensoringProxy creates a new CensoringProxy instance using
|
|
||||||
// the specified list of keywords to censor. keywords is the list
|
|
||||||
// of keywords that trigger censorship if any of them appears in
|
|
||||||
// the SNII record of a ClientHello. dnsNetwork and dnsAddress are
|
|
||||||
// settings to configure the upstream, non censored DNS.
|
|
||||||
func NewCensoringProxy(
|
|
||||||
keywords []string, to string, uncensored Dialer,
|
|
||||||
) *CensoringProxy {
|
|
||||||
return &CensoringProxy{
|
|
||||||
keywords: keywords,
|
|
||||||
to: to,
|
|
||||||
dial: func(network, address string) (net.Conn, error) {
|
|
||||||
return uncensored.DialContext(context.Background(), network, address)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handshakeReader is a hack to perform the initial part of the
|
|
||||||
// TLS handshake so to know the SNI and then replay the bytes of
|
|
||||||
// this initial part of the handshake with the server.
|
|
||||||
//type handshakeReader struct {
|
|
||||||
// net.Conn
|
|
||||||
// incoming []byte
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//// Read saves the initial bytes of the handshake such that later
|
|
||||||
//// we can replay the handshake with the real TLS server.
|
|
||||||
//func (c *handshakeReader) Read(b []byte) (int, error) {
|
|
||||||
// count, err := c.Conn.Read(b)
|
|
||||||
// if err == nil {
|
|
||||||
// c.incoming = append(c.incoming, b[:count]...)
|
|
||||||
// }
|
|
||||||
// return count, err
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//// Write prevents writing on the real connection
|
|
||||||
//func (c *handshakeReader) Write(b []byte) (int, error) {
|
|
||||||
// return 0, errors.New("cannot write on this connection")
|
|
||||||
//}
|
|
||||||
type censoredReader struct {
|
|
||||||
net.Conn
|
|
||||||
outgoing []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// forward forwards left traffic to right
|
|
||||||
func forward(wg *sync.WaitGroup, left, right net.Conn) {
|
|
||||||
data := make([]byte, 1<<18)
|
|
||||||
for {
|
|
||||||
n, err := left.Read(data)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if _, err = right.Write(data[:n]); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wg.Done()
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset closes the connection with a RST segment
|
|
||||||
func reset(conn net.Conn) {
|
|
||||||
if tc, ok := conn.(*net.TCPConn); ok {
|
|
||||||
tc.SetLinger(0)
|
|
||||||
}
|
|
||||||
conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// alertclose sends a TLS alert and then closes the connection
|
|
||||||
func alertclose(conn net.Conn) {
|
|
||||||
alertdata := []byte{
|
|
||||||
21, // alert
|
|
||||||
3, // version[0]
|
|
||||||
3, // version[1]
|
|
||||||
0, // length[0]
|
|
||||||
2, // length[1]
|
|
||||||
2, // fatal
|
|
||||||
80, // internal error
|
|
||||||
}
|
|
||||||
conn.Write(alertdata)
|
|
||||||
conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// getsni attempts the handshakeReader hack to obtain the SNI by reading
|
|
||||||
// the beginning of the TLS handshake. On success a nonempty SNI string
|
|
||||||
// is returned. Otherwise we cannot distinguish between the absence of a
|
|
||||||
// SNI and any other reading network error that may have occurred.
|
|
||||||
func getsni(conn *handshakeReader) string {
|
|
||||||
var (
|
|
||||||
sni string
|
|
||||||
mutex sync.Mutex // just for safety
|
|
||||||
)
|
|
||||||
tls.Server(conn, &tls.Config{
|
|
||||||
GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
|
||||||
mutex.Lock()
|
|
||||||
sni = info.ServerName
|
|
||||||
mutex.Unlock()
|
|
||||||
return nil, errors.New("tlsproxy: we can't really continue handshake")
|
|
||||||
},
|
|
||||||
}).Handshake()
|
|
||||||
return sni
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *CensoringProxy) connectingToMyself(conn net.Conn) bool {
|
|
||||||
local := conn.LocalAddr().String()
|
|
||||||
localAddr, _, localErr := net.SplitHostPort(local)
|
|
||||||
remote := conn.RemoteAddr().String()
|
|
||||||
remoteAddr, _, remoteErr := net.SplitHostPort(remote)
|
|
||||||
return localErr != nil || remoteErr != nil || localAddr == remoteAddr
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle implements the TLS SNI proxy
|
|
||||||
func (p *CensoringProxy) handle(clientconn net.Conn, to string) {
|
|
||||||
lr := &ougGoingReader{Conn: clientconn }
|
|
||||||
line := readline(lr)
|
|
||||||
//hr := &handshakeReader{Conn: clientconn}
|
|
||||||
//sni := getsni(hr)
|
|
||||||
// if sni == "" {
|
|
||||||
// log.Warn("tlsproxy: network failure or SNI not provided")
|
|
||||||
// reset(clientconn)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// TODO
|
|
||||||
for _, pattern := range p.keywords {
|
|
||||||
if strings.Contains(line, pattern) {
|
|
||||||
log.Warnf("tlsproxy: reject SNI by policy: %s", sni)
|
|
||||||
alertclose(clientconn)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
serverconn, err := p.dial("tcp", to)
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Warn("tlsproxy: p.dial failed")
|
|
||||||
alertclose(clientconn)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if p.connectingToMyself(serverconn) {
|
|
||||||
log.Warn("tlsproxy: connecting to myself")
|
|
||||||
alertclose(clientconn)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if _, err := serverconn.Write(hr.incoming); err != nil {
|
|
||||||
log.WithError(err).Warn("tlsproxy: serverconn.Write failed")
|
|
||||||
alertclose(clientconn)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Debugf("tlsproxy: routing for %s", sni)
|
|
||||||
defer clientconn.Close()
|
|
||||||
defer serverconn.Close()
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(2)
|
|
||||||
go forward(&wg, clientconn, serverconn)
|
|
||||||
go forward(&wg, serverconn, clientconn)
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *CensoringProxy) run(listener net.Listener, outboundPort string) {
|
|
||||||
for {
|
|
||||||
conn, err := listener.Accept()
|
|
||||||
if err != nil && strings.Contains(
|
|
||||||
err.Error(), "use of closed network connection") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
// It's difficult to make accept fail, so restructure
|
|
||||||
// the code such that we enter into the happy path
|
|
||||||
go p.handle(conn, outboundPort)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start starts the censoring proxy.
|
|
||||||
func (p *CensoringProxy) Start(address string, to string) (net.Listener, error) {
|
|
||||||
_, port, err := net.SplitHostPort(address)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
listener, err := net.Listen("tcp", address)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
go p.run(listener, to)
|
|
||||||
return listener, nil
|
|
||||||
}
|
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
package tlsproxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/cmd/jafar/uncensored"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPass(t *testing.T) {
|
|
||||||
listener := newproxy(t, "ooni.io")
|
|
||||||
checkdialtls(t, listener.Addr().String(), true, &tls.Config{
|
|
||||||
ServerName: "example.com",
|
|
||||||
})
|
|
||||||
killproxy(t, listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBlock(t *testing.T) {
|
|
||||||
listener := newproxy(t, "ooni.io")
|
|
||||||
checkdialtls(t, listener.Addr().String(), false, &tls.Config{
|
|
||||||
ServerName: "api.ooni.io",
|
|
||||||
})
|
|
||||||
killproxy(t, listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNoSNI(t *testing.T) {
|
|
||||||
listener := newproxy(t, "ooni.io")
|
|
||||||
checkdialtls(t, listener.Addr().String(), false, &tls.Config{
|
|
||||||
ServerName: "",
|
|
||||||
})
|
|
||||||
killproxy(t, listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInvalidDomain(t *testing.T) {
|
|
||||||
listener := newproxy(t, "ooni.io")
|
|
||||||
checkdialtls(t, listener.Addr().String(), false, &tls.Config{
|
|
||||||
ServerName: "antani.local",
|
|
||||||
})
|
|
||||||
killproxy(t, listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFailHandshake(t *testing.T) {
|
|
||||||
listener := newproxy(t, "ooni.io")
|
|
||||||
checkdialtls(t, listener.Addr().String(), false, &tls.Config{
|
|
||||||
ServerName: "expired.badssl.com",
|
|
||||||
})
|
|
||||||
killproxy(t, listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFailConnectingToSelf(t *testing.T) {
|
|
||||||
proxy := &CensoringProxy{
|
|
||||||
dial: func(network string, address string) (net.Conn, error) {
|
|
||||||
return &mockedConnWriteError{}, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
listener, err := proxy.Start("127.0.0.1:0")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if listener == nil {
|
|
||||||
t.Fatal("expected non nil listener here")
|
|
||||||
}
|
|
||||||
checkdialtls(t, listener.Addr().String(), false, &tls.Config{
|
|
||||||
ServerName: "www.google.com",
|
|
||||||
})
|
|
||||||
killproxy(t, listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFailWriteAfterConnect(t *testing.T) {
|
|
||||||
proxy := &CensoringProxy{
|
|
||||||
dial: func(network string, address string) (net.Conn, error) {
|
|
||||||
return &mockedConnWriteError{
|
|
||||||
// must be different or it refuses connecting to self
|
|
||||||
localIP: net.IPv4(127, 0, 0, 1),
|
|
||||||
remoteIP: net.IPv4(127, 0, 0, 2),
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
listener, err := proxy.Start("127.0.0.1:0")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if listener == nil {
|
|
||||||
t.Fatal("expected non nil listener here")
|
|
||||||
}
|
|
||||||
checkdialtls(t, listener.Addr().String(), false, &tls.Config{
|
|
||||||
ServerName: "www.google.com",
|
|
||||||
})
|
|
||||||
killproxy(t, listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestListenError(t *testing.T) {
|
|
||||||
proxy := NewCensoringProxy(
|
|
||||||
[]string{""}, uncensored.NewClient("https://1.1.1.1/dns-query"),
|
|
||||||
)
|
|
||||||
listener, err := proxy.Start("8.8.8.8:80")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected an error here")
|
|
||||||
}
|
|
||||||
if listener != nil {
|
|
||||||
t.Fatal("expected nil listener here")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newproxy(t *testing.T, blocked string) net.Listener {
|
|
||||||
proxy := NewCensoringProxy(
|
|
||||||
[]string{blocked}, uncensored.NewClient("https://1.1.1.1/dns-query"),
|
|
||||||
)
|
|
||||||
listener, err := proxy.Start("127.0.0.1:0")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
return listener
|
|
||||||
}
|
|
||||||
|
|
||||||
func killproxy(t *testing.T, listener net.Listener) {
|
|
||||||
err := listener.Close()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkdialtls(
|
|
||||||
t *testing.T, proxyAddr string, expectSuccess bool, config *tls.Config,
|
|
||||||
) {
|
|
||||||
conn, err := tls.Dial("tcp", proxyAddr, config)
|
|
||||||
if err != nil && expectSuccess {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err == nil && !expectSuccess {
|
|
||||||
t.Fatal("expected failure here")
|
|
||||||
}
|
|
||||||
if conn == nil && expectSuccess {
|
|
||||||
t.Fatal("expected actionable conn")
|
|
||||||
}
|
|
||||||
if conn != nil && !expectSuccess {
|
|
||||||
t.Fatal("expected nil conn")
|
|
||||||
}
|
|
||||||
if conn != nil {
|
|
||||||
conn.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type mockedConnWriteError struct {
|
|
||||||
net.Conn
|
|
||||||
localIP net.IP
|
|
||||||
remoteIP net.IP
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *mockedConnWriteError) Write(b []byte) (int, error) {
|
|
||||||
return 0, errors.New("cannot write sorry")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *mockedConnWriteError) LocalAddr() net.Addr {
|
|
||||||
return &net.TCPAddr{
|
|
||||||
IP: c.localIP,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *mockedConnWriteError) RemoteAddr() net.Addr {
|
|
||||||
return &net.TCPAddr{
|
|
||||||
IP: c.remoteIP,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestForwardWriteError(t *testing.T) {
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(1)
|
|
||||||
forward(&wg, &mockedConnReadOkay{}, &mockedConnWriteError{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type mockedConnReadOkay struct {
|
|
||||||
net.Conn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *mockedConnReadOkay) Read(b []byte) (int, error) {
|
|
||||||
return len(b), nil
|
|
||||||
}
|
|
||||||
@@ -1,240 +0,0 @@
|
|||||||
package bittorrent
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/anacrolix/torrent"
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/experiment/urlgetter"
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/measurexlite"
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/model"
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/tracex"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// errNoInputProvided indicates no input was passed
|
|
||||||
errNoInputProvided = errors.New("no input provided")
|
|
||||||
|
|
||||||
// errInputIsNotAnURL indicates that input is not an URL
|
|
||||||
errInputIsNotAnURL = errors.New("input is not an URL")
|
|
||||||
|
|
||||||
// errInvalidScheme indicates that the scheme is invalid
|
|
||||||
// golint is stupid and does not let us end erorr with ":"
|
|
||||||
errInvalidScheme = errors.New("scheme must be magnet")
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
testName = "bittorrent"
|
|
||||||
testVersion = "0.0.1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Config contains the experiment config.
|
|
||||||
type Config struct{}
|
|
||||||
|
|
||||||
type runtimeConfig struct {
|
|
||||||
magnet string
|
|
||||||
}
|
|
||||||
|
|
||||||
func config(input model.MeasurementTarget) (*runtimeConfig, error) {
|
|
||||||
if input == "" {
|
|
||||||
return nil, errNoInputProvided
|
|
||||||
}
|
|
||||||
|
|
||||||
parsed, err := url.Parse(string(input))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("%w: %s", errInputIsNotAnURL, err.Error())
|
|
||||||
}
|
|
||||||
if parsed.Scheme != "magnet" {
|
|
||||||
return nil, errInvalidScheme
|
|
||||||
}
|
|
||||||
|
|
||||||
validConfig := runtimeConfig{
|
|
||||||
magnet: string(input),
|
|
||||||
}
|
|
||||||
|
|
||||||
return &validConfig, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestKeys contains the experiment results
|
|
||||||
type TestKeys struct {
|
|
||||||
// DNS queries when resolving trackers
|
|
||||||
Queries []*model.ArchivalDNSLookupResult `json:"queries"`
|
|
||||||
// Indicates any kind of failure
|
|
||||||
Failure string `json:"failure"`
|
|
||||||
// The total number of peers contacted about the requested magnet
|
|
||||||
PeersNum int `json:"peers_num"`
|
|
||||||
// The complete list of peers contacted
|
|
||||||
Peers []string `json:"peers"`
|
|
||||||
// The total number of bytes received by the client
|
|
||||||
TotalBytesRead int64 `json:"total_bytes_received"`
|
|
||||||
// The total number of bad pieces (failed verification) received by the client
|
|
||||||
TotalBadPieces int64 `json:"total_bad_pieces"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tk *TestKeys) failure(err error) {
|
|
||||||
tk.Failure = *tracex.NewFailure(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Measurer performs the measurement.
|
|
||||||
type Measurer struct {
|
|
||||||
// Config contains the experiment settings. If empty we
|
|
||||||
// will be using default settings.
|
|
||||||
Config Config
|
|
||||||
|
|
||||||
// Getter is an optional getter to be used for testing.
|
|
||||||
Getter urlgetter.MultiGetter
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExperimentName implements ExperimentMeasurer.ExperimentName
|
|
||||||
func (m Measurer) ExperimentName() string {
|
|
||||||
return testName
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExperimentVersion implements ExperimentMeasurer.ExperimentVersion
|
|
||||||
func (m Measurer) ExperimentVersion() string {
|
|
||||||
return testVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
func torrentStats(torrent *torrent.Torrent, client *torrent.Client, tk *TestKeys) {
|
|
||||||
stats := torrent.Stats()
|
|
||||||
tk.PeersNum = len(tk.Peers)
|
|
||||||
tk.TotalBytesRead = stats.ConnStats.BytesRead.Int64()
|
|
||||||
tk.TotalBadPieces = stats.ConnStats.PiecesDirtiedBad.Int64()
|
|
||||||
}
|
|
||||||
|
|
||||||
func timeoutStats(torrent *torrent.Torrent, client *torrent.Client, tk *TestKeys) {
|
|
||||||
torrentStats(torrent, client, tk)
|
|
||||||
tk.Failure = "download_timeout"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run implements ExperimentMeasurer.Run
|
|
||||||
func (m Measurer) Run(ctx context.Context, args *model.ExperimentArgs) error {
|
|
||||||
//ctx context.Context, sess model.ExperimentSession,
|
|
||||||
//measurement *model.Measurement, callbacks model.ExperimentCallbacks,
|
|
||||||
//) error {
|
|
||||||
sess := args.Session
|
|
||||||
measurement := args.Measurement
|
|
||||||
log := sess.Logger()
|
|
||||||
trace := measurexlite.NewTrace(0, measurement.MeasurementStartTimeSaved)
|
|
||||||
resolver := trace.NewStdlibResolver(log)
|
|
||||||
|
|
||||||
config, err := config(measurement.Input)
|
|
||||||
if err != nil {
|
|
||||||
// Invalid input data, we don't even generate report
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tk := new(TestKeys)
|
|
||||||
measurement.TestKeys = tk
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(ctx, 120*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
tmpdir, err := os.MkdirTemp("", "ooni")
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf(*tracex.NewFailure(err))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
log.Infof("Using temporary directory %s", tmpdir)
|
|
||||||
defer os.RemoveAll(tmpdir)
|
|
||||||
|
|
||||||
conf := torrent.NewDefaultClientConfig()
|
|
||||||
conf.DataDir = tmpdir
|
|
||||||
conf.NoUpload = true
|
|
||||||
|
|
||||||
// Lookup tracker IPs via ooni utils
|
|
||||||
conf.LookupTrackerIp = func(u *url.URL) ([]net.IP, error) {
|
|
||||||
|
|
||||||
log.Infof("Resolving DNS for %s", u.Hostname())
|
|
||||||
resolvedAddrs, err := resolver.LookupHost(ctx, u.Hostname())
|
|
||||||
addrs := []net.IP{}
|
|
||||||
if err != nil {
|
|
||||||
return addrs, nil
|
|
||||||
}
|
|
||||||
log.Infof("Finished DNS for %s: %v", u.Hostname(), resolvedAddrs)
|
|
||||||
for _, addr := range resolvedAddrs {
|
|
||||||
addrs = append(addrs, net.ParseIP(addr))
|
|
||||||
}
|
|
||||||
tk.Queries = append(tk.Queries, trace.DNSLookupsFromRoundTrip()...)
|
|
||||||
return addrs, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// We want to test Bittorrent connectivity, not HTTPS/websockets
|
|
||||||
conf.DisableWebtorrent = true
|
|
||||||
conf.DisableWebseeds = true
|
|
||||||
|
|
||||||
// Register new peers to the test keys
|
|
||||||
clientCallbacks := new(torrent.Callbacks)
|
|
||||||
clientCallbacks.NewPeer = append(clientCallbacks.NewPeer,
|
|
||||||
func(peer *torrent.Peer) {
|
|
||||||
log.Debugf("Found new peer: %s", peer.RemoteAddr.String())
|
|
||||||
tk.Peers = append(tk.Peers, peer.RemoteAddr.String())
|
|
||||||
},
|
|
||||||
)
|
|
||||||
conf.Callbacks = *clientCallbacks
|
|
||||||
|
|
||||||
client, err := torrent.NewClient(conf)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf(*tracex.NewFailure(err))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
defer client.Close()
|
|
||||||
|
|
||||||
torrent, err := client.AddMagnet(config.magnet)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf(*tracex.NewFailure(err))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
tk.Failure = "metainfo_timeout"
|
|
||||||
return nil
|
|
||||||
case <-torrent.GotInfo():
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent.DownloadAll()
|
|
||||||
|
|
||||||
// Setup a new chan to know when the torrent is finished... allows to apply timeout
|
|
||||||
finished := make(chan bool)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
client.WaitAll()
|
|
||||||
finished <- true
|
|
||||||
}()
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
timeoutStats(torrent, client, tk)
|
|
||||||
case <-finished:
|
|
||||||
torrentStats(torrent, client, tk)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewExperimentMeasurer creates a new ExperimentMeasurer.
|
|
||||||
func NewExperimentMeasurer(config Config) model.ExperimentMeasurer {
|
|
||||||
return &Measurer{Config: config}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SummaryKeys contains summary keys for this experiment.
|
|
||||||
//
|
|
||||||
// Note that this structure is part of the ABI contract with ooniprobe
|
|
||||||
// therefore we should be careful when changing it.
|
|
||||||
type SummaryKeys struct {
|
|
||||||
IsAnomaly bool `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSummaryKeys implements model.ExperimentMeasurer.GetSummaryKeys.
|
|
||||||
func (m Measurer) GetSummaryKeys(measurement *model.Measurement) (interface{}, error) {
|
|
||||||
sk := SummaryKeys{IsAnomaly: false}
|
|
||||||
_, ok := measurement.TestKeys.(*TestKeys)
|
|
||||||
if !ok {
|
|
||||||
return sk, errors.New("invalid test keys type")
|
|
||||||
}
|
|
||||||
return sk, nil
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
package bittorrent
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/mockable"
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMeasurer_run(t *testing.T) {
|
|
||||||
// runHelper is an helper function to run this set of tests.
|
|
||||||
runHelper := func(input string) (*model.Measurement, model.ExperimentMeasurer, error) {
|
|
||||||
measurer := NewExperimentMeasurer(Config{})
|
|
||||||
ctx := context.Background()
|
|
||||||
measurement := &model.Measurement{
|
|
||||||
Input: model.MeasurementTarget(input),
|
|
||||||
}
|
|
||||||
session := &mockable.Session{
|
|
||||||
MockableLogger: model.DiscardLogger,
|
|
||||||
}
|
|
||||||
|
|
||||||
args := &model.ExperimentArgs{
|
|
||||||
Callbacks: model.NewPrinterCallbacks(model.DiscardLogger),
|
|
||||||
Measurement: measurement,
|
|
||||||
Session: session,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := measurer.Run(ctx, args)
|
|
||||||
return measurement, measurer, err
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("with empty input", func(t *testing.T) {
|
|
||||||
_, _, err := runHelper("")
|
|
||||||
if !errors.Is(err, errNoInputProvided) {
|
|
||||||
t.Fatal("unexpected error", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("with invalid URL", func(t *testing.T) {
|
|
||||||
_, _, err := runHelper("\t")
|
|
||||||
if !errors.Is(err, errInputIsNotAnURL) {
|
|
||||||
t.Fatal("unexpected error", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("with invalid scheme", func(t *testing.T) {
|
|
||||||
_, _, err := runHelper("https://8.8.8.8:443/")
|
|
||||||
if !errors.Is(err, errInvalidScheme) {
|
|
||||||
t.Fatal("unexpected error", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,349 +0,0 @@
|
|||||||
package dht
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/anacrolix/dht/v2"
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/experiment/urlgetter"
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/measurexlite"
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/model"
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/tracex"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// errNoInputProvided indicates no input was passed
|
|
||||||
errNoInputProvided = errors.New("no input provided")
|
|
||||||
|
|
||||||
// errInputIsNotAnURL indicates that input is not an URL
|
|
||||||
errInputIsNotAnURL = errors.New("input is not an URL")
|
|
||||||
|
|
||||||
// errInvalidScheme indicates that the scheme is invalid
|
|
||||||
errInvalidScheme = errors.New("scheme must be dht://")
|
|
||||||
|
|
||||||
// errMissingPort indicates that no port was provided
|
|
||||||
errMissingPort = errors.New("no port was provided but dht:// requires explicit port")
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
testName = "dht"
|
|
||||||
testVersion = "0.0.1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Config contains the experiment config.
|
|
||||||
type Config struct{}
|
|
||||||
|
|
||||||
type runtimeConfig struct {
|
|
||||||
// nodeaddr IP or domain name
|
|
||||||
dhtnode string
|
|
||||||
port string
|
|
||||||
infohash string
|
|
||||||
}
|
|
||||||
|
|
||||||
func config(input model.MeasurementTarget) (*runtimeConfig, error) {
|
|
||||||
// Bittorrent v2 hybrid test torrent: https://blog.libtorrent.org/2020/09/bittorrent-v2/
|
|
||||||
// Has good chances of being seeded years from now
|
|
||||||
hash := "631a31dd0a46257d5078c0dee4e66e26f73e42ac"
|
|
||||||
|
|
||||||
if input == "" {
|
|
||||||
return nil, errNoInputProvided
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: static input from defaultDHTBoostrapNodes()
|
|
||||||
// input == "" triggers runtime error from the experiment runner
|
|
||||||
if input == "DUMMY" {
|
|
||||||
// No requested DHT bootstrap node, let the DHT library try all it knows
|
|
||||||
return &runtimeConfig{
|
|
||||||
dhtnode: "",
|
|
||||||
port: "",
|
|
||||||
infohash: hash,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
parsed, err := url.Parse(string(input))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("%w: %s", errInputIsNotAnURL, err.Error())
|
|
||||||
}
|
|
||||||
if parsed.Scheme != "dht" {
|
|
||||||
return nil, errInvalidScheme
|
|
||||||
}
|
|
||||||
|
|
||||||
if parsed.Port() == "" {
|
|
||||||
// Port is mandatory because DHT bootstrap nodes use different ports
|
|
||||||
return nil, errMissingPort
|
|
||||||
}
|
|
||||||
|
|
||||||
validConfig := runtimeConfig{
|
|
||||||
dhtnode: parsed.Hostname(),
|
|
||||||
port: parsed.Port(),
|
|
||||||
infohash: hash,
|
|
||||||
}
|
|
||||||
|
|
||||||
return &validConfig, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestKeys contains the experiment results
|
|
||||||
type TestKeys struct {
|
|
||||||
Queries []*model.ArchivalDNSLookupResult `json:"queries"`
|
|
||||||
Runs []*IndividualTestKeys `json:"runs"`
|
|
||||||
// Used for global failure (DNS resolution)
|
|
||||||
Failure string `json:"failure"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tk *TestKeys) failure(err error) {
|
|
||||||
tk.Failure = *tracex.NewFailure(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tk *TestKeys) computeFailure() {
|
|
||||||
if tk.Failure != "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, itk := range tk.Runs {
|
|
||||||
if itk.Failure != "" {
|
|
||||||
tk.Failure = itk.Failure
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IndividualTestKeys indicate results for a single IP/port combo DHT bootstrap node
|
|
||||||
// in case the DNS resolves to several IPs, or multiple bootstrap domains were used
|
|
||||||
type IndividualTestKeys struct {
|
|
||||||
// Logger, not exported to JSON
|
|
||||||
logger model.Logger
|
|
||||||
|
|
||||||
// List of IP/port combos tried to boostrap DHT
|
|
||||||
BootstrapNodes []string `json:"bootstrap_nodes"`
|
|
||||||
// Number of DHT bootsrap nodes
|
|
||||||
BootstrapNum int `json:"bootstrap_num"`
|
|
||||||
// Number of DHT peers contacted
|
|
||||||
PeersTriedNum uint32 `json:"peers_tried_num"`
|
|
||||||
// Number of DHT peers who answered
|
|
||||||
PeersRespondedNum uint32 `json:"peers_responded_num"`
|
|
||||||
// Number of DHT peers found for specific requested infohash
|
|
||||||
InfohashPeersNum int `json:"infohash_peers_num"`
|
|
||||||
// Actual DHT peers found for requested infohash
|
|
||||||
InfohashPeers []string `json:"infohash_peers"`
|
|
||||||
// Individual failure aborting the test run for this address/port combo
|
|
||||||
Failure string `json:"failure"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (itk *IndividualTestKeys) error(err error) {
|
|
||||||
itk.Failure = *tracex.NewFailure(err)
|
|
||||||
itk.logger.Warn(itk.Failure)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newITK(tk *TestKeys, log model.Logger) *IndividualTestKeys {
|
|
||||||
itk := new(IndividualTestKeys)
|
|
||||||
itk.logger = log
|
|
||||||
tk.Runs = append(tk.Runs, itk)
|
|
||||||
return itk
|
|
||||||
}
|
|
||||||
|
|
||||||
// Measurer performs the measurement.
|
|
||||||
type Measurer struct {
|
|
||||||
// Config contains the experiment settings. If empty we
|
|
||||||
// will be using default settings.
|
|
||||||
Config Config
|
|
||||||
|
|
||||||
// Getter is an optional getter to be used for testing.
|
|
||||||
Getter urlgetter.MultiGetter
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExperimentName implements ExperimentMeasurer.ExperimentName
|
|
||||||
func (m Measurer) ExperimentName() string {
|
|
||||||
return testName
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExperimentVersion implements ExperimentMeasurer.ExperimentVersion
|
|
||||||
func (m Measurer) ExperimentVersion() string {
|
|
||||||
return testVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
func defaultDHTBoostrapNodes() []string {
|
|
||||||
return []string{
|
|
||||||
"router.utorrent.com:6881",
|
|
||||||
"router.bittorrent.com:6881",
|
|
||||||
"dht.transmissionbt.com:6881",
|
|
||||||
"dht.aelitis.com:6881",
|
|
||||||
"router.silotis.us:6881",
|
|
||||||
"dht.libtorrent.org:25401",
|
|
||||||
"dht.anacrolix.link:42069",
|
|
||||||
"router.bittorrent.cloud:42069",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server starts a DHT server with a list of bootstrap nodes and stores
|
|
||||||
// failure cases inside a IndividualTestKeys
|
|
||||||
func Server(bootstrapNodes []string, itk *IndividualTestKeys) (*dht.Server, bool) {
|
|
||||||
itk.BootstrapNodes = bootstrapNodes
|
|
||||||
itk.BootstrapNum = len(bootstrapNodes)
|
|
||||||
|
|
||||||
// Starting new DHT client
|
|
||||||
dhtconf := dht.NewDefaultServerConfig()
|
|
||||||
dhtconf.QueryResendDelay = func() time.Duration {
|
|
||||||
return 10 * time.Second
|
|
||||||
}
|
|
||||||
|
|
||||||
dhtconf.StartingNodes = func() (addrs []dht.Addr, err error) {
|
|
||||||
for _, addrport := range bootstrapNodes {
|
|
||||||
udpAddr, err := net.ResolveUDPAddr("udp", addrport)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
addrs = append(addrs, dht.NewAddr(udpAddr))
|
|
||||||
}
|
|
||||||
return addrs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
dhtsrv, err := dht.NewServer(dhtconf)
|
|
||||||
if err != nil {
|
|
||||||
itk.error(err)
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
itk.logger.Infof("Finished starting DHT server with bootstrap nodes: %v", bootstrapNodes)
|
|
||||||
return dhtsrv, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func testServer(dht *dht.Server, infohash [20]byte, itk *IndividualTestKeys) bool {
|
|
||||||
announce, err := dht.AnnounceTraversal(infohash)
|
|
||||||
if err != nil {
|
|
||||||
itk.error(err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
defer announce.Close()
|
|
||||||
|
|
||||||
counter := 0
|
|
||||||
for entry := range announce.Peers {
|
|
||||||
counter++
|
|
||||||
itk.InfohashPeers = append(itk.InfohashPeers, entry.NodeInfo.Addr.String())
|
|
||||||
itk.logger.Debugf("peer %d: %s", counter, entry.NodeInfo.Addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
stats := announce.TraversalStats()
|
|
||||||
itk.PeersTriedNum = stats.NumAddrsTried
|
|
||||||
itk.PeersRespondedNum = stats.NumResponses
|
|
||||||
itk.InfohashPeersNum = counter
|
|
||||||
|
|
||||||
if itk.PeersRespondedNum == 0 {
|
|
||||||
itk.error(errors.New("No DHT peers were found"))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
itk.logger.Infof("Tried %d peers obtained from %d bootstrap nodes. Got response from %d. %d have requested infohash.", itk.PeersTriedNum, itk.BootstrapNum, itk.PeersRespondedNum, itk.InfohashPeersNum)
|
|
||||||
|
|
||||||
return true
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run implements ExperimentMeasurer.Run
|
|
||||||
func (m Measurer) Run(ctx context.Context, args *model.ExperimentArgs) error {
|
|
||||||
//ctx context.Context, sess model.ExperimentSession,
|
|
||||||
//measurement *model.Measurement, callbacks model.ExperimentCallbacks,
|
|
||||||
//) error {
|
|
||||||
sess := args.Session
|
|
||||||
measurement := args.Measurement
|
|
||||||
log := sess.Logger()
|
|
||||||
trace := measurexlite.NewTrace(0, measurement.MeasurementStartTimeSaved)
|
|
||||||
resolver := trace.NewStdlibResolver(log)
|
|
||||||
|
|
||||||
config, err := config(measurement.Input)
|
|
||||||
if err != nil {
|
|
||||||
// Invalid input data, we don't even generate report
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tk := new(TestKeys)
|
|
||||||
measurement.TestKeys = tk
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(ctx, 60*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
// Turn string infohash into 20-bytes array
|
|
||||||
var infohash [20]byte
|
|
||||||
copy(infohash[:], config.infohash)
|
|
||||||
|
|
||||||
if config.dhtnode != "" {
|
|
||||||
// Specific node provided: resolve it
|
|
||||||
log.Infof("Resolving DNS for %s", config.dhtnode)
|
|
||||||
resolvedAddrs, err := resolver.LookupHost(ctx, config.dhtnode)
|
|
||||||
tk.Queries = append(tk.Queries, trace.DNSLookupsFromRoundTrip()...)
|
|
||||||
if err != nil {
|
|
||||||
tk.failure(err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
log.Infof("Finished DNS for %s: %v", config.dhtnode, resolvedAddrs)
|
|
||||||
|
|
||||||
for _, addr := range resolvedAddrs {
|
|
||||||
|
|
||||||
nodeAddrport := net.JoinHostPort(addr, config.port)
|
|
||||||
log.Infof("Trying DHT bootstrap node %s", nodeAddrport)
|
|
||||||
nodeAddrports := []string{nodeAddrport}
|
|
||||||
|
|
||||||
itk := newITK(tk, log)
|
|
||||||
|
|
||||||
dht, success := Server(nodeAddrports, itk)
|
|
||||||
if !success {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
testServer(dht, infohash, itk)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Use default DHT bootstrap nodes because none was given by input
|
|
||||||
resolvedAddrports := []string{}
|
|
||||||
for _, bootstrapDomain := range defaultDHTBoostrapNodes() {
|
|
||||||
// Ignore error because we use static input so panic chance is 0
|
|
||||||
host, port, _ := net.SplitHostPort(bootstrapDomain)
|
|
||||||
log.Infof("Resolving DNS for %s", host)
|
|
||||||
resolvedAddrs, err := resolver.LookupHost(ctx, host)
|
|
||||||
tk.Queries = append(tk.Queries, trace.DNSLookupsFromRoundTrip()...)
|
|
||||||
if err != nil {
|
|
||||||
tk.failure(err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
log.Infof("Finished DNS for %s: %v", host, resolvedAddrs)
|
|
||||||
for _, resolvedAddr := range resolvedAddrs {
|
|
||||||
resolvedAddrports = append(resolvedAddrports, net.JoinHostPort(resolvedAddr, port))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Infof("Resolved the following bootstrap nodes: %v", resolvedAddrports)
|
|
||||||
|
|
||||||
itk := newITK(tk, log)
|
|
||||||
dht, success := Server(resolvedAddrports, itk)
|
|
||||||
if success {
|
|
||||||
testServer(dht, infohash, itk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tk.computeFailure()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewExperimentMeasurer creates a new ExperimentMeasurer.
|
|
||||||
func NewExperimentMeasurer(config Config) model.ExperimentMeasurer {
|
|
||||||
return Measurer{Config: config}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SummaryKeys contains summary keys for this experiment.
|
|
||||||
//
|
|
||||||
// Note that this structure is part of the ABI contract with ooniprobe
|
|
||||||
// therefore we should be careful when changing it.
|
|
||||||
type SummaryKeys struct {
|
|
||||||
IsAnomaly bool `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSummaryKeys implements model.ExperimentMeasurer.GetSummaryKeys.
|
|
||||||
func (m Measurer) GetSummaryKeys(measurement *model.Measurement) (interface{}, error) {
|
|
||||||
sk := SummaryKeys{IsAnomaly: false}
|
|
||||||
_, ok := measurement.TestKeys.(*TestKeys)
|
|
||||||
if !ok {
|
|
||||||
return sk, errors.New("invalid test keys type")
|
|
||||||
}
|
|
||||||
return sk, nil
|
|
||||||
}
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
package dht
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/anacrolix/dht/v2"
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/mockable"
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMeasurer_run(t *testing.T) {
|
|
||||||
// runHelper is an helper function to run this set of tests.
|
|
||||||
runHelper := func(input string) (*model.Measurement, model.ExperimentMeasurer, error) {
|
|
||||||
measurer := NewExperimentMeasurer(Config{})
|
|
||||||
ctx := context.Background()
|
|
||||||
measurement := &model.Measurement{
|
|
||||||
Input: model.MeasurementTarget(input),
|
|
||||||
}
|
|
||||||
session := &mockable.Session{
|
|
||||||
MockableLogger: model.DiscardLogger,
|
|
||||||
}
|
|
||||||
|
|
||||||
args := &model.ExperimentArgs{
|
|
||||||
Callbacks: model.NewPrinterCallbacks(model.DiscardLogger),
|
|
||||||
Measurement: measurement,
|
|
||||||
Session: session,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := measurer.Run(ctx, args)
|
|
||||||
return measurement, measurer, err
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("with empty input", func(t *testing.T) {
|
|
||||||
_, _, err := runHelper("")
|
|
||||||
if !errors.Is(err, errNoInputProvided) {
|
|
||||||
t.Fatal("unexpected error", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("with invalid URL", func(t *testing.T) {
|
|
||||||
_, _, err := runHelper("\t")
|
|
||||||
if !errors.Is(err, errInputIsNotAnURL) {
|
|
||||||
t.Fatal("unexpected error", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("with invalid scheme", func(t *testing.T) {
|
|
||||||
_, _, err := runHelper("https://8.8.8.8:443/")
|
|
||||||
if !errors.Is(err, errInvalidScheme) {
|
|
||||||
t.Fatal("unexpected error", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("with missing port", func(t *testing.T) {
|
|
||||||
_, _, err := runHelper("dht://8.8.8.8")
|
|
||||||
if !errors.Is(err, errMissingPort) {
|
|
||||||
t.Fatal("unexpected error", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("with local listener", func(t *testing.T) {
|
|
||||||
conf := new(dht.ServerConfig)
|
|
||||||
conf.StartingNodes = func() (addrs []dht.Addr, err error) {
|
|
||||||
return []dht.Addr{}, nil
|
|
||||||
}
|
|
||||||
conf.Passive = false
|
|
||||||
dht, err := dht.NewServer(conf)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer dht.Close()
|
|
||||||
_, _ = dht.Bootstrap()
|
|
||||||
|
|
||||||
println(dht.Addr().String())
|
|
||||||
url := fmt.Sprintf("dht://%s", dht.Addr().String())
|
|
||||||
|
|
||||||
meas, m, err := runHelper(url)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tk := meas.TestKeys.(*TestKeys)
|
|
||||||
|
|
||||||
if tk.Failure != "" {
|
|
||||||
t.Fatal(tk.Failure)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(tk.Runs) != 1 {
|
|
||||||
t.Fatal("Expected one DHT run")
|
|
||||||
}
|
|
||||||
|
|
||||||
run := tk.Runs[0]
|
|
||||||
if run.Failure != "" {
|
|
||||||
t.Fatal(run.Failure)
|
|
||||||
}
|
|
||||||
|
|
||||||
if run.BootstrapNum != 1 {
|
|
||||||
t.Fatal("Expected only one bootstrap node")
|
|
||||||
}
|
|
||||||
|
|
||||||
if run.PeersRespondedNum != 1 {
|
|
||||||
t.Fatal("Expected bootstrap node to respond")
|
|
||||||
}
|
|
||||||
|
|
||||||
ask, err := m.GetSummaryKeys(meas)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("cannot obtain summary")
|
|
||||||
}
|
|
||||||
summary := ask.(SummaryKeys)
|
|
||||||
if summary.IsAnomaly {
|
|
||||||
t.Fatal("expected no anomaly")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,325 @@
|
|||||||
|
package imap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/engine/experiment/urlgetter"
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/measurexlite"
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/model"
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/tcprunner"
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/tracex"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// errNoInputProvided indicates you didn't provide any input
|
||||||
|
errNoInputProvided = errors.New("not input provided")
|
||||||
|
|
||||||
|
// errInputIsNotAnURL indicates that input is not an URL
|
||||||
|
errInputIsNotAnURL = errors.New("input is not an URL")
|
||||||
|
|
||||||
|
// errInvalidScheme indicates that the scheme is invalid
|
||||||
|
errInvalidScheme = errors.New("scheme must be imap(s)")
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testName = "imap"
|
||||||
|
testVersion = "0.0.1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config contains the experiment config.
|
||||||
|
type Config struct{}
|
||||||
|
|
||||||
|
type runtimeConfig struct {
|
||||||
|
host string
|
||||||
|
port string
|
||||||
|
forcedTLS bool
|
||||||
|
noopCount uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func config(input model.MeasurementTarget) (*runtimeConfig, error) {
|
||||||
|
if input == "" {
|
||||||
|
// TODO: static input data (eg. gmail/riseup..)
|
||||||
|
return nil, errNoInputProvided
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed, err := url.Parse(string(input))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%w: %s", errInputIsNotAnURL, err.Error())
|
||||||
|
}
|
||||||
|
if parsed.Scheme != "imap" && parsed.Scheme != "imaps" {
|
||||||
|
return nil, errInvalidScheme
|
||||||
|
}
|
||||||
|
|
||||||
|
port := ""
|
||||||
|
|
||||||
|
if parsed.Port() == "" {
|
||||||
|
// Default ports for StartTLS and forced TLS respectively
|
||||||
|
if parsed.Scheme == "imap" {
|
||||||
|
port = "143"
|
||||||
|
} else {
|
||||||
|
port = "993"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Valid port is checked by URL parsing
|
||||||
|
port = parsed.Port()
|
||||||
|
}
|
||||||
|
|
||||||
|
validConfig := runtimeConfig{
|
||||||
|
host: parsed.Hostname(),
|
||||||
|
forcedTLS: parsed.Scheme == "imaps",
|
||||||
|
port: port,
|
||||||
|
noopCount: 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &validConfig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestKeys contains the experiment results for an entire domain host
|
||||||
|
type TestKeys struct {
|
||||||
|
Host string `json:"hostname"`
|
||||||
|
Queries []*model.ArchivalDNSLookupResult `json:"queries"`
|
||||||
|
// Individual IP/port results
|
||||||
|
Runs []*IndividualTestKeys `json:"runs"`
|
||||||
|
// Used for global failure (DNS resolution)
|
||||||
|
Failure string `json:"failure"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestKeys(host string) *TestKeys {
|
||||||
|
tk := new(TestKeys)
|
||||||
|
tk.Host = host
|
||||||
|
return tk
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hostname TCPRunnerModel
|
||||||
|
func (tk *TestKeys) Hostname(host string) {
|
||||||
|
tk.Host = host
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSResults TCPRunnerModel
|
||||||
|
func (tk *TestKeys) DNSResults(res []*model.ArchivalDNSLookupResult) {
|
||||||
|
// TODO: not sure if we are passed the overall trace results and should overwrite key, or just append
|
||||||
|
tk.Queries = append(tk.Queries, res...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failed TCPRunnerModel
|
||||||
|
func (tk *TestKeys) Failed(msg string) {
|
||||||
|
tk.Failure = msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRun TCPRunnerModel
|
||||||
|
func (tk *TestKeys) NewRun(addr string, port string) tcprunner.TCPSessionModel {
|
||||||
|
itk := newIndividualTestKeys(addr, port)
|
||||||
|
tk.Runs = append(tk.Runs, itk)
|
||||||
|
return itk
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndividualTestKeys contains the experiment results for a single IP/port combo
|
||||||
|
type IndividualTestKeys struct {
|
||||||
|
TCPConnect []*model.ArchivalTCPConnectResult `json:"tcp_connect"`
|
||||||
|
TLSHandshake *model.ArchivalTLSOrQUICHandshakeResult `json:"tls_handshakes"`
|
||||||
|
Failure string `json:"failure"`
|
||||||
|
FailureStep string `json:"failed_step"`
|
||||||
|
IP string `json:"ip"`
|
||||||
|
Port string `json:"port"`
|
||||||
|
noopCounter uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIndividualTestKeys(addr string, port string) *IndividualTestKeys {
|
||||||
|
itk := new(IndividualTestKeys)
|
||||||
|
itk.IP = addr
|
||||||
|
itk.Port = port
|
||||||
|
return itk
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPPort TCPSessionModel
|
||||||
|
func (itk *IndividualTestKeys) IPPort(ip string, port string) {
|
||||||
|
itk.IP = ip
|
||||||
|
itk.Port = port
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectResults TCPSessionModel
|
||||||
|
func (itk *IndividualTestKeys) ConnectResults(res []*model.ArchivalTCPConnectResult) {
|
||||||
|
itk.TCPConnect = append(itk.TCPConnect, res...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandshakeResult TCPSessionModel
|
||||||
|
func (itk *IndividualTestKeys) HandshakeResult(res *model.ArchivalTLSOrQUICHandshakeResult) {
|
||||||
|
itk.TLSHandshake = res
|
||||||
|
}
|
||||||
|
|
||||||
|
// FailedStep TCPSessionModel
|
||||||
|
func (itk *IndividualTestKeys) FailedStep(failure string, step string) {
|
||||||
|
itk.Failure = failure
|
||||||
|
itk.FailureStep = step
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measurer performs the measurement.
|
||||||
|
type Measurer struct {
|
||||||
|
// Config contains the experiment settings. If empty we
|
||||||
|
// will be using default settings.
|
||||||
|
Config Config
|
||||||
|
|
||||||
|
// Getter is an optional getter to be used for testing.
|
||||||
|
Getter urlgetter.MultiGetter
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExperimentName implements ExperimentMeasurer.ExperimentName
|
||||||
|
func (m Measurer) ExperimentName() string {
|
||||||
|
return testName
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExperimentVersion implements ExperimentMeasurer.ExperimentVersion
|
||||||
|
func (m Measurer) ExperimentVersion() string {
|
||||||
|
return testVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run implements ExperimentMeasurer.Run
|
||||||
|
func (m Measurer) Run(ctx context.Context, args *model.ExperimentArgs) error {
|
||||||
|
sess := args.Session
|
||||||
|
measurement := args.Measurement
|
||||||
|
log := sess.Logger()
|
||||||
|
trace := measurexlite.NewTrace(0, measurement.MeasurementStartTimeSaved)
|
||||||
|
|
||||||
|
config, err := config(measurement.Input)
|
||||||
|
if err != nil {
|
||||||
|
// Invalid input data, we don't even generate report
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tk := new(TestKeys)
|
||||||
|
measurement.TestKeys = tk
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
tlsconfig := tls.Config{
|
||||||
|
InsecureSkipVerify: false,
|
||||||
|
ServerName: config.host,
|
||||||
|
}
|
||||||
|
|
||||||
|
runner := &tcprunner.TCPRunner{
|
||||||
|
Tk: tk,
|
||||||
|
Trace: trace,
|
||||||
|
Logger: log,
|
||||||
|
Ctx: ctx,
|
||||||
|
Tlsconfig: &tlsconfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
// First resolve DNS
|
||||||
|
addrs, success := runner.Resolve(config.host)
|
||||||
|
if !success {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
tcpSession, success := runner.Conn(addr, config.port)
|
||||||
|
if !success {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
defer tcpSession.Close()
|
||||||
|
|
||||||
|
if config.forcedTLS {
|
||||||
|
log.Infof("Running direct TLS mode to %s:%s", addr, config.port)
|
||||||
|
|
||||||
|
if !tcpSession.Handshake() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try NoOps
|
||||||
|
if !testIMAP(tcpSession, config.noopCount) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Infof("Running StartTLS mode to %s:%s", addr, config.port)
|
||||||
|
|
||||||
|
// Upgrade via StartTLS and try NoOps
|
||||||
|
if !tcpSession.StartTLS("A1 STARTTLS\n", "TLS") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !testIMAP(tcpSession, config.noopCount) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testIMAP(s *tcprunner.TCPSession, noop uint8) bool {
|
||||||
|
// Auto-choose plaintext/TCP session
|
||||||
|
// TODO: move to Debugf
|
||||||
|
s.Runner.Logger.Infof("Retrieving existing connection")
|
||||||
|
conn := s.CurrentConn()
|
||||||
|
s.Runner.Logger.Infof("Starting IMAP query")
|
||||||
|
|
||||||
|
command, err := bufio.NewReader(conn).ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
s.FailedStep(*tracex.NewFailure(err), "imap_wait_capability")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(command, "CAPABILITY") {
|
||||||
|
s.FailedStep(fmt.Sprintf("Received unexpected IMAP response: %s", command), "imap_wrong_capability")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Runner.Logger.Infof("Finished starting IMAP")
|
||||||
|
|
||||||
|
if noop > 0 {
|
||||||
|
// Downcast TCPSession's itk into typed IndividualTestKeys to access noopCounter field
|
||||||
|
concreteITK := s.Itk.(*IndividualTestKeys)
|
||||||
|
s.Runner.Logger.Infof("Trying to generate more no-op traffic")
|
||||||
|
concreteITK.noopCounter = 0
|
||||||
|
for concreteITK.noopCounter < noop {
|
||||||
|
concreteITK.noopCounter++
|
||||||
|
s.Runner.Logger.Infof("NoOp Iteration %d", concreteITK.noopCounter)
|
||||||
|
_, err = conn.Write([]byte("A1 NOOP\n"))
|
||||||
|
if err != nil {
|
||||||
|
s.FailedStep(*tracex.NewFailure(err), fmt.Sprintf("imap_noop_%d", concreteITK.noopCounter))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if concreteITK.noopCounter == noop {
|
||||||
|
s.Runner.Logger.Infof("Successfully generated no-op traffic")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
s.Runner.Logger.Warnf("Failed no-op traffic at iteration %d", concreteITK.noopCounter)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewExperimentMeasurer creates a new ExperimentMeasurer.
|
||||||
|
func NewExperimentMeasurer(config Config) model.ExperimentMeasurer {
|
||||||
|
return Measurer{Config: config}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SummaryKeys contains summary keys for this experiment.
|
||||||
|
//
|
||||||
|
// Note that this structure is part of the ABI contract with ooniprobe
|
||||||
|
// therefore we should be careful when changing it.
|
||||||
|
type SummaryKeys struct {
|
||||||
|
//DNSBlocking bool `json:"facebook_dns_blocking"`
|
||||||
|
//TCPBlocking bool `json:"facebook_tcp_blocking"`
|
||||||
|
IsAnomaly bool `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSummaryKeys implements model.ExperimentMeasurer.GetSummaryKeys.
|
||||||
|
func (m Measurer) GetSummaryKeys(measurement *model.Measurement) (interface{}, error) {
|
||||||
|
sk := SummaryKeys{IsAnomaly: false}
|
||||||
|
_, ok := measurement.TestKeys.(*TestKeys)
|
||||||
|
if !ok {
|
||||||
|
return sk, errors.New("invalid test keys type")
|
||||||
|
}
|
||||||
|
return sk, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
package imap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
//"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/engine/mockable"
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func plaintextListener() net.Listener {
|
||||||
|
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||||
|
if err != nil {
|
||||||
|
if l, err = net.Listen("tcp6", "[::1]:0"); err != nil {
|
||||||
|
panic(fmt.Sprintf("httptest: failed to listen on a port: %v", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func tlsListener(l net.Listener) net.Listener {
|
||||||
|
return tls.NewListener(l, &tls.Config{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func listenerAddr(l net.Listener) string {
|
||||||
|
return l.Addr().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidIMAPServer(conn net.Conn) {
|
||||||
|
starttls := false
|
||||||
|
for {
|
||||||
|
command, err := bufio.NewReader(conn).ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(command, "NOOP") {
|
||||||
|
conn.Write([]byte("A1 OK NOOP completed.\n"))
|
||||||
|
} else if command == "STARTTLS" {
|
||||||
|
starttls = true
|
||||||
|
conn.Write([]byte("A1 OK Begin TLS negotiation now.\n"))
|
||||||
|
// TODO: conn.Close does not actually close connection? or does client not detect it?
|
||||||
|
//conn.Close()
|
||||||
|
return
|
||||||
|
} else if starttls {
|
||||||
|
conn.Write([]byte("GARBAGE TO BREAK STARTTLS"))
|
||||||
|
}
|
||||||
|
conn.Write([]byte("\n"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TCPServer(l net.Listener) {
|
||||||
|
for {
|
||||||
|
conn, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
conn.Write([]byte("* OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE LITERAL+ STARTTLS LOGINDISABLED] howdy, ready.\n"))
|
||||||
|
ValidIMAPServer(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMeasurer_run(t *testing.T) {
|
||||||
|
// runHelper is an helper function to run this set of tests.
|
||||||
|
runHelper := func(input string) (*model.Measurement, model.ExperimentMeasurer, error) {
|
||||||
|
m := NewExperimentMeasurer(Config{})
|
||||||
|
if m.ExperimentName() != "imap" {
|
||||||
|
t.Fatal("invalid experiment name")
|
||||||
|
}
|
||||||
|
if m.ExperimentVersion() != "0.0.1" {
|
||||||
|
t.Fatal("invalid experiment version")
|
||||||
|
}
|
||||||
|
ctx := context.Background()
|
||||||
|
meas := &model.Measurement{
|
||||||
|
Input: model.MeasurementTarget(input),
|
||||||
|
}
|
||||||
|
sess := &mockable.Session{
|
||||||
|
MockableLogger: model.DiscardLogger,
|
||||||
|
}
|
||||||
|
|
||||||
|
args := &model.ExperimentArgs{
|
||||||
|
Callbacks: model.NewPrinterCallbacks(model.DiscardLogger),
|
||||||
|
Measurement: meas,
|
||||||
|
Session: sess,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := m.Run(ctx, args)
|
||||||
|
return meas, m, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("with empty input", func(t *testing.T) {
|
||||||
|
_, _, err := runHelper("")
|
||||||
|
if !errors.Is(err, errNoInputProvided) {
|
||||||
|
t.Fatal("unexpected error", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("with invalid URL", func(t *testing.T) {
|
||||||
|
_, _, err := runHelper("\t")
|
||||||
|
if !errors.Is(err, errInputIsNotAnURL) {
|
||||||
|
t.Fatal("unexpected error", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("with invalid scheme", func(t *testing.T) {
|
||||||
|
_, _, err := runHelper("https://8.8.8.8:443/")
|
||||||
|
if !errors.Is(err, errInvalidScheme) {
|
||||||
|
t.Fatal("unexpected error", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("with broken TLS", func(t *testing.T) {
|
||||||
|
p := plaintextListener()
|
||||||
|
defer p.Close()
|
||||||
|
|
||||||
|
l := tlsListener(p)
|
||||||
|
defer l.Close()
|
||||||
|
addr := listenerAddr(l)
|
||||||
|
go TCPServer(l)
|
||||||
|
|
||||||
|
meas, m, err := runHelper("imaps://" + addr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tk := meas.TestKeys.(*TestKeys)
|
||||||
|
|
||||||
|
for _, run := range tk.Runs {
|
||||||
|
if *run.TLSHandshake.Failure != "unknown_failure: remote error: tls: unrecognized name" {
|
||||||
|
t.Fatal("expected unrecognized_name in TLS handshake")
|
||||||
|
}
|
||||||
|
|
||||||
|
if run.noopCounter != 0 {
|
||||||
|
t.Fatalf("expected to not have any noops, not %d noops", run.noopCounter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ask, err := m.GetSummaryKeys(meas)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("cannot obtain summary")
|
||||||
|
}
|
||||||
|
summary := ask.(SummaryKeys)
|
||||||
|
if summary.IsAnomaly {
|
||||||
|
t.Fatal("expected no anomaly")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("with broken starttls", func(t *testing.T) {
|
||||||
|
l := plaintextListener()
|
||||||
|
defer l.Close()
|
||||||
|
addr := listenerAddr(l)
|
||||||
|
|
||||||
|
go TCPServer(l)
|
||||||
|
|
||||||
|
meas, m, err := runHelper("imap://" + addr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tk := meas.TestKeys.(*TestKeys)
|
||||||
|
//bs, _ := json.Marshal(tk)
|
||||||
|
//fmt.Println(string(bs))
|
||||||
|
|
||||||
|
for _, run := range tk.Runs {
|
||||||
|
if *run.TLSHandshake.Failure != "unknown_failure: tls: first record does not look like a TLS handshake" {
|
||||||
|
t.Fatalf("s%ss", *run.TLSHandshake.Failure)
|
||||||
|
t.Fatal("expected broken handshake")
|
||||||
|
}
|
||||||
|
|
||||||
|
if run.noopCounter != 0 {
|
||||||
|
t.Fatalf("expected to not have any noops, not %d noops", run.noopCounter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ask, err := m.GetSummaryKeys(meas)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("cannot obtain summary")
|
||||||
|
}
|
||||||
|
summary := ask.(SummaryKeys)
|
||||||
|
if summary.IsAnomaly {
|
||||||
|
t.Fatal("expected no anomaly")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,333 @@
|
|||||||
|
package smtp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"net/smtp"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/engine/experiment/urlgetter"
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/measurexlite"
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/model"
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/tcprunner"
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/tracex"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// errNoInputProvided indicates you didn't provide any input
|
||||||
|
errNoInputProvided = errors.New("not input provided")
|
||||||
|
|
||||||
|
// errInputIsNotAnURL indicates that input is not an URL
|
||||||
|
errInputIsNotAnURL = errors.New("input is not an URL")
|
||||||
|
|
||||||
|
// errInvalidScheme indicates that the scheme is invalid
|
||||||
|
errInvalidScheme = errors.New("scheme must be smtp(s)")
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testName = "smtp"
|
||||||
|
testVersion = "0.0.1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config contains the experiment config.
|
||||||
|
type Config struct{}
|
||||||
|
|
||||||
|
type runtimeConfig struct {
|
||||||
|
host string
|
||||||
|
port string
|
||||||
|
forcedTLS bool
|
||||||
|
noopCount uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func config(input model.MeasurementTarget) (*runtimeConfig, error) {
|
||||||
|
if input == "" {
|
||||||
|
// TODO: static input data (eg. gmail/riseup..)
|
||||||
|
return nil, errNoInputProvided
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed, err := url.Parse(string(input))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%w: %s", errInputIsNotAnURL, err.Error())
|
||||||
|
}
|
||||||
|
if parsed.Scheme != "smtp" && parsed.Scheme != "smtps" {
|
||||||
|
return nil, errInvalidScheme
|
||||||
|
}
|
||||||
|
|
||||||
|
port := ""
|
||||||
|
|
||||||
|
if parsed.Port() == "" {
|
||||||
|
// Default ports for StartTLS and forced TLS respectively
|
||||||
|
if parsed.Scheme == "smtp" {
|
||||||
|
port = "587"
|
||||||
|
} else {
|
||||||
|
port = "465"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Valid port is checked by URL parsing
|
||||||
|
port = parsed.Port()
|
||||||
|
}
|
||||||
|
|
||||||
|
validConfig := runtimeConfig{
|
||||||
|
host: parsed.Hostname(),
|
||||||
|
forcedTLS: parsed.Scheme == "smtps",
|
||||||
|
port: port,
|
||||||
|
noopCount: 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &validConfig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestKeys contains the experiment results for an entire domain host
|
||||||
|
type TestKeys struct {
|
||||||
|
Host string `json:"hostname"`
|
||||||
|
Queries []*model.ArchivalDNSLookupResult `json:"queries"`
|
||||||
|
// Individual IP/port results
|
||||||
|
Runs []*IndividualTestKeys `json:"runs"`
|
||||||
|
// Used for global failure (DNS resolution)
|
||||||
|
Failure string `json:"failure"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestKeys(host string) *TestKeys {
|
||||||
|
tk := new(TestKeys)
|
||||||
|
tk.Host = host
|
||||||
|
return tk
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hostname TCPRunnerModel
|
||||||
|
func (tk *TestKeys) Hostname(host string) {
|
||||||
|
tk.Host = host
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSResults TCPRunnerModel
|
||||||
|
func (tk *TestKeys) DNSResults(res []*model.ArchivalDNSLookupResult) {
|
||||||
|
// TODO: not sure if we are passed the overall trace results and should overwrite key, or just append
|
||||||
|
tk.Queries = append(tk.Queries, res...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failed TCPRunnerModel
|
||||||
|
func (tk *TestKeys) Failed(msg string) {
|
||||||
|
tk.Failure = msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRun TCPRunnerModel
|
||||||
|
func (tk *TestKeys) NewRun(addr string, port string) tcprunner.TCPSessionModel {
|
||||||
|
itk := newIndividualTestKeys(addr, port)
|
||||||
|
tk.Runs = append(tk.Runs, itk)
|
||||||
|
return itk
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndividualTestKeys contains the experiment results for a single IP/port combo
|
||||||
|
type IndividualTestKeys struct {
|
||||||
|
TCPConnect []*model.ArchivalTCPConnectResult `json:"tcp_connect"`
|
||||||
|
TLSHandshake *model.ArchivalTLSOrQUICHandshakeResult `json:"tls_handshakes"`
|
||||||
|
Failure string `json:"failure"`
|
||||||
|
FailureStep string `json:"failed_step"`
|
||||||
|
IP string `json:"ip"`
|
||||||
|
Port string `json:"port"`
|
||||||
|
noopCounter uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIndividualTestKeys(addr string, port string) *IndividualTestKeys {
|
||||||
|
itk := new(IndividualTestKeys)
|
||||||
|
itk.IP = addr
|
||||||
|
itk.Port = port
|
||||||
|
return itk
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPPort TCPSessionModel
|
||||||
|
func (itk *IndividualTestKeys) IPPort(ip string, port string) {
|
||||||
|
itk.IP = ip
|
||||||
|
itk.Port = port
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectResults TCPSessionModel
|
||||||
|
func (itk *IndividualTestKeys) ConnectResults(res []*model.ArchivalTCPConnectResult) {
|
||||||
|
itk.TCPConnect = append(itk.TCPConnect, res...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandshakeResult TCPSessionModel
|
||||||
|
func (itk *IndividualTestKeys) HandshakeResult(res *model.ArchivalTLSOrQUICHandshakeResult) {
|
||||||
|
itk.TLSHandshake = res
|
||||||
|
}
|
||||||
|
|
||||||
|
// FailedStep TCPSessionModel
|
||||||
|
func (itk *IndividualTestKeys) FailedStep(failure string, step string) {
|
||||||
|
itk.Failure = failure
|
||||||
|
itk.FailureStep = step
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measurer performs the measurement.
|
||||||
|
type Measurer struct {
|
||||||
|
// Config contains the experiment settings. If empty we
|
||||||
|
// will be using default settings.
|
||||||
|
Config Config
|
||||||
|
|
||||||
|
// Getter is an optional getter to be used for testing.
|
||||||
|
Getter urlgetter.MultiGetter
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExperimentName implements ExperimentMeasurer.ExperimentName
|
||||||
|
func (m Measurer) ExperimentName() string {
|
||||||
|
return testName
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExperimentVersion implements ExperimentMeasurer.ExperimentVersion
|
||||||
|
func (m Measurer) ExperimentVersion() string {
|
||||||
|
return testVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run implements ExperimentMeasurer.Run
|
||||||
|
func (m Measurer) Run(ctx context.Context, args *model.ExperimentArgs) error {
|
||||||
|
sess := args.Session
|
||||||
|
measurement := args.Measurement
|
||||||
|
log := sess.Logger()
|
||||||
|
trace := measurexlite.NewTrace(0, measurement.MeasurementStartTimeSaved)
|
||||||
|
|
||||||
|
config, err := config(measurement.Input)
|
||||||
|
if err != nil {
|
||||||
|
// Invalid input data, we don't even generate report
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tk := new(TestKeys)
|
||||||
|
measurement.TestKeys = tk
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
tlsconfig := tls.Config{
|
||||||
|
InsecureSkipVerify: false,
|
||||||
|
ServerName: config.host,
|
||||||
|
}
|
||||||
|
|
||||||
|
runner := &tcprunner.TCPRunner{
|
||||||
|
Tk: tk,
|
||||||
|
Trace: trace,
|
||||||
|
Logger: log,
|
||||||
|
Ctx: ctx,
|
||||||
|
Tlsconfig: &tlsconfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
// First resolve DNS
|
||||||
|
addrs, success := runner.Resolve(config.host)
|
||||||
|
if !success {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
tcpSession, success := runner.Conn(addr, config.port)
|
||||||
|
if !success {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
defer tcpSession.Close()
|
||||||
|
|
||||||
|
if config.forcedTLS {
|
||||||
|
log.Infof("Running direct TLS mode to %s:%s", addr, config.port)
|
||||||
|
|
||||||
|
if !tcpSession.Handshake() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try EHLO + NoOps
|
||||||
|
if !testSMTP(tcpSession, "localhost", config.noopCount) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Infof("Running StartTLS mode to %s:%s", addr, config.port)
|
||||||
|
|
||||||
|
if !testSMTP(tcpSession, "localhost", 0) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upgrade via StartTLS and try EHLO + NoOps
|
||||||
|
if !tcpSession.StartTLS("STARTTLS\n", "TLS") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !testSMTP(tcpSession, "localhost", config.noopCount) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSMTP(s *tcprunner.TCPSession, ehlo string, noop uint8) bool {
|
||||||
|
// Auto-choose plaintext/TCP session
|
||||||
|
// TODO: move to Debugf
|
||||||
|
s.Runner.Logger.Infof("Retrieving existing connection")
|
||||||
|
conn := s.CurrentConn()
|
||||||
|
s.Runner.Logger.Infof("Initializing SMTP client")
|
||||||
|
client, err := smtp.NewClient(conn, ehlo)
|
||||||
|
if err != nil {
|
||||||
|
s.FailedStep(*tracex.NewFailure(err), "smtp_init")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Runner.Logger.Infof("Starting SMTP EHLO")
|
||||||
|
err = client.Hello(ehlo)
|
||||||
|
if err != nil {
|
||||||
|
if s.TLS {
|
||||||
|
s.FailedStep(*tracex.NewFailure(err), "smtp_tls_ehlo")
|
||||||
|
} else {
|
||||||
|
s.FailedStep(*tracex.NewFailure(err), "smtp_plaintext_ehlo")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Runner.Logger.Infof("Finished SMTP EHLO")
|
||||||
|
|
||||||
|
if noop > 0 {
|
||||||
|
// Downcast TCPSession's itk into typed IndividualTestKeys to access noopCounter field
|
||||||
|
concreteITK := s.Itk.(*IndividualTestKeys)
|
||||||
|
s.Runner.Logger.Infof("Trying to generate more no-op traffic")
|
||||||
|
concreteITK.noopCounter = 0
|
||||||
|
for concreteITK.noopCounter < noop {
|
||||||
|
concreteITK.noopCounter++
|
||||||
|
s.Runner.Logger.Infof("NoOp Iteration %d", concreteITK.noopCounter)
|
||||||
|
err = client.Noop()
|
||||||
|
if err != nil {
|
||||||
|
s.FailedStep(*tracex.NewFailure(err), fmt.Sprintf("smtp_noop_%d", concreteITK.noopCounter))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if concreteITK.noopCounter == noop {
|
||||||
|
s.Runner.Logger.Infof("Successfully generated no-op traffic")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
s.Runner.Logger.Warnf("Failed no-op traffic at iteration %d", concreteITK.noopCounter)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewExperimentMeasurer creates a new ExperimentMeasurer.
|
||||||
|
func NewExperimentMeasurer(config Config) model.ExperimentMeasurer {
|
||||||
|
return Measurer{Config: config}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SummaryKeys contains summary keys for this experiment.
|
||||||
|
//
|
||||||
|
// Note that this structure is part of the ABI contract with ooniprobe
|
||||||
|
// therefore we should be careful when changing it.
|
||||||
|
type SummaryKeys struct {
|
||||||
|
//DNSBlocking bool `json:"facebook_dns_blocking"`
|
||||||
|
//TCPBlocking bool `json:"facebook_tcp_blocking"`
|
||||||
|
IsAnomaly bool `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSummaryKeys implements model.ExperimentMeasurer.GetSummaryKeys.
|
||||||
|
func (m Measurer) GetSummaryKeys(measurement *model.Measurement) (interface{}, error) {
|
||||||
|
sk := SummaryKeys{IsAnomaly: false}
|
||||||
|
_, ok := measurement.TestKeys.(*TestKeys)
|
||||||
|
if !ok {
|
||||||
|
return sk, errors.New("invalid test keys type")
|
||||||
|
}
|
||||||
|
return sk, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
package smtp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/engine/mockable"
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func plaintextListener() net.Listener {
|
||||||
|
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||||
|
if err != nil {
|
||||||
|
if l, err = net.Listen("tcp6", "[::1]:0"); err != nil {
|
||||||
|
panic(fmt.Sprintf("httptest: failed to listen on a port: %v", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func tlsListener(l net.Listener) net.Listener {
|
||||||
|
return tls.NewListener(l, &tls.Config{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func listenerAddr(l net.Listener) string {
|
||||||
|
return l.Addr().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidSMTPServer(conn net.Conn) {
|
||||||
|
for {
|
||||||
|
command, err := bufio.NewReader(conn).ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if command == "" {
|
||||||
|
} else if command == "NOOP" {
|
||||||
|
conn.Write([]byte("250 2.0.0 Ok\n"))
|
||||||
|
} else if command == "STARTTLS" {
|
||||||
|
conn.Write([]byte("220 2.0.0 Ready to start TLS\n"))
|
||||||
|
// TODO: conn.Close does not actually close connection? or does client not detect it?
|
||||||
|
conn.Close()
|
||||||
|
return
|
||||||
|
} else if strings.HasPrefix(command, "EHLO") {
|
||||||
|
conn.Write([]byte("250 mock.example.com\n"))
|
||||||
|
}
|
||||||
|
conn.Write([]byte("\n"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TCPServer(l net.Listener) {
|
||||||
|
for {
|
||||||
|
conn, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
conn.Write([]byte("220 mock.example.com ESMTP (spam is not appreciated)\n"))
|
||||||
|
ValidSMTPServer(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMeasurer_run(t *testing.T) {
|
||||||
|
// runHelper is an helper function to run this set of tests.
|
||||||
|
runHelper := func(input string) (*model.Measurement, model.ExperimentMeasurer, error) {
|
||||||
|
m := NewExperimentMeasurer(Config{})
|
||||||
|
if m.ExperimentName() != "smtp" {
|
||||||
|
t.Fatal("invalid experiment name")
|
||||||
|
}
|
||||||
|
if m.ExperimentVersion() != "0.0.1" {
|
||||||
|
t.Fatal("invalid experiment version")
|
||||||
|
}
|
||||||
|
ctx := context.Background()
|
||||||
|
meas := &model.Measurement{
|
||||||
|
Input: model.MeasurementTarget(input),
|
||||||
|
}
|
||||||
|
sess := &mockable.Session{
|
||||||
|
MockableLogger: model.DiscardLogger,
|
||||||
|
}
|
||||||
|
|
||||||
|
args := &model.ExperimentArgs{
|
||||||
|
Callbacks: model.NewPrinterCallbacks(model.DiscardLogger),
|
||||||
|
Measurement: meas,
|
||||||
|
Session: sess,
|
||||||
|
}
|
||||||
|
err := m.Run(ctx, args)
|
||||||
|
return meas, m, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("with empty input", func(t *testing.T) {
|
||||||
|
_, _, err := runHelper("")
|
||||||
|
if !errors.Is(err, errNoInputProvided) {
|
||||||
|
t.Fatal("unexpected error", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("with invalid URL", func(t *testing.T) {
|
||||||
|
_, _, err := runHelper("\t")
|
||||||
|
if !errors.Is(err, errInputIsNotAnURL) {
|
||||||
|
t.Fatal("unexpected error", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("with invalid scheme", func(t *testing.T) {
|
||||||
|
_, _, err := runHelper("https://8.8.8.8:443/")
|
||||||
|
if !errors.Is(err, errInvalidScheme) {
|
||||||
|
t.Fatal("unexpected error", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("with broken TLS", func(t *testing.T) {
|
||||||
|
p := plaintextListener()
|
||||||
|
defer p.Close()
|
||||||
|
|
||||||
|
l := tlsListener(p)
|
||||||
|
defer l.Close()
|
||||||
|
addr := listenerAddr(l)
|
||||||
|
go TCPServer(l)
|
||||||
|
|
||||||
|
meas, m, err := runHelper("smtps://" + addr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tk := meas.TestKeys.(*TestKeys)
|
||||||
|
|
||||||
|
for _, run := range tk.Runs {
|
||||||
|
if *run.TLSHandshake.Failure != "unknown_failure: remote error: tls: unrecognized name" {
|
||||||
|
t.Fatal("expected unrecognized_name in TLS handshake")
|
||||||
|
}
|
||||||
|
|
||||||
|
if run.noopCounter != 0 {
|
||||||
|
t.Fatalf("expected to not have any noops, not %d noops", run.noopCounter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ask, err := m.GetSummaryKeys(meas)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("cannot obtain summary")
|
||||||
|
}
|
||||||
|
summary := ask.(SummaryKeys)
|
||||||
|
if summary.IsAnomaly {
|
||||||
|
t.Fatal("expected no anomaly")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("with broken starttls", func(t *testing.T) {
|
||||||
|
l := plaintextListener()
|
||||||
|
defer l.Close()
|
||||||
|
addr := listenerAddr(l)
|
||||||
|
|
||||||
|
go TCPServer(l)
|
||||||
|
|
||||||
|
meas, m, err := runHelper("smtp://" + addr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tk := meas.TestKeys.(*TestKeys)
|
||||||
|
|
||||||
|
for _, run := range tk.Runs {
|
||||||
|
if *run.TLSHandshake.Failure != "generic_timeout_error" {
|
||||||
|
t.Fatal("expected timeout in TLS handshake")
|
||||||
|
}
|
||||||
|
|
||||||
|
if run.noopCounter != 0 {
|
||||||
|
t.Fatalf("expected to not have any noops, not %d noops", run.noopCounter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ask, err := m.GetSummaryKeys(meas)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("cannot obtain summary")
|
||||||
|
}
|
||||||
|
summary := ask.(SummaryKeys)
|
||||||
|
if summary.IsAnomaly {
|
||||||
|
t.Fatal("expected no anomaly")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package registry
|
|
||||||
|
|
||||||
//
|
|
||||||
// Registers the `dnsping' experiment.
|
|
||||||
//
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/experiment/bittorrent"
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
AllExperiments["bittorrent"] = &Factory{
|
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
|
||||||
return bittorrent.NewExperimentMeasurer(
|
|
||||||
*config.(*bittorrent.Config),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
config: &bittorrent.Config{},
|
|
||||||
inputPolicy: model.InputOrStaticDefault,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package registry
|
|
||||||
|
|
||||||
//
|
|
||||||
// Registers the `dnsping' experiment.
|
|
||||||
//
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/experiment/dht"
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
AllExperiments["dht"] = &Factory{
|
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
|
||||||
return dht.NewExperimentMeasurer(
|
|
||||||
*config.(*dht.Config),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
config: &dht.Config{},
|
|
||||||
inputPolicy: model.InputOrStaticDefault,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package registry
|
||||||
|
|
||||||
|
//
|
||||||
|
// Registers the 'imap' experiment.
|
||||||
|
//
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/engine/experiment/imap"
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
AllExperiments["imap"] = &Factory{
|
||||||
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
|
return imap.NewExperimentMeasurer(
|
||||||
|
*config.(*imap.Config),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
config: &imap.Config{},
|
||||||
|
inputPolicy: model.InputOrStaticDefault,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package registry
|
||||||
|
|
||||||
|
//
|
||||||
|
// Registers the 'smtp' experiment.
|
||||||
|
//
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/engine/experiment/smtp"
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
AllExperiments["smtp"] = &Factory{
|
||||||
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
|
return smtp.NewExperimentMeasurer(
|
||||||
|
*config.(*smtp.Config),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
config: &smtp.Config{},
|
||||||
|
inputPolicy: model.InputOrStaticDefault,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
package tcprunner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/measurexlite"
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/model"
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/tracex"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Model describes a type that does a DNS lookup(s), then attempts several TCP sessions
|
||||||
|
type Model interface {
|
||||||
|
// Stores the provided hostname
|
||||||
|
Hostname(string)
|
||||||
|
// Store DNS query result
|
||||||
|
DNSResults([]*model.ArchivalDNSLookupResult)
|
||||||
|
// Indicates one or more steps failed (can be overwritten)
|
||||||
|
Failed(string)
|
||||||
|
// Stores a new individual test key (for a TCP session) and returns a pointer to it
|
||||||
|
NewRun(string, string) TCPSessionModel
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCPSessionModel describes a type that does a single TCP connection and TLS handshake with a given IP/Port combo
|
||||||
|
type TCPSessionModel interface {
|
||||||
|
// Store IP/port address used for this session
|
||||||
|
IPPort(string, string)
|
||||||
|
// Store TCP connect result
|
||||||
|
ConnectResults([]*model.ArchivalTCPConnectResult)
|
||||||
|
// Store TLS handshake result
|
||||||
|
HandshakeResult(*model.ArchivalTLSOrQUICHandshakeResult)
|
||||||
|
// Indicates a failure string, as well as an identifier for the failed step
|
||||||
|
FailedStep(string, string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCPRunner manages sequential TCP sessions to the same hostname (over different IPs)
|
||||||
|
type TCPRunner struct {
|
||||||
|
Tk Model
|
||||||
|
Trace *measurexlite.Trace
|
||||||
|
Logger model.Logger
|
||||||
|
Ctx context.Context
|
||||||
|
Tlsconfig *tls.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCPSession Manages a single TCP session and TLS handshake to a given ip:port
|
||||||
|
type TCPSession struct {
|
||||||
|
Itk TCPSessionModel
|
||||||
|
Runner *TCPRunner
|
||||||
|
Addr string
|
||||||
|
Port string
|
||||||
|
TLS bool
|
||||||
|
RawConn *net.Conn
|
||||||
|
TLSConn *net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
// FailedStep saves a failure (with an associated failed step identifier) into IndividualTestKeys
|
||||||
|
func (s *TCPSession) FailedStep(failure string, step string) {
|
||||||
|
// Save FailedStep inside ITK
|
||||||
|
s.Itk.FailedStep(failure, step)
|
||||||
|
// Copy FailedStep to global TK
|
||||||
|
s.Runner.Tk.Failed(failure)
|
||||||
|
// Print the warning message
|
||||||
|
s.Runner.Logger.Warn(failure)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the open TCP connections
|
||||||
|
func (s *TCPSession) Close() {
|
||||||
|
if s.TLS {
|
||||||
|
var conn = *s.TLSConn
|
||||||
|
conn.Close()
|
||||||
|
} else {
|
||||||
|
// TODO: should raw connection be closed anyway?
|
||||||
|
var conn = *s.RawConn
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurrentConn returns the currently active connection (TLS or plaintext)
|
||||||
|
func (s *TCPSession) CurrentConn() net.Conn {
|
||||||
|
if s.TLS {
|
||||||
|
// TODO: move to Debugf
|
||||||
|
s.Runner.Logger.Infof("Reusing TLS connection")
|
||||||
|
return *s.TLSConn
|
||||||
|
}
|
||||||
|
s.Runner.Logger.Infof("Reusing plaintext connection")
|
||||||
|
return *s.RawConn
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conn initializes a new Run and IndividualTestKeys
|
||||||
|
func (r *TCPRunner) Conn(addr string, port string) (*TCPSession, bool) {
|
||||||
|
// Get new individual test keys
|
||||||
|
itk := r.Tk.NewRun(addr, port)
|
||||||
|
|
||||||
|
s := new(TCPSession)
|
||||||
|
s.Runner = r
|
||||||
|
s.Itk = itk
|
||||||
|
s.Addr = addr
|
||||||
|
s.Port = port
|
||||||
|
s.TLS = false
|
||||||
|
|
||||||
|
if !s.Conn(addr, port) {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return s, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conn starts a new TCP/IP connection to addr/port
|
||||||
|
func (s *TCPSession) Conn(addr string, port string) bool {
|
||||||
|
dialer := s.Runner.Trace.NewDialerWithoutResolver(s.Runner.Logger)
|
||||||
|
s.Runner.Logger.Infof("Dialing to %s:%s", addr, port)
|
||||||
|
conn, err := dialer.DialContext(s.Runner.Ctx, "tcp", net.JoinHostPort(addr, port))
|
||||||
|
s.Itk.ConnectResults(s.Runner.Trace.TCPConnects())
|
||||||
|
if err != nil {
|
||||||
|
s.FailedStep(*tracex.NewFailure(err), "tcp_connect")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s.RawConn = &conn
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve resolves a hostname to a list of addresses
|
||||||
|
func (r *TCPRunner) Resolve(host string) ([]string, bool) {
|
||||||
|
r.Logger.Infof("Resolving DNS for %s", host)
|
||||||
|
resolver := r.Trace.NewStdlibResolver(r.Logger)
|
||||||
|
addrs, err := resolver.LookupHost(r.Ctx, host)
|
||||||
|
r.Tk.DNSResults(r.Trace.DNSLookupsFromRoundTrip())
|
||||||
|
if err != nil {
|
||||||
|
r.Tk.Failed(*tracex.NewFailure(err))
|
||||||
|
return []string{}, false
|
||||||
|
}
|
||||||
|
r.Logger.Infof("Finished DNS for %s: %v", host, addrs)
|
||||||
|
|
||||||
|
return addrs, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handshake performs a TLS handshake over the currently active connection
|
||||||
|
func (s *TCPSession) Handshake() bool {
|
||||||
|
if s.TLS {
|
||||||
|
// TLS already initialized...
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
s.Runner.Logger.Infof("Starting TLS handshake with %s:%s", s.Addr, s.Port)
|
||||||
|
thx := s.Runner.Trace.NewTLSHandshakerStdlib(s.Runner.Logger)
|
||||||
|
tconn, _, err := thx.Handshake(s.Runner.Ctx, *s.RawConn, s.Runner.Tlsconfig)
|
||||||
|
s.Itk.HandshakeResult(s.Runner.Trace.FirstTLSHandshakeOrNil())
|
||||||
|
if err != nil {
|
||||||
|
s.FailedStep(*tracex.NewFailure(err), "tls_handshake")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
s.TLS = true
|
||||||
|
s.TLSConn = &tconn
|
||||||
|
s.Runner.Logger.Infof("Handshake succeeded")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartTLS performs a StartTLS exchange by sending a message over the plaintext connection, waiting for a specific
|
||||||
|
// response, then performing a TLS handshake
|
||||||
|
func (s *TCPSession) StartTLS(message string, waitForResponse string) bool {
|
||||||
|
if s.TLS {
|
||||||
|
s.Runner.Logger.Warn("Requested TCPSession to do StartTLS when TLS is already enabled")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if message != "" {
|
||||||
|
s.Runner.Logger.Infof("Asking for StartTLS upgrade")
|
||||||
|
s.CurrentConn().Write([]byte(message))
|
||||||
|
}
|
||||||
|
|
||||||
|
if waitForResponse != "" {
|
||||||
|
s.Runner.Logger.Infof("Waiting for server response containing: %s", waitForResponse)
|
||||||
|
conn := s.CurrentConn()
|
||||||
|
for {
|
||||||
|
line, err := bufio.NewReader(conn).ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
s.FailedStep(*tracex.NewFailure(err), "starttls_wait_ok")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s.Runner.Logger.Debugf("Received: %s", line)
|
||||||
|
if strings.Contains(line, waitForResponse) {
|
||||||
|
s.Runner.Logger.Infof("Server is ready for StartTLS")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.Handshake()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user