mirror of
https://github.com/AdguardTeam/AdGuardDNS.git
synced 2025-02-20 11:23:36 +08:00
Sync v2.1.1
This commit is contained in:
parent
328512e850
commit
5de1a253fc
1
.gitignore
vendored
1
.gitignore
vendored
@ -18,3 +18,4 @@ config.yml
|
||||
country.mmdb
|
||||
dnsdb.bolt
|
||||
querylog.jsonl
|
||||
profilecache.json
|
||||
|
42
CHANGELOG.md
42
CHANGELOG.md
@ -11,9 +11,47 @@ The format is **not** based on [Keep a Changelog][kec], since the project
|
||||
|
||||
|
||||
|
||||
## AGDNS-1174 / Build 397
|
||||
|
||||
* DNS Server Check now responds with NODATA message to all non-A neither non-AAAA requests.
|
||||
|
||||
|
||||
|
||||
## AGDNS-911 / Build 375
|
||||
|
||||
* Added support for running a DoH3 server. No configuration changes are
|
||||
required to run it. If there was a DoH server configured, it will start
|
||||
listening for HTTP/3 connections on the same port where it listens for
|
||||
HTTP/2. Make sure that udp/443 is allowed in the iptables configuration on
|
||||
the server.
|
||||
|
||||
|
||||
|
||||
## AGDNS-842 / Build 372
|
||||
|
||||
* The new environment variable `PROFILES_CACHE_PATH` has been added. Its
|
||||
default value is `./profilecache.json`. Adjust the value, if necessary.
|
||||
|
||||
|
||||
|
||||
## AGDNS-891 / Build 371
|
||||
|
||||
* The property `server` of `upstream` object has been changed. Now it
|
||||
is a URL optionally starting with `tcp://` or `udp://`, and then an address
|
||||
in `ip:port` format.
|
||||
|
||||
```yaml
|
||||
upstream:
|
||||
server: 'tcp://8.8.8.8:53'
|
||||
```
|
||||
|
||||
Adjust the value, if necessary.
|
||||
|
||||
|
||||
|
||||
## AGDNS-1032 / Build 363
|
||||
|
||||
* The new optional field `static_content.*.allow_origin` has been added:
|
||||
* The new optional field `static_content.*.allow_origin` has been added:
|
||||
|
||||
```yaml
|
||||
static_content:
|
||||
@ -25,7 +63,7 @@ The format is **not** based on [Keep a Changelog][kec], since the project
|
||||
|
||||
## AGDNS-898 / Build 359
|
||||
|
||||
* The new optional object `additional_metrics_info` has been added:
|
||||
* The new optional object `additional_metrics_info` has been added:
|
||||
|
||||
```yaml
|
||||
additional_metrics_info:
|
||||
|
@ -48,7 +48,7 @@ cache:
|
||||
|
||||
# DNS upstream configuration.
|
||||
upstream:
|
||||
server: '127.0.0.9:53'
|
||||
server: '8.8.8.8:53'
|
||||
timeout: 2s
|
||||
fallback:
|
||||
- 1.1.1.1:53
|
||||
|
@ -139,9 +139,13 @@ The `cache` object has the following properties:
|
||||
The `upstream` object has the following properties:
|
||||
|
||||
* <a href="#upstream-server" id="upstream-server" name="upstream-server">`server`</a>:
|
||||
The address of the main upstream server, in the `ip:port` format.
|
||||
The URL of the main upstream server, in the `[scheme://]ip:port` format.
|
||||
|
||||
**Example:** `8.8.8.8:53` or `[2001:4860:4860::8844]:53`.
|
||||
**Examples:**
|
||||
|
||||
- `8.8.8.8:53`: regular DNS (over UDP with TCP fallback).
|
||||
- `tcp://1.1.1.1:53`: regular DNS (over TCP).
|
||||
- `udp://1.1.1.1:53`: regular DNS (over UDP).
|
||||
|
||||
* <a href="#upstream-timeout" id="upstream-timeout" name="upstream-timeout">`timeout`</a>:
|
||||
Timeout for all outgoing DNS requests, as a human-readable duration.
|
||||
@ -223,8 +227,9 @@ The `backend` object has the following properties:
|
||||
**Example:** `1m`.
|
||||
|
||||
* <a href="#backend-full_refresh_interval" id="backend-full_refresh_interval" name="backend-full_refresh_interval">`full_refresh_interval`</a>:
|
||||
How often AdGuard DNS performs full synchronization, as a human-readable
|
||||
duration.
|
||||
How often AdGuard DNS performs a full profile refresh, as a human-readable
|
||||
duration. If [`PROFILES_CACHE_PATH`][env-profiles_cache_path] is set, the
|
||||
profile cache is also saved after a full refresh.
|
||||
|
||||
**Example:** `24h`.
|
||||
|
||||
@ -234,6 +239,8 @@ The `backend` object has the following properties:
|
||||
|
||||
**Example:** `1m`.
|
||||
|
||||
[env-profiles_cache_path]: environment.md#PROFILES_CACHE_PATH
|
||||
|
||||
|
||||
|
||||
## <a href="#query_log" id="query_log" name="query_log">Query Log</a>
|
||||
@ -825,7 +832,7 @@ The `connectivity_check` object has the following properties:
|
||||
The `additional_metrics_info` object is a map of strings with extra information
|
||||
which is exposed by `dns_app_additional_info` metric.
|
||||
|
||||
Map keys must match reqular expression `^[a-zA-Z_][a-zA-Z0-9_]*$`. See
|
||||
Map keys must match regular expression `^[a-zA-Z_][a-zA-Z0-9_]*$`. See
|
||||
[Prometheus documentation on valid labels][prom-label].
|
||||
|
||||
**Property example:**
|
||||
|
@ -206,6 +206,8 @@ Examples below are for the configuration with the following changes:
|
||||
* DoH: `443` → `8443`
|
||||
* DoQ: `853` → `8853`
|
||||
|
||||
You may also need to remove `probe_ipv6` if your network does not support IPv6.
|
||||
|
||||
```sh
|
||||
env \
|
||||
BACKEND_ENDPOINT='PUT BACKEND URL HERE' \
|
||||
@ -215,6 +217,7 @@ env \
|
||||
DNSDB_PATH='./test/cache/dnsdb.bolt' \
|
||||
FILTER_INDEX_URL='https://atropnikov.github.io/HostlistsRegistry/assets/filters.json' \
|
||||
FILTER_CACHE_PATH='./test/cache' \
|
||||
PROFILES_CACHE_PATH='./test/profilecache.json' \
|
||||
GENERAL_SAFE_SEARCH_URL='https://adguardteam.github.io/HostlistsRegistry/assets/engines_safe_search.txt' \
|
||||
GEOIP_ASN_PATH='./test/GeoLite2-ASN-Test.mmdb' \
|
||||
GEOIP_COUNTRY_PATH='./test/GeoIP2-Country-Test.mmdb' \
|
||||
|
@ -15,6 +15,7 @@ sensitive configuration. All other configuration is stored in the
|
||||
* [`DNSDB_PATH`](#DNSDB_PATH)
|
||||
* [`FILTER_INDEX_URL`](#FILTER_INDEX_URL)
|
||||
* [`FILTER_CACHE_PATH`](#FILTER_CACHE_PATH)
|
||||
* [`PROFILES_CACHE_PATH`](#PROFILES_CACHE_PATH)
|
||||
* [`GENERAL_SAFE_SEARCH_URL`](#GENERAL_SAFE_SEARCH_URL)
|
||||
* [`GEOIP_ASN_PATH` and `GEOIP_COUNTRY_PATH`](#GEOIP_ASN_PATH)
|
||||
* [`LISTEN_ADDR`](#LISTEN_ADDR)
|
||||
@ -118,6 +119,17 @@ The path to the directory with the filter lists cache.
|
||||
|
||||
|
||||
|
||||
## <a href="#PROFILES_CACHE_PATH" id="PROFILES_CACHE_PATH" name="PROFILES_CACHE_PATH">`PROFILES_CACHE_PATH`</a>
|
||||
|
||||
The path to the profile cache file. The profile cache is read on start and is
|
||||
later updated on every [full refresh][conf-backend-full_refresh_interval].
|
||||
|
||||
**Default:** `./profilecache.json`.
|
||||
|
||||
[conf-backend-full_refresh_interval]: configuration.md#backend-full_refresh_interval
|
||||
|
||||
|
||||
|
||||
## <a href="#FILTER_INDEX_URL" id="FILTER_INDEX_URL" name="FILTER_INDEX_URL">`FILTER_INDEX_URL`</a>
|
||||
|
||||
The URL of the filtering rule index file server. See the [external HTTP API
|
||||
|
24
doc/http.md
24
doc/http.md
@ -42,6 +42,10 @@ with a random ID prepended to one of the [check domains][conf-check-domains]
|
||||
with a hyphen. The random ID must have from 4 to 63 characters and only include
|
||||
the alphanumeric characters and a hyphen.
|
||||
|
||||
<!--
|
||||
TODO(a.garipov): Describe the check process in details.
|
||||
-->
|
||||
|
||||
Example of the request:
|
||||
|
||||
```sh
|
||||
@ -67,28 +71,16 @@ The `protocol` field can have one of the following values:
|
||||
|
||||
<dl>
|
||||
<dt>
|
||||
<code>"dns-tcp"</code>
|
||||
<code>"dns"</code>
|
||||
</dt>
|
||||
<dd>
|
||||
Plain DNS over TCP.
|
||||
Plain DNS.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>"dns-udp"</code>
|
||||
<code>"dnscrypt"</code>
|
||||
</dt>
|
||||
<dd>
|
||||
Plain DNS over UDP.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>"dnscrypt-tcp"</code>
|
||||
</dt>
|
||||
<dd>
|
||||
DNSCrypt over TCP.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>"dnscrypt-udp"</code>
|
||||
</dt>
|
||||
<dd>
|
||||
DNSCrypt over UDP.
|
||||
DNSCrypt.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>"doh"</code>
|
||||
|
@ -5,8 +5,8 @@ entries are designed to be concise and easily compressable. An example of the
|
||||
log output:
|
||||
|
||||
```jsonl
|
||||
{"u":"ABCD","b":"prof1234","i":"dev1234","c":"RU","d":"US","n":"example.com.","l":"cdef5678","m":"||example.com^","t":1628590394000,"a":1234,"e":5,"q":1,"f":2,"s":0,"p":2,"r":0}
|
||||
{"u":"DEFG","b":"prof1234","i":"dev1234","c":"RU","d":"JP","n":"example.org.","l":"hijk9012","m":"||example.org^","t":1628590394100,"a":6789,"e":6,"q":1,"f":2,"s":0,"p":2,"r":0}
|
||||
{"u":"ABCD","b":"prof1234","i":"dev1234","c":"RU","d":"US","n":"example.com.","l":"cdef5678","m":"||example.com^","t":1628590394000,"a":1234,"e":5,"q":1,"f":2,"s":0,"p":8,"r":0}
|
||||
{"u":"DEFG","b":"prof1234","i":"dev1234","c":"RU","d":"JP","n":"example.org.","l":"hijk9012","m":"||example.org^","t":1628590394100,"a":6789,"e":6,"q":1,"f":2,"s":0,"p":8,"r":0}
|
||||
```
|
||||
|
||||
AdGuard DNS opens and closes the log file on each write to prevent issues with
|
||||
@ -207,23 +207,11 @@ rules to remember, which property means what. The properties are:
|
||||
<dd>
|
||||
Invalid or unknown protocol. Typically, this value is never used.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>1</code>
|
||||
</dt>
|
||||
<dd>
|
||||
Plain DNS over TCP.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>2</code>
|
||||
</dt>
|
||||
<dd>
|
||||
Plain DNS over UDP.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>3</code>
|
||||
</dt>
|
||||
<dd>
|
||||
DNS-over-HTTP.
|
||||
DNS-over-HTTPS.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>4</code>
|
||||
@ -238,20 +226,20 @@ rules to remember, which property means what. The properties are:
|
||||
DNS-over-TLS.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>6</code>
|
||||
<code>8</code>
|
||||
</dt>
|
||||
<dd>
|
||||
DNSCrypt over TCP.
|
||||
Plain DNS.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>7</code>
|
||||
<code>9</code>
|
||||
</dt>
|
||||
<dd>
|
||||
DNSCrypt over UDP.
|
||||
DNSCrypt.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
**Example:** `2`
|
||||
**Example:** `3`
|
||||
|
||||
* <a href="#properties-r" id="properties-r" name="properties-r">`r`</a>:
|
||||
The response code (aka `RCODE`) sent to the client. The short name `r`
|
||||
|
44
go.mod
44
go.mod
@ -4,26 +4,26 @@ go 1.19
|
||||
|
||||
require (
|
||||
github.com/AdguardTeam/AdGuardDNS/internal/dnsserver v0.100.0
|
||||
github.com/AdguardTeam/golibs v0.10.9
|
||||
github.com/AdguardTeam/golibs v0.11.3
|
||||
github.com/AdguardTeam/urlfilter v0.16.0
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.3
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.5
|
||||
github.com/bluele/gcache v0.0.2
|
||||
github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b
|
||||
github.com/caarlos0/env/v6 v6.9.3
|
||||
github.com/getsentry/raven-go v0.2.0
|
||||
github.com/caarlos0/env/v6 v6.10.1
|
||||
github.com/getsentry/sentry-go v0.14.0
|
||||
github.com/google/renameio v1.0.1
|
||||
github.com/miekg/dns v1.1.50
|
||||
github.com/oschwald/maxminddb-golang v1.10.0
|
||||
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible
|
||||
github.com/prometheus/client_golang v1.13.0
|
||||
github.com/prometheus/client_model v0.2.0
|
||||
github.com/prometheus/client_golang v1.13.1
|
||||
github.com/prometheus/client_model v0.3.0
|
||||
github.com/prometheus/common v0.37.0
|
||||
github.com/stretchr/testify v1.8.0
|
||||
go.etcd.io/bbolt v1.3.6
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234
|
||||
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9
|
||||
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326
|
||||
golang.org/x/net v0.1.0
|
||||
golang.org/x/sys v0.1.0
|
||||
golang.org/x/time v0.1.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
@ -32,28 +32,26 @@ require (
|
||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect
|
||||
github.com/ameshkov/dnsstamps v1.0.3 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cheekybits/genny v1.0.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/lucas-clemente/quic-go v0.28.1 // indirect
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
|
||||
github.com/marten-seemann/qtls-go1-17 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/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/lucas-clemente/quic-go v0.29.2 // indirect
|
||||
github.com/marten-seemann/qpack v0.3.0 // indirect
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
golang.org/x/crypto v0.1.0 // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
206
go.sum
206
go.sum
@ -1,7 +1,5 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
@ -32,17 +30,11 @@ 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.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
|
||||
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/golibs v0.4.2/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/golibs v0.10.4/go.mod h1:rSfQRGHIdgfxriDDNgNJ7HmE5zRoURq8R+VdR81Zuzw=
|
||||
github.com/AdguardTeam/golibs v0.10.9 h1:F9oP2da0dQ9RQDM1lGR7LxUTfUWu8hEFOs4icwAkKM0=
|
||||
github.com/AdguardTeam/golibs v0.10.9/go.mod h1:W+5rznZa1cSNSFt+gPS7f4Wytnr9fOrd5ZYqwadPw14=
|
||||
github.com/AdguardTeam/golibs v0.11.3 h1:Oif+REq2WLycQ2Xm3ZPmJdfftptss0HbGWbxdFaC310=
|
||||
github.com/AdguardTeam/golibs v0.11.3/go.mod h1:87bN2x4VsTritptE3XZg9l8T6gznWsIxHBcQ1DeRIXA=
|
||||
github.com/AdguardTeam/gomitmproxy v0.2.0/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU=
|
||||
github.com/AdguardTeam/urlfilter v0.16.0 h1:IO29m+ZyQuuOnPLTzHuXj35V1DZOp1Dcryl576P2syg=
|
||||
github.com/AdguardTeam/urlfilter v0.16.0/go.mod h1:46YZDOV1+qtdRDuhZKVPSSp7JWWes0KayqHrKAFBdEI=
|
||||
@ -59,58 +51,44 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.3 h1:X9UP5AHtwp46Ji+sGFfF/1Is6OPI/SjxLqhKpx0P5UI=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.3/go.mod h1:xJB9cE1/GF+NB6EEQqRlkoa4bjcV2w7VYn1G+zVq7Bs=
|
||||
github.com/ameshkov/dnsstamps v1.0.1/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.5 h1:Ju1gQeez+6XLtk/b/k3RoJ2t+Ls+BSItLTZjZeedneY=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.5/go.mod h1:Cu5GgMvCR10BeXgACiGDwXyOpfMktsSIidml1XBp6uM=
|
||||
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
|
||||
github.com/ameshkov/dnsstamps v1.0.3/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
|
||||
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b h1:6+ZFm0flnudZzdSE0JxlhR2hKnGPcNB35BjQf4RYQDY=
|
||||
github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
|
||||
github.com/caarlos0/env/v6 v6.9.3 h1:Tyg69hoVXDnpO5Qvpsu8EoquarbPyQb+YwExWHP8wWU=
|
||||
github.com/caarlos0/env/v6 v6.9.3/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc=
|
||||
github.com/caarlos0/env/v6 v6.10.1 h1:t1mPSxNpei6M5yAeu1qtRdPAK29Nbcf/n3G7x+b3/II=
|
||||
github.com/caarlos0/env/v6 v6.10.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d h1:S2NE3iHSwP0XV47EEXL8mWmRdEfGscSJ+7EgePNgt0s=
|
||||
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
|
||||
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
||||
github.com/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.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
|
||||
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/getsentry/sentry-go v0.14.0 h1:rlOBkuFZRKKdUnKO+0U3JclRDQKlRu5vVQtkWSQvC70=
|
||||
github.com/getsentry/sentry-go v0.14.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@ -122,6 +100,8 @@ 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.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
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-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
|
||||
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
@ -132,7 +112,6 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
@ -170,9 +149,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
@ -186,18 +163,12 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/renameio v1.0.1 h1:Lh/jXZmvZxb0BBeSY5VKEfidcbcbenKjZFzM/q0fSeU=
|
||||
github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
@ -214,28 +185,20 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
|
||||
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/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/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/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.2 h1:JADBlm0LYiVbuSySCHeY863dNkcpMmDR7s0bLKJeYlQ=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.2/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s=
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.2 h1:JH6jmzbduz0ITVQ7ShevK10Av5+jBEKAHMntXmIV7kM=
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.0 h1:rLFKD/9mp/uq1SYGYuVZhm83wkmU95pK5df3GufyYYU=
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.0/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/lucas-clemente/quic-go v0.29.2 h1:O8Mt0O6LpvEW+wfC40vZdcw0DngwYzoxq5xULZNzSI8=
|
||||
github.com/lucas-clemente/quic-go v0.29.2/go.mod h1:g6/h9YMmLuU54tL1gW25uIi3VlBp3uv+sBihplIuskE=
|
||||
github.com/marten-seemann/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE=
|
||||
github.com/marten-seemann/qpack v0.3.0/go.mod h1:cGfKPBiP4a9EQdxCwEwI/GEeWAsjSekBvx/X8mh58+g=
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.3 h1:R4H2Ks8P6pAtUagjFty2p7BVHn3XiwDAl7TTQf5h7TI=
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.3/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.1 h1:mnbxeq3oEyQxQXwI4ReCgW9DPoPR94sNlqWoDZnjRIE=
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
@ -246,8 +209,6 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
@ -255,47 +216,42 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
|
||||
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
|
||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI=
|
||||
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
|
||||
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
|
||||
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible h1:IWzUvJ72xMjmrjR9q3H1PF+jwdN0uNQiR2t1BLNalyo=
|
||||
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
|
||||
github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
|
||||
github.com/prometheus/client_golang v1.13.1 h1:3gMjIY2+/hzmqhtUC/aQNYldJA6DtH3CgQvwS+02K1c=
|
||||
github.com/prometheus/client_golang v1.13.1/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
||||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
|
||||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
@ -304,37 +260,11 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
|
||||
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shirou/gopsutil/v3 v3.21.8 h1:nKct+uP0TV8DjjNiHanKf8SAuub+GNsbrOtM9Nl9biA=
|
||||
github.com/shirou/gopsutil/v3 v3.21.8/go.mod h1:YWp/H8Qs5fVmf17v7JNZzA0mPJ+mS2e9JdiUF9LlKzQ=
|
||||
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
|
||||
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
|
||||
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
|
||||
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
|
||||
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
|
||||
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
|
||||
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
|
||||
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
|
||||
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
|
||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
|
||||
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
|
||||
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
|
||||
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
|
||||
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
|
||||
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
|
||||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
|
||||
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
@ -342,18 +272,14 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo=
|
||||
github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
|
||||
github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ=
|
||||
github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=
|
||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@ -361,26 +287,19 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 h1:GIAS/yBem/gq2MUqgNIzUHW7cJMmx3TGZOrnyYaNQ6c=
|
||||
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@ -391,11 +310,10 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
|
||||
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 h1:QfTh0HpN6hlw6D3vu8DAwC8pBIwikq0AI1evdm+FksE=
|
||||
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@ -415,18 +333,15 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.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 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
|
||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
@ -435,7 +350,6 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@ -454,25 +368,20 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E=
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -488,11 +397,9 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+v
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -502,7 +409,6 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -520,7 +426,6 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -542,9 +447,8 @@ golang.org/x/sys v0.0.0-20210909193231-528a39cd75f3/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U=
|
||||
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -553,17 +457,15 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ=
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA=
|
||||
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
@ -583,7 +485,6 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
@ -607,15 +508,12 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
|
||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
@ -633,18 +531,12 @@ google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
@ -673,9 +565,6 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
@ -710,7 +599,6 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8X
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@ -724,8 +612,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
@ -736,5 +622,3 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||
|
14
go.work.sum
14
go.work.sum
@ -10,6 +10,8 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412 h1:GvWw74
|
||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c h1:ivON6cwHK1OH26MZyWDCnbTRZZf0IhNsENoNAKFS1g4=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999 h1:OR8VhtwhcAI3U48/rzBsVOuHi0zDPzYI1xASVcdSgR8=
|
||||
github.com/AdguardTeam/golibs v0.10.7/go.mod h1:rSfQRGHIdgfxriDDNgNJ7HmE5zRoURq8R+VdR81Zuzw=
|
||||
github.com/AdguardTeam/golibs v0.11.2 h1:JbQB1Dg2JWStXgHh1QqBbOLWnP4t9oDjppoBH6TVXSE=
|
||||
github.com/AdguardTeam/golibs v0.11.2/go.mod h1:87bN2x4VsTritptE3XZg9l8T6gznWsIxHBcQ1DeRIXA=
|
||||
github.com/AdguardTeam/gomitmproxy v0.2.0 h1:rvCOf17pd1/CnMyMQW891zrEiIQBpQ8cIGjKN9pinUU=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
|
||||
@ -31,6 +33,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrp
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
|
||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||
github.com/getsentry/sentry-go v0.13.0 h1:20dgTiUSfxRB/EhMPtxcL9ZEbM1ZdR+W/7f7NWD+xWo=
|
||||
github.com/getsentry/sentry-go v0.13.0/go.mod h1:EOsfu5ZdvKPfeHYV6pTVQnsjfp30+XA7//UooKNumH0=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/gliderlabs/ssh v0.1.1 h1:j3L6gSLQalDETeEg/Jg0mGY0/y/N6zI2xX1978P0Uqw=
|
||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
||||
@ -45,7 +49,6 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekf
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7 h1:2hRPrmiwPrp3fQX967rNJIhQPtiGXdlQWAxKbKw3VHA=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||
@ -75,7 +78,6 @@ github.com/lucas-clemente/quic-go v0.25.0/go.mod h1:YtzP8bxRVCBlO77yRanE264+fY/T
|
||||
github.com/lucas-clemente/quic-go v0.27.1/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI=
|
||||
github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe h1:W/GaMY0y69G4cFlmsC6B9sbuo2fP8OFP1ABjt4kPz+w=
|
||||
github.com/marten-seemann/qpack v0.2.1 h1:jvTsT/HpCn2UZJdP+UUB53FfUUgeOyG5K1ns0OJOGVs=
|
||||
github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.4/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8=
|
||||
@ -90,6 +92,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86 h1:D6paGObi5Wud7xg83MaEFyjxQB1W5bz5d0IFppr+ymk=
|
||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab h1:eFXv9Nu1lGbrNbj619aWwZfVF5HBrm9Plte8aNptuTI=
|
||||
github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo=
|
||||
github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=
|
||||
github.com/openzipkin/zipkin-go v0.1.1 h1:A/ADD6HaPnAKj3yS7HjGHRK77qi41Hi0DirOOIQAeIw=
|
||||
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
@ -124,7 +128,6 @@ github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 h1:UyzmZLoiDWMRywV4DUY
|
||||
github.com/viant/assertly v0.4.8 h1:5x1GzBaRteIwTr5RAGFVG14uNeRFxVNbXPWrK2qAgpc=
|
||||
github.com/viant/toolbox v0.24.0 h1:6TteTDQ68CjgcCe8wH3D3ZhUQQOJXMTbj/D9rkk2a1k=
|
||||
github.com/yuin/goldmark v1.4.1 h1:/vn0k+RBvwlxEmP5E7SZMqNxPhfMVFEJiykr15/0XKM=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5 h1:+hE86LblG4AyDgwMCLTE6FOlM9+qjHSYS+rKqxUVdsM=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d h1:E2M5QgjZ/Jg+ObCQAudsXxuTsLj7Nl5RV/lZcQZmKSo=
|
||||
@ -137,13 +140,14 @@ golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220516155154-20f960328961/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg=
|
||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852 h1:xYq6+9AtI+xP3M4r0N1hCkHrInHDBohhquRgx9Kk6gI=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY=
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -6,13 +6,13 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
)
|
||||
|
||||
// Common Constants And Utilities
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
agdtest.DiscardLogOutput(m)
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
||||
// testTimeout is the timeout for common test operations.
|
||||
|
@ -21,13 +21,11 @@ const (
|
||||
// values are a part of the API.
|
||||
|
||||
ProtoInvalid = dnsserver.ProtoInvalid
|
||||
ProtoDNSTCP = dnsserver.ProtoDNSTCP
|
||||
ProtoDNSUDP = dnsserver.ProtoDNSUDP
|
||||
ProtoDNS = dnsserver.ProtoDNS
|
||||
ProtoDoH = dnsserver.ProtoDoH
|
||||
ProtoDoQ = dnsserver.ProtoDoQ
|
||||
ProtoDoT = dnsserver.ProtoDoT
|
||||
ProtoDNSCryptTCP = dnsserver.ProtoDNSCryptTCP
|
||||
ProtoDNSCryptUDP = dnsserver.ProtoDNSCryptUDP
|
||||
ProtoDNSCrypt = dnsserver.ProtoDNSCrypt
|
||||
)
|
||||
|
||||
// Resolver is the DNS resolver interface.
|
||||
|
@ -14,6 +14,8 @@ import (
|
||||
// the infrastructure, a profile is also called a “DNS server”. We call it
|
||||
// profile, because it's less confusing.
|
||||
//
|
||||
// NOTE: Increment defaultProfileDBCacheVersion on any change of this structure.
|
||||
//
|
||||
// TODO(a.garipov): Consider making it closer to the config file and the backend
|
||||
// response by grouping parental, rule list, and safe browsing settings into
|
||||
// separate structs.
|
||||
|
@ -2,8 +2,10 @@ package agd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -53,6 +55,9 @@ type DefaultProfileDB struct {
|
||||
// syncTimeFull is the time of the last full synchronization.
|
||||
syncTimeFull time.Time
|
||||
|
||||
// cacheFilePath is the path to profiles cache file.
|
||||
cacheFilePath string
|
||||
|
||||
// fullSyncIvl is the interval between two full synchronizations with the
|
||||
// storage
|
||||
fullSyncIvl time.Duration
|
||||
@ -64,6 +69,7 @@ type DefaultProfileDB struct {
|
||||
func NewDefaultProfileDB(
|
||||
ds ProfileStorage,
|
||||
fullRefreshIvl time.Duration,
|
||||
cacheFilePath string,
|
||||
) (db *DefaultProfileDB, err error) {
|
||||
db = &DefaultProfileDB{
|
||||
mapsMu: &sync.RWMutex{},
|
||||
@ -75,6 +81,12 @@ func NewDefaultProfileDB(
|
||||
deviceIDToIP: make(map[DeviceID]netip.Addr),
|
||||
ipToDeviceID: make(map[netip.Addr]DeviceID),
|
||||
fullSyncIvl: fullRefreshIvl,
|
||||
cacheFilePath: cacheFilePath,
|
||||
}
|
||||
|
||||
err = db.loadProfileCache()
|
||||
if err != nil {
|
||||
log.Error("profiledb: cache: loading: %s", err)
|
||||
}
|
||||
|
||||
// initialTimeout defines the maximum duration of the first attempt to load
|
||||
@ -94,7 +106,7 @@ func NewDefaultProfileDB(
|
||||
return db, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("profiledb: initial refresh: %w", err)
|
||||
return nil, fmt.Errorf("initial refresh: %w", err)
|
||||
}
|
||||
|
||||
log.Info("profiledb: initial refresh succeeded")
|
||||
@ -146,11 +158,105 @@ func (db *DefaultProfileDB) Refresh(ctx context.Context) (err error) {
|
||||
db.syncTime = resp.SyncTime
|
||||
if isFullSync {
|
||||
db.syncTimeFull = time.Now()
|
||||
|
||||
err = db.saveProfileCache(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("saving cache: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// profileCache is the structure for profiles db cache.
|
||||
type profileCache struct {
|
||||
SyncTime time.Time `json:"sync_time"`
|
||||
Profiles []*Profile `json:"profiles"`
|
||||
Version int `json:"version"`
|
||||
}
|
||||
|
||||
// saveStorageCache saves profiles data to cache file.
|
||||
func (db *DefaultProfileDB) saveProfileCache(ctx context.Context) (err error) {
|
||||
var resp *PSProfilesResponse
|
||||
resp, err = db.storage.Profiles(ctx, &PSProfilesRequest{
|
||||
SyncTime: time.Time{},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := &profileCache{
|
||||
Profiles: resp.Profiles,
|
||||
Version: defaultProfileDBCacheVersion,
|
||||
SyncTime: time.Now(),
|
||||
}
|
||||
|
||||
cache, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("encoding json: %w", err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(db.cacheFilePath, cache, 0o600)
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debug("profiledb: cache: saved %d profiles to %q", len(resp.Profiles), db.cacheFilePath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// defaultProfileDBCacheVersion is the version of cached data structure. It's
|
||||
// manually incremented on every change in [profileCache] structure.
|
||||
const defaultProfileDBCacheVersion = 1
|
||||
|
||||
// loadProfileCache loads profiles data from cache file.
|
||||
func (db *DefaultProfileDB) loadProfileCache() (err error) {
|
||||
data, err := db.loadStorageCache()
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading cache: %w", err)
|
||||
}
|
||||
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if data.Version == defaultProfileDBCacheVersion {
|
||||
profiles := data.Profiles
|
||||
devNum := db.setProfiles(profiles)
|
||||
log.Debug("profiledb: cache: got %d profiles with %d devices", len(profiles), devNum)
|
||||
|
||||
db.syncTime = data.SyncTime
|
||||
db.syncTimeFull = data.SyncTime
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadStorageCache loads data from cache file.
|
||||
func (db *DefaultProfileDB) loadStorageCache() (data *profileCache, err error) {
|
||||
file, err := os.Open(db.cacheFilePath)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// File could be deleted or not yet created, go on.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
defer func() { err = errors.WithDeferred(err, file.Close()) }()
|
||||
|
||||
data = &profileCache{}
|
||||
err = json.NewDecoder(file).Decode(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decoding json: %w", err)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// setProfiles adds or updates the data for all profiles.
|
||||
func (db *DefaultProfileDB) setProfiles(profiles []*Profile) (devNum int) {
|
||||
db.mapsMu.Lock()
|
||||
|
@ -3,6 +3,7 @@ package agd_test
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -32,7 +33,8 @@ func newDefaultProfileDB(tb testing.TB, dev *agd.Device) (db *agd.DefaultProfile
|
||||
OnProfiles: onProfiles,
|
||||
}
|
||||
|
||||
db, err := agd.NewDefaultProfileDB(ds, 1*time.Minute)
|
||||
cacheFilePath := filepath.Join(tb.TempDir(), "profiles.json")
|
||||
db, err := agd.NewDefaultProfileDB(ds, 1*time.Minute, cacheFilePath)
|
||||
require.NoError(tb, err)
|
||||
|
||||
return db
|
||||
|
@ -13,51 +13,53 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRefreshWorker(t *testing.T) {
|
||||
// Test Constants
|
||||
// sig is a convenient alias for struct{} when it's used as a signal for
|
||||
// synchronization.
|
||||
type sig = struct{}
|
||||
|
||||
const (
|
||||
const (
|
||||
testIvl = 5 * time.Millisecond
|
||||
testIvlLong = 1 * time.Hour
|
||||
name = "test refresher"
|
||||
testError errors.Error = "test error"
|
||||
)
|
||||
)
|
||||
|
||||
// Test Mocks
|
||||
// newTestRefresher is a helper that returns refr and linked syncCh channel.
|
||||
func newTestRefresher(t *testing.T, respErr error) (refr *agdtest.Refresher, syncCh chan sig) {
|
||||
t.Helper()
|
||||
|
||||
pt := testutil.PanicT{}
|
||||
refreshSync := make(chan agdtest.Signal, 1)
|
||||
|
||||
refr := &agdtest.Refresher{
|
||||
syncCh = make(chan sig, 1)
|
||||
refr = &agdtest.Refresher{
|
||||
OnRefresh: func(_ context.Context) (err error) {
|
||||
agdtest.RequireSend(pt, refreshSync, testTimeout)
|
||||
testutil.RequireSend(pt, syncCh, sig{}, testTimeout)
|
||||
|
||||
return nil
|
||||
return respErr
|
||||
},
|
||||
}
|
||||
|
||||
errRefr := &agdtest.Refresher{
|
||||
OnRefresh: func(_ context.Context) (err error) {
|
||||
agdtest.RequireSend(pt, refreshSync, testTimeout)
|
||||
return refr, syncCh
|
||||
}
|
||||
|
||||
return testError
|
||||
},
|
||||
}
|
||||
|
||||
errCh := make(chan agdtest.Signal, 1)
|
||||
errColl := &agdtest.ErrorCollector{
|
||||
OnCollect: func(_ context.Context, _ error) {
|
||||
agdtest.RequireSend(pt, errCh, testTimeout)
|
||||
},
|
||||
}
|
||||
|
||||
// Test Helpers
|
||||
|
||||
refrConf := func(
|
||||
// newRefrConf returns worker configuration.
|
||||
func newRefrConf(
|
||||
t *testing.T,
|
||||
refr agd.Refresher,
|
||||
ivl time.Duration,
|
||||
refrOnShutDown bool,
|
||||
) (conf *agd.RefreshWorkerConfig) {
|
||||
errCh chan sig,
|
||||
) (conf *agd.RefreshWorkerConfig) {
|
||||
t.Helper()
|
||||
|
||||
pt := testutil.PanicT{}
|
||||
|
||||
errColl := &agdtest.ErrorCollector{
|
||||
OnCollect: func(_ context.Context, _ error) {
|
||||
testutil.RequireSend(pt, errCh, sig{}, testTimeout)
|
||||
},
|
||||
}
|
||||
|
||||
return &agd.RefreshWorkerConfig{
|
||||
Context: func() (ctx context.Context, cancel context.CancelFunc) {
|
||||
return context.WithTimeout(context.Background(), testTimeout)
|
||||
@ -69,17 +71,19 @@ func TestRefreshWorker(t *testing.T) {
|
||||
RefreshOnShutdown: refrOnShutDown,
|
||||
RoutineLogsAreDebug: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Tests
|
||||
}
|
||||
|
||||
func TestRefreshWorker(t *testing.T) {
|
||||
t.Run("success", func(t *testing.T) {
|
||||
w := agd.NewRefreshWorker(refrConf(refr, testIvl, false))
|
||||
refr, syncCh := newTestRefresher(t, nil)
|
||||
errCh := make(chan sig, 1)
|
||||
|
||||
w := agd.NewRefreshWorker(newRefrConf(t, refr, testIvl, false, errCh))
|
||||
|
||||
err := w.Start()
|
||||
require.NoError(t, err)
|
||||
|
||||
agdtest.RequireReceive(pt, refreshSync, testTimeout)
|
||||
testutil.RequireReceive(t, syncCh, testTimeout)
|
||||
require.Empty(t, errCh)
|
||||
|
||||
shutdown, cancel := context.WithTimeout(context.Background(), testTimeout)
|
||||
@ -90,7 +94,10 @@ func TestRefreshWorker(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("success_on_shutdown", func(t *testing.T) {
|
||||
w := agd.NewRefreshWorker(refrConf(refr, testIvlLong, true))
|
||||
refr, syncCh := newTestRefresher(t, nil)
|
||||
errCh := make(chan sig, 1)
|
||||
|
||||
w := agd.NewRefreshWorker(newRefrConf(t, refr, testIvlLong, true, errCh))
|
||||
|
||||
err := w.Start()
|
||||
require.NoError(t, err)
|
||||
@ -101,18 +108,21 @@ func TestRefreshWorker(t *testing.T) {
|
||||
err = w.Shutdown(shutdown)
|
||||
require.NoError(t, err)
|
||||
|
||||
agdtest.RequireReceive(pt, refreshSync, testTimeout)
|
||||
testutil.RequireReceive(t, syncCh, testTimeout)
|
||||
require.Empty(t, errCh)
|
||||
})
|
||||
|
||||
t.Run("error", func(t *testing.T) {
|
||||
w := agd.NewRefreshWorker(refrConf(errRefr, testIvl, false))
|
||||
errRefr, syncCh := newTestRefresher(t, testError)
|
||||
errCh := make(chan sig, 1)
|
||||
|
||||
w := agd.NewRefreshWorker(newRefrConf(t, errRefr, testIvl, false, errCh))
|
||||
|
||||
err := w.Start()
|
||||
require.NoError(t, err)
|
||||
|
||||
agdtest.RequireReceive(pt, refreshSync, testTimeout)
|
||||
agdtest.RequireReceive(pt, errCh, testTimeout)
|
||||
testutil.RequireReceive(t, syncCh, testTimeout)
|
||||
testutil.RequireReceive(t, errCh, testTimeout)
|
||||
|
||||
shutdown, cancel := context.WithTimeout(context.Background(), testTimeout)
|
||||
defer cancel()
|
||||
@ -122,7 +132,10 @@ func TestRefreshWorker(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("error_on_shutdown", func(t *testing.T) {
|
||||
w := agd.NewRefreshWorker(refrConf(errRefr, testIvlLong, true))
|
||||
errRefr, syncCh := newTestRefresher(t, testError)
|
||||
errCh := make(chan sig, 1)
|
||||
|
||||
w := agd.NewRefreshWorker(newRefrConf(t, errRefr, testIvlLong, true, errCh))
|
||||
|
||||
err := w.Start()
|
||||
require.NoError(t, err)
|
||||
@ -133,7 +146,7 @@ func TestRefreshWorker(t *testing.T) {
|
||||
err = w.Shutdown(shutdown)
|
||||
assert.ErrorIs(t, err, testError)
|
||||
|
||||
agdtest.RequireReceive(pt, refreshSync, testTimeout)
|
||||
testutil.RequireReceive(t, syncCh, testTimeout)
|
||||
require.Empty(t, errCh)
|
||||
})
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ package agd
|
||||
import (
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/forward"
|
||||
)
|
||||
|
||||
// Upstream
|
||||
@ -12,6 +14,9 @@ type Upstream struct {
|
||||
// Server is the upstream server we're using to forward DNS queries.
|
||||
Server netip.AddrPort
|
||||
|
||||
// Network is the Server network protocol.
|
||||
Network forward.Network
|
||||
|
||||
// FallbackServers is a list of the DNS servers we're using to fallback to
|
||||
// when the upstream server fails to respond.
|
||||
FallbackServers []netip.AddrPort
|
||||
|
@ -5,37 +5,10 @@ package agdnet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// AddrFamily is an IANA address family number.
|
||||
type AddrFamily uint16
|
||||
|
||||
// IANA address family numbers.
|
||||
//
|
||||
// See https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xhtml.
|
||||
const (
|
||||
AddrFamilyNone AddrFamily = 0
|
||||
AddrFamilyIPv4 AddrFamily = 1
|
||||
AddrFamilyIPv6 AddrFamily = 2
|
||||
)
|
||||
|
||||
// String implements the fmt.Stringer interface for AddrFamily.
|
||||
func (f AddrFamily) String() (s string) {
|
||||
switch f {
|
||||
case AddrFamilyNone:
|
||||
return "none"
|
||||
case AddrFamilyIPv4:
|
||||
return "ipv4"
|
||||
case AddrFamilyIPv6:
|
||||
return "ipv6"
|
||||
default:
|
||||
return fmt.Sprintf("!bad_addr_fam_%d", f)
|
||||
}
|
||||
}
|
||||
|
||||
// androidMetricFQDNSuffix is the suffix of the FQDN in DNS queries for
|
||||
// metrics that the DNS resolver of the Android operating system seems to
|
||||
// send a lot and because of that we apply special rules to these queries.
|
||||
@ -67,102 +40,6 @@ func IsImmediateSubdomain(domain, top string) (ok bool) {
|
||||
strings.Count(domain, ".") == strings.Count(top, ".")+1
|
||||
}
|
||||
|
||||
// ZeroSubnet returns an IP subnet with prefix 0 and all bytes of the IP address
|
||||
// set to 0. fam must be either AddrFamilyIPv4 or AddrFamilyIPv6.
|
||||
//
|
||||
// TODO(a.garipov): Move to netutil.
|
||||
func ZeroSubnet(fam AddrFamily) (n netip.Prefix) {
|
||||
switch fam {
|
||||
case AddrFamilyIPv4:
|
||||
return netip.PrefixFrom(netip.IPv4Unspecified(), 0)
|
||||
case AddrFamilyIPv6:
|
||||
return netip.PrefixFrom(netip.IPv6Unspecified(), 0)
|
||||
default:
|
||||
panic(fmt.Errorf("agdnet: unsupported addr fam %s", fam))
|
||||
}
|
||||
}
|
||||
|
||||
// IPNetToPrefix is a helper that converts a *net.IPNet into a netip.Prefix. If
|
||||
// subnet is nil, it returns netip.Prefix{}. fam must be either AddrFamilyIPv4
|
||||
// or AddrFamilyIPv6.
|
||||
func IPNetToPrefix(subnet *net.IPNet, fam AddrFamily) (p netip.Prefix, err error) {
|
||||
if subnet == nil {
|
||||
return netip.Prefix{}, nil
|
||||
}
|
||||
|
||||
addr, err := IPToAddr(subnet.IP, fam)
|
||||
if err != nil {
|
||||
return netip.Prefix{}, fmt.Errorf("bad ip for subnet %v: %w", subnet, err)
|
||||
}
|
||||
|
||||
ones, _ := subnet.Mask.Size()
|
||||
p = netip.PrefixFrom(addr, ones)
|
||||
if !p.IsValid() {
|
||||
return netip.Prefix{}, fmt.Errorf("bad subnet %v", subnet)
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// IPNetToPrefixNoMapped is like IPNetToPrefix but it detects the address family
|
||||
// automatically by assuming that every IPv6-mapped IPv4 address is actually an
|
||||
// IPv4 address. Do not use IPNetToPrefixNoMapped where this assumption isn't
|
||||
// safe.
|
||||
func IPNetToPrefixNoMapped(subnet *net.IPNet) (p netip.Prefix, err error) {
|
||||
if subnet == nil {
|
||||
return netip.Prefix{}, nil
|
||||
}
|
||||
|
||||
if ip4 := subnet.IP.To4(); ip4 != nil {
|
||||
subnet.IP = ip4
|
||||
|
||||
return IPNetToPrefix(subnet, AddrFamilyIPv4)
|
||||
}
|
||||
|
||||
return IPNetToPrefix(subnet, AddrFamilyIPv6)
|
||||
}
|
||||
|
||||
// IPToAddr converts a net.IP into a netip.Addr of the given family and returns
|
||||
// a meaningful error. fam must be either AddrFamilyIPv4 or AddrFamilyIPv6.
|
||||
func IPToAddr(ip net.IP, fam AddrFamily) (addr netip.Addr, err error) {
|
||||
switch fam {
|
||||
case AddrFamilyIPv4:
|
||||
// Make sure that we use the IPv4 form of the address to make sure that
|
||||
// netip.Addr doesn't turn out to be an IPv6 one when it really should
|
||||
// be an IPv4 one.
|
||||
ip4 := ip.To4()
|
||||
if ip4 == nil {
|
||||
return netip.Addr{}, fmt.Errorf("bad ipv4 net.IP %v", ip)
|
||||
}
|
||||
|
||||
ip = ip4
|
||||
case AddrFamilyIPv6:
|
||||
// Again, make sure that we use the correct form according to the
|
||||
// address family.
|
||||
ip = ip.To16()
|
||||
default:
|
||||
panic(fmt.Errorf("agdnet: unsupported addr fam %s", fam))
|
||||
}
|
||||
|
||||
addr, ok := netip.AddrFromSlice(ip)
|
||||
if !ok {
|
||||
return netip.Addr{}, fmt.Errorf("bad net.IP value %v", ip)
|
||||
}
|
||||
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
// IPToAddrNoMapped is like IPToAddr but it detects the address family
|
||||
// automatically by assuming that every IPv6-mapped IPv4 address is actually an
|
||||
// IPv4 address. Do not use IPToAddrNoMapped where this assumption isn't safe.
|
||||
func IPToAddrNoMapped(ip net.IP) (addr netip.Addr, err error) {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
return IPToAddr(ip4, AddrFamilyIPv4)
|
||||
}
|
||||
|
||||
return IPToAddr(ip, AddrFamilyIPv6)
|
||||
}
|
||||
|
||||
// ParseSubnets parses IP networks, including single-address ones, from strings.
|
||||
func ParseSubnets(strs ...string) (subnets []netip.Prefix, err error) {
|
||||
subnets = make([]netip.Prefix, len(strs))
|
||||
|
@ -2,10 +2,8 @@ package agdnet_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
)
|
||||
|
||||
func ExampleIsAndroidTLSMetricDomain() {
|
||||
@ -67,109 +65,3 @@ func ExampleIsImmediateSubdomain() {
|
||||
// similar 1 : false
|
||||
// similar 2 : false
|
||||
}
|
||||
|
||||
func ExampleZeroSubnet() {
|
||||
fmt.Printf("%-5s: %9s\n", "ipv4", agdnet.ZeroSubnet(agdnet.AddrFamilyIPv4))
|
||||
fmt.Printf("%-5s: %9s\n", "ipv6", agdnet.ZeroSubnet(agdnet.AddrFamilyIPv6))
|
||||
|
||||
func() {
|
||||
defer func() { fmt.Println(recover()) }()
|
||||
|
||||
_ = agdnet.ZeroSubnet(42)
|
||||
}()
|
||||
|
||||
// Output:
|
||||
// ipv4 : 0.0.0.0/0
|
||||
// ipv6 : ::/0
|
||||
// agdnet: unsupported addr fam !bad_addr_fam_42
|
||||
}
|
||||
|
||||
func ExampleIPToAddr() {
|
||||
ip := net.IP{1, 2, 3, 4}
|
||||
addr, err := agdnet.IPToAddr(ip, agdnet.AddrFamilyIPv4)
|
||||
fmt.Println(addr, err)
|
||||
|
||||
addr, err = agdnet.IPToAddr(ip, agdnet.AddrFamilyIPv6)
|
||||
fmt.Println(addr, err)
|
||||
|
||||
addr, err = agdnet.IPToAddr(nil, agdnet.AddrFamilyIPv4)
|
||||
fmt.Println(addr, err)
|
||||
|
||||
// Output:
|
||||
// 1.2.3.4 <nil>
|
||||
// ::ffff:1.2.3.4 <nil>
|
||||
// invalid IP bad ipv4 net.IP <nil>
|
||||
}
|
||||
|
||||
func ExampleIPToAddrNoMapped() {
|
||||
addr, err := agdnet.IPToAddrNoMapped(net.IP{1, 2, 3, 4})
|
||||
fmt.Println(addr, err)
|
||||
|
||||
addrMapped, err := agdnet.IPToAddrNoMapped(net.IPv4(1, 2, 3, 4))
|
||||
fmt.Println(addr, err)
|
||||
fmt.Printf("%s == %s is %t\n", addr, addrMapped, addr == addrMapped)
|
||||
|
||||
addr, err = agdnet.IPToAddrNoMapped(net.ParseIP("1234::cdef"))
|
||||
fmt.Println(addr, err)
|
||||
|
||||
// Output:
|
||||
// 1.2.3.4 <nil>
|
||||
// 1.2.3.4 <nil>
|
||||
// 1.2.3.4 == 1.2.3.4 is true
|
||||
// 1234::cdef <nil>
|
||||
}
|
||||
|
||||
func ExampleIPNetToPrefix() {
|
||||
prefix, err := agdnet.IPNetToPrefix(nil, agdnet.AddrFamilyIPv4)
|
||||
fmt.Println(prefix, err)
|
||||
|
||||
prefix, err = agdnet.IPNetToPrefix(&net.IPNet{
|
||||
IP: nil,
|
||||
}, agdnet.AddrFamilyIPv4)
|
||||
fmt.Println(prefix, err)
|
||||
|
||||
prefix, err = agdnet.IPNetToPrefix(&net.IPNet{
|
||||
IP: net.IP{1, 2, 3, 0},
|
||||
Mask: net.CIDRMask(64, netutil.IPv6BitLen),
|
||||
}, agdnet.AddrFamilyIPv4)
|
||||
fmt.Println(prefix, err)
|
||||
|
||||
prefix, err = agdnet.IPNetToPrefix(&net.IPNet{
|
||||
IP: net.IP{1, 2, 3, 0},
|
||||
Mask: net.CIDRMask(24, netutil.IPv4BitLen),
|
||||
}, agdnet.AddrFamilyIPv4)
|
||||
fmt.Println(prefix, err)
|
||||
|
||||
// Output:
|
||||
// invalid Prefix <nil>
|
||||
// invalid Prefix bad ip for subnet <nil>: bad ipv4 net.IP <nil>
|
||||
// invalid Prefix bad subnet 1.2.3.0/0
|
||||
// 1.2.3.0/24 <nil>
|
||||
}
|
||||
|
||||
func ExampleIPNetToPrefixNoMapped() {
|
||||
prefix, err := agdnet.IPNetToPrefixNoMapped(&net.IPNet{
|
||||
IP: net.IP{1, 2, 3, 0},
|
||||
Mask: net.CIDRMask(24, netutil.IPv4BitLen),
|
||||
})
|
||||
fmt.Println(prefix, err)
|
||||
|
||||
prefixMapped, err := agdnet.IPNetToPrefixNoMapped(&net.IPNet{
|
||||
IP: net.IPv4(1, 2, 3, 0),
|
||||
Mask: net.CIDRMask(24, netutil.IPv4BitLen),
|
||||
})
|
||||
fmt.Println(prefix, err)
|
||||
fmt.Printf("%s == %s is %t\n", prefix, prefixMapped, prefix == prefixMapped)
|
||||
|
||||
prefix, err = agdnet.IPNetToPrefixNoMapped(&net.IPNet{
|
||||
IP: net.ParseIP("1234::cdef"),
|
||||
Mask: net.CIDRMask(64, netutil.IPv6BitLen),
|
||||
})
|
||||
fmt.Println(prefix, err)
|
||||
|
||||
// Output:
|
||||
// 1.2.3.0/24 <nil>
|
||||
// 1.2.3.0/24 <nil>
|
||||
// 1.2.3.0/24 == 1.2.3.0/24 is true
|
||||
// 1234::cdef/64 <nil>
|
||||
}
|
||||
|
193
internal/agdnet/resolver.go
Normal file
193
internal/agdnet/resolver.go
Normal file
@ -0,0 +1,193 @@
|
||||
package agdnet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
)
|
||||
|
||||
// Resolvers
|
||||
|
||||
// Resolver is the DNS resolver interface.
|
||||
//
|
||||
// See go doc net.Resolver.
|
||||
type Resolver interface {
|
||||
LookupIP(ctx context.Context, fam netutil.AddrFamily, host string) (ips []net.IP, err error)
|
||||
}
|
||||
|
||||
// DefaultResolver uses [net.DefaultResolver] to resolve addresses.
|
||||
type DefaultResolver struct{}
|
||||
|
||||
// type check
|
||||
var _ Resolver = DefaultResolver{}
|
||||
|
||||
// LookupIP implements the [Resolver] interface for DefaultResolver.
|
||||
func (DefaultResolver) LookupIP(
|
||||
ctx context.Context,
|
||||
fam netutil.AddrFamily,
|
||||
host string,
|
||||
) (ips []net.IP, err error) {
|
||||
switch fam {
|
||||
case netutil.AddrFamilyIPv4:
|
||||
return net.DefaultResolver.LookupIP(ctx, "ip4", host)
|
||||
case netutil.AddrFamilyIPv6:
|
||||
return net.DefaultResolver.LookupIP(ctx, "ip6", host)
|
||||
default:
|
||||
return nil, net.UnknownNetworkError(fam.String())
|
||||
}
|
||||
}
|
||||
|
||||
// resolveCache is a simple address resolving cache.
|
||||
type resolveCache map[string]*resolveCacheItem
|
||||
|
||||
// resolveCacheItem is an item of [resolveCache].
|
||||
type resolveCacheItem struct {
|
||||
refrTime time.Time
|
||||
ips []net.IP
|
||||
}
|
||||
|
||||
// CachingResolver caches resolved results for hosts for a certain time,
|
||||
// regardless of the actual TTLs of the records. It is used for caching the
|
||||
// results of lookups of hostnames that don't change their IP addresses often.
|
||||
type CachingResolver struct {
|
||||
resolver Resolver
|
||||
|
||||
// mu protects ip4 and ip6.
|
||||
mu *sync.Mutex
|
||||
ipv4 resolveCache
|
||||
ipv6 resolveCache
|
||||
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
// NewCachingResolver returns a new caching resolver.
|
||||
func NewCachingResolver(resolver Resolver, ttl time.Duration) (c *CachingResolver) {
|
||||
return &CachingResolver{
|
||||
resolver: resolver,
|
||||
|
||||
mu: &sync.Mutex{},
|
||||
ipv4: resolveCache{},
|
||||
ipv6: resolveCache{},
|
||||
|
||||
ttl: ttl,
|
||||
}
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ Resolver = (*CachingResolver)(nil)
|
||||
|
||||
// LookupIP implements the [Resolver] interface for *CachingResolver. host
|
||||
// should be normalized. Slice ips and its elements must not be mutated.
|
||||
func (c *CachingResolver) LookupIP(
|
||||
ctx context.Context,
|
||||
fam netutil.AddrFamily,
|
||||
host string,
|
||||
) (ips []net.IP, err error) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
var item *resolveCacheItem
|
||||
switch fam {
|
||||
case netutil.AddrFamilyIPv4:
|
||||
item = c.ipv4[host]
|
||||
case netutil.AddrFamilyIPv6:
|
||||
item = c.ipv6[host]
|
||||
default:
|
||||
return nil, net.UnknownNetworkError(fam.String())
|
||||
}
|
||||
|
||||
if item == nil || time.Since(item.refrTime) > c.ttl {
|
||||
item, err = c.resolve(ctx, fam, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return item.ips, nil
|
||||
}
|
||||
|
||||
// resolve looks up the IP addresses for host and puts them into the cache.
|
||||
func (c *CachingResolver) resolve(
|
||||
ctx context.Context,
|
||||
fam netutil.AddrFamily,
|
||||
host string,
|
||||
) (item *resolveCacheItem, err error) {
|
||||
var ips []net.IP
|
||||
|
||||
refrTime := time.Now()
|
||||
|
||||
// Don't resolve IP addresses.
|
||||
ip := net.ParseIP(host)
|
||||
if ip != nil {
|
||||
ip4 := ip.To4()
|
||||
if fam == netutil.AddrFamilyIPv4 && ip4 != nil {
|
||||
ips = []net.IP{ip4}
|
||||
} else if fam == netutil.AddrFamilyIPv6 && ip4 == nil {
|
||||
ips = []net.IP{ip}
|
||||
} else {
|
||||
// Not the right kind of IP address. Cache absence of IP addresses
|
||||
// for this network forever.
|
||||
ips = []net.IP{}
|
||||
}
|
||||
|
||||
// Set the refresh time to the maximum date that time.Duration allows to
|
||||
// prevent this item from refreshing.
|
||||
refrTime = time.Unix(0, math.MaxInt64)
|
||||
} else {
|
||||
ips, err = c.resolver.LookupIP(ctx, fam, host)
|
||||
if err != nil {
|
||||
if !isExpectedLookupError(fam, err) {
|
||||
return nil, fmt.Errorf("resolving %s addr for %q: %w", fam, host, err)
|
||||
}
|
||||
|
||||
log.Debug("caching resolver: warning: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
var cache resolveCache
|
||||
if fam == netutil.AddrFamilyIPv4 {
|
||||
cache = c.ipv4
|
||||
} else {
|
||||
cache = c.ipv6
|
||||
}
|
||||
|
||||
item = &resolveCacheItem{
|
||||
refrTime: refrTime,
|
||||
ips: ips,
|
||||
}
|
||||
|
||||
cache[host] = item
|
||||
|
||||
return item, nil
|
||||
}
|
||||
|
||||
// isExpectedLookupError returns true if the error is an expected lookup error.
|
||||
func isExpectedLookupError(fam netutil.AddrFamily, err error) (ok bool) {
|
||||
var dnsErr *net.DNSError
|
||||
if fam == netutil.AddrFamilyIPv6 && errors.As(err, &dnsErr) {
|
||||
// It's expected that Go default DNS resolver returns a DNS error in
|
||||
// some cases when it receives an empty response. It's unclear what
|
||||
// exactly triggers this error, though.
|
||||
//
|
||||
// TODO(ameshkov): Consider researching this in detail.
|
||||
return true
|
||||
}
|
||||
|
||||
var addrErr *net.AddrError
|
||||
if !errors.As(err, &addrErr) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Expect the error about no suitable addresses. For example, no IPv6
|
||||
// addresses for a host that does have IPv4 ones.
|
||||
//
|
||||
// See function filterAddrList in ${GOROOT}/src/net/ipsock.go.
|
||||
return addrErr.Err == "no suitable address found"
|
||||
}
|
88
internal/agdnet/resolver_test.go
Normal file
88
internal/agdnet/resolver_test.go
Normal file
@ -0,0 +1,88 @@
|
||||
package agdnet_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCachingResolver_Resolve(t *testing.T) {
|
||||
const testHost = "addr.example"
|
||||
|
||||
var numLookups uint64
|
||||
wantIPv4 := []net.IP{{1, 2, 3, 4}}
|
||||
wantIPv6 := []net.IP{net.ParseIP("1234::5678")}
|
||||
r := &agdtest.Resolver{
|
||||
OnLookupIP: func(
|
||||
_ context.Context,
|
||||
fam netutil.AddrFamily,
|
||||
_ string,
|
||||
) (ips []net.IP, err error) {
|
||||
numLookups++
|
||||
|
||||
if fam == netutil.AddrFamilyIPv4 {
|
||||
return wantIPv4, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
cached := agdnet.NewCachingResolver(r, 1*timeutil.Day)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
host string
|
||||
wantIPs []net.IP
|
||||
wantNum uint64
|
||||
fam netutil.AddrFamily
|
||||
}{{
|
||||
name: "initial",
|
||||
host: testHost,
|
||||
wantIPs: wantIPv4,
|
||||
wantNum: 1,
|
||||
fam: netutil.AddrFamilyIPv4,
|
||||
}, {
|
||||
name: "cached",
|
||||
host: testHost,
|
||||
wantIPs: wantIPv4,
|
||||
wantNum: 1,
|
||||
fam: netutil.AddrFamilyIPv4,
|
||||
}, {
|
||||
name: "other_network",
|
||||
host: testHost,
|
||||
wantIPs: nil,
|
||||
wantNum: 2,
|
||||
fam: netutil.AddrFamilyIPv6,
|
||||
}, {
|
||||
name: "ipv4",
|
||||
host: wantIPv4[0].String(),
|
||||
wantIPs: wantIPv4,
|
||||
wantNum: 2,
|
||||
fam: netutil.AddrFamilyIPv4,
|
||||
}, {
|
||||
name: "ipv6",
|
||||
host: wantIPv6[0].String(),
|
||||
wantIPs: wantIPv6,
|
||||
wantNum: 2,
|
||||
fam: netutil.AddrFamilyIPv6,
|
||||
}}
|
||||
|
||||
ctx := context.Background()
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := cached.LookupIP(ctx, tc.fam, tc.host)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.wantNum, numLookups)
|
||||
assert.Equal(t, tc.wantIPs, got)
|
||||
})
|
||||
}
|
||||
}
|
@ -1,18 +1,3 @@
|
||||
// Package agdtest contains simple mocks for common interfaces and other test
|
||||
// utilities.
|
||||
package agdtest
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
|
||||
// DiscardLogOutput runs tests with discarded logger's output.
|
||||
func DiscardLogOutput(m *testing.M) {
|
||||
log.SetOutput(io.Discard)
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/querylog"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/rulestat"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
@ -23,8 +24,6 @@ import (
|
||||
//
|
||||
// Keep entities in this file in alphabetic order.
|
||||
|
||||
// agd.ErrorCollector
|
||||
|
||||
// type check
|
||||
var _ agd.ErrorCollector = (*ErrorCollector)(nil)
|
||||
|
||||
@ -40,8 +39,6 @@ func (c *ErrorCollector) Collect(ctx context.Context, err error) {
|
||||
c.OnCollect(ctx, err)
|
||||
}
|
||||
|
||||
// agd.ProfileDB
|
||||
|
||||
// type check
|
||||
var _ agd.ProfileDB = (*ProfileDB)(nil)
|
||||
|
||||
@ -73,8 +70,6 @@ func (db *ProfileDB) ProfileByIP(
|
||||
return db.OnProfileByIP(ctx, ip)
|
||||
}
|
||||
|
||||
// agd.ProfileStorage
|
||||
|
||||
// type check
|
||||
var _ agd.ProfileStorage = (*ProfileStorage)(nil)
|
||||
|
||||
@ -94,8 +89,6 @@ func (ds *ProfileStorage) Profiles(
|
||||
return ds.OnProfiles(ctx, req)
|
||||
}
|
||||
|
||||
// agd.Refresher
|
||||
|
||||
// type check
|
||||
var _ agd.Refresher = (*Refresher)(nil)
|
||||
|
||||
@ -109,28 +102,30 @@ func (r *Refresher) Refresh(ctx context.Context) (err error) {
|
||||
return r.OnRefresh(ctx)
|
||||
}
|
||||
|
||||
// agd.Resolver
|
||||
// Package agdnet
|
||||
|
||||
// type check
|
||||
var _ agd.Resolver = (*Resolver)(nil)
|
||||
var _ agdnet.Resolver = (*Resolver)(nil)
|
||||
|
||||
// Resolver is an agd.Resolver for tests.
|
||||
type Resolver struct {
|
||||
OnLookupIP func(ctx context.Context, network, addr string) (ips []net.IP, err error)
|
||||
OnLookupIP func(
|
||||
ctx context.Context,
|
||||
fam netutil.AddrFamily,
|
||||
host string,
|
||||
) (ips []net.IP, err error)
|
||||
}
|
||||
|
||||
// LookupIP implements the agd.Resolver interface for *Resolver.
|
||||
func (r *Resolver) LookupIP(
|
||||
ctx context.Context,
|
||||
network string,
|
||||
addr string,
|
||||
fam netutil.AddrFamily,
|
||||
host string,
|
||||
) (ips []net.IP, err error) {
|
||||
return r.OnLookupIP(ctx, network, addr)
|
||||
return r.OnLookupIP(ctx, fam, host)
|
||||
}
|
||||
|
||||
// Package BillStat
|
||||
|
||||
// billstat.Recorder
|
||||
// Package billstat
|
||||
|
||||
// type check
|
||||
var _ billstat.Recorder = (*BillStatRecorder)(nil)
|
||||
@ -159,8 +154,6 @@ func (r *BillStatRecorder) Record(
|
||||
r.OnRecord(ctx, id, ctry, asn, start, proto)
|
||||
}
|
||||
|
||||
// billstat.Uploader
|
||||
|
||||
// type check
|
||||
var _ billstat.Uploader = (*BillStatUploader)(nil)
|
||||
|
||||
@ -174,9 +167,7 @@ func (b *BillStatUploader) Upload(ctx context.Context, records billstat.Records)
|
||||
return b.OnUpload(ctx, records)
|
||||
}
|
||||
|
||||
// Package DNSCheck
|
||||
|
||||
// dnscheck.Interface
|
||||
// Package dnscheck
|
||||
|
||||
// type check
|
||||
var _ dnscheck.Interface = (*DNSCheck)(nil)
|
||||
@ -195,9 +186,7 @@ func (db *DNSCheck) Check(
|
||||
return db.OnCheck(ctx, req, ri)
|
||||
}
|
||||
|
||||
// Package DNSDB
|
||||
|
||||
// dnsdb.Interface
|
||||
// Package dnsdb
|
||||
|
||||
// type check
|
||||
var _ dnsdb.Interface = (*DNSDB)(nil)
|
||||
@ -212,9 +201,7 @@ func (db *DNSDB) Record(ctx context.Context, resp *dns.Msg, ri *agd.RequestInfo)
|
||||
db.OnRecord(ctx, resp, ri)
|
||||
}
|
||||
|
||||
// Package Filter
|
||||
|
||||
// filter.Interface
|
||||
// Package filter
|
||||
|
||||
// type check
|
||||
var _ filter.Interface = (*Filter)(nil)
|
||||
@ -257,8 +244,6 @@ func (f *Filter) Close() (err error) {
|
||||
return f.OnClose()
|
||||
}
|
||||
|
||||
// filter.Storage
|
||||
|
||||
// type check
|
||||
var _ filter.Storage = (*FilterStorage)(nil)
|
||||
|
||||
@ -281,9 +266,7 @@ func (s *FilterStorage) HasListID(id agd.FilterListID) (ok bool) {
|
||||
return s.OnHasListID(id)
|
||||
}
|
||||
|
||||
// Package GeoIP
|
||||
|
||||
// geoip.Interface
|
||||
// Package geoip
|
||||
|
||||
// type check
|
||||
var _ geoip.Interface = (*GeoIP)(nil)
|
||||
@ -293,7 +276,7 @@ type GeoIP struct {
|
||||
OnSubnetByLocation func(
|
||||
c agd.Country,
|
||||
a agd.ASN,
|
||||
fam agdnet.AddrFamily,
|
||||
fam netutil.AddrFamily,
|
||||
) (n netip.Prefix, err error)
|
||||
OnData func(host string, ip netip.Addr) (l *agd.Location, err error)
|
||||
}
|
||||
@ -302,7 +285,7 @@ type GeoIP struct {
|
||||
func (g *GeoIP) SubnetByLocation(
|
||||
c agd.Country,
|
||||
a agd.ASN,
|
||||
fam agdnet.AddrFamily,
|
||||
fam netutil.AddrFamily,
|
||||
) (n netip.Prefix, err error) {
|
||||
return g.OnSubnetByLocation(c, a, fam)
|
||||
}
|
||||
@ -312,9 +295,7 @@ func (g *GeoIP) Data(host string, ip netip.Addr) (l *agd.Location, err error) {
|
||||
return g.OnData(host, ip)
|
||||
}
|
||||
|
||||
// Package Querylog
|
||||
|
||||
// querylog.Interface
|
||||
// Package querylog
|
||||
|
||||
// type check
|
||||
var _ querylog.Interface = (*QueryLog)(nil)
|
||||
@ -329,9 +310,7 @@ func (ql *QueryLog) Write(ctx context.Context, e *querylog.Entry) (err error) {
|
||||
return ql.OnWrite(ctx, e)
|
||||
}
|
||||
|
||||
// Package RuleStat
|
||||
|
||||
// rulestat.Interface
|
||||
// Package rulestat
|
||||
|
||||
// type check
|
||||
var _ rulestat.Interface = (*RuleStat)(nil)
|
||||
@ -346,11 +325,9 @@ func (s *RuleStat) Collect(ctx context.Context, id agd.FilterListID, text agd.Fi
|
||||
s.OnCollect(ctx, id, text)
|
||||
}
|
||||
|
||||
// Module DNSServer
|
||||
// Module dnsserver
|
||||
|
||||
// Package RateLimit
|
||||
|
||||
// ratelimit.RateLimit
|
||||
// Package ratelimit
|
||||
|
||||
// type check
|
||||
var _ ratelimit.Interface = (*RateLimit)(nil)
|
||||
|
@ -1,54 +0,0 @@
|
||||
package agdtest
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Synchronization Utilities
|
||||
//
|
||||
// TODO(a.garipov): Add generic versions when we can.
|
||||
//
|
||||
// TODO(a.garipov): Add to golibs once the API is stabilized.
|
||||
|
||||
// Signal is a simple signal type alias for tests.
|
||||
type Signal = struct{}
|
||||
|
||||
// RequireSend waits until a signal is sent to ch or until the timeout is
|
||||
// reached. If the timeout is reached, the test is failed.
|
||||
func RequireSend(t require.TestingT, ch chan<- Signal, timeout time.Duration) {
|
||||
if h, ok := t.(interface{ Helper() }); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
timer := time.NewTimer(timeout)
|
||||
defer timer.Stop()
|
||||
|
||||
select {
|
||||
case ch <- Signal{}:
|
||||
// Go on.
|
||||
case <-timer.C:
|
||||
t.Errorf("did not send after %s", timeout)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
// RequireReceive waits until a signal is received from ch or until the timeout
|
||||
// is reached. If the timeout is reached, the test is failed.
|
||||
func RequireReceive(t require.TestingT, ch <-chan Signal, timeout time.Duration) {
|
||||
if h, ok := t.(interface{ Helper() }); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
timer := time.NewTimer(timeout)
|
||||
defer timer.Stop()
|
||||
|
||||
select {
|
||||
case <-ch:
|
||||
// Go on.
|
||||
case <-timer.C:
|
||||
t.Errorf("did not receive after %s", timeout)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
@ -3,9 +3,9 @@ package backend_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
agdtest.DiscardLogOutput(m)
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
@ -82,9 +82,7 @@ func (s *ProfileStorage) Profiles(
|
||||
return nil, fmt.Errorf("loading from url %s: %w", redURL, err)
|
||||
}
|
||||
|
||||
updTime := s.now()
|
||||
|
||||
return settResp.toInternal(ctx, updTime, s.errColl), nil
|
||||
return settResp.toInternal(ctx, s.now(), s.errColl), nil
|
||||
}
|
||||
|
||||
// loadSettingsResponse fetches, decodes, and returns the settings response.
|
||||
|
@ -2,6 +2,7 @@ package backend_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/netip"
|
||||
@ -18,6 +19,12 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Common test constants.
|
||||
var (
|
||||
syncTime = time.Unix(0, 1_624_443_079_309_000_000)
|
||||
updTime = time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
)
|
||||
|
||||
func TestProfileStorage_Profiles(t *testing.T) {
|
||||
reqURLStr := ""
|
||||
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
@ -40,7 +47,6 @@ func TestProfileStorage_Profiles(t *testing.T) {
|
||||
u, err := url.Parse(srv.URL)
|
||||
require.NoError(t, err)
|
||||
|
||||
updTime := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
c := &backend.ProfileStorageConfig{
|
||||
BaseEndpoint: u,
|
||||
Now: func() (t time.Time) { return updTime },
|
||||
@ -50,7 +56,6 @@ func TestProfileStorage_Profiles(t *testing.T) {
|
||||
require.NotNil(t, ds)
|
||||
|
||||
ctx := context.Background()
|
||||
syncTime := time.Unix(0, 1_624_443_079_309_000_000)
|
||||
req := &agd.PSProfilesRequest{
|
||||
SyncTime: syncTime,
|
||||
}
|
||||
@ -61,10 +66,19 @@ func TestProfileStorage_Profiles(t *testing.T) {
|
||||
|
||||
// Compare against a relative URL since the URL inside an HTTP handler
|
||||
// seems to always be relative.
|
||||
wantURLStr := backend.PathDNSAPIV1Settings + "?sync_time=1624443079309"
|
||||
wantURLStr := fmt.Sprintf("%s?sync_time=%d", backend.PathDNSAPIV1Settings, syncTime.UnixMilli())
|
||||
assert.Equal(t, wantURLStr, reqURLStr)
|
||||
|
||||
// Keep in sync with the testdata one.
|
||||
want := testProfileResp(t)
|
||||
assert.Equal(t, want, resp)
|
||||
}
|
||||
|
||||
// testProfileResp returns profile resp corresponding with testdata.
|
||||
//
|
||||
// Keep in sync with the testdata one.
|
||||
func testProfileResp(t *testing.T) *agd.PSProfilesResponse {
|
||||
t.Helper()
|
||||
|
||||
wantLoc, err := time.LoadLocation("GMT")
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -151,5 +165,5 @@ func TestProfileStorage_Profiles(t *testing.T) {
|
||||
}},
|
||||
}
|
||||
|
||||
assert.Equal(t, want, resp)
|
||||
return want
|
||||
}
|
||||
|
@ -4,13 +4,13 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
)
|
||||
|
||||
// Common Constants And Utilities
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
agdtest.DiscardLogOutput(m)
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
||||
// testTimeout is the timeout for common test operations.
|
||||
|
@ -14,6 +14,10 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// sig is a convenient alias for struct{} when it's used as a signal for
|
||||
// synchronization.
|
||||
type sig = struct{}
|
||||
|
||||
// Common constants for tests.
|
||||
const (
|
||||
devID = "dev1234"
|
||||
@ -62,7 +66,7 @@ func TestRuntimeRecorder_fail(t *testing.T) {
|
||||
const testError errors.Error = "test error"
|
||||
var emulateFail bool
|
||||
var gotRecord *billstat.Record
|
||||
uploadSync := make(chan agdtest.Signal)
|
||||
uploadSync := make(chan sig)
|
||||
onUpload := func(_ context.Context, records billstat.Records) (err error) {
|
||||
if emulateFail {
|
||||
pt := testutil.PanicT{}
|
||||
@ -70,11 +74,11 @@ func TestRuntimeRecorder_fail(t *testing.T) {
|
||||
// Give the goroutine a signal that it can now record another query
|
||||
// to emulate a situation where a query gets recorded while an
|
||||
// upload is in progress.
|
||||
agdtest.RequireSend(pt, uploadSync, testTimeout)
|
||||
testutil.RequireSend(pt, uploadSync, sig{}, testTimeout)
|
||||
|
||||
// Wait for the other query in the goroutine to be recorded and
|
||||
// proceed to returning an error after that.
|
||||
agdtest.RequireReceive(pt, uploadSync, testTimeout)
|
||||
testutil.RequireReceive(pt, uploadSync, testTimeout)
|
||||
|
||||
return testError
|
||||
}
|
||||
@ -104,11 +108,11 @@ func TestRuntimeRecorder_fail(t *testing.T) {
|
||||
go func() {
|
||||
pt := testutil.PanicT{}
|
||||
|
||||
agdtest.RequireReceive(pt, uploadSync, testTimeout)
|
||||
testutil.RequireReceive(pt, uploadSync, testTimeout)
|
||||
|
||||
r.Record(ctx, devID, clientCtry, clientASN, start, proto)
|
||||
|
||||
agdtest.RequireSend(pt, uploadSync, testTimeout)
|
||||
testutil.RequireSend(pt, uploadSync, sig{}, testTimeout)
|
||||
}()
|
||||
|
||||
err := r.Refresh(context.Background())
|
||||
|
@ -1,11 +1,11 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/backend"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
)
|
||||
|
||||
@ -33,15 +33,17 @@ type backendConfig struct {
|
||||
// toInternal converts c to the data storage configuration for the DNS server.
|
||||
// c is assumed to be valid.
|
||||
func (c *backendConfig) toInternal(
|
||||
backendEndpoint *url.URL,
|
||||
envs *environments,
|
||||
errColl agd.ErrorCollector,
|
||||
) (profStrg *backend.ProfileStorageConfig, billStat *backend.BillStatConfig) {
|
||||
backendEndpoint := &envs.BackendEndpoint.URL
|
||||
|
||||
return &backend.ProfileStorageConfig{
|
||||
BaseEndpoint: backendEndpoint,
|
||||
BaseEndpoint: netutil.CloneURL(backendEndpoint),
|
||||
Now: time.Now,
|
||||
ErrColl: errColl,
|
||||
}, &backend.BillStatConfig{
|
||||
BaseEndpoint: backendEndpoint,
|
||||
BaseEndpoint: netutil.CloneURL(backendEndpoint),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnscheck"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
@ -115,13 +114,13 @@ func (c *checkConfig) validate() (err error) {
|
||||
return errors.Error("no domains")
|
||||
}
|
||||
|
||||
err = validateNonNilIPs(c.IPv4, agdnet.AddrFamilyIPv4)
|
||||
err = validateNonNilIPs(c.IPv4, netutil.AddrFamilyIPv4)
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
|
||||
err = validateNonNilIPs(c.IPv6, agdnet.AddrFamilyIPv6)
|
||||
err = validateNonNilIPs(c.IPv6, netutil.AddrFamilyIPv6)
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
return err
|
||||
@ -134,7 +133,9 @@ func (c *checkConfig) validate() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateNonNilIPs(ips []netip.Addr, fam agdnet.AddrFamily) (err error) {
|
||||
// validateNonNilIPs returns an error if ips is empty or had IP addresses of
|
||||
// incorrect protocol version.
|
||||
func validateNonNilIPs(ips []netip.Addr, fam netutil.AddrFamily) (err error) {
|
||||
if len(ips) == 0 {
|
||||
return fmt.Errorf("no %s", fam)
|
||||
}
|
||||
@ -143,9 +144,9 @@ func validateNonNilIPs(ips []netip.Addr, fam agdnet.AddrFamily) (err error) {
|
||||
|
||||
var checkProto func(ip netip.Addr) (ok bool)
|
||||
switch fam {
|
||||
case agdnet.AddrFamilyIPv4:
|
||||
case netutil.AddrFamilyIPv4:
|
||||
checkProto = netip.Addr.Is4
|
||||
case agdnet.AddrFamilyIPv6:
|
||||
case netutil.AddrFamilyIPv6:
|
||||
checkProto = netip.Addr.Is6
|
||||
default:
|
||||
panic(fmt.Errorf("agdnet: unsupported addr fam %s", fam))
|
||||
|
@ -192,7 +192,7 @@ func Main() {
|
||||
|
||||
// Profiles Database
|
||||
|
||||
profStrgConf, billStatConf := c.Backend.toInternal(&envs.BackendEndpoint.URL, errColl)
|
||||
profStrgConf, billStatConf := c.Backend.toInternal(envs, errColl)
|
||||
profStrg := backend.NewProfileStorage(profStrgConf)
|
||||
|
||||
// Billing Statistics
|
||||
@ -213,7 +213,11 @@ func Main() {
|
||||
err = billStatRecUpd.Start()
|
||||
fatalOnError(err)
|
||||
|
||||
profDB, err := agd.NewDefaultProfileDB(profStrg, c.Backend.FullRefreshIvl.Duration)
|
||||
profDB, err := agd.NewDefaultProfileDB(
|
||||
profStrg,
|
||||
c.Backend.FullRefreshIvl.Duration,
|
||||
envs.ProfilesCachePath,
|
||||
)
|
||||
fatalOnError(err)
|
||||
|
||||
profDBUpd := agd.NewRefreshWorker(&agd.RefreshWorkerConfig{
|
||||
@ -308,8 +312,12 @@ func Main() {
|
||||
|
||||
metricsListener := prometheus.NewForwardMetricsListener(len(c.Upstream.FallbackServers) + 1)
|
||||
|
||||
upstream, err := c.Upstream.toInternal()
|
||||
fatalOnError(err)
|
||||
|
||||
handler := forward.NewHandler(&forward.HandlerConfig{
|
||||
Address: c.Upstream.Server,
|
||||
Address: upstream.Server,
|
||||
Network: upstream.Network,
|
||||
MetricsListener: metricsListener,
|
||||
HealthcheckDomainTmpl: c.Upstream.Healthcheck.DomainTmpl,
|
||||
FallbackAddresses: c.Upstream.FallbackServers,
|
||||
@ -331,7 +339,7 @@ func Main() {
|
||||
Handler: handler,
|
||||
QueryLog: queryLog,
|
||||
RuleStat: ruleStat,
|
||||
Upstream: c.Upstream.toInternal(),
|
||||
Upstream: upstream,
|
||||
RateLimit: rateLimiter,
|
||||
FilteringGroups: fltGroups,
|
||||
ServerGroups: srvGrps,
|
||||
|
@ -17,7 +17,7 @@ import (
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
env "github.com/caarlos0/env/v6"
|
||||
"github.com/getsentry/raven-go"
|
||||
"github.com/getsentry/sentry-go"
|
||||
)
|
||||
|
||||
// Environment Configuration
|
||||
@ -37,6 +37,7 @@ type environments struct {
|
||||
ConfPath string `env:"CONFIG_PATH" envDefault:"./config.yml"`
|
||||
DNSDBPath string `env:"DNSDB_PATH" envDefault:"./dnsdb.bolt"`
|
||||
FilterCachePath string `env:"FILTER_CACHE_PATH" envDefault:"./filters/"`
|
||||
ProfilesCachePath string `env:"PROFILES_CACHE_PATH" envDefault:"./profilecache.json"`
|
||||
GeoIPASNPath string `env:"GEOIP_ASN_PATH" envDefault:"./asn.mmdb"`
|
||||
GeoIPCountryPath string `env:"GEOIP_COUNTRY_PATH" envDefault:"./country.mmdb"`
|
||||
QueryLogPath string `env:"QUERYLOG_PATH" envDefault:"./querylog.jsonl"`
|
||||
@ -83,12 +84,16 @@ func (envs *environments) buildErrColl() (errColl agd.ErrorCollector, err error)
|
||||
return errcoll.NewWriterErrorCollector(os.Stderr), nil
|
||||
}
|
||||
|
||||
rc, err := raven.New(dsn)
|
||||
cli, err := sentry.NewClient(sentry.ClientOptions{
|
||||
Dsn: dsn,
|
||||
AttachStacktrace: true,
|
||||
Release: agd.Version(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return errcoll.NewRavenErrorCollector(rc), nil
|
||||
return errcoll.NewSentryErrorCollector(cli), nil
|
||||
}
|
||||
|
||||
// buildDNSDB builds and returns an anonymous statistics collector and its
|
||||
|
@ -1,10 +1,10 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
@ -46,7 +46,7 @@ func (c *filtersConfig) toInternal(
|
||||
YoutubeSafeSearchRulesURL: netutil.CloneURL(&envs.YoutubeSafeSearchURL.URL),
|
||||
Now: time.Now,
|
||||
ErrColl: errColl,
|
||||
Resolver: net.DefaultResolver,
|
||||
Resolver: agdnet.DefaultResolver{},
|
||||
CacheDir: envs.FilterCachePath,
|
||||
CustomFilterCacheSize: c.CustomFilterCacheSize,
|
||||
// TODO(a.garipov): Consider making this configurable.
|
||||
|
@ -24,12 +24,7 @@ func (srvs servers) toInternal(tlsConfig *agd.TLS) (dnsSrvs []*agd.Server, err e
|
||||
dnsSrvs = append(dnsSrvs, &agd.Server{
|
||||
Name: name,
|
||||
BindAddresses: slices.Clone(srv.BindAddresses),
|
||||
Protocol: agd.ProtoDNSTCP,
|
||||
LinkedIPEnabled: srv.LinkedIPEnabled,
|
||||
}, &agd.Server{
|
||||
Name: name,
|
||||
BindAddresses: slices.Clone(srv.BindAddresses),
|
||||
Protocol: agd.ProtoDNSUDP,
|
||||
Protocol: agd.ProtoDNS,
|
||||
LinkedIPEnabled: srv.LinkedIPEnabled,
|
||||
})
|
||||
case srvProtoDNSCrypt:
|
||||
@ -43,13 +38,7 @@ func (srvs servers) toInternal(tlsConfig *agd.TLS) (dnsSrvs []*agd.Server, err e
|
||||
DNSCrypt: dcConf,
|
||||
Name: name,
|
||||
BindAddresses: slices.Clone(srv.BindAddresses),
|
||||
Protocol: agd.ProtoDNSCryptTCP,
|
||||
LinkedIPEnabled: srv.LinkedIPEnabled,
|
||||
}, &agd.Server{
|
||||
DNSCrypt: dcConf,
|
||||
Name: name,
|
||||
BindAddresses: slices.Clone(srv.BindAddresses),
|
||||
Protocol: agd.ProtoDNSCryptUDP,
|
||||
Protocol: agd.ProtoDNSCrypt,
|
||||
LinkedIPEnabled: srv.LinkedIPEnabled,
|
||||
})
|
||||
default:
|
||||
|
@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/forward"
|
||||
@ -18,8 +20,9 @@ type upstreamConfig struct {
|
||||
// Healthcheck contains the upstream healthcheck configuration.
|
||||
Healthcheck *upstreamHealthcheckConfig `yaml:"healthcheck"`
|
||||
|
||||
// Server is the upstream server we're using to forward DNS queries.
|
||||
Server netip.AddrPort `yaml:"server"`
|
||||
// Server is the upstream url of the server we're using to forward DNS
|
||||
// queries. It starts with tcp://, udp://, or with an IP address.
|
||||
Server string `yaml:"server"`
|
||||
|
||||
// FallbackServers is a list of the DNS servers we're using to fallback to
|
||||
// when the upstream server fails to respond
|
||||
@ -30,13 +33,18 @@ type upstreamConfig struct {
|
||||
}
|
||||
|
||||
// toInternal converts c to the data storage configuration for the DNS server.
|
||||
// c is assumed to be valid.
|
||||
func (c *upstreamConfig) toInternal() (conf *agd.Upstream) {
|
||||
func (c *upstreamConfig) toInternal() (conf *agd.Upstream, err error) {
|
||||
net, addrPort, err := splitUpstreamURL(c.Server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &agd.Upstream{
|
||||
Server: c.Server,
|
||||
Server: addrPort,
|
||||
Network: net,
|
||||
FallbackServers: c.FallbackServers,
|
||||
Timeout: c.Timeout.Duration,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
// validate returns an error if the upstream configuration is invalid.
|
||||
@ -44,7 +52,7 @@ func (c *upstreamConfig) validate() (err error) {
|
||||
switch {
|
||||
case c == nil:
|
||||
return errNilConfig
|
||||
case c.Server == netip.AddrPort{}:
|
||||
case c.Server == "":
|
||||
return errors.Error("no server")
|
||||
case len(c.FallbackServers) == 0:
|
||||
return errors.Error("no fallback")
|
||||
@ -61,6 +69,37 @@ func (c *upstreamConfig) validate() (err error) {
|
||||
return errors.Annotate(c.Healthcheck.validate(), "healthcheck: %w")
|
||||
}
|
||||
|
||||
// splitUpstreamURL separates server url to net protocol and port address.
|
||||
func splitUpstreamURL(raw string) (upsNet forward.Network, addrPort netip.AddrPort, err error) {
|
||||
addr := raw
|
||||
upsNet = forward.NetworkAny
|
||||
|
||||
if strings.Contains(raw, "://") {
|
||||
var u *url.URL
|
||||
u, err = url.Parse(raw)
|
||||
if err != nil {
|
||||
return upsNet, addrPort, fmt.Errorf("bad server url: %q: %w", raw, err)
|
||||
}
|
||||
|
||||
addr = u.Host
|
||||
upsNet = forward.Network(u.Scheme)
|
||||
|
||||
switch upsNet {
|
||||
case forward.NetworkTCP, forward.NetworkUDP:
|
||||
// Go on.
|
||||
break
|
||||
default:
|
||||
return upsNet, addrPort, fmt.Errorf("bad server protocol: %q", u.Scheme)
|
||||
}
|
||||
}
|
||||
|
||||
if addrPort, err = netip.ParseAddrPort(addr); err != nil {
|
||||
return upsNet, addrPort, fmt.Errorf("bad server address: %q", addr)
|
||||
}
|
||||
|
||||
return upsNet, addrPort, nil
|
||||
}
|
||||
|
||||
// upstreamHealthcheckConfig is the configuration for the upstream healthcheck
|
||||
// feature.
|
||||
type upstreamHealthcheckConfig struct {
|
||||
|
@ -9,7 +9,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/consul"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
@ -18,7 +17,7 @@ import (
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
agdtest.DiscardLogOutput(m)
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
||||
// handleWithURL starts the test server with h, finishes it on cleanup, and
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/debugsvc"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -16,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
agdtest.DiscardLogOutput(m)
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
||||
func TestService_Start(t *testing.T) {
|
||||
|
@ -160,7 +160,7 @@ func (cc *Consul) Check(
|
||||
|
||||
qt := ri.QType
|
||||
if qt != dns.TypeA && qt != dns.TypeAAAA {
|
||||
return nil, nil
|
||||
return ri.Messages.NewMsgNODATA(req), nil
|
||||
}
|
||||
|
||||
var randomID string
|
||||
|
@ -87,7 +87,7 @@ func TestConsul_ServeHTTP(t *testing.T) {
|
||||
"profile_id": "some-profile-id",
|
||||
"server_group_name": "some-server-group-name",
|
||||
"server_name": "some-server-name",
|
||||
"protocol": agd.ProtoDNSUDP.String(),
|
||||
"protocol": agd.ProtoDNS.String(),
|
||||
"node_location": "some-node-location",
|
||||
"node_name": "some-node-name",
|
||||
"client_ip": "1.2.3.4",
|
||||
@ -109,7 +109,7 @@ func TestConsul_ServeHTTP(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ctx = dnsserver.ContextWithServerInfo(ctx, dnsserver.ServerInfo{
|
||||
Name: theOnlyVal["server_name"].(string),
|
||||
Proto: agd.ProtoDNSUDP,
|
||||
Proto: agd.ProtoDNS,
|
||||
})
|
||||
|
||||
var resp *dns.Msg
|
||||
|
@ -4,11 +4,11 @@ import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
agdtest.DiscardLogOutput(m)
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
||||
// Test data.
|
||||
|
@ -71,6 +71,8 @@ func (db *testKV) get(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(a.garipov): Consider making testutil.RequireTypeAssert accept
|
||||
// testutil.PanicT.
|
||||
require.IsType(pt, ([]byte)(nil), v)
|
||||
|
||||
val := v.([]byte)
|
||||
@ -152,7 +154,7 @@ func TestHTTPKV(t *testing.T) {
|
||||
|
||||
ctx := context.Background()
|
||||
ctx = dnsserver.ContextWithServerInfo(ctx, dnsserver.ServerInfo{
|
||||
Proto: agd.ProtoDNSUDP,
|
||||
Proto: agd.ProtoDNS,
|
||||
})
|
||||
|
||||
req := dnsservertest.CreateMessage(randomid+"-"+localDomain, dns.TypeA)
|
||||
@ -176,7 +178,7 @@ func TestHTTPKV(t *testing.T) {
|
||||
"profile_id": "some-profile-id",
|
||||
"server_group_name": "some-server-group-name",
|
||||
"server_name": "some-server-name",
|
||||
"protocol": agd.ProtoDNSUDP.String(),
|
||||
"protocol": agd.ProtoDNS.String(),
|
||||
"node_location": "some-node-location",
|
||||
"node_name": "some-node-name",
|
||||
"client_ip": "1.2.3.4",
|
||||
|
@ -3,9 +3,9 @@ package dnsdb_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
agdtest.DiscardLogOutput(m)
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
@ -72,6 +72,13 @@ func (c *Constructor) NewMsgSERVFAIL(req *dns.Msg) (resp *dns.Msg) {
|
||||
return c.newMsgRCode(req, dns.RcodeServerFailure)
|
||||
}
|
||||
|
||||
// NewMsgNODATA returns a properly initialized NODATA response.
|
||||
//
|
||||
// See https://www.rfc-editor.org/rfc/rfc2308#section-2.2.
|
||||
func (c *Constructor) NewMsgNODATA(req *dns.Msg) (resp *dns.Msg) {
|
||||
return c.newMsgRCode(req, dns.RcodeSuccess)
|
||||
}
|
||||
|
||||
// newMsgRCode returns a properly initialized response with the given RCode.
|
||||
func (c *Constructor) newMsgRCode(req *dns.Msg, rc RCode) (resp *dns.Msg) {
|
||||
resp = (&dns.Msg{}).SetRcode(req, int(rc))
|
||||
|
@ -102,6 +102,10 @@ func TestConstructor_noAnswerMethods(t *testing.T) {
|
||||
method: mc.NewMsgSERVFAIL,
|
||||
name: "servfail",
|
||||
want: dns.RcodeServerFailure,
|
||||
}, {
|
||||
method: mc.NewMsgNODATA,
|
||||
name: "nodata",
|
||||
want: dns.RcodeSuccess,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
"fmt"
|
||||
"net/netip"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
@ -107,12 +107,12 @@ func ECSFromMsg(msg *dns.Msg) (subnet netip.Prefix, scope uint8, err error) {
|
||||
// option. It returns an error if esn does not contain valid, RFC-compliant
|
||||
// EDNS Client Subnet information or the address family is unsupported.
|
||||
func ecsData(esn *dns.EDNS0_SUBNET) (subnet netip.Prefix, scope uint8, err error) {
|
||||
fam := agdnet.AddrFamily(esn.Family)
|
||||
if fam != agdnet.AddrFamilyIPv4 && fam != agdnet.AddrFamilyIPv6 {
|
||||
fam := netutil.AddrFamily(esn.Family)
|
||||
if fam != netutil.AddrFamilyIPv4 && fam != netutil.AddrFamilyIPv6 {
|
||||
return netip.Prefix{}, 0, fmt.Errorf("unsupported addr family number %d", fam)
|
||||
}
|
||||
|
||||
ip, err := agdnet.IPToAddr(esn.Address, fam)
|
||||
ip, err := netutil.IPToAddr(esn.Address, fam)
|
||||
if err != nil {
|
||||
return netip.Prefix{}, 0, fmt.Errorf("bad ecs ip addr: %w", err)
|
||||
}
|
||||
|
@ -7,17 +7,16 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
agdtest.DiscardLogOutput(m)
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
||||
// testFltRespTTL is the common filtered response TTL.
|
||||
@ -32,12 +31,12 @@ const (
|
||||
)
|
||||
|
||||
// newECSExtraMsg is a helper constructor for ECS messages.
|
||||
func newECSExtraMsg(ip netip.Addr, ecsFam agdnet.AddrFamily, mask uint8) (msg *dns.Msg) {
|
||||
func newECSExtraMsg(ip netip.Addr, ecsFam netutil.AddrFamily, mask uint8) (msg *dns.Msg) {
|
||||
var scope uint8
|
||||
switch ecsFam {
|
||||
case agdnet.AddrFamilyIPv4:
|
||||
case netutil.AddrFamilyIPv4:
|
||||
scope = ipv4Scope
|
||||
case agdnet.AddrFamilyIPv6:
|
||||
case netutil.AddrFamilyIPv6:
|
||||
scope = ipv6Scope
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported ecs addr fam %s", ecsFam))
|
||||
@ -108,49 +107,49 @@ func TestECSFromMsg(t *testing.T) {
|
||||
wantErrMsg: "",
|
||||
wantScope: 0,
|
||||
}, {
|
||||
msg: newECSExtraMsg(ipv4Net.Addr(), agdnet.AddrFamilyIPv4, ipv4MaskBits),
|
||||
msg: newECSExtraMsg(ipv4Net.Addr(), netutil.AddrFamilyIPv4, ipv4MaskBits),
|
||||
wantECS: ipv4Net,
|
||||
name: "ecs_ipv4",
|
||||
wantErrMsg: "",
|
||||
wantScope: ipv4Scope,
|
||||
}, {
|
||||
msg: newECSExtraMsg(ipv4Net.Addr(), agdnet.AddrFamilyIPv4, 0),
|
||||
msg: newECSExtraMsg(ipv4Net.Addr(), netutil.AddrFamilyIPv4, 0),
|
||||
wantECS: netip.Prefix{},
|
||||
name: "ecs_ipv4_zero_mask_addr",
|
||||
wantErrMsg: "bad ecs: ip 1.2.3.0 has non-zero bits beyond prefix 0",
|
||||
wantScope: 0,
|
||||
}, {
|
||||
msg: newECSExtraMsg(netip.IPv4Unspecified(), agdnet.AddrFamilyIPv4, 0),
|
||||
msg: newECSExtraMsg(netip.IPv4Unspecified(), netutil.AddrFamilyIPv4, 0),
|
||||
wantECS: netip.PrefixFrom(netip.IPv4Unspecified(), 0),
|
||||
name: "ecs_ipv4_zero",
|
||||
wantErrMsg: "",
|
||||
wantScope: ipv4Scope,
|
||||
}, {
|
||||
msg: newECSExtraMsg(ipv4Net.Addr(), agdnet.AddrFamilyIPv4, 1),
|
||||
msg: newECSExtraMsg(ipv4Net.Addr(), netutil.AddrFamilyIPv4, 1),
|
||||
wantECS: netip.Prefix{},
|
||||
name: "ecs_ipv4_bad_ones",
|
||||
wantErrMsg: "bad ecs: ip 1.2.3.0 has non-zero bits beyond prefix 1",
|
||||
wantScope: 0,
|
||||
}, {
|
||||
msg: newECSExtraMsg(ipv4Net.Addr(), agdnet.AddrFamilyIPv4, math.MaxUint8),
|
||||
msg: newECSExtraMsg(ipv4Net.Addr(), netutil.AddrFamilyIPv4, math.MaxUint8),
|
||||
wantECS: netip.Prefix{},
|
||||
name: "ecs_ipv4_bad_too_much",
|
||||
wantErrMsg: "bad ecs: bad src netmask 255 for addr family ipv4",
|
||||
wantScope: 0,
|
||||
}, {
|
||||
msg: newECSExtraMsg(ipv6Net.Addr(), agdnet.AddrFamilyIPv6, ipv6MaskBits),
|
||||
msg: newECSExtraMsg(ipv6Net.Addr(), netutil.AddrFamilyIPv6, ipv6MaskBits),
|
||||
wantECS: ipv6Net,
|
||||
name: "ecs_ipv6",
|
||||
wantErrMsg: "",
|
||||
wantScope: ipv6Scope,
|
||||
}, {
|
||||
msg: newECSExtraMsg(ipv6Net.Addr(), agdnet.AddrFamilyIPv6, 1),
|
||||
msg: newECSExtraMsg(ipv6Net.Addr(), netutil.AddrFamilyIPv6, 1),
|
||||
wantECS: netip.Prefix{},
|
||||
name: "ecs_ipv6_bad_ones",
|
||||
wantErrMsg: "bad ecs: ip 2001:0:102:304:: has non-zero bits beyond prefix 1",
|
||||
wantScope: 0,
|
||||
}, {
|
||||
msg: newECSExtraMsg(ipv6Net.Addr(), agdnet.AddrFamilyIPv6, math.MaxUint8),
|
||||
msg: newECSExtraMsg(ipv6Net.Addr(), netutil.AddrFamilyIPv6, math.MaxUint8),
|
||||
wantECS: netip.Prefix{},
|
||||
name: "ecs_ipv6_bad_too_much",
|
||||
wantErrMsg: "bad ecs: bad src netmask 255 for addr family ipv6",
|
||||
|
@ -254,10 +254,8 @@ func TestConstructor_NewDDR(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, unsupProto := range []dnsserver.Protocol{
|
||||
dnsserver.ProtoDNSTCP,
|
||||
dnsserver.ProtoDNSUDP,
|
||||
dnsserver.ProtoDNSCryptTCP,
|
||||
dnsserver.ProtoDNSCryptUDP,
|
||||
dnsserver.ProtoDNS,
|
||||
dnsserver.ProtoDNSCrypt,
|
||||
} {
|
||||
t.Run(unsupProto.String(), func(t *testing.T) {
|
||||
assert.Panics(t, func() {
|
||||
|
@ -13,8 +13,7 @@ type ctxKey int
|
||||
const (
|
||||
ctxKeyServerInfo ctxKey = iota
|
||||
ctxKeyStartTime
|
||||
ctxKeyRequestSize
|
||||
ctxKeyResponseSize
|
||||
ctxKeyRequestInfo
|
||||
ctxKeyClientInfo
|
||||
)
|
||||
|
||||
@ -76,27 +75,38 @@ func MustStartTimeFromContext(ctx context.Context) (t time.Time) {
|
||||
return st
|
||||
}
|
||||
|
||||
// ContextWithRequestSize attaches request's size to the specified context.
|
||||
func ContextWithRequestSize(parent context.Context, size int) (ctx context.Context) {
|
||||
return context.WithValue(parent, ctxKeyRequestSize, size)
|
||||
// RequestInfo is a structure that contains basic request information. It is
|
||||
// attached to every context.Context linked to processing a DNS request.
|
||||
type RequestInfo struct {
|
||||
// RequestSize is the size of a DNS request in bytes.
|
||||
RequestSize int
|
||||
|
||||
// ResponseSize is the size of a DNS response in bytes. May be 0 if no
|
||||
// response was sent.
|
||||
ResponseSize int
|
||||
}
|
||||
|
||||
// RequestSizeFromContext gets request's size from the context.
|
||||
func RequestSizeFromContext(ctx context.Context) (size int, found bool) {
|
||||
size, found = ctx.Value(ctxKeyRequestSize).(int)
|
||||
|
||||
return size, found
|
||||
// ContextWithRequestInfo attaches RequestInfo to the specified context.
|
||||
func ContextWithRequestInfo(parent context.Context, ri RequestInfo) (ctx context.Context) {
|
||||
return context.WithValue(parent, ctxKeyRequestInfo, ri)
|
||||
}
|
||||
|
||||
// ContextWithResponseSize attaches response's size to the specified context.
|
||||
func ContextWithResponseSize(parent context.Context, size int) (ctx context.Context) {
|
||||
return context.WithValue(parent, ctxKeyResponseSize, size)
|
||||
// RequestInfoFromContext gets RequestInfo from the specified context.
|
||||
func RequestInfoFromContext(ctx context.Context) (ri RequestInfo, found bool) {
|
||||
ri, found = ctx.Value(ctxKeyRequestInfo).(RequestInfo)
|
||||
|
||||
return ri, found
|
||||
}
|
||||
|
||||
// ResponseSizeFromContext gets response's size from the context.
|
||||
func ResponseSizeFromContext(ctx context.Context) (size int, found bool) {
|
||||
size, found = ctx.Value(ctxKeyResponseSize).(int)
|
||||
return size, found
|
||||
// MustRequestInfoFromContext gets RequestInfo attached to the context and
|
||||
// panics if it is not found.
|
||||
func MustRequestInfoFromContext(ctx context.Context) (ri RequestInfo) {
|
||||
ri, found := RequestInfoFromContext(ctx)
|
||||
if !found {
|
||||
panic("request info not found in the context")
|
||||
}
|
||||
|
||||
return ri
|
||||
}
|
||||
|
||||
// ClientInfo is a structure that contains basic information about the client.
|
||||
@ -105,9 +115,12 @@ type ClientInfo struct {
|
||||
// URL is the request URL. It is set only if the protocol of the
|
||||
// server is DoH.
|
||||
URL *url.URL
|
||||
|
||||
// TLSServerName is the server name field of the client's TLS hello
|
||||
// request. It is set only if the protocol of the server is either DoQ
|
||||
// or DoT. Note, that the original SNI is transformed to lower-case.
|
||||
// or DoT or DoH. Note, that the original SNI is transformed to lower-case.
|
||||
//
|
||||
// TODO(ameshkov): use r.TLS with DoH3 (see httpContextWithClientInfo).
|
||||
TLSServerName string
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ func TestServerInfoFromContext(t *testing.T) {
|
||||
serverInfo := dnsserver.ServerInfo{
|
||||
Name: "test",
|
||||
Addr: "127.0.0.1",
|
||||
Proto: dnsserver.ProtoDNSUDP,
|
||||
Proto: dnsserver.ProtoDNS,
|
||||
}
|
||||
ctx = dnsserver.ContextWithServerInfo(ctx, serverInfo)
|
||||
|
||||
|
@ -23,14 +23,15 @@ type Handler interface {
|
||||
// attached to it. This context always contains ServerInfo which can be
|
||||
// retrieved using ServerInfoFromContext or MustServerInfoFromContext.
|
||||
// Also, it always contains request's start time that can be retrieved
|
||||
// using StartTimeFromContext.
|
||||
ServeDNS(context.Context, ResponseWriter, *dns.Msg) error
|
||||
// using StartTimeFromContext. Finally, it also must contain ClientInfo
|
||||
// that can be retrieved using MustClientInfoFromContext.
|
||||
ServeDNS(context.Context, ResponseWriter, *dns.Msg) (err error)
|
||||
}
|
||||
|
||||
// The HandlerFunc type is an adapter to allow the use of ordinary functions
|
||||
// as DNS handlers. If f is a function with the appropriate signature,
|
||||
// HandlerFunc(f) is a Handler that calls f.
|
||||
type HandlerFunc func(context.Context, ResponseWriter, *dns.Msg) error
|
||||
type HandlerFunc func(context.Context, ResponseWriter, *dns.Msg) (err error)
|
||||
|
||||
// ServeDNS implements the Handler interface for HandlerFunc.
|
||||
func (f HandlerFunc) ServeDNS(ctx context.Context, rw ResponseWriter, req *dns.Msg) (err error) {
|
||||
@ -50,15 +51,34 @@ var notImplementedHandlerFunc HandlerFunc = func(
|
||||
}
|
||||
|
||||
// Server represents a DNS server.
|
||||
//
|
||||
// TODO(ameshkov): move validation to ctors (for all structs that inherit this).
|
||||
//
|
||||
// TODO(ameshkov): consider Proto()/Network()/Addr() -> single Info() func.
|
||||
type Server interface {
|
||||
// Name returns the server name.
|
||||
Name() (name string)
|
||||
// Proto returns the protocol of the server.
|
||||
Proto() (proto Protocol)
|
||||
// Network is a network (tcp, udp or empty) this server listens to. If it
|
||||
// is empty, the server listens to all networks that are supposed to be
|
||||
// used by its protocol.
|
||||
Network() (network Network)
|
||||
// Addr returns the address the server was configured to listen to.
|
||||
Addr() (addr string)
|
||||
// Start starts the server, exits immediately if it failed to start
|
||||
// listening. Start returns once all servers are considered up.
|
||||
Start(ctx context.Context) (err error)
|
||||
// Shutdown stops the server and waits for all active connections to close.
|
||||
Shutdown(ctx context.Context) (err error)
|
||||
// LocalAddr returns the address the server listens to at the moment. It
|
||||
// must be safe for concurrent use.
|
||||
LocalAddr() (lAddr net.Addr)
|
||||
// LocalTCPAddr returns the TCP address the server listens to at the moment
|
||||
// or nil if it does not listen to TCP. Depending on the server protocol
|
||||
// it may correspond to DNS-over-TCP, DNS-over-TLS, HTTP2, DNSCrypt (TCP).
|
||||
LocalTCPAddr() (addr net.Addr)
|
||||
// LocalUDPAddr returns the UDP address the server listens to at the moment or
|
||||
// nil if it does not listen to UDP. Depending on the server protocol
|
||||
// it may correspond to DNS-over-UDP, HTTP3, QUIC, DNSCrypt (UDP).
|
||||
LocalUDPAddr() (addr net.Addr)
|
||||
}
|
||||
|
||||
// A ResponseWriter interface is used by a DNS handler to construct a DNS
|
||||
|
@ -3,9 +3,9 @@ package dnsserver_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
dnsservertest.DiscardLogOutput(m)
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
@ -1,18 +1,3 @@
|
||||
// Package dnsservertest provides convenient helper functions for unit-tests
|
||||
// in packages related to dnsserver.
|
||||
package dnsservertest
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
|
||||
// DiscardLogOutput runs tests with discarded logger's output.
|
||||
func DiscardLogOutput(m *testing.M) {
|
||||
log.SetOutput(io.Discard)
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
13
internal/dnsserver/dnsservertest/error_unix.go
Normal file
13
internal/dnsserver/dnsservertest/error_unix.go
Normal file
@ -0,0 +1,13 @@
|
||||
//go:build unix
|
||||
|
||||
package dnsservertest
|
||||
|
||||
import (
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// errorIsAddrInUse returns true if err is an address already in use error.
|
||||
func errorIsAddrInUse(err error) (ok bool) {
|
||||
return errors.Is(err, unix.EADDRINUSE)
|
||||
}
|
13
internal/dnsserver/dnsservertest/error_windows.go
Normal file
13
internal/dnsserver/dnsservertest/error_windows.go
Normal file
@ -0,0 +1,13 @@
|
||||
//go:build windows
|
||||
|
||||
package dnsservertest
|
||||
|
||||
import (
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// errorIsAddrInUse returns true if err is an address already in use error.
|
||||
func errorIsAddrInUse(err error) (ok bool) {
|
||||
return errors.Is(err, windows.WSAEADDRINUSE)
|
||||
}
|
@ -5,12 +5,21 @@ import (
|
||||
"net"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// CreateTestHandler creates a dnsserver.Handler with the specified parameters.
|
||||
// CreateTestHandler creates a [dnsserver.Handler] with the specified parameters.
|
||||
func CreateTestHandler(recordsCount int) (h dnsserver.Handler) {
|
||||
f := func(ctx context.Context, rw dnsserver.ResponseWriter, req *dns.Msg) (err error) {
|
||||
// Check that necessary context keys are set.
|
||||
si := dnsserver.MustServerInfoFromContext(ctx)
|
||||
_ = dnsserver.MustStartTimeFromContext(ctx)
|
||||
ci := dnsserver.MustClientInfoFromContext(ctx)
|
||||
if si.Proto.IsStdEncrypted() && ci.TLSServerName == "" {
|
||||
return errors.Error("client info does not contain server name")
|
||||
}
|
||||
|
||||
hostname := req.Question[0].Name
|
||||
|
||||
resp := &dns.Msg{
|
||||
|
@ -7,128 +7,78 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/ameshkov/dnscrypt/v2"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// RunLocalDNSServer runs a simple test server with the specified handler. addr
|
||||
// is the address that can be used to reach that server.
|
||||
func RunLocalDNSServer(
|
||||
h dnsserver.Handler,
|
||||
proto dnsserver.Protocol,
|
||||
) (s *dnsserver.ServerDNS, addr string, err error) {
|
||||
// RunDNSServer runs a simple test server with the specified handler for the
|
||||
// duration of the test. It also registers a cleanup function that stops the
|
||||
// server. addr is the address that can be used to reach that server.
|
||||
//
|
||||
// TODO(a.garipov): s seems to only be used for LocalUDPAddr. Perhaps, only
|
||||
// return it?
|
||||
func RunDNSServer(t testing.TB, h dnsserver.Handler) (s *dnsserver.ServerDNS, addr string) {
|
||||
t.Helper()
|
||||
|
||||
conf := dnsserver.ConfigDNS{
|
||||
ConfigBase: dnsserver.ConfigBase{
|
||||
Name: "test",
|
||||
Addr: "127.0.0.1:0",
|
||||
Proto: proto,
|
||||
Handler: h,
|
||||
},
|
||||
}
|
||||
s = dnsserver.NewServerDNS(conf)
|
||||
require.Equal(t, dnsserver.ProtoDNS, s.Proto())
|
||||
|
||||
err = s.Start(context.Background())
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
err := runWithRetry(func() error { return s.Start(context.Background()) })
|
||||
require.NoError(t, err)
|
||||
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return s.Shutdown(context.Background())
|
||||
})
|
||||
|
||||
localAddr := s.LocalTCPAddr()
|
||||
if localAddr == nil {
|
||||
localAddr = s.LocalUDPAddr()
|
||||
}
|
||||
|
||||
return s, s.LocalAddr().String(), nil
|
||||
return s, localAddr.String()
|
||||
}
|
||||
|
||||
// DNSServer represents a plain DNS server that listens to both TCP and UDP.
|
||||
type DNSServer struct {
|
||||
Addr string
|
||||
SrvTCP *dnsserver.ServerDNS
|
||||
SrvUDP *dnsserver.ServerDNS
|
||||
}
|
||||
// RunTLSServer runs a simple test server with the specified handler for the
|
||||
// duration of the test. It also registers a cleanup function that stops the
|
||||
// server. addr is the address that can be used to reach that server.
|
||||
func RunTLSServer(t testing.TB, h dnsserver.Handler, tlsConfig *tls.Config) (addr *net.TCPAddr) {
|
||||
t.Helper()
|
||||
|
||||
// Shutdown stops both servers
|
||||
func (s *DNSServer) Shutdown(ctx context.Context) (err error) {
|
||||
err = s.SrvUDP.Shutdown(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.SrvTCP.Shutdown(ctx)
|
||||
}
|
||||
|
||||
// RunDNSServer runs a test DNS server with the specified handler. Actually, it
|
||||
// runs two DNS servers, one with dnsserver.ProtoDNSUDP and the other one with
|
||||
// dnsserver.ProtoDNSTCP, both of which use the same port.
|
||||
func RunDNSServer(h dnsserver.Handler) (s *DNSServer, err error) {
|
||||
s = &DNSServer{}
|
||||
|
||||
// First let's run the TCP server
|
||||
conf := dnsserver.ConfigDNS{
|
||||
ConfigBase: dnsserver.ConfigBase{
|
||||
Name: "test_tcp",
|
||||
Addr: "127.0.0.1:0",
|
||||
Proto: dnsserver.ProtoDNSTCP,
|
||||
Handler: h,
|
||||
},
|
||||
}
|
||||
s.SrvTCP = dnsserver.NewServerDNS(conf)
|
||||
err = s.SrvTCP.Start(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Now let's get the port that it uses
|
||||
port := s.SrvTCP.LocalAddr().(*net.TCPAddr).Port
|
||||
s.Addr = fmt.Sprintf("127.0.0.1:%d", port)
|
||||
|
||||
// Now we can run the UDP server
|
||||
conf = dnsserver.ConfigDNS{
|
||||
ConfigBase: dnsserver.ConfigBase{
|
||||
Name: "test_udp",
|
||||
Addr: s.Addr,
|
||||
Proto: dnsserver.ProtoDNSUDP,
|
||||
Handler: h,
|
||||
},
|
||||
}
|
||||
s.SrvUDP = dnsserver.NewServerDNS(conf)
|
||||
err = s.SrvUDP.Start(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// RunLocalTLSServer runs a simple test server with the specified handler
|
||||
// returns the address that can be used to reach that server
|
||||
func RunLocalTLSServer(
|
||||
h dnsserver.Handler,
|
||||
tlsConfig *tls.Config,
|
||||
) (s *dnsserver.ServerTLS, addr *net.TCPAddr, err error) {
|
||||
conf := dnsserver.ConfigTLS{
|
||||
ConfigDNS: dnsserver.ConfigDNS{
|
||||
ConfigBase: dnsserver.ConfigBase{
|
||||
Name: "test",
|
||||
Addr: "127.0.0.1:0",
|
||||
Proto: dnsserver.ProtoDoT,
|
||||
Handler: h,
|
||||
},
|
||||
},
|
||||
TLSConfig: tlsConfig,
|
||||
}
|
||||
|
||||
s = dnsserver.NewServerTLS(conf)
|
||||
if s.Proto() != dnsserver.ProtoDoT {
|
||||
return nil, nil, errors.Error("invalid protocol")
|
||||
}
|
||||
err = s.Start(context.Background())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
s := dnsserver.NewServerTLS(conf)
|
||||
require.Equal(t, dnsserver.ProtoDoT, s.Proto())
|
||||
|
||||
addr, ok := s.LocalAddr().(*net.TCPAddr)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("invalid listen addr: %s", addr)
|
||||
}
|
||||
err := runWithRetry(func() error { return s.Start(context.Background()) })
|
||||
require.NoError(t, err)
|
||||
|
||||
return s, addr, nil
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return s.Shutdown(context.Background())
|
||||
})
|
||||
|
||||
return testutil.RequireTypeAssert[*net.TCPAddr](t, s.LocalTCPAddr())
|
||||
}
|
||||
|
||||
// TestDNSCryptServer is a structure that contains the initialized DNSCrypt
|
||||
@ -136,98 +86,82 @@ func RunLocalTLSServer(
|
||||
type TestDNSCryptServer struct {
|
||||
Srv *dnsserver.ServerDNSCrypt
|
||||
ProviderName string
|
||||
ResolverPk ed25519.PublicKey
|
||||
ServerAddr string
|
||||
ResolverPk ed25519.PublicKey
|
||||
}
|
||||
|
||||
// RunLocalDNSCryptServer runs a simple test DNSCrypt server with the specified
|
||||
// handler. Returns the address that can be used to reach that server.
|
||||
func RunLocalDNSCryptServer(
|
||||
h dnsserver.Handler,
|
||||
network dnsserver.Network,
|
||||
) (s *TestDNSCryptServer, err error) {
|
||||
// RunDNSCryptServer runs a simple test DNSCrypt server with the specified
|
||||
// handler for the duration of the test. It also registers a cleanup function
|
||||
// that stops the server.
|
||||
func RunDNSCryptServer(t testing.TB, h dnsserver.Handler) (s *TestDNSCryptServer) {
|
||||
t.Helper()
|
||||
|
||||
s = &TestDNSCryptServer{
|
||||
ProviderName: "example.org",
|
||||
}
|
||||
|
||||
// Generate DNSCrypt configuration for the server
|
||||
var rc dnscrypt.ResolverConfig
|
||||
rc, err = dnscrypt.GenerateResolverConfig(s.ProviderName, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var cert *dnscrypt.Cert
|
||||
cert, err = rc.CreateCert()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rc, err := dnscrypt.GenerateResolverConfig(s.ProviderName, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
cert, err := rc.CreateCert()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Extract the public key (we'll use it for the dnscrypt.Client)
|
||||
var privateKey []byte
|
||||
privateKey, err = dnscrypt.HexDecodeKey(rc.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resolverPk, ok := ed25519.PrivateKey(privateKey).Public().(ed25519.PublicKey)
|
||||
if !ok {
|
||||
return nil, errors.Error("could not create a private key")
|
||||
}
|
||||
s.ResolverPk = resolverPk
|
||||
require.NoError(t, err)
|
||||
|
||||
proto := dnsserver.ProtoDNSCryptUDP
|
||||
if network != dnsserver.NetworkUDP {
|
||||
proto = dnsserver.ProtoDNSCryptTCP
|
||||
}
|
||||
pk := ed25519.PrivateKey(privateKey).Public()
|
||||
|
||||
s.ResolverPk = testutil.RequireTypeAssert[ed25519.PublicKey](t, pk)
|
||||
|
||||
conf := dnsserver.ConfigDNSCrypt{
|
||||
ConfigBase: dnsserver.ConfigBase{
|
||||
Name: "test",
|
||||
Addr: "127.0.0.1:0",
|
||||
Proto: proto,
|
||||
Handler: h,
|
||||
},
|
||||
DNSCryptProviderName: s.ProviderName,
|
||||
DNSCryptResolverCert: cert,
|
||||
}
|
||||
|
||||
// Create a new ServerDNSCrypt and run it
|
||||
// Create a new ServerDNSCrypt and run it.
|
||||
s.Srv = dnsserver.NewServerDNSCrypt(conf)
|
||||
err = s.Srv.Start(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
require.Equal(t, dnsserver.ProtoDNSCrypt, s.Srv.Proto())
|
||||
|
||||
// Get the address it listens to
|
||||
addr := s.Srv.LocalAddr()
|
||||
if addr == nil {
|
||||
return nil, errors.Error("wrong address")
|
||||
}
|
||||
err = runWithRetry(func() error { return s.Srv.Start(context.Background()) })
|
||||
require.NoError(t, err)
|
||||
|
||||
switch v := addr.(type) {
|
||||
case *net.UDPAddr:
|
||||
s.ServerAddr = fmt.Sprintf("127.0.0.1:%d", v.Port)
|
||||
case *net.TCPAddr:
|
||||
s.ServerAddr = fmt.Sprintf("127.0.0.1:%d", v.Port)
|
||||
default:
|
||||
return nil, fmt.Errorf("wrong address %v", addr)
|
||||
}
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return s.Srv.Shutdown(context.Background())
|
||||
})
|
||||
|
||||
return s, nil
|
||||
// Get the address it listens to. It does not matter which one will be
|
||||
// used (UDP or TCP) since we need it in the string format.
|
||||
s.ServerAddr = s.Srv.LocalUDPAddr().String()
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// RunLocalHTTPSServer runs a simple test HTTP server with the specified handler.
|
||||
// addr is the address that can be used to reach that server.
|
||||
// RunLocalHTTPSServer runs a simple test HTTP server with the specified
|
||||
// handler. addr is the address that can be used to reach that server.
|
||||
func RunLocalHTTPSServer(
|
||||
h dnsserver.Handler,
|
||||
tlsConfig *tls.Config,
|
||||
nonDNSHandler http.Handler,
|
||||
) (s *dnsserver.ServerHTTPS, addr *net.TCPAddr, err error) {
|
||||
) (s *dnsserver.ServerHTTPS, err error) {
|
||||
network := dnsserver.NetworkAny
|
||||
if tlsConfig == nil {
|
||||
network = dnsserver.NetworkTCP
|
||||
}
|
||||
|
||||
conf := dnsserver.ConfigHTTPS{
|
||||
ConfigBase: dnsserver.ConfigBase{
|
||||
Name: "test",
|
||||
Addr: "127.0.0.1:0",
|
||||
Proto: dnsserver.ProtoDoH,
|
||||
Handler: h,
|
||||
Network: network,
|
||||
},
|
||||
TLSConfig: tlsConfig,
|
||||
NonDNSHandler: nonDNSHandler,
|
||||
@ -235,21 +169,15 @@ func RunLocalHTTPSServer(
|
||||
|
||||
s = dnsserver.NewServerHTTPS(conf)
|
||||
if s.Proto() != dnsserver.ProtoDoH {
|
||||
return nil, nil, errors.Error("invalid protocol")
|
||||
return nil, errors.Error("invalid protocol")
|
||||
}
|
||||
|
||||
err = s.Start(context.Background())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
addr, ok = s.LocalAddr().(*net.TCPAddr)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("invalid listen addr: %s", addr)
|
||||
}
|
||||
|
||||
return s, addr, nil
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// RunLocalQUICServer runs a simple test HTTP server with the specified handler.
|
||||
@ -262,7 +190,6 @@ func RunLocalQUICServer(
|
||||
ConfigBase: dnsserver.ConfigBase{
|
||||
Name: "test",
|
||||
Addr: "127.0.0.1:0",
|
||||
Proto: dnsserver.ProtoDoQ,
|
||||
Handler: h,
|
||||
},
|
||||
TLSConfig: tlsConfig,
|
||||
@ -278,10 +205,29 @@ func RunLocalQUICServer(
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
addr, ok := s.LocalAddr().(*net.UDPAddr)
|
||||
addr, ok := s.LocalUDPAddr().(*net.UDPAddr)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("invalid listen addr: %s", addr)
|
||||
}
|
||||
|
||||
return s, addr, nil
|
||||
}
|
||||
|
||||
// runWithRetry runs exec func and retries in case of address already in use
|
||||
// error.
|
||||
func runWithRetry(exec func() error) (err error) {
|
||||
err = exec()
|
||||
if err != nil {
|
||||
if errorIsAddrInUse(err) {
|
||||
// Give system time to release sockets.
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
err = exec()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("after one retry: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
@ -44,8 +44,9 @@ Alternatively, you can use forward.NewHandler to create a DNS forwarding handler
|
||||
|
||||
# Plain DNS
|
||||
|
||||
Plain DNS server may use either TCP or UDP protocols. Here's how to create a
|
||||
simple plain DNS server:
|
||||
By default, plain DNS server will listen to both TCP and UDP unless Network
|
||||
is specified in the configuration. Here's how to create a simple plain
|
||||
DNS server:
|
||||
|
||||
conf := dnsserver.ConfigDNS{
|
||||
ConfigBase: dnsserver.ConfigBase{
|
||||
@ -53,8 +54,6 @@ simple plain DNS server:
|
||||
Name: "test",
|
||||
// listen address
|
||||
Addr: "127.0.0.1:0",
|
||||
// protocol (ProtoDNSUDP or ProtoDNSTCP)
|
||||
Proto: dnsserver.ProtoDNSUDP,
|
||||
// handler that will process incoming DNS queries
|
||||
Handler: handler,
|
||||
},
|
||||
@ -75,7 +74,6 @@ certificate and its private key.
|
||||
ConfigBase: dnsserver.ConfigBase{
|
||||
Name: "test",
|
||||
Addr: "127.0.0.1:0",
|
||||
Proto: dnsserver.ProtoDoT,
|
||||
Handler: h,
|
||||
},
|
||||
},
|
||||
@ -86,12 +84,14 @@ certificate and its private key.
|
||||
|
||||
# DNS-over-HTTPS
|
||||
|
||||
DoH server uses an [*http.Server] internally. There are a couple of things to
|
||||
note:
|
||||
DoH server uses an [*http.Server] and/or [*http3.Server] internally. There are
|
||||
a couple of things to note:
|
||||
|
||||
1. tls.Config is optional. If you don't pass it, the server works simply as a
|
||||
plain HTTP server. This might be useful if you're running a reverse proxy
|
||||
like nginx in front of your DoH server.
|
||||
1. tls.Config can be omitted, but you must set [ConfigBase.Network] to
|
||||
NetworkTCP. In this case the server will work simply as a plain HTTP
|
||||
server. This might be useful if you're running a reverse proxy like Nginx
|
||||
in front of your DoH server. If you do specify it, the server will listen
|
||||
to both DoH2 and DoH3 by default.
|
||||
|
||||
2. In the constructor you can specify an optional [http.HandlerFunc] that
|
||||
processes non-DNS requests, e.g. requests to paths different from
|
||||
@ -103,7 +103,6 @@ Example:
|
||||
ConfigBase: dnsserver.ConfigBase{
|
||||
Name: "test",
|
||||
Addr: "127.0.0.1:0",
|
||||
Proto: dnsserver.ProtoDoH,
|
||||
Handler: h,
|
||||
},
|
||||
TLSConfig: tlsConfig,
|
||||
@ -121,7 +120,6 @@ DoQ server uses the [quic-go module]. Just like DoH and DoT, it requires a
|
||||
ConfigBase: dnsserver.ConfigBase{
|
||||
Name: "test",
|
||||
Addr: "127.0.0.1:0",
|
||||
Proto: dnsserver.ProtoDoQ,
|
||||
Handler: h,
|
||||
},
|
||||
TLSConfig: tlsConfig,
|
||||
@ -139,7 +137,6 @@ documentation] about how to initialize it.
|
||||
ConfigBase: dnsserver.ConfigBase{
|
||||
Name: "test",
|
||||
Addr: "127.0.0.1:0",
|
||||
Proto: dnsserver.ProtoDNSCryptUDP,
|
||||
Handler: h,
|
||||
},
|
||||
DNSCryptProviderName: s.ProviderName,
|
||||
@ -148,9 +145,6 @@ documentation] about how to initialize it.
|
||||
s := dnsserver.NewServerDNSCrypt(conf)
|
||||
err := s.Start(context.Background())
|
||||
|
||||
Normally, you would like to run two servers on the same address. One would
|
||||
listen to TCP, and the other one would listen to UDP.
|
||||
|
||||
# Middlewares
|
||||
|
||||
Package dnsserver supports customizing server behavior using middlewares. All
|
||||
|
@ -34,8 +34,6 @@ func ExampleNewServerDNS() {
|
||||
Name: "test",
|
||||
// listen address
|
||||
Addr: "127.0.0.1:0",
|
||||
// protocol (ProtoDNSUDP or ProtoDNSTCP)
|
||||
Proto: dnsserver.ProtoDNSUDP,
|
||||
// handler that will process incoming DNS queries
|
||||
Handler: handler,
|
||||
},
|
||||
@ -60,6 +58,7 @@ func ExampleWithMiddlewares() {
|
||||
// Init a handler func function with middlewares.
|
||||
forwarder := forward.NewHandler(&forward.HandlerConfig{
|
||||
Address: netip.MustParseAddrPort("94.140.14.140:53"),
|
||||
Network: forward.NetworkAny,
|
||||
}, true)
|
||||
|
||||
middleware := querylog.NewLogMiddleware(os.Stdout)
|
||||
@ -70,7 +69,6 @@ func ExampleWithMiddlewares() {
|
||||
ConfigBase: dnsserver.ConfigBase{
|
||||
Name: "test",
|
||||
Addr: "127.0.0.1:0",
|
||||
Proto: dnsserver.ProtoDNSUDP,
|
||||
Handler: handler,
|
||||
},
|
||||
}
|
||||
|
@ -14,9 +14,9 @@ func ExampleNewHandler() {
|
||||
ConfigBase: dnsserver.ConfigBase{
|
||||
Name: "srv",
|
||||
Addr: "127.0.0.1:0",
|
||||
Proto: dnsserver.ProtoDNSUDP,
|
||||
Handler: forward.NewHandler(&forward.HandlerConfig{
|
||||
Address: netip.MustParseAddrPort("8.8.8.8:53"),
|
||||
Network: forward.NetworkAny,
|
||||
FallbackAddresses: []netip.AddrPort{
|
||||
netip.MustParseAddrPort("1.1.1.1:53"),
|
||||
},
|
||||
|
@ -84,6 +84,9 @@ type HandlerConfig struct {
|
||||
// all DNS queries.
|
||||
Address netip.AddrPort
|
||||
|
||||
// Network is the network protocol for the main upstream address.
|
||||
Network Network
|
||||
|
||||
// MetricsListener is the optional listener for the handler events. Set it
|
||||
// if you want to keep track of what the handler does and record performance
|
||||
// metrics. If not set, EmptyMetricsListener is used.
|
||||
@ -117,7 +120,7 @@ type HandlerConfig struct {
|
||||
// support plain DNS upstreams. c must not be nil.
|
||||
func NewHandler(c *HandlerConfig, initialHealthcheck bool) (h *Handler) {
|
||||
h = &Handler{
|
||||
upstream: NewUpstreamPlain(c.Address, NetworkAny),
|
||||
upstream: NewUpstreamPlain(c.Address, c.Network),
|
||||
hcDomainTmpl: c.HealthcheckDomainTmpl,
|
||||
timeout: c.Timeout,
|
||||
hcBackoff: c.HealthcheckBackoffDuration,
|
||||
|
@ -14,28 +14,23 @@ import (
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
dnsservertest.DiscardLogOutput(m)
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
||||
func TestHandler_ServeDNS(t *testing.T) {
|
||||
srv, err := dnsservertest.RunDNSServer(dnsservertest.DefaultHandler())
|
||||
require.NoError(t, err)
|
||||
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return srv.Shutdown(context.Background())
|
||||
})
|
||||
srv, addr := dnsservertest.RunDNSServer(t, dnsservertest.DefaultHandler())
|
||||
|
||||
// No-fallbacks handler.
|
||||
handler := forward.NewHandler(&forward.HandlerConfig{
|
||||
Address: netip.MustParseAddrPort(srv.Addr),
|
||||
Address: netip.MustParseAddrPort(addr),
|
||||
Network: forward.NetworkAny,
|
||||
}, true)
|
||||
|
||||
req := dnsservertest.CreateMessage("example.org.", dns.TypeA)
|
||||
addr := srv.SrvTCP.LocalAddr()
|
||||
rw := dnsserver.NewNonWriterResponseWriter(addr, addr)
|
||||
rw := dnsserver.NewNonWriterResponseWriter(srv.LocalUDPAddr(), srv.LocalUDPAddr())
|
||||
|
||||
// Check the handler's ServeDNS method
|
||||
err = handler.ServeDNS(context.Background(), rw, req)
|
||||
err := handler.ServeDNS(context.Background(), rw, req)
|
||||
require.NoError(t, err)
|
||||
|
||||
res := rw.Msg()
|
||||
@ -44,26 +39,20 @@ func TestHandler_ServeDNS(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHandler_ServeDNS_fallbackNetError(t *testing.T) {
|
||||
srv, err := dnsservertest.RunDNSServer(dnsservertest.DefaultHandler())
|
||||
require.NoError(t, err)
|
||||
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return srv.Shutdown(context.Background())
|
||||
})
|
||||
|
||||
srv, _ := dnsservertest.RunDNSServer(t, dnsservertest.DefaultHandler())
|
||||
handler := forward.NewHandler(&forward.HandlerConfig{
|
||||
Address: netip.MustParseAddrPort("127.0.0.1:0"),
|
||||
Network: forward.NetworkAny,
|
||||
FallbackAddresses: []netip.AddrPort{
|
||||
netip.MustParseAddrPort(srv.Addr),
|
||||
netip.MustParseAddrPort(srv.LocalUDPAddr().String()),
|
||||
},
|
||||
}, true)
|
||||
|
||||
req := dnsservertest.CreateMessage("example.org.", dns.TypeA)
|
||||
addr := srv.SrvTCP.LocalAddr()
|
||||
rw := dnsserver.NewNonWriterResponseWriter(addr, addr)
|
||||
rw := dnsserver.NewNonWriterResponseWriter(srv.LocalUDPAddr(), srv.LocalUDPAddr())
|
||||
|
||||
// Check the handler's ServeDNS method
|
||||
err = handler.ServeDNS(context.Background(), rw, req)
|
||||
err := handler.ServeDNS(context.Background(), rw, req)
|
||||
require.NoError(t, err)
|
||||
|
||||
res := rw.Msg()
|
||||
|
@ -9,7 +9,6 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/forward"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -41,25 +40,14 @@ func TestHandler_Refresh(t *testing.T) {
|
||||
return rw.WriteMsg(ctx, req, nrw.Msg())
|
||||
})
|
||||
|
||||
upstream, err := dnsservertest.RunDNSServer(handlerFunc)
|
||||
require.NoError(t, err)
|
||||
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return upstream.Shutdown(context.Background())
|
||||
})
|
||||
|
||||
fallback, err := dnsservertest.RunDNSServer(dnsservertest.DefaultHandler())
|
||||
require.NoError(t, err)
|
||||
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return fallback.Shutdown(context.Background())
|
||||
})
|
||||
|
||||
upstream, _ := dnsservertest.RunDNSServer(t, handlerFunc)
|
||||
fallback, _ := dnsservertest.RunDNSServer(t, dnsservertest.DefaultHandler())
|
||||
handler := forward.NewHandler(&forward.HandlerConfig{
|
||||
Address: netip.MustParseAddrPort(upstream.Addr),
|
||||
Address: netip.MustParseAddrPort(upstream.LocalUDPAddr().String()),
|
||||
Network: forward.NetworkAny,
|
||||
HealthcheckDomainTmpl: "${RANDOM}.upstream-check.example",
|
||||
FallbackAddresses: []netip.AddrPort{
|
||||
netip.MustParseAddrPort(fallback.Addr),
|
||||
netip.MustParseAddrPort(fallback.LocalUDPAddr().String()),
|
||||
},
|
||||
// Make sure that the handler routs queries back to the main upstream
|
||||
// immediately.
|
||||
@ -67,10 +55,9 @@ func TestHandler_Refresh(t *testing.T) {
|
||||
}, false)
|
||||
|
||||
req := dnsservertest.CreateMessage("example.org.", dns.TypeA)
|
||||
addr := fallback.SrvTCP.LocalAddr()
|
||||
rw := dnsserver.NewNonWriterResponseWriter(addr, addr)
|
||||
rw := dnsserver.NewNonWriterResponseWriter(fallback.LocalUDPAddr(), fallback.LocalUDPAddr())
|
||||
|
||||
err = handler.ServeDNS(context.Background(), rw, req)
|
||||
err := handler.ServeDNS(context.Background(), rw, req)
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, uint64(1), atomic.LoadUint64(&upstreamRequestsCount))
|
||||
|
||||
|
@ -9,7 +9,6 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/forward"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@ -31,14 +30,8 @@ func TestUpstreamPlain_Exchange(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
srv, err := dnsservertest.RunDNSServer(dnsservertest.DefaultHandler())
|
||||
require.NoError(t, err)
|
||||
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return srv.Shutdown(context.Background())
|
||||
})
|
||||
|
||||
u := forward.NewUpstreamPlain(netip.MustParseAddrPort(srv.Addr), tc.network)
|
||||
_, addr := dnsservertest.RunDNSServer(t, dnsservertest.DefaultHandler())
|
||||
u := forward.NewUpstreamPlain(netip.MustParseAddrPort(addr), tc.network)
|
||||
defer log.OnCloserError(u, log.DEBUG)
|
||||
|
||||
req := dnsservertest.CreateMessage("example.org.", dns.TypeA)
|
||||
@ -68,8 +61,9 @@ func TestUpstreamPlain_Exchange_truncated(t *testing.T) {
|
||||
}
|
||||
|
||||
res := nrw.Msg()
|
||||
si := dnsserver.MustServerInfoFromContext(ctx)
|
||||
if si.Proto == dnsserver.ProtoDNSUDP {
|
||||
network := dnsserver.NetworkFromAddr(rw.LocalAddr())
|
||||
|
||||
if network == dnsserver.NetworkUDP {
|
||||
res.Truncated = true
|
||||
res.Answer = nil
|
||||
}
|
||||
@ -77,19 +71,14 @@ func TestUpstreamPlain_Exchange_truncated(t *testing.T) {
|
||||
return rw.WriteMsg(ctx, req, res)
|
||||
})
|
||||
|
||||
srv, err := dnsservertest.RunDNSServer(handlerFunc)
|
||||
require.NoError(t, err)
|
||||
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return srv.Shutdown(context.Background())
|
||||
})
|
||||
_, addr := dnsservertest.RunDNSServer(t, handlerFunc)
|
||||
|
||||
// Create a test message.
|
||||
req := dnsservertest.CreateMessage("example.org.", dns.TypeA)
|
||||
|
||||
// First, check that we receive truncated response over UDP.
|
||||
addr := netip.MustParseAddrPort(srv.Addr)
|
||||
uUDP := forward.NewUpstreamPlain(addr, forward.NetworkUDP)
|
||||
uAddr := netip.MustParseAddrPort(addr)
|
||||
uUDP := forward.NewUpstreamPlain(uAddr, forward.NetworkUDP)
|
||||
defer log.OnCloserError(uUDP, log.DEBUG)
|
||||
|
||||
res, err := uUDP.Exchange(context.Background(), req)
|
||||
@ -97,7 +86,7 @@ func TestUpstreamPlain_Exchange_truncated(t *testing.T) {
|
||||
dnsservertest.RequireResponse(t, req, res, 0, dns.RcodeSuccess, true)
|
||||
|
||||
// Second, check that nothing is truncated over TCP.
|
||||
uTCP := forward.NewUpstreamPlain(addr, forward.NetworkTCP)
|
||||
uTCP := forward.NewUpstreamPlain(uAddr, forward.NetworkTCP)
|
||||
defer log.OnCloserError(uTCP, log.DEBUG)
|
||||
|
||||
res, err = uTCP.Exchange(context.Background(), req)
|
||||
@ -106,7 +95,7 @@ func TestUpstreamPlain_Exchange_truncated(t *testing.T) {
|
||||
|
||||
// Now with NetworkANY response is also not truncated since the upstream
|
||||
// fallbacks to TCP.
|
||||
uAny := forward.NewUpstreamPlain(addr, forward.NetworkAny)
|
||||
uAny := forward.NewUpstreamPlain(uAddr, forward.NetworkAny)
|
||||
defer log.OnCloserError(uAny, log.DEBUG)
|
||||
|
||||
res, err = uAny.Exchange(context.Background(), req)
|
||||
|
@ -3,18 +3,18 @@ module github.com/AdguardTeam/AdGuardDNS/internal/dnsserver
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/AdguardTeam/golibs v0.10.9
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.3
|
||||
github.com/AdguardTeam/golibs v0.11.3
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.5
|
||||
github.com/ameshkov/dnsstamps v1.0.3
|
||||
github.com/bluele/gcache v0.0.2
|
||||
github.com/lucas-clemente/quic-go v0.28.1
|
||||
github.com/lucas-clemente/quic-go v0.29.2
|
||||
github.com/miekg/dns v1.1.50
|
||||
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible
|
||||
github.com/prometheus/client_golang v1.13.0
|
||||
github.com/prometheus/client_golang v1.13.1
|
||||
github.com/stretchr/testify v1.8.0
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234
|
||||
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6
|
||||
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326
|
||||
golang.org/x/net v0.1.0
|
||||
golang.org/x/sys v0.1.0
|
||||
)
|
||||
|
||||
require (
|
||||
@ -22,28 +22,31 @@ require (
|
||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cheekybits/genny v1.0.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
|
||||
github.com/marten-seemann/qtls-go1-17 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/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/marten-seemann/qpack v0.3.0 // indirect
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.4.0 // indirect
|
||||
github.com/onsi/gomega v1.22.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
golang.org/x/crypto v0.1.0 // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
|
@ -1,7 +1,5 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
@ -32,15 +30,8 @@ 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.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
|
||||
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/AdguardTeam/golibs v0.4.2/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/golibs v0.10.9 h1:F9oP2da0dQ9RQDM1lGR7LxUTfUWu8hEFOs4icwAkKM0=
|
||||
github.com/AdguardTeam/golibs v0.10.9/go.mod h1:W+5rznZa1cSNSFt+gPS7f4Wytnr9fOrd5ZYqwadPw14=
|
||||
github.com/AdguardTeam/golibs v0.11.3 h1:Oif+REq2WLycQ2Xm3ZPmJdfftptss0HbGWbxdFaC310=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
|
||||
@ -52,50 +43,36 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.3 h1:X9UP5AHtwp46Ji+sGFfF/1Is6OPI/SjxLqhKpx0P5UI=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.3/go.mod h1:xJB9cE1/GF+NB6EEQqRlkoa4bjcV2w7VYn1G+zVq7Bs=
|
||||
github.com/ameshkov/dnsstamps v1.0.1/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.5 h1:Ju1gQeez+6XLtk/b/k3RoJ2t+Ls+BSItLTZjZeedneY=
|
||||
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
|
||||
github.com/ameshkov/dnsstamps v1.0.3/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
|
||||
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
|
||||
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
||||
github.com/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.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@ -107,6 +84,7 @@ 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.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
@ -115,7 +93,6 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
@ -153,9 +130,8 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
@ -167,19 +143,12 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
@ -195,28 +164,18 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
|
||||
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/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/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/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.2 h1:JADBlm0LYiVbuSySCHeY863dNkcpMmDR7s0bLKJeYlQ=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.2/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s=
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.2 h1:JH6jmzbduz0ITVQ7ShevK10Av5+jBEKAHMntXmIV7kM=
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.0 h1:rLFKD/9mp/uq1SYGYuVZhm83wkmU95pK5df3GufyYYU=
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.0/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/lucas-clemente/quic-go v0.29.2 h1:O8Mt0O6LpvEW+wfC40vZdcw0DngwYzoxq5xULZNzSI8=
|
||||
github.com/lucas-clemente/quic-go v0.29.2/go.mod h1:g6/h9YMmLuU54tL1gW25uIi3VlBp3uv+sBihplIuskE=
|
||||
github.com/marten-seemann/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE=
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.3 h1:R4H2Ks8P6pAtUagjFty2p7BVHn3XiwDAl7TTQf5h7TI=
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.3/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.1 h1:mnbxeq3oEyQxQXwI4ReCgW9DPoPR94sNlqWoDZnjRIE=
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@ -226,8 +185,6 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
@ -235,16 +192,12 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
|
||||
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
|
||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI=
|
||||
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible h1:IWzUvJ72xMjmrjR9q3H1PF+jwdN0uNQiR2t1BLNalyo=
|
||||
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@ -252,27 +205,23 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
|
||||
github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
|
||||
github.com/prometheus/client_golang v1.13.1 h1:3gMjIY2+/hzmqhtUC/aQNYldJA6DtH3CgQvwS+02K1c=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
|
||||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
@ -281,35 +230,9 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
|
||||
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
|
||||
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
|
||||
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
|
||||
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
|
||||
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
|
||||
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
|
||||
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
|
||||
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
|
||||
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
|
||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
|
||||
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
|
||||
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
|
||||
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
|
||||
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
|
||||
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
|
||||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
|
||||
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
@ -317,38 +240,26 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 h1:GIAS/yBem/gq2MUqgNIzUHW7cJMmx3TGZOrnyYaNQ6c=
|
||||
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@ -359,11 +270,9 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
|
||||
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 h1:QfTh0HpN6hlw6D3vu8DAwC8pBIwikq0AI1evdm+FksE=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@ -383,18 +292,14 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.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 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
|
||||
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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
@ -403,7 +308,6 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@ -421,24 +325,18 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E=
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -454,11 +352,9 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+v
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -467,7 +363,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -485,7 +380,6 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -503,9 +397,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U=
|
||||
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -514,15 +406,12 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/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-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
@ -542,7 +431,6 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
@ -566,15 +454,11 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
@ -592,18 +476,12 @@ google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
@ -632,9 +510,6 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
@ -669,22 +544,17 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8X
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
@ -695,5 +565,3 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||
|
@ -4,12 +4,30 @@ package dnsserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
)
|
||||
|
||||
// listenUDP listens to the specified address on UDP.
|
||||
func listenUDP(_ context.Context, addr string) (conn net.PacketConn, err error) {
|
||||
return net.ListenPacket("udp", addr)
|
||||
func listenUDP(_ context.Context, addr string, _ bool) (conn *net.UDPConn, err error) {
|
||||
defer func() { err = errors.Annotate(err, "opening packet listener: %w") }()
|
||||
|
||||
c, err := net.ListenPacket("udp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn, ok := c.(*net.UDPConn)
|
||||
if !ok {
|
||||
// TODO(ameshkov): should not happen, consider panic here.
|
||||
err = fmt.Errorf("expected conn of type %T, got %T", conn, c)
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// listenTCP listens to the specified address on TCP.
|
||||
|
@ -4,9 +4,11 @@ package dnsserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
@ -22,11 +24,35 @@ func reuseportControl(_, _ string, c syscall.RawConn) (err error) {
|
||||
return opErr
|
||||
}
|
||||
|
||||
// listenUDP listens to the specified address on UDP.
|
||||
func listenUDP(ctx context.Context, addr string) (conn net.PacketConn, err error) {
|
||||
// listenUDP listens to the specified address on UDP. If oob flag is set to
|
||||
// true this method also enables OOB for the listen socket that enables using of
|
||||
// ReadMsgUDP/WriteMsgUDP. Doing it this way is necessary to correctly discover
|
||||
// the source address when it listens to 0.0.0.0.
|
||||
func listenUDP(ctx context.Context, addr string, oob bool) (conn *net.UDPConn, err error) {
|
||||
defer func() { err = errors.Annotate(err, "opening packet listener: %w") }()
|
||||
|
||||
var lc net.ListenConfig
|
||||
lc.Control = reuseportControl
|
||||
return lc.ListenPacket(ctx, "udp", addr)
|
||||
c, err := lc.ListenPacket(ctx, "udp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn, ok := c.(*net.UDPConn)
|
||||
if !ok {
|
||||
// TODO(ameshkov): should not happen, consider panic here.
|
||||
err = fmt.Errorf("expected conn of type %T, got %T", conn, c)
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if oob {
|
||||
if err = setUDPSocketOptions(conn); err != nil {
|
||||
return nil, fmt.Errorf("failed to set socket options: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return conn, err
|
||||
}
|
||||
|
||||
// listenTCP listens to the specified address on TCP.
|
||||
|
@ -15,20 +15,18 @@ import (
|
||||
// ServerInfoFromContext or MustServerInfoFromContext.
|
||||
// N.B. The implementation must be thread-safe.
|
||||
type MetricsListener interface {
|
||||
// OnRequest called when we finished processing a request and we know what
|
||||
// OnRequest called when we finished processing a request, and we know what
|
||||
// response has been written.
|
||||
//
|
||||
// ctx is the context of the DNS request. Besides the server info it also must
|
||||
// contain request's start time (can be retrieved by StartTimeFromContext or
|
||||
// MustStartTimeFromContext) and request and response sizes
|
||||
// (RequestSizeFromContext and ResponseSizeFromContext). Response size will
|
||||
// only be attached if there was any response written by the server.
|
||||
// ctx is the context of the DNS request. Besides the server info, it also
|
||||
// must contain request's start time (retrieved by MustStartTimeFromContext)
|
||||
// and request info (MustRequestInfoFromContext).
|
||||
//
|
||||
// req is a DNS request. Note, that
|
||||
// if the request was discarded (BadFormat or NotImplemented) this method
|
||||
// will still be called so the request message may be incorrect (i.e. no
|
||||
// Question section or whatever). res is a DNS response, will always be
|
||||
// present. rw is the ResponseWriter that was used to write the response.
|
||||
// req is a DNS request. Note, that if the request was discarded (BadFormat
|
||||
// or NotImplemented) this method will still be called so the request
|
||||
// message may be incorrect (i.e. no Question section or whatever). res is
|
||||
// a DNS response, will always be present. rw is the ResponseWriter that was
|
||||
// used to write the response.
|
||||
OnRequest(ctx context.Context, req, resp *dns.Msg, rw ResponseWriter)
|
||||
|
||||
// OnInvalidMsg called when the server encounters an invalid DNS message.
|
||||
@ -45,6 +43,12 @@ type MetricsListener interface {
|
||||
// OnPanic called when a panic happened in a goroutine. ctx is the context
|
||||
// of the DNS request. v is the object returned by the recover() method.
|
||||
OnPanic(ctx context.Context, v any)
|
||||
|
||||
// OnQUICAddressValidation called when a QUIC connection needs to determine
|
||||
// whether it's required or not to send the retry packet. This metric
|
||||
// allows to keep an eye on how the addresses cache performs.
|
||||
// TODO(ameshkov): find a way to attach this info to ctx and remove this.
|
||||
OnQUICAddressValidation(hit bool)
|
||||
}
|
||||
|
||||
// EmptyMetricsListener implements MetricsListener with empty functions.
|
||||
@ -70,3 +74,8 @@ func (e *EmptyMetricsListener) OnError(_ context.Context, _ error) {
|
||||
// OnPanic implements the MetricsListener interface for *EmptyMetricsListener.
|
||||
func (e *EmptyMetricsListener) OnPanic(_ context.Context, _ any) {
|
||||
}
|
||||
|
||||
// OnQUICAddressValidation implements the MetricsListener interface for
|
||||
// *EmptyMetricsListener.
|
||||
func (e *EmptyMetricsListener) OnQUICAddressValidation(_ bool) {
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/cache"
|
||||
@ -33,7 +34,15 @@ func TestCacheMetricsListener_integration_cache(t *testing.T) {
|
||||
req := dnsservertest.CreateMessage("example.org.", dns.TypeA)
|
||||
addr := &net.UDPAddr{IP: net.IP{1, 2, 3, 4}, Port: 53}
|
||||
nrw := dnsserver.NewNonWriterResponseWriter(addr, addr)
|
||||
err := handlerWithMiddleware.ServeDNS(context.Background(), nrw, req)
|
||||
ctx := dnsserver.ContextWithServerInfo(context.Background(), dnsserver.ServerInfo{
|
||||
Name: "test_server",
|
||||
Addr: "127.0.0.1:0",
|
||||
Proto: dnsserver.ProtoDNS,
|
||||
})
|
||||
ctx = dnsserver.ContextWithStartTime(ctx, time.Now())
|
||||
ctx = dnsserver.ContextWithClientInfo(ctx, dnsserver.ClientInfo{})
|
||||
|
||||
err := handlerWithMiddleware.ServeDNS(ctx, nrw, req)
|
||||
require.NoError(t, err)
|
||||
dnsservertest.RequireResponse(t, req, nrw.Msg(), 1, dns.RcodeSuccess, false)
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/forward"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/prometheus"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@ -18,16 +17,12 @@ import (
|
||||
// normal unit test, we create a forward handler, emulate a query and then
|
||||
// check if prom metrics were incremented.
|
||||
func TestForwardMetricsListener_integration_request(t *testing.T) {
|
||||
srv, err := dnsservertest.RunDNSServer(dnsservertest.DefaultHandler())
|
||||
require.NoError(t, err)
|
||||
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return srv.Shutdown(context.Background())
|
||||
})
|
||||
srv, addr := dnsservertest.RunDNSServer(t, dnsservertest.DefaultHandler())
|
||||
|
||||
// Initialize a new forward.Handler and set the metrics listener.
|
||||
handler := forward.NewHandler(&forward.HandlerConfig{
|
||||
Address: netip.MustParseAddrPort(srv.Addr),
|
||||
Address: netip.MustParseAddrPort(addr),
|
||||
Network: forward.NetworkAny,
|
||||
MetricsListener: prometheus.NewForwardMetricsListener(0),
|
||||
}, true)
|
||||
|
||||
@ -35,10 +30,9 @@ func TestForwardMetricsListener_integration_request(t *testing.T) {
|
||||
// It will then call the metrics listener and prom metrics should be
|
||||
// incremented.
|
||||
req := dnsservertest.CreateMessage("example.org.", dns.TypeA)
|
||||
addr := srv.SrvTCP.LocalAddr()
|
||||
rw := dnsserver.NewNonWriterResponseWriter(addr, addr)
|
||||
rw := dnsserver.NewNonWriterResponseWriter(srv.LocalUDPAddr(), srv.LocalUDPAddr())
|
||||
|
||||
err = handler.ServeDNS(context.Background(), rw, req)
|
||||
err := handler.ServeDNS(context.Background(), rw, req)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Now make sure that prometheus metrics were incremented properly.
|
||||
|
@ -15,13 +15,16 @@ func requestLabels(
|
||||
req *dns.Msg,
|
||||
rw dnsserver.ResponseWriter,
|
||||
) prometheus.Labels {
|
||||
// base labels
|
||||
// Base labels with general server information (name, addr, proto).
|
||||
labels := baseLabels(ctx)
|
||||
|
||||
// DNS query type
|
||||
// DNS query type (only use those we're interested in).
|
||||
labels["type"] = typeToString(req)
|
||||
|
||||
// Address family
|
||||
// Network type (tcp or udp).
|
||||
labels["network"] = string(dnsserver.NetworkFromAddr(rw.LocalAddr()))
|
||||
|
||||
// Address family.
|
||||
ip, _ := netutil.IPAndPortFromAddr(rw.RemoteAddr())
|
||||
if ip == nil {
|
||||
labels["family"] = "0"
|
||||
|
@ -15,14 +15,15 @@ like using "github.com/prometheus/client_golang/prometheus/promhttp" package.
|
||||
dnsserver.MetricsListener metrics:
|
||||
|
||||
- "dns_server_request_total" is the number of processed DNS requests. Labels
|
||||
include the common labels: server name, address and protocol, and also
|
||||
include request-specific labels: "type" is a DNS query type (string);
|
||||
"family" is the Addr family. 1 for IPv4, 2 for IPv6, 0 for unknown.
|
||||
include the common labels: server name, address, network and protocol, and
|
||||
also include request-specific labels: "type" is a DNS query type (string);
|
||||
"family" is the Addr family. 1 for IPv4, 2 for IPv6, 0 for unknown,
|
||||
"network" is "tcp" or "udp".
|
||||
- "dns_server_request_duration_seconds" is a histogram with request durations.
|
||||
- "dns_server_request_size_bytes" is a histogram with request sizes.
|
||||
- "dns_server_response_size_bytes" is a histogram with response sizes.
|
||||
- "dns_server_response_rcode_total" is the number of received DNS responses.
|
||||
Besides basic labels it also includes "rcode" label. "rcode" is either a
|
||||
Besides basic labels, it also includes "rcode" label. "rcode" is either a
|
||||
response code string representation or "DROPPED" if there actually was no
|
||||
response at all.
|
||||
- "dns_server_error_total" is the number of errors occurred in the DNS server.
|
||||
@ -30,6 +31,8 @@ dnsserver.MetricsListener metrics:
|
||||
- "dns_server_invalid_msg_total" is the number of invalid messages received by
|
||||
the DNS server. It may be just crap bytes, but it also may be incorrect DNS
|
||||
messages (i.e. no Question records, unsupported Opcode, etc).
|
||||
- "dns_server_quic_addr_validation_lookups" is the number of quic address
|
||||
validation cache lookups. hit=1 means that a cached item was found.
|
||||
|
||||
forward.MetricsListener metrics:
|
||||
|
||||
|
@ -3,14 +3,13 @@ package prometheus_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
dnsservertest.DiscardLogOutput(m)
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
||||
// requireMetrics accepts a list of metrics names and checks that
|
||||
|
@ -46,12 +46,12 @@ var (
|
||||
Namespace: namespace,
|
||||
Subsystem: subsystemRateLimit,
|
||||
Help: "The total number of rate-limited DNS queries.",
|
||||
}, []string{"name", "proto", "addr", "type", "family"})
|
||||
}, []string{"name", "proto", "network", "addr", "type", "family"})
|
||||
|
||||
allowlistedTotal = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "allowlisted_total",
|
||||
Namespace: namespace,
|
||||
Subsystem: subsystemRateLimit,
|
||||
Help: "The total number of allowlisted DNS queries.",
|
||||
}, []string{"name", "proto", "addr", "type", "family"})
|
||||
}, []string{"name", "proto", "network", "addr", "type", "family"})
|
||||
)
|
||||
|
@ -47,8 +47,10 @@ func TestRateLimiterMetricsListener_integration_cache(t *testing.T) {
|
||||
ctx := dnsserver.ContextWithServerInfo(context.Background(), dnsserver.ServerInfo{
|
||||
Name: "test",
|
||||
Addr: "127.0.0.1",
|
||||
Proto: dnsserver.ProtoDNSUDP,
|
||||
Proto: dnsserver.ProtoDNS,
|
||||
})
|
||||
ctx = dnsserver.ContextWithStartTime(ctx, time.Now())
|
||||
ctx = dnsserver.ContextWithClientInfo(ctx, dnsserver.ClientInfo{})
|
||||
|
||||
err = handlerWithMiddleware.ServeDNS(ctx, nrw, req)
|
||||
require.NoError(t, err)
|
||||
|
@ -5,21 +5,20 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
// ServerMetricsListener implements the dnsserver.MetricsListener interface
|
||||
// ServerMetricsListener implements the [dnsserver.MetricsListener] interface
|
||||
// and increments prom counters.
|
||||
type ServerMetricsListener struct{}
|
||||
|
||||
// type check
|
||||
var _ dnsserver.MetricsListener = (*ServerMetricsListener)(nil)
|
||||
|
||||
// OnRequest implements the dnsserver.MetricsListener interface for
|
||||
// *ServerMetricsListener.
|
||||
// OnRequest implements the [dnsserver.MetricsListener] interface for
|
||||
// [*ServerMetricsListener].
|
||||
func (l *ServerMetricsListener) OnRequest(
|
||||
ctx context.Context,
|
||||
req, resp *dns.Msg,
|
||||
@ -37,19 +36,12 @@ func (l *ServerMetricsListener) OnRequest(
|
||||
requestDuration.With(srvLabels).Observe(elapsed)
|
||||
|
||||
// Increment request size.
|
||||
size, found := dnsserver.RequestSizeFromContext(ctx)
|
||||
if !found {
|
||||
log.Error("request size is not attached to the context")
|
||||
}
|
||||
requestSize.With(srvLabels).Observe(float64(size))
|
||||
ri := dnsserver.MustRequestInfoFromContext(ctx)
|
||||
requestSize.With(srvLabels).Observe(float64(ri.RequestSize))
|
||||
|
||||
// If resp is not nil, increment response-related metrics
|
||||
if resp != nil {
|
||||
size, found = dnsserver.ResponseSizeFromContext(ctx)
|
||||
if !found {
|
||||
log.Error("response size is not attached to the context")
|
||||
}
|
||||
responseSize.With(srvLabels).Observe(float64(size))
|
||||
responseSize.With(srvLabels).Observe(float64(ri.ResponseSize))
|
||||
|
||||
resLabels := baseLabels(ctx)
|
||||
resLabels["rcode"] = rCodeToString(resp.Rcode)
|
||||
@ -63,35 +55,45 @@ func (l *ServerMetricsListener) OnRequest(
|
||||
}
|
||||
}
|
||||
|
||||
// OnInvalidMsg implements the dnsserver.MetricsListener interface for
|
||||
// *ServerMetricsListener.
|
||||
// OnInvalidMsg implements the [dnsserver.MetricsListener] interface for
|
||||
// [*ServerMetricsListener].
|
||||
func (l *ServerMetricsListener) OnInvalidMsg(ctx context.Context) {
|
||||
labels := baseLabels(ctx)
|
||||
invalidMsgTotal.With(labels).Inc()
|
||||
}
|
||||
|
||||
// OnError implements the dnsserver.MetricsListener interface for
|
||||
// *ServerMetricsListener.
|
||||
// OnError implements the [dnsserver.MetricsListener] interface for
|
||||
// [*ServerMetricsListener].
|
||||
func (l *ServerMetricsListener) OnError(ctx context.Context, _ error) {
|
||||
labels := baseLabels(ctx)
|
||||
errorTotal.With(labels).Inc()
|
||||
}
|
||||
|
||||
// OnPanic implements the dnsserver.MetricsListener interface for
|
||||
// *ServerMetricsListener.
|
||||
// OnPanic implements the [dnsserver.MetricsListener] interface for
|
||||
// [*ServerMetricsListener].
|
||||
func (l *ServerMetricsListener) OnPanic(ctx context.Context, _ any) {
|
||||
labels := baseLabels(ctx)
|
||||
panicTotal.With(labels).Inc()
|
||||
}
|
||||
|
||||
// This block contains prometheus metrics declarations for dnsserver.Server
|
||||
// OnQUICAddressValidation implements the [dnsserver.MetricsListener] interface
|
||||
// for [*ServerMetricsListener].
|
||||
func (l *ServerMetricsListener) OnQUICAddressValidation(hit bool) {
|
||||
if hit {
|
||||
quicAddrValidationCacheLookupsHits.Inc()
|
||||
} else {
|
||||
quicAddrValidationCacheLookupsMisses.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
// This block contains prometheus metrics declarations for [dnsserver.Server]
|
||||
var (
|
||||
requestTotal = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "request_total",
|
||||
Namespace: namespace,
|
||||
Subsystem: subsystemServer,
|
||||
Help: "The number of processed DNS requests.",
|
||||
}, []string{"name", "proto", "addr", "type", "family"})
|
||||
}, []string{"name", "proto", "network", "addr", "type", "family"})
|
||||
|
||||
requestDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "request_duration_seconds",
|
||||
@ -148,3 +150,16 @@ var (
|
||||
Help: "The number of invalid DNS messages processed by the DNS server.",
|
||||
}, []string{"name", "proto", "addr"})
|
||||
)
|
||||
|
||||
var (
|
||||
quicAddrValidationCacheLookups = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "quic_addr_validation_lookups",
|
||||
Namespace: namespace,
|
||||
Subsystem: subsystemServer,
|
||||
Help: "The number of QUIC address validation lookups." +
|
||||
"hit=1 means that a cached item was found.",
|
||||
}, []string{"hit"})
|
||||
|
||||
quicAddrValidationCacheLookupsHits = quicAddrValidationCacheLookups.WithLabelValues("1")
|
||||
quicAddrValidationCacheLookupsMisses = quicAddrValidationCacheLookups.WithLabelValues("0")
|
||||
)
|
||||
|
@ -23,29 +23,28 @@ func TestServerMetricsListener_integration_requestLifetime(t *testing.T) {
|
||||
ConfigBase: dnsserver.ConfigBase{
|
||||
Name: "test",
|
||||
Addr: "127.0.0.1:0",
|
||||
Proto: dnsserver.ProtoDNSTCP,
|
||||
Handler: dnsservertest.DefaultHandler(),
|
||||
Metrics: &prom.ServerMetricsListener{},
|
||||
},
|
||||
}
|
||||
srv := dnsserver.NewServerDNS(conf)
|
||||
|
||||
// Start the server
|
||||
// Start the server.
|
||||
err := srv.Start(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Make sure the server shuts down in the end
|
||||
// Make sure the server shuts down in the end.
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return srv.Shutdown(context.Background())
|
||||
})
|
||||
|
||||
// Create a test message
|
||||
// Create a test message.
|
||||
req := dnsservertest.CreateMessage("example.org", dns.TypeA)
|
||||
|
||||
c := &dns.Client{Net: "tcp"}
|
||||
|
||||
// Send a test DNS query
|
||||
addr := srv.LocalAddr().String()
|
||||
// Send a test DNS query.
|
||||
addr := srv.LocalUDPAddr().String()
|
||||
|
||||
// Pass 10 requests to make the test less flaky.
|
||||
for i := 0; i < 10; i++ {
|
||||
@ -55,7 +54,7 @@ func TestServerMetricsListener_integration_requestLifetime(t *testing.T) {
|
||||
require.Equal(t, dns.RcodeSuccess, res.Rcode)
|
||||
}
|
||||
|
||||
// Now make sure that prometheus metrics were incremented properly
|
||||
// Now make sure that prometheus metrics were incremented properly.
|
||||
requireMetrics(
|
||||
t,
|
||||
"dns_server_request_total",
|
||||
|
@ -2,6 +2,7 @@ package dnsserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
@ -18,11 +19,8 @@ const (
|
||||
// ProtoInvalid is the invalid default value.
|
||||
ProtoInvalid Protocol = 0
|
||||
|
||||
// ProtoDNSTCP is plain DNS over TCP.
|
||||
ProtoDNSTCP Protocol = 1
|
||||
|
||||
// ProtoDNSUDP is plain DNS over UDP.
|
||||
ProtoDNSUDP Protocol = 2
|
||||
// ProtoDNS is plain DNS.
|
||||
ProtoDNS Protocol = 8
|
||||
|
||||
// ProtoDoH is DNS-over-HTTPS.
|
||||
ProtoDoH Protocol = 3
|
||||
@ -33,30 +31,23 @@ const (
|
||||
// ProtoDoT is DNS-over-TLS.
|
||||
ProtoDoT Protocol = 5
|
||||
|
||||
// ProtoDNSCryptTCP is DNSCrypt over TCP.
|
||||
ProtoDNSCryptTCP Protocol = 6
|
||||
|
||||
// ProtoDNSCryptUDP is DNSCrypt over UDP.
|
||||
ProtoDNSCryptUDP Protocol = 7
|
||||
// ProtoDNSCrypt is DNSCrypt.
|
||||
ProtoDNSCrypt Protocol = 9
|
||||
)
|
||||
|
||||
// String implements the fmt.Stringer interface for Protocol.
|
||||
func (p Protocol) String() (s string) {
|
||||
switch p {
|
||||
case ProtoDNSTCP:
|
||||
return "dns-tcp"
|
||||
case ProtoDNSUDP:
|
||||
return "dns-udp"
|
||||
case ProtoDNS:
|
||||
return "dns"
|
||||
case ProtoDoH:
|
||||
return "doh"
|
||||
case ProtoDoQ:
|
||||
return "doq"
|
||||
case ProtoDoT:
|
||||
return "dot"
|
||||
case ProtoDNSCryptTCP:
|
||||
return "dnscrypt-tcp"
|
||||
case ProtoDNSCryptUDP:
|
||||
return "dnscrypt-udp"
|
||||
case ProtoDNSCrypt:
|
||||
return "dnscrypt"
|
||||
default:
|
||||
return fmt.Sprintf("!bad_protocol_%d", p)
|
||||
}
|
||||
@ -71,17 +62,12 @@ func (p Protocol) ALPN() (alpn []string) {
|
||||
case ProtoDoQ:
|
||||
return []string{nextProtoDoQ}
|
||||
case ProtoDoH:
|
||||
return slices.Clone(nextProtoDoH)
|
||||
return slices.Clone(nextProtoDoH3)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// IsPlain returns true if the protocol is a plain DNS over TCP or UDP.
|
||||
func (p Protocol) IsPlain() (ok bool) {
|
||||
return p == ProtoDNSTCP || p == ProtoDNSUDP
|
||||
}
|
||||
|
||||
// IsStdEncrypted returns true if the protocol is one of the standard encrypted
|
||||
// DNS protocol as defined by an RFC.
|
||||
func (p Protocol) IsStdEncrypted() (ok bool) {
|
||||
@ -97,8 +83,31 @@ type Network string
|
||||
const (
|
||||
NetworkTCP Network = "tcp"
|
||||
NetworkUDP Network = "udp"
|
||||
NetworkAny Network = ""
|
||||
)
|
||||
|
||||
// CanTCP returns true if this Network supports TCP.
|
||||
func (n Network) CanTCP() (ok bool) {
|
||||
return n == NetworkAny || n == NetworkTCP
|
||||
}
|
||||
|
||||
// CanUDP returns true if this Network supports UDP.
|
||||
func (n Network) CanUDP() (ok bool) {
|
||||
return n == NetworkAny || n == NetworkUDP
|
||||
}
|
||||
|
||||
// NetworkFromAddr returns NetworkTCP or NetworkUDP depending on the address.
|
||||
func NetworkFromAddr(addr net.Addr) (network Network) {
|
||||
switch addr.Network() {
|
||||
case "udp":
|
||||
return NetworkUDP
|
||||
case "tcp":
|
||||
return NetworkTCP
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected network type %s", addr.Network()))
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// DNSHeaderSize is the DNS query header size.
|
||||
DNSHeaderSize = 12
|
||||
|
@ -41,8 +41,10 @@ func (l *LogMiddleware) Wrap(h dnsserver.Handler) (wrapped dnsserver.Handler) {
|
||||
// [{name} {proto}://{addr}] {id} {type} {name} {size} {rcode} {rsize} {duration}
|
||||
sb := strings.Builder{}
|
||||
|
||||
// Server info: [{name} {proto}://{addr}]
|
||||
serverInfo := dnsserver.MustServerInfoFromContext(ctx)
|
||||
requestInfo := dnsserver.MustRequestInfoFromContext(ctx)
|
||||
|
||||
// [{name} {proto}://{addr}]
|
||||
sb.WriteString(
|
||||
fmt.Sprintf("[%s %s://%s] ",
|
||||
serverInfo.Name,
|
||||
@ -56,7 +58,6 @@ func (l *LogMiddleware) Wrap(h dnsserver.Handler) (wrapped dnsserver.Handler) {
|
||||
if len(req.Question) > 0 {
|
||||
hostname = req.Question[0].Name
|
||||
}
|
||||
reqSize, _ := dnsserver.RequestSizeFromContext(ctx)
|
||||
var qType uint16
|
||||
if len(req.Question) == 1 {
|
||||
qType = req.Question[0].Qtype
|
||||
@ -70,7 +71,7 @@ func (l *LogMiddleware) Wrap(h dnsserver.Handler) (wrapped dnsserver.Handler) {
|
||||
req.Id,
|
||||
qTypeStr,
|
||||
hostname,
|
||||
reqSize,
|
||||
requestInfo.RequestSize,
|
||||
),
|
||||
)
|
||||
|
||||
@ -79,7 +80,7 @@ func (l *LogMiddleware) Wrap(h dnsserver.Handler) (wrapped dnsserver.Handler) {
|
||||
rsize := 0
|
||||
if recW.Resp != nil {
|
||||
rcode = recW.Resp.Rcode
|
||||
rsize, _ = dnsserver.ResponseSizeFromContext(ctx)
|
||||
rsize = requestInfo.ResponseSize
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf("%d %d ", rcode, rsize))
|
||||
|
||||
|
@ -49,11 +49,13 @@ func TestLogMiddleware_Wrap(t *testing.T) {
|
||||
dnsserver.ServerInfo{
|
||||
Name: "test",
|
||||
Addr: "0.0.0.0:53",
|
||||
Proto: dnsserver.ProtoDNSUDP,
|
||||
Proto: dnsserver.ProtoDNS,
|
||||
})
|
||||
ctx = dnsserver.ContextWithStartTime(ctx, time.Now().Add(-time.Second))
|
||||
ctx = dnsserver.ContextWithRequestSize(ctx, req.Len())
|
||||
ctx = dnsserver.ContextWithResponseSize(ctx, req.Len())
|
||||
ctx = dnsserver.ContextWithRequestInfo(ctx, dnsserver.RequestInfo{
|
||||
RequestSize: req.Len(),
|
||||
ResponseSize: req.Len(),
|
||||
})
|
||||
|
||||
// Init response writer with test data
|
||||
localAddr := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 53}
|
||||
@ -68,7 +70,7 @@ func TestLogMiddleware_Wrap(t *testing.T) {
|
||||
require.True(t,
|
||||
strings.HasPrefix(
|
||||
w.String(),
|
||||
"[test dns-udp://0.0.0.0:53] 1 A example.org. 29 0 29",
|
||||
"[test dns://0.0.0.0:53] 1 A example.org. 29 0 29",
|
||||
),
|
||||
"invalid message: %s",
|
||||
w.String(),
|
||||
|
@ -29,7 +29,7 @@ type Middleware struct {
|
||||
Metrics MetricsListener
|
||||
|
||||
// rateLimit is defines whether the query should be dropped or not. The
|
||||
// default implementation of it is *BackOff.
|
||||
// default implementation of it is [*BackOff].
|
||||
rateLimit Interface
|
||||
|
||||
// protos is a list of protocols this middleware applies rate-limiting logic
|
||||
@ -40,7 +40,8 @@ type Middleware struct {
|
||||
// type check
|
||||
var _ dnsserver.Middleware = (*Middleware)(nil)
|
||||
|
||||
// NewMiddleware returns a properly initialized *Middleware.
|
||||
// NewMiddleware returns a properly initialized [*Middleware]. protos is a list
|
||||
// of [dnsserver.Protocol] the rate limit will be used for.
|
||||
func NewMiddleware(rl Interface, protos []dnsserver.Protocol) (m *Middleware, err error) {
|
||||
return &Middleware{
|
||||
Metrics: &EmptyMetricsListener{},
|
||||
@ -49,7 +50,7 @@ func NewMiddleware(rl Interface, protos []dnsserver.Protocol) (m *Middleware, er
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Wrap implements the dnsserver.Middleware interface for *Middleware.
|
||||
// Wrap implements the [dnsserver.Middleware] interface for [*Middleware].
|
||||
func (m *Middleware) Wrap(handler dnsserver.Handler) (wrapped dnsserver.Handler) {
|
||||
f := func(ctx context.Context, rw dnsserver.ResponseWriter, req *dns.Msg) (err error) {
|
||||
if !m.isEnabledForProto(ctx) {
|
||||
@ -101,9 +102,9 @@ func (m *Middleware) Wrap(handler dnsserver.Handler) (wrapped dnsserver.Handler)
|
||||
}
|
||||
|
||||
// addrPortFromNetAddr returns the IP address and port from addr. If one cannot
|
||||
// be obtained from addr, it returns netip.AddrPort{}.
|
||||
// be obtained from addr, it returns a zero value of [netip.AddrPort].
|
||||
//
|
||||
// NOTE: Keep in sync with dnssvc.ipFromNetAddr.
|
||||
// NOTE: Keep in sync with [dnssvc.ipFromNetAddr].
|
||||
//
|
||||
// TODO(a.garipov): Perhaps this normalization should be done in package
|
||||
// dnsserver.
|
||||
@ -133,6 +134,7 @@ func (m *Middleware) isEnabledForProto(ctx context.Context) (enabled bool) {
|
||||
}
|
||||
|
||||
si := dnsserver.MustServerInfoFromContext(ctx)
|
||||
|
||||
for _, proto := range m.protos {
|
||||
if proto == si.Proto {
|
||||
return true
|
||||
|
@ -10,13 +10,14 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
dnsservertest.DiscardLogOutput(m)
|
||||
testutil.DiscardLogOutput(m)
|
||||
}
|
||||
|
||||
func TestRatelimitMiddleware(t *testing.T) {
|
||||
@ -103,7 +104,7 @@ func TestRatelimitMiddleware(t *testing.T) {
|
||||
RefuseANY: true,
|
||||
})
|
||||
rlMw, err := ratelimit.NewMiddleware(rl, []dnsserver.Protocol{
|
||||
dnsserver.ProtoDNSUDP,
|
||||
dnsserver.ProtoDNS,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -115,12 +116,17 @@ func TestRatelimitMiddleware(t *testing.T) {
|
||||
ctx := dnsserver.ContextWithServerInfo(context.Background(), dnsserver.ServerInfo{
|
||||
Name: "test",
|
||||
Addr: "127.0.0.1",
|
||||
Proto: dnsserver.ProtoDNSUDP,
|
||||
Proto: dnsserver.ProtoDNS,
|
||||
})
|
||||
ctx = dnsserver.ContextWithStartTime(ctx, time.Now())
|
||||
ctx = dnsserver.ContextWithClientInfo(ctx, dnsserver.ClientInfo{})
|
||||
|
||||
n := 0
|
||||
for i := 0; i < tc.reqsNum; i++ {
|
||||
nrw := dnsserver.NewNonWriterResponseWriter(nil, tc.remoteAddr)
|
||||
nrw := dnsserver.NewNonWriterResponseWriter(
|
||||
&net.UDPAddr{IP: []byte{1, 2, 3, 4}},
|
||||
tc.remoteAddr,
|
||||
)
|
||||
err = withMw.ServeDNS(ctx, nrw, tc.req)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -17,18 +17,27 @@ import (
|
||||
type ConfigBase struct {
|
||||
// Name is used for logging, and it may be used for perf counters reporting.
|
||||
Name string
|
||||
|
||||
// Addr is the address the server listens to. See go doc net.Dial for
|
||||
// the documentation on the address format.
|
||||
Addr string
|
||||
// Proto is the server protocol.
|
||||
Proto Protocol
|
||||
|
||||
// Network is the network this server listens to. If empty, the server will
|
||||
// listen to all networks that are supposed to be used by the server's
|
||||
// protocol. Note, that it only makes sense for [ServerDNS],
|
||||
// [ServerDNSCrypt], and [ServerHTTPS].
|
||||
Network Network
|
||||
|
||||
// Handler is a handler that processes incoming DNS messages.
|
||||
// If not set, we'll use the default handler that returns error response
|
||||
// to any query.
|
||||
|
||||
Handler Handler
|
||||
// Metrics is the object we use for collecting performance metrics.
|
||||
// This field is optional.
|
||||
|
||||
Metrics MetricsListener
|
||||
|
||||
// BaseContext is a function that should return the base context. If not
|
||||
// set, we'll be using context.Background().
|
||||
BaseContext func() (ctx context.Context)
|
||||
@ -42,6 +51,9 @@ type ServerBase struct {
|
||||
addr string
|
||||
// proto is the server protocol.
|
||||
proto Protocol
|
||||
// network is the network to listen to. It only makes sense for the
|
||||
// following protocols: ProtoDNS, ProtoDNSCrypt, ProtoDoH.
|
||||
network Network
|
||||
// handler is a handler that processes incoming DNS messages.
|
||||
handler Handler
|
||||
// baseContext is a function that should return the base context.
|
||||
@ -68,13 +80,17 @@ type ServerBase struct {
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ Server = (*ServerBase)(nil)
|
||||
|
||||
// newServerBase creates a new instance of ServerBase and initializes
|
||||
// some of its internal properties.
|
||||
func newServerBase(conf ConfigBase) (s *ServerBase) {
|
||||
func newServerBase(proto Protocol, conf ConfigBase) (s *ServerBase) {
|
||||
s = &ServerBase{
|
||||
name: conf.Name,
|
||||
addr: conf.Addr,
|
||||
proto: conf.Proto,
|
||||
proto: proto,
|
||||
network: conf.Network,
|
||||
handler: conf.Handler,
|
||||
metrics: conf.Metrics,
|
||||
baseContext: conf.BaseContext,
|
||||
@ -95,22 +111,38 @@ func newServerBase(conf ConfigBase) (s *ServerBase) {
|
||||
return s
|
||||
}
|
||||
|
||||
// Name returns the server name. It is safe for concurrent use.
|
||||
// Name implements the [dnsserver.Server] interface for *ServerBase.
|
||||
func (s *ServerBase) Name() (name string) {
|
||||
return s.name
|
||||
}
|
||||
|
||||
// Addr returns the address the server was configured to listen to. It is safe
|
||||
// for concurrent use.
|
||||
// Proto implements the [dnsserver.Server] interface for *ServerBase.
|
||||
func (s *ServerBase) Proto() (proto Protocol) {
|
||||
return s.proto
|
||||
}
|
||||
|
||||
// Network implements the [dnsserver.Server] interface for *ServerBase.
|
||||
func (s *ServerBase) Network() (network Network) {
|
||||
return s.network
|
||||
}
|
||||
|
||||
// Addr implements the [dnsserver.Server] interface for *ServerBase.
|
||||
func (s *ServerBase) Addr() (addr string) {
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// LocalAddr returns the address the server listens to at the moment.
|
||||
func (s *ServerBase) LocalAddr() (addr net.Addr) {
|
||||
if s.udpListener != nil {
|
||||
return s.udpListener.LocalAddr()
|
||||
}
|
||||
// Start implements the [dnsserver.Server] interface for *ServerBase.
|
||||
func (s *ServerBase) Start(_ context.Context) (err error) {
|
||||
panic("*ServerBase must not be used directly")
|
||||
}
|
||||
|
||||
// Shutdown implements the [dnsserver.Server] interface for *ServerBase.
|
||||
func (s *ServerBase) Shutdown(_ context.Context) (err error) {
|
||||
panic("*ServerBase must not be used directly")
|
||||
}
|
||||
|
||||
// LocalTCPAddr implements the [dnsserver.Server] interface for *ServerBase.
|
||||
func (s *ServerBase) LocalTCPAddr() (addr net.Addr) {
|
||||
if s.tcpListener != nil {
|
||||
return s.tcpListener.Addr()
|
||||
}
|
||||
@ -118,9 +150,13 @@ func (s *ServerBase) LocalAddr() (addr net.Addr) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Proto returns the protocol of the server. It is safe for concurrent use.
|
||||
func (s *ServerBase) Proto() (proto Protocol) {
|
||||
return s.proto
|
||||
// LocalUDPAddr implements the [dnsserver.Server] interface for *ServerBase.
|
||||
func (s *ServerBase) LocalUDPAddr() (addr net.Addr) {
|
||||
if s.udpListener != nil {
|
||||
return s.udpListener.LocalAddr()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// requestContext returns a context for one request. It adds the start time and
|
||||
@ -162,16 +198,17 @@ func (s *ServerBase) serveDNSMsg(
|
||||
hostname, qType := questionData(req)
|
||||
log.Debug("[%d] processing \"%s %s\"", req.Id, qType, hostname)
|
||||
|
||||
ctx = ContextWithRequestSize(ctx, req.Len())
|
||||
recW := NewRecorderResponseWriter(rw)
|
||||
s.serveDNSMsgInternal(ctx, req, recW)
|
||||
|
||||
ri := RequestInfo{RequestSize: req.Len()}
|
||||
resp := recW.Resp
|
||||
written = resp != nil
|
||||
if written {
|
||||
ctx = ContextWithResponseSize(ctx, resp.Len())
|
||||
ri.ResponseSize = resp.Len()
|
||||
}
|
||||
|
||||
ctx = ContextWithRequestInfo(ctx, ri)
|
||||
s.metrics.OnRequest(ctx, req, resp, rw)
|
||||
|
||||
log.Debug("[%d]: finished processing \"%s %s\"", req.Id, qType, hostname)
|
||||
@ -303,6 +340,49 @@ func (s *ServerBase) handlePanicAndRecover(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// listenUDP creates a UDP listener for the ServerBase.addr. This function will
|
||||
// initialize and start ServerBase.udpListener or return an error. If the TCP
|
||||
// listener is already running, its address is used instead. The point of this
|
||||
// is to properly handle the case when port 0 is used as both listeners should
|
||||
// use the same port, and we only learn it after the first one was started.
|
||||
func (s *ServerBase) listenUDP(ctx context.Context) (err error) {
|
||||
addr := s.addr
|
||||
if s.tcpListener != nil {
|
||||
addr = s.tcpListener.Addr().String()
|
||||
}
|
||||
|
||||
conn, err := listenUDP(ctx, addr, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.udpListener = conn
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// listenTCP creates a TCP listener for the ServerBase.addr. This function will
|
||||
// initialize and start ServerBase.tcpListener or return an error. If the UDP
|
||||
// listener is already running, its address is used instead. The point of this
|
||||
// is to properly handle the case when port 0 is used as both listeners should
|
||||
// use the same port, and we only learn it after the first one was started.
|
||||
func (s *ServerBase) listenTCP(ctx context.Context) (err error) {
|
||||
addr := s.addr
|
||||
if s.udpListener != nil {
|
||||
addr = s.udpListener.LocalAddr().String()
|
||||
}
|
||||
|
||||
var l net.Listener
|
||||
l, err = listenTCP(ctx, addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.tcpListener = l
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// closeListeners stops UDP and TCP listeners.
|
||||
func (s *ServerBase) closeListeners() {
|
||||
if s.udpListener != nil {
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/ameshkov/dnscrypt/v2"
|
||||
"github.com/ameshkov/dnsstamps"
|
||||
@ -20,27 +21,40 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func BenchmarkServeUDP(b *testing.B) {
|
||||
srv, _, err := dnsservertest.RunLocalDNSServer(
|
||||
dnsservertest.DefaultHandler(),
|
||||
dnsserver.ProtoDNSUDP,
|
||||
)
|
||||
require.NoError(b, err)
|
||||
func BenchmarkServeDNS(b *testing.B) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
network dnsserver.Network
|
||||
}{{
|
||||
name: "udp",
|
||||
network: dnsserver.NetworkUDP,
|
||||
}, {
|
||||
name: "tcp",
|
||||
network: dnsserver.NetworkTCP,
|
||||
}}
|
||||
|
||||
testutil.CleanupAndRequireSuccess(b, func() (err error) {
|
||||
return srv.Shutdown(context.Background())
|
||||
})
|
||||
for _, tc := range testCases {
|
||||
b.Run(tc.name, func(b *testing.B) {
|
||||
_, addr := dnsservertest.RunDNSServer(b, dnsservertest.DefaultHandler())
|
||||
|
||||
// Prepare a test message
|
||||
// Prepare a test message.
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("example.org.", dns.TypeA)
|
||||
msg, _ := m.Pack()
|
||||
var msg []byte
|
||||
msgPacket, _ := m.Pack()
|
||||
if tc.network == dnsserver.NetworkTCP {
|
||||
msg = make([]byte, 2+len(msgPacket))
|
||||
binary.BigEndian.PutUint16(msg, uint16(len(msgPacket)))
|
||||
copy(msg[2:], msgPacket)
|
||||
} else {
|
||||
msg, _ = m.Pack()
|
||||
}
|
||||
|
||||
// Open a UDP connection (using one to avoid client-side allocations)
|
||||
conn, err := net.DialUDP("udp", nil, srv.LocalAddr().(*net.UDPAddr))
|
||||
// Open connection (using one to avoid client-side allocations).
|
||||
conn, err := net.Dial(string(tc.network), addr)
|
||||
require.NoError(b, err)
|
||||
|
||||
// Prepare a buffer to read responses
|
||||
// Prepare a buffer to read responses.
|
||||
resBuf := make([]byte, 512)
|
||||
|
||||
b.ReportAllocs()
|
||||
@ -49,72 +63,48 @@ func BenchmarkServeUDP(b *testing.B) {
|
||||
_, err = conn.Write(msg)
|
||||
require.NoError(b, err)
|
||||
|
||||
var n int
|
||||
n, err = conn.Read(resBuf)
|
||||
err = readMsg(resBuf, tc.network, conn)
|
||||
require.NoError(b, err)
|
||||
require.GreaterOrEqual(b, n, dnsserver.DNSHeaderSize)
|
||||
}
|
||||
b.StopTimer()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkServeTCP(b *testing.B) {
|
||||
srv, _, err := dnsservertest.RunLocalDNSServer(
|
||||
dnsservertest.DefaultHandler(),
|
||||
dnsserver.ProtoDNSTCP)
|
||||
require.NoError(b, err)
|
||||
|
||||
testutil.CleanupAndRequireSuccess(b, func() (err error) {
|
||||
return srv.Shutdown(context.Background())
|
||||
})
|
||||
|
||||
// Prepare a test message
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("example.org.", dns.TypeA)
|
||||
data, _ := m.Pack()
|
||||
msg := make([]byte, 2+len(data))
|
||||
binary.BigEndian.PutUint16(msg, uint16(len(data)))
|
||||
copy(msg[2:], data)
|
||||
|
||||
// Open a TCP connection (using one to avoid client-side allocations)
|
||||
conn, err := net.DialTCP("tcp", nil, srv.LocalAddr().(*net.TCPAddr))
|
||||
require.NoError(b, err)
|
||||
|
||||
// Prepare a buffer to read responses
|
||||
resBuf := make([]byte, 512)
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err = conn.Write(msg)
|
||||
require.NoError(b, err)
|
||||
|
||||
var length uint16
|
||||
if err = binary.Read(conn, binary.BigEndian, &length); err != nil {
|
||||
b.Fatalf("failed to read the DNS query response: %v", err)
|
||||
}
|
||||
// readMsg is a helper function for reading DNS responses from a plain DNS
|
||||
// connection.
|
||||
func readMsg(resBuf []byte, network dnsserver.Network, conn net.Conn) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "failed to read DNS msg: %w", err) }()
|
||||
|
||||
var n int
|
||||
n, err = io.ReadFull(conn, resBuf[:length])
|
||||
if err != nil {
|
||||
b.Fatalf("failed to read the DNS query response: %v", err)
|
||||
|
||||
if network == dnsserver.NetworkTCP {
|
||||
var length uint16
|
||||
if err = binary.Read(conn, binary.BigEndian, &length); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
require.GreaterOrEqual(b, n, dnsserver.DNSHeaderSize)
|
||||
n, err = io.ReadFull(conn, resBuf[:length])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.StopTimer()
|
||||
} else {
|
||||
n, err = conn.Read(resBuf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if n < dnsserver.DNSHeaderSize {
|
||||
return dns.ErrShortRead
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func BenchmarkServeTLS(b *testing.B) {
|
||||
tlsConfig := dnsservertest.CreateServerTLSConfig("example.org")
|
||||
srv, addr, err := dnsservertest.RunLocalTLSServer(
|
||||
dnsservertest.DefaultHandler(),
|
||||
tlsConfig,
|
||||
)
|
||||
require.NoError(b, err)
|
||||
|
||||
testutil.CleanupAndRequireSuccess(b, func() (err error) {
|
||||
return srv.Shutdown(context.Background())
|
||||
})
|
||||
addr := dnsservertest.RunTLSServer(b, dnsservertest.DefaultHandler(), tlsConfig)
|
||||
|
||||
// Prepare a test message
|
||||
m := new(dns.Msg)
|
||||
@ -158,10 +148,37 @@ func BenchmarkServeTLS(b *testing.B) {
|
||||
b.StopTimer()
|
||||
}
|
||||
|
||||
func BenchmarkServeHTTPS(b *testing.B) {
|
||||
func BenchmarkServeDoH(b *testing.B) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
https bool
|
||||
http3Enabled bool
|
||||
}{{
|
||||
name: "doh2",
|
||||
https: true,
|
||||
http3Enabled: false,
|
||||
}, {
|
||||
name: "doh3",
|
||||
https: true,
|
||||
http3Enabled: true,
|
||||
}, {
|
||||
name: "plain_http",
|
||||
https: true,
|
||||
http3Enabled: true,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
b.Run(tc.name, func(b *testing.B) {
|
||||
proto := "https"
|
||||
tlsConfig := dnsservertest.CreateServerTLSConfig("example.org")
|
||||
srv, addr, err := dnsservertest.RunLocalHTTPSServer(
|
||||
if !tc.https {
|
||||
proto = "http"
|
||||
}
|
||||
|
||||
var tlsConfig *tls.Config
|
||||
if tc.https {
|
||||
tlsConfig = dnsservertest.CreateServerTLSConfig("example.org")
|
||||
}
|
||||
srv, err := dnsservertest.RunLocalHTTPSServer(
|
||||
dnsservertest.DefaultHandler(),
|
||||
tlsConfig,
|
||||
nil,
|
||||
@ -172,7 +189,7 @@ func BenchmarkServeHTTPS(b *testing.B) {
|
||||
return srv.Shutdown(context.Background())
|
||||
})
|
||||
|
||||
// Prepare a test message
|
||||
// Prepare a test message.
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("example.org.", dns.TypeA)
|
||||
data, _ := m.Pack()
|
||||
@ -180,11 +197,16 @@ func BenchmarkServeHTTPS(b *testing.B) {
|
||||
binary.BigEndian.PutUint16(msg, uint16(len(data)))
|
||||
copy(msg[2:], data)
|
||||
|
||||
// Prepare client
|
||||
// Prepare client.
|
||||
addr := srv.LocalTCPAddr()
|
||||
if tc.http3Enabled {
|
||||
addr = srv.LocalUDPAddr()
|
||||
}
|
||||
|
||||
client, err := createDoHClient(addr, tlsConfig)
|
||||
require.NoError(b, err)
|
||||
|
||||
// Prepare http.Request
|
||||
// Prepare http.Request.
|
||||
req, err := createDoHRequest(proto, http.MethodPost, m)
|
||||
require.NoError(b, err)
|
||||
|
||||
@ -202,59 +224,66 @@ func BenchmarkServeHTTPS(b *testing.B) {
|
||||
require.GreaterOrEqual(b, len(buf), dnsserver.DNSHeaderSize)
|
||||
}
|
||||
b.StopTimer()
|
||||
}
|
||||
|
||||
func BenchmarkServePlainHTTP(b *testing.B) {
|
||||
proto := "http"
|
||||
srv, addr, err := dnsservertest.RunLocalHTTPSServer(
|
||||
dnsservertest.DefaultHandler(),
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
require.NoError(b, err)
|
||||
|
||||
testutil.CleanupAndRequireSuccess(b, func() (err error) {
|
||||
return srv.Shutdown(context.Background())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare a test message
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("example.org.", dns.TypeA)
|
||||
data, _ := m.Pack()
|
||||
msg := make([]byte, 2+len(data))
|
||||
binary.BigEndian.PutUint16(msg, uint16(len(data)))
|
||||
copy(msg[2:], data)
|
||||
func BenchmarkServeDNSCrypt(b *testing.B) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
network dnsserver.Network
|
||||
}{{
|
||||
name: "udp",
|
||||
network: dnsserver.NetworkUDP,
|
||||
}, {
|
||||
name: "tcp",
|
||||
network: dnsserver.NetworkTCP,
|
||||
}}
|
||||
|
||||
// Prepare client
|
||||
client, err := createDoHClient(addr, nil)
|
||||
for _, tc := range testCases {
|
||||
b.Run(tc.name, func(b *testing.B) {
|
||||
// Create a test message
|
||||
req := new(dns.Msg)
|
||||
req.Id = dns.Id()
|
||||
req.RecursionDesired = true
|
||||
name := "example.org."
|
||||
req.Question = []dns.Question{
|
||||
{Name: name, Qtype: dns.TypeA, Qclass: dns.ClassINET},
|
||||
}
|
||||
|
||||
client := &dnscrypt.Client{
|
||||
Timeout: 1 * time.Second,
|
||||
Net: string(tc.network),
|
||||
}
|
||||
|
||||
s := dnsservertest.RunDNSCryptServer(b, dnsservertest.DefaultHandler())
|
||||
stamp := dnsstamps.ServerStamp{
|
||||
ServerAddrStr: s.ServerAddr,
|
||||
ServerPk: s.ResolverPk,
|
||||
ProviderName: s.ProviderName,
|
||||
Proto: dnsstamps.StampProtoTypeDNSCrypt,
|
||||
}
|
||||
|
||||
// Load server info
|
||||
ri, err := client.DialStamp(stamp)
|
||||
require.NoError(b, err)
|
||||
require.NotNil(b, ri)
|
||||
|
||||
// Prepare http.Request
|
||||
req, err := createDoHRequest(proto, http.MethodPost, m)
|
||||
// Open a single connection
|
||||
conn, err := net.Dial(string(tc.network), stamp.ServerAddrStr)
|
||||
require.NoError(b, err)
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
var res *http.Response
|
||||
res, err = client.Do(req)
|
||||
var resp *dns.Msg
|
||||
resp, err = client.ExchangeConn(conn, req, ri)
|
||||
require.NoError(b, err)
|
||||
|
||||
var buf []byte
|
||||
buf, err = io.ReadAll(res.Body)
|
||||
_ = res.Body.Close()
|
||||
require.NoError(b, err)
|
||||
require.GreaterOrEqual(b, len(buf), dnsserver.DNSHeaderSize)
|
||||
require.True(b, resp.Response)
|
||||
}
|
||||
b.StopTimer()
|
||||
}
|
||||
|
||||
func BenchmarkServeDNSCryptUDP(b *testing.B) {
|
||||
benchmarkServeDNSCrypt(b, dnsserver.NetworkUDP)
|
||||
}
|
||||
|
||||
func BenchmarkServeDNSCryptTCP(b *testing.B) {
|
||||
benchmarkServeDNSCrypt(b, dnsserver.NetworkTCP)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkServeQUIC(b *testing.B) {
|
||||
@ -296,55 +325,3 @@ func BenchmarkServeQUIC(b *testing.B) {
|
||||
}
|
||||
b.StopTimer()
|
||||
}
|
||||
|
||||
func benchmarkServeDNSCrypt(b *testing.B, network dnsserver.Network) {
|
||||
s, err := dnsservertest.RunLocalDNSCryptServer(
|
||||
dnsservertest.DefaultHandler(),
|
||||
network,
|
||||
)
|
||||
require.NoError(b, err)
|
||||
b.Cleanup(func() {
|
||||
err = s.Srv.Shutdown(context.Background())
|
||||
require.NoError(b, err)
|
||||
})
|
||||
|
||||
// Create a test message
|
||||
req := new(dns.Msg)
|
||||
req.Id = dns.Id()
|
||||
req.RecursionDesired = true
|
||||
name := "example.org."
|
||||
req.Question = []dns.Question{
|
||||
{Name: name, Qtype: dns.TypeA, Qclass: dns.ClassINET},
|
||||
}
|
||||
|
||||
client := &dnscrypt.Client{
|
||||
Timeout: 1 * time.Second,
|
||||
Net: string(network),
|
||||
}
|
||||
|
||||
stamp := dnsstamps.ServerStamp{
|
||||
ServerAddrStr: s.ServerAddr,
|
||||
ServerPk: s.ResolverPk,
|
||||
ProviderName: s.ProviderName,
|
||||
Proto: dnsstamps.StampProtoTypeDNSCrypt,
|
||||
}
|
||||
|
||||
// Load server info
|
||||
ri, err := client.DialStamp(stamp)
|
||||
require.NoError(b, err)
|
||||
require.NotNil(b, ri)
|
||||
|
||||
// Open a single connection
|
||||
conn, err := net.Dial(string(network), stamp.ServerAddrStr)
|
||||
require.NoError(b, err)
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
var resp *dns.Msg
|
||||
resp, err = client.ExchangeConn(conn, req, ri)
|
||||
require.NoError(b, err)
|
||||
require.True(b, resp.Response)
|
||||
}
|
||||
b.StopTimer()
|
||||
}
|
||||
|
@ -2,11 +2,11 @@ package dnsserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
@ -77,18 +77,16 @@ type ServerDNS struct {
|
||||
// type check
|
||||
var _ Server = (*ServerDNS)(nil)
|
||||
|
||||
// NewServerDNS creates a new ServerDNS instance. conf.Proto must be either
|
||||
// [ProtoDNSTCP] or [ProtoDNSUDP].
|
||||
// NewServerDNS creates a new ServerDNS instance.
|
||||
func NewServerDNS(conf ConfigDNS) (s *ServerDNS) {
|
||||
if !conf.Proto.IsPlain() {
|
||||
panic(fmt.Errorf("invalid proto %s in NewServerDNS", conf.Proto))
|
||||
}
|
||||
|
||||
return newServerDNS(conf)
|
||||
return newServerDNS(ProtoDNS, conf)
|
||||
}
|
||||
|
||||
func newServerDNS(conf ConfigDNS) (s *ServerDNS) {
|
||||
// Init default settings first
|
||||
// newServerDNS initializes a new ServerDNS instance with the specified proto.
|
||||
// This function is reused in ServerTLS as it is basically a plain DNS-over-TCP
|
||||
// server with a TLS layer on top of it.
|
||||
func newServerDNS(proto Protocol, conf ConfigDNS) (s *ServerDNS) {
|
||||
// Init default settings first.
|
||||
if conf.ReadTimeout == 0 {
|
||||
conf.ReadTimeout = DefaultReadTimeout
|
||||
}
|
||||
@ -108,7 +106,7 @@ func newServerDNS(conf ConfigDNS) (s *ServerDNS) {
|
||||
}
|
||||
|
||||
s = &ServerDNS{
|
||||
ServerBase: newServerBase(conf.ConfigBase),
|
||||
ServerBase: newServerBase(proto, conf.ConfigBase),
|
||||
conf: conf,
|
||||
}
|
||||
|
||||
@ -120,8 +118,10 @@ func newServerDNS(conf ConfigDNS) (s *ServerDNS) {
|
||||
return s
|
||||
}
|
||||
|
||||
// Start starts all listeners and starts processing queries.
|
||||
// Start implements the dnsserver.Server interface for *ServerDNS.
|
||||
func (s *ServerDNS) Start(ctx context.Context) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "starting dns server: %w", err) }()
|
||||
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
@ -138,9 +138,12 @@ func (s *ServerDNS) Start(ctx context.Context) (err error) {
|
||||
Proto: s.proto,
|
||||
})
|
||||
|
||||
// Start listening to UDP on the specified addrs.
|
||||
switch s.proto {
|
||||
case ProtoDNSUDP:
|
||||
if s.proto != ProtoDNS {
|
||||
return ErrInvalidArgument
|
||||
}
|
||||
|
||||
// Start listening to UDP on the specified address.
|
||||
if s.network.CanUDP() {
|
||||
err = s.listenUDP(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -148,7 +151,10 @@ func (s *ServerDNS) Start(ctx context.Context) (err error) {
|
||||
|
||||
s.wg.Add(1)
|
||||
go s.startServeUDP(ctx)
|
||||
case ProtoDNSTCP:
|
||||
}
|
||||
|
||||
// Start listening to TCP on the specified address.
|
||||
if s.network.CanTCP() {
|
||||
err = s.listenTCP(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -156,8 +162,6 @@ func (s *ServerDNS) Start(ctx context.Context) (err error) {
|
||||
|
||||
s.wg.Add(1)
|
||||
go s.startServeTCP(ctx)
|
||||
default:
|
||||
return ErrInvalidArgument
|
||||
}
|
||||
|
||||
log.Info("[%s]: Server has been started", s.Name())
|
||||
@ -165,8 +169,10 @@ func (s *ServerDNS) Start(ctx context.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown closes active connections and listeners (if they're not closed already).
|
||||
// Shutdown implements the dnsserver.Server interface for *ServerDNS.
|
||||
func (s *ServerDNS) Shutdown(ctx context.Context) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "shutting down dns server: %w", err) }()
|
||||
|
||||
err = s.shutdown()
|
||||
if err != nil {
|
||||
log.Info("[%s]: Failed to shutdown: %v", s.Name(), err)
|
||||
|
@ -12,28 +12,18 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestServerDNS_StartShutdown(t *testing.T) {
|
||||
srv, _, err := dnsservertest.RunLocalDNSServer(
|
||||
dnsservertest.DefaultHandler(),
|
||||
dnsserver.ProtoDNSUDP,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return srv.Shutdown(context.Background())
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, _ = dnsservertest.RunDNSServer(t, dnsservertest.DefaultHandler())
|
||||
}
|
||||
|
||||
func TestServerDNS_integration_query(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
proto dnsserver.Protocol
|
||||
network dnsserver.Network
|
||||
req *dns.Msg
|
||||
// if nil, use defaultTestHandler
|
||||
handler dnsserver.Handler
|
||||
@ -43,7 +33,7 @@ func TestServerDNS_integration_query(t *testing.T) {
|
||||
expectedMsg func(t *testing.T, m *dns.Msg)
|
||||
}{{
|
||||
name: "valid_udp_msg",
|
||||
proto: dnsserver.ProtoDNSUDP,
|
||||
network: dnsserver.NetworkUDP,
|
||||
expectedRecordsCount: 1,
|
||||
expectedRCode: dns.RcodeSuccess,
|
||||
req: &dns.Msg{
|
||||
@ -54,7 +44,7 @@ func TestServerDNS_integration_query(t *testing.T) {
|
||||
},
|
||||
}, {
|
||||
name: "valid_tcp_msg",
|
||||
proto: dnsserver.ProtoDNSTCP,
|
||||
network: dnsserver.NetworkTCP,
|
||||
expectedRecordsCount: 1,
|
||||
expectedRCode: dns.RcodeSuccess,
|
||||
req: &dns.Msg{
|
||||
@ -67,7 +57,7 @@ func TestServerDNS_integration_query(t *testing.T) {
|
||||
// This test checks that we remove unsupported EDNS0 options from
|
||||
// the response.
|
||||
name: "udp_edns0_supported_options",
|
||||
proto: dnsserver.ProtoDNSUDP,
|
||||
network: dnsserver.NetworkUDP,
|
||||
expectedRecordsCount: 1,
|
||||
expectedRCode: dns.RcodeSuccess,
|
||||
expectedMsg: func(t *testing.T, m *dns.Msg) {
|
||||
@ -107,7 +97,7 @@ func TestServerDNS_integration_query(t *testing.T) {
|
||||
}, {
|
||||
// Check that we reject invalid DNS messages (like having two questions)
|
||||
name: "reject_msg",
|
||||
proto: dnsserver.ProtoDNSUDP,
|
||||
network: dnsserver.NetworkUDP,
|
||||
expectedRecordsCount: 0,
|
||||
expectedRCode: dns.RcodeFormatError,
|
||||
req: &dns.Msg{
|
||||
@ -120,7 +110,7 @@ func TestServerDNS_integration_query(t *testing.T) {
|
||||
}, {
|
||||
// Checks that we handle mixed case domain names
|
||||
name: "udp_mixed_case",
|
||||
proto: dnsserver.ProtoDNSUDP,
|
||||
network: dnsserver.NetworkUDP,
|
||||
expectedRecordsCount: 1,
|
||||
expectedRCode: dns.RcodeSuccess,
|
||||
req: &dns.Msg{
|
||||
@ -133,7 +123,7 @@ func TestServerDNS_integration_query(t *testing.T) {
|
||||
// Checks that we respond with NotImplemented to requests with OpcodeStatus
|
||||
// also checks that Opcode is unchanged in the response
|
||||
name: "not_implemented_msg",
|
||||
proto: dnsserver.ProtoDNSUDP,
|
||||
network: dnsserver.NetworkUDP,
|
||||
expectedRecordsCount: 0,
|
||||
expectedRCode: dns.RcodeNotImplemented,
|
||||
req: &dns.Msg{
|
||||
@ -146,7 +136,7 @@ func TestServerDNS_integration_query(t *testing.T) {
|
||||
// Checks that we respond with SERVFAIL in case if the handler
|
||||
// returns an error
|
||||
name: "handler_failure",
|
||||
proto: dnsserver.ProtoDNSUDP,
|
||||
network: dnsserver.NetworkUDP,
|
||||
expectedRecordsCount: 0,
|
||||
expectedRCode: dns.RcodeServerFailure,
|
||||
handler: dnsserver.HandlerFunc(func(
|
||||
@ -166,7 +156,7 @@ func TestServerDNS_integration_query(t *testing.T) {
|
||||
// Checks that Z flag is set to zero even when the query has it
|
||||
// See https://github.com/miekg/dns/issues/975
|
||||
name: "msg_with_zflag",
|
||||
proto: dnsserver.ProtoDNSUDP,
|
||||
network: dnsserver.NetworkUDP,
|
||||
expectedRecordsCount: 1,
|
||||
expectedRCode: dns.RcodeSuccess,
|
||||
req: &dns.Msg{
|
||||
@ -179,7 +169,7 @@ func TestServerDNS_integration_query(t *testing.T) {
|
||||
// Checks that large responses are getting truncated when
|
||||
// sent over UDP
|
||||
name: "udp_truncate_response",
|
||||
proto: dnsserver.ProtoDNSUDP,
|
||||
network: dnsserver.NetworkUDP,
|
||||
// Set a handler that generates a large response
|
||||
handler: dnsservertest.CreateTestHandler(64),
|
||||
expectedRecordsCount: 0,
|
||||
@ -195,7 +185,7 @@ func TestServerDNS_integration_query(t *testing.T) {
|
||||
// Checks that if UDP size is large enough there would be no
|
||||
// truncated responses
|
||||
name: "udp_edns0_no_truncate",
|
||||
proto: dnsserver.ProtoDNSUDP,
|
||||
network: dnsserver.NetworkUDP,
|
||||
// Set a handler that generates a large response
|
||||
handler: dnsservertest.CreateTestHandler(64),
|
||||
expectedRecordsCount: 64,
|
||||
@ -220,7 +210,7 @@ func TestServerDNS_integration_query(t *testing.T) {
|
||||
// Checks that large responses are NOT truncated when
|
||||
// sent over UDP
|
||||
name: "tcp_no_truncate_response",
|
||||
proto: dnsserver.ProtoDNSTCP,
|
||||
network: dnsserver.NetworkTCP,
|
||||
// Set a handler that generates a large response
|
||||
handler: dnsservertest.CreateTestHandler(64),
|
||||
// No truncate
|
||||
@ -241,21 +231,12 @@ func TestServerDNS_integration_query(t *testing.T) {
|
||||
if tc.handler != nil {
|
||||
handler = tc.handler
|
||||
}
|
||||
srv, addr, err := dnsservertest.RunLocalDNSServer(handler, tc.proto)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.proto, srv.Proto())
|
||||
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return srv.Shutdown(context.Background())
|
||||
})
|
||||
_, addr := dnsservertest.RunDNSServer(t, handler)
|
||||
|
||||
// Send this test message to our server over UDP
|
||||
c := new(dns.Client)
|
||||
c.Net = "udp"
|
||||
c.Net = string(tc.network)
|
||||
c.UDPSize = 7000 // need to be set to read large responses
|
||||
if tc.proto == dnsserver.ProtoDNSTCP {
|
||||
c.Net = "tcp"
|
||||
}
|
||||
|
||||
res, _, err := c.Exchange(tc.req, addr)
|
||||
require.NoError(t, err)
|
||||
@ -270,19 +251,10 @@ func TestServerDNS_integration_query(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestServerDNS_integration_tcpQueriesPipelining(t *testing.T) {
|
||||
// As per RFC 7766 we should support queries pipelining for TCP,
|
||||
// i.e. we should be able to process incoming queries in parallel and
|
||||
// write responses out of order.
|
||||
srv, addr, err := dnsservertest.RunLocalDNSServer(
|
||||
dnsservertest.DefaultHandler(),
|
||||
dnsserver.ProtoDNSTCP,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, dnsserver.ProtoDNSTCP, srv.Proto())
|
||||
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return srv.Shutdown(context.Background())
|
||||
})
|
||||
// As per RFC 7766 we should support queries pipelining for TCP, that is we
|
||||
// should be able to process incoming queries in parallel and write
|
||||
// responses out of order.
|
||||
_, addr := dnsservertest.RunDNSServer(t, dnsservertest.DefaultHandler())
|
||||
|
||||
// First - establish a connection
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
@ -348,17 +320,7 @@ func TestServerDNS_integration_tcpQueriesPipelining(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestServerDNS_integration_udpMsgIgnore(t *testing.T) {
|
||||
srv, addr, err := dnsservertest.RunLocalDNSServer(
|
||||
dnsservertest.DefaultHandler(),
|
||||
dnsserver.ProtoDNSUDP,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, dnsserver.ProtoDNSUDP, srv.Proto())
|
||||
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return srv.Shutdown(context.Background())
|
||||
})
|
||||
|
||||
_, addr := dnsservertest.RunDNSServer(t, dnsservertest.DefaultHandler())
|
||||
conn, err := net.Dial("udp", addr)
|
||||
require.Nil(t, err)
|
||||
|
||||
@ -449,17 +411,7 @@ func TestServerDNS_integration_tcpMsgIgnore(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
srv, addr, err := dnsservertest.RunLocalDNSServer(
|
||||
dnsservertest.DefaultHandler(),
|
||||
dnsserver.ProtoDNSTCP,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, dnsserver.ProtoDNSTCP, srv.Proto())
|
||||
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return srv.Shutdown(context.Background())
|
||||
})
|
||||
|
||||
_, addr := dnsservertest.RunDNSServer(t, dnsservertest.DefaultHandler())
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
require.Nil(t, err)
|
||||
|
||||
|
@ -2,7 +2,6 @@ package dnsserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
@ -40,16 +39,23 @@ var _ Server = (*ServerDNSCrypt)(nil)
|
||||
// NewServerDNSCrypt creates a new instance of ServerDNSCrypt.
|
||||
func NewServerDNSCrypt(conf ConfigDNSCrypt) (s *ServerDNSCrypt) {
|
||||
return &ServerDNSCrypt{
|
||||
ServerBase: newServerBase(conf.ConfigBase),
|
||||
ServerBase: newServerBase(ProtoDNSCrypt, conf.ConfigBase),
|
||||
conf: conf,
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts the server and starts processing queries.
|
||||
// Start implements the dnsserver.Server interface for *ServerDNSCrypt.
|
||||
func (s *ServerDNSCrypt) Start(ctx context.Context) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "starting dnscrypt server: %w", err) }()
|
||||
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
// First, validate the protocol.
|
||||
if s.proto != ProtoDNSCrypt {
|
||||
return ErrInvalidArgument
|
||||
}
|
||||
|
||||
if s.started {
|
||||
return ErrServerAlreadyStarted
|
||||
}
|
||||
@ -72,32 +78,20 @@ func (s *ServerDNSCrypt) Start(ctx context.Context) (err error) {
|
||||
},
|
||||
}
|
||||
|
||||
switch s.proto {
|
||||
case ProtoDNSCryptUDP:
|
||||
err = s.listenUDP(ctx)
|
||||
err = s.startServe(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go s.startServeUDP(ctx)
|
||||
case ProtoDNSCryptTCP:
|
||||
err = s.listenTCP(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go s.startServeTCP(ctx)
|
||||
default:
|
||||
return ErrInvalidArgument
|
||||
}
|
||||
|
||||
log.Info("[%s]: Server has been started", s.Name())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown closes active connections and listeners (if they're not closed already).
|
||||
// Shutdown implements the dnsserver.Server interface for *ServerDNSCrypt.
|
||||
func (s *ServerDNSCrypt) Shutdown(ctx context.Context) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "shutting down dnscrypt server: %w", err) }()
|
||||
|
||||
log.Info("[%s]: Stopping the server", s.Name())
|
||||
err = s.shutdown()
|
||||
if err != nil {
|
||||
@ -112,6 +106,29 @@ func (s *ServerDNSCrypt) Shutdown(ctx context.Context) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// startServe creates listeners and starts serving DNSCrypt.
|
||||
func (s *ServerDNSCrypt) startServe(ctx context.Context) (err error) {
|
||||
if s.network.CanUDP() {
|
||||
err = s.listenUDP(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go s.startServeUDP(ctx)
|
||||
}
|
||||
|
||||
if s.network.CanTCP() {
|
||||
err = s.listenTCP(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go s.startServeTCP(ctx)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// startServeUDP starts the UDP listener loop.
|
||||
func (s *ServerDNSCrypt) startServeUDP(ctx context.Context) {
|
||||
// We do not recover from panics here since if this go routine panics
|
||||
@ -186,48 +203,9 @@ func (h *dnsCryptHandler) ServeDNS(rw dnscrypt.ResponseWriter, r *dns.Msg) (err
|
||||
return rw.WriteMsg(genErrorResponse(r, dns.RcodeServerFailure))
|
||||
}
|
||||
|
||||
network := NetworkUDP
|
||||
if h.srv.proto == ProtoDNSCryptTCP {
|
||||
network = NetworkTCP
|
||||
}
|
||||
|
||||
network := NetworkFromAddr(rw.LocalAddr())
|
||||
msg := nrw.Msg()
|
||||
normalize(network, r, msg)
|
||||
|
||||
return rw.WriteMsg(msg)
|
||||
}
|
||||
|
||||
// listenUDP creates the UDP listener for the ServerDNSCrypt.addr.
|
||||
func (s *ServerDNSCrypt) listenUDP(ctx context.Context) (err error) {
|
||||
var l net.PacketConn
|
||||
l, err = listenUDP(ctx, s.addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u, ok := l.(*net.UDPConn)
|
||||
if !ok {
|
||||
return ErrInvalidArgument
|
||||
}
|
||||
|
||||
if err = setUDPSocketOptions(u); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.udpListener = u
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// listenTCP creates the TCP listener for the ServerDNSCrypt.addr.
|
||||
func (s *ServerDNSCrypt) listenTCP(ctx context.Context) (err error) {
|
||||
var l net.Listener
|
||||
l, err = listenTCP(ctx, s.addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.tcpListener = l
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package dnsserver_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -16,7 +15,7 @@ import (
|
||||
func TestServerDNSCrypt_integration_query(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
proto dnsserver.Protocol
|
||||
network dnsserver.Network
|
||||
req *dns.Msg
|
||||
// if nil, use DefaultTestHandler
|
||||
handler dnsserver.Handler
|
||||
@ -25,7 +24,7 @@ func TestServerDNSCrypt_integration_query(t *testing.T) {
|
||||
expectedTruncated bool
|
||||
}{{
|
||||
name: "udp_valid_msg",
|
||||
proto: dnsserver.ProtoDNSCryptUDP,
|
||||
network: dnsserver.NetworkUDP,
|
||||
expectedRecordsCount: 1,
|
||||
expectedRCode: dns.RcodeSuccess,
|
||||
req: &dns.Msg{
|
||||
@ -36,7 +35,7 @@ func TestServerDNSCrypt_integration_query(t *testing.T) {
|
||||
},
|
||||
}, {
|
||||
name: "tcp_valid_msg",
|
||||
proto: dnsserver.ProtoDNSCryptTCP,
|
||||
network: dnsserver.NetworkTCP,
|
||||
expectedRecordsCount: 1,
|
||||
expectedRCode: dns.RcodeSuccess,
|
||||
req: &dns.Msg{
|
||||
@ -49,7 +48,7 @@ func TestServerDNSCrypt_integration_query(t *testing.T) {
|
||||
// Checks that large responses are getting truncated when
|
||||
// sent over UDP
|
||||
name: "udp_truncate_response",
|
||||
proto: dnsserver.ProtoDNSCryptUDP,
|
||||
network: dnsserver.NetworkUDP,
|
||||
// Set a handler that generates a large response
|
||||
handler: dnsservertest.CreateTestHandler(64),
|
||||
// DNSCrypt server removes all records from a truncated response
|
||||
@ -66,7 +65,7 @@ func TestServerDNSCrypt_integration_query(t *testing.T) {
|
||||
// Checks that if UDP size is large enough there would be no
|
||||
// truncated responses
|
||||
name: "udp_edns0_no_truncate",
|
||||
proto: dnsserver.ProtoDNSCryptUDP,
|
||||
network: dnsserver.NetworkUDP,
|
||||
// Set a handler that generates a large response
|
||||
handler: dnsservertest.CreateTestHandler(64),
|
||||
expectedRecordsCount: 64,
|
||||
@ -89,26 +88,15 @@ func TestServerDNSCrypt_integration_query(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
network := dnsserver.NetworkUDP
|
||||
if tc.proto == dnsserver.ProtoDNSCryptTCP {
|
||||
network = dnsserver.NetworkTCP
|
||||
}
|
||||
handler := tc.handler
|
||||
if tc.handler == nil {
|
||||
handler = dnsservertest.DefaultHandler()
|
||||
}
|
||||
s, err := dnsservertest.RunLocalDNSCryptServer(handler, network)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.proto, s.Srv.Proto())
|
||||
|
||||
defer func() {
|
||||
err = s.Srv.Shutdown(context.Background())
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
|
||||
s := dnsservertest.RunDNSCryptServer(t, handler)
|
||||
client := &dnscrypt.Client{
|
||||
Timeout: 1 * time.Second,
|
||||
Net: string(network),
|
||||
Net: string(tc.network),
|
||||
UDPSize: 7000, // Make sure that we can read any response
|
||||
}
|
||||
|
||||
|
@ -16,19 +16,6 @@ import (
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// listenTCP creates a TCP listener for the ServerDNS.addr.
|
||||
func (s *ServerDNS) listenTCP(ctx context.Context) (err error) {
|
||||
var l net.Listener
|
||||
l, err = listenTCP(ctx, s.addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.tcpListener = l
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// serveTCP runs the TCP serving loop.
|
||||
func (s *ServerDNS) serveTCP(ctx context.Context, l net.Listener) (err error) {
|
||||
defer log.OnCloserError(l, log.DEBUG)
|
||||
@ -108,7 +95,7 @@ func (s *ServerDNS) serveTCPConn(ctx context.Context, conn net.Conn) {
|
||||
reqCtx := s.requestContext()
|
||||
|
||||
ci := ClientInfo{}
|
||||
if cs, ok := conn.(tlsConnecionStater); ok {
|
||||
if cs, ok := conn.(tlsConnectionStater); ok {
|
||||
ci.TLSServerName = strings.ToLower(cs.ConnectionState().ServerName)
|
||||
}
|
||||
reqCtx = ContextWithClientInfo(reqCtx, ci)
|
||||
@ -120,9 +107,9 @@ func (s *ServerDNS) serveTCPConn(ctx context.Context, conn net.Conn) {
|
||||
}
|
||||
}
|
||||
|
||||
// tlsConnecionStater is a common interface for connections that can return
|
||||
// tlsConnectionStater is a common interface for connections that can return
|
||||
// a TLS connection state.
|
||||
type tlsConnecionStater interface {
|
||||
type tlsConnectionStater interface {
|
||||
ConnectionState() tls.ConnectionState
|
||||
}
|
||||
|
||||
|
@ -14,27 +14,6 @@ import (
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
// listenUDP creates a UDP listener for the ServerDNS.addr.
|
||||
func (s *ServerDNS) listenUDP(ctx context.Context) (err error) {
|
||||
l, err := listenUDP(ctx, s.addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u, ok := l.(*net.UDPConn)
|
||||
if !ok {
|
||||
return ErrInvalidArgument
|
||||
}
|
||||
|
||||
if err = setUDPSocketOptions(u); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.udpListener = u
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// serveUDP runs the UDP serving loop.
|
||||
func (s *ServerDNS) serveUDP(ctx context.Context, conn *net.UDPConn) (err error) {
|
||||
defer log.OnCloserError(conn, log.DEBUG)
|
||||
|
@ -14,8 +14,11 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/lucas-clemente/quic-go/http3"
|
||||
"github.com/miekg/dns"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
@ -37,18 +40,27 @@ const (
|
||||
httpIdleTimeout = 120 * time.Second
|
||||
)
|
||||
|
||||
// nextProtoDoH is a list of ALPN that we would add by default to the server
|
||||
// nextProtoDoH is a list of ALPN that we would add by default to the server's
|
||||
// *tls.Config if no NextProto is specified there. Note, that with this order,
|
||||
// we prioritize HTTP/2 over HTTP/1.1.
|
||||
var nextProtoDoH = []string{http2.NextProtoTLS, "http/1.1"}
|
||||
|
||||
// nextProtoDoH3 is a list of ALPN that we should add by default to the server's
|
||||
// *tls.Config if no NextProto is specified there and DoH3 is supposed to be
|
||||
// used.
|
||||
var nextProtoDoH3 = []string{"h3", http2.NextProtoTLS, "http/1.1"}
|
||||
|
||||
// ConfigHTTPS is a struct that needs to be passed to NewServerHTTPS to
|
||||
// initialize a new ServerHTTPS instance.
|
||||
// initialize a new ServerHTTPS instance. You can choose whether HTTP/3 is
|
||||
// enabled or not by specifying [ConfigBase.Network]. By default, the server
|
||||
// will listen to both HTTP/2 and HTTP/3, but if you set it to NetworkTCP, the
|
||||
// server will only use HTTP/2 and NetworkUDP will mean HTTP/3 only.
|
||||
type ConfigHTTPS struct {
|
||||
ConfigBase
|
||||
|
||||
// TLSConfig is the TLS configuration for HTTPS.
|
||||
// if not set, we'll run a plain HTTP server.
|
||||
// TLSConfig is the TLS configuration for HTTPS. If not set and
|
||||
// [ConfigBase.Network] is set to NetworkTCP the server will listen to
|
||||
// plain HTTP.
|
||||
TLSConfig *tls.Config
|
||||
|
||||
// NonDNSHandler handles requests with the path not equal to /dns-query.
|
||||
@ -63,8 +75,17 @@ type ConfigHTTPS struct {
|
||||
type ServerHTTPS struct {
|
||||
*ServerBase
|
||||
|
||||
// httpServer is an instance of an *http.Server that is responsible for
|
||||
// handling HTTP/1.1 and HTTP/2 requests.
|
||||
httpServer *http.Server
|
||||
|
||||
// h3Server is an instance of an *http.Server that is responsible for
|
||||
// handling HTTP/3 requests.
|
||||
h3Server *http3.Server
|
||||
|
||||
// quicListener is a listener that we use to serve DoH3 requests.
|
||||
quicListener quic.EarlyListener
|
||||
|
||||
conf ConfigHTTPS
|
||||
}
|
||||
|
||||
@ -73,21 +94,18 @@ var _ Server = (*ServerHTTPS)(nil)
|
||||
|
||||
// NewServerHTTPS creates a new ServerHTTPS instance.
|
||||
func NewServerHTTPS(conf ConfigHTTPS) (s *ServerHTTPS) {
|
||||
tlsConfig := conf.TLSConfig
|
||||
if tlsConfig != nil && len(tlsConfig.NextProtos) == 0 {
|
||||
tlsConfig.NextProtos = nextProtoDoH
|
||||
}
|
||||
|
||||
s = &ServerHTTPS{
|
||||
ServerBase: newServerBase(conf.ConfigBase),
|
||||
ServerBase: newServerBase(ProtoDoH, conf.ConfigBase),
|
||||
conf: conf,
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// Start starts the ServerHTTPS.
|
||||
// Start implements the dnsserver.Server interface for *ServerHTTPS.
|
||||
func (s *ServerHTTPS) Start(ctx context.Context) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "starting doh server: %w", err) }()
|
||||
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
@ -104,37 +122,33 @@ func (s *ServerHTTPS) Start(ctx context.Context) (err error) {
|
||||
Proto: s.proto,
|
||||
})
|
||||
|
||||
// Start the TLS or TCP listener
|
||||
err = s.listenTLS(ctx)
|
||||
if s.proto != ProtoDoH {
|
||||
return ErrInvalidArgument
|
||||
}
|
||||
|
||||
if s.network.CanTCP() {
|
||||
err = s.startHTTPSServer(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Prepare and run the HTTP server
|
||||
handler := &httpHandler{
|
||||
srv: s,
|
||||
listener: s.tcpListener,
|
||||
}
|
||||
|
||||
s.httpServer = &http.Server{
|
||||
Handler: handler,
|
||||
ReadTimeout: httpReadTimeout,
|
||||
ReadHeaderTimeout: httpReadTimeout,
|
||||
WriteTimeout: httpWriteTimeout,
|
||||
IdleTimeout: httpIdleTimeout,
|
||||
ErrorLog: log.StdLog("dnsserver/serverhttps: "+s.name, log.DEBUG),
|
||||
if s.network.CanUDP() {
|
||||
err = s.startH3Server(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Start the server thread
|
||||
s.wg.Add(1)
|
||||
go s.serveHTTPS(ctx, s.httpServer, s.tcpListener)
|
||||
log.Info("[%s]: Server has been started", s.Name())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown stops the ServerHTTPS.
|
||||
// Shutdown implements the dnsserver.Server interface for *ServerHTTPS.
|
||||
func (s *ServerHTTPS) Shutdown(ctx context.Context) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "shutting down doh server: %w", err) }()
|
||||
|
||||
log.Info("[%s]: Stopping the server", s.Name())
|
||||
err = s.shutdown(ctx)
|
||||
if err != nil {
|
||||
@ -149,6 +163,63 @@ func (s *ServerHTTPS) Shutdown(ctx context.Context) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// startHTTPSServer starts the HTTPS server that will handle HTTP/1.1 and HTTP2.
|
||||
func (s *ServerHTTPS) startHTTPSServer(ctx context.Context) (err error) {
|
||||
// Start the TLS or TCP listener.
|
||||
err = s.listenTLS(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Prepare and run the HTTP server.
|
||||
handler := &httpHandler{
|
||||
srv: s,
|
||||
localAddr: s.tcpListener.Addr(),
|
||||
}
|
||||
|
||||
// Create an instance of the HTTP server.
|
||||
s.httpServer = &http.Server{
|
||||
Handler: handler,
|
||||
ReadTimeout: httpReadTimeout,
|
||||
ReadHeaderTimeout: httpReadTimeout,
|
||||
WriteTimeout: httpWriteTimeout,
|
||||
IdleTimeout: httpIdleTimeout,
|
||||
ErrorLog: log.StdLog("dnsserver/serverhttps: "+s.name, log.DEBUG),
|
||||
}
|
||||
|
||||
// Start the server worker goroutine.
|
||||
s.wg.Add(1)
|
||||
go s.serveHTTPS(ctx, s.httpServer, s.tcpListener)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// startH3Server starts the HTTP/3 server.
|
||||
func (s *ServerHTTPS) startH3Server(ctx context.Context) (err error) {
|
||||
// Start the QUIC listener.
|
||||
err = s.listenQUIC(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Prepare and run the HTTP/3 server.
|
||||
handler := &httpHandler{
|
||||
srv: s,
|
||||
localAddr: s.quicListener.Addr(),
|
||||
}
|
||||
|
||||
// Create an instance of the HTTP/3 server.
|
||||
s.h3Server = &http3.Server{
|
||||
Handler: handler,
|
||||
}
|
||||
|
||||
// Start the server worker goroutine.
|
||||
s.wg.Add(1)
|
||||
go s.serveH3(ctx, s.h3Server, s.quicListener)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// shutdown marks the server as stopped and closes active listeners.
|
||||
func (s *ServerHTTPS) shutdown(ctx context.Context) (err error) {
|
||||
s.lock.Lock()
|
||||
@ -159,19 +230,33 @@ func (s *ServerHTTPS) shutdown(ctx context.Context) (err error) {
|
||||
|
||||
s.started = false
|
||||
|
||||
// First step, close the active listener right away
|
||||
// First step, close the active listener right away.
|
||||
s.closeListeners()
|
||||
|
||||
// Second, shutdown the HTTP server
|
||||
// Second, shutdown the HTTP server.
|
||||
err = s.httpServer.Shutdown(ctx)
|
||||
if err != nil {
|
||||
log.Debug("[%s]: http server shutdown: %v", s.Name(), err)
|
||||
}
|
||||
|
||||
// Finally, shutdown the HTTP/3 server.
|
||||
if s.h3Server != nil {
|
||||
err = s.quicListener.Close()
|
||||
if err != nil {
|
||||
log.Debug("[%s]: quic listener shutdown: %v", s.Name(), err)
|
||||
}
|
||||
|
||||
err = s.h3Server.Close()
|
||||
if err != nil {
|
||||
log.Debug("[%s]: http/3 server shutdown: %v", s.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// serveHTTPS is a worker goroutine that serves HTTP.
|
||||
// serveHTTPS is launched in a worker goroutine and serves HTTP/1.1 and HTTP/2
|
||||
// requests.
|
||||
func (s *ServerHTTPS) serveHTTPS(ctx context.Context, hs *http.Server, l net.Listener) {
|
||||
defer s.wg.Done()
|
||||
|
||||
@ -196,16 +281,62 @@ func (s *ServerHTTPS) serveHTTPS(ctx context.Context, hs *http.Server, l net.Lis
|
||||
}
|
||||
}
|
||||
|
||||
// serveH3 is launched in a worker goroutine and serves HTTP/3 requests.
|
||||
func (s *ServerHTTPS) serveH3(ctx context.Context, hs *http3.Server, ql quic.EarlyListener) {
|
||||
defer s.wg.Done()
|
||||
|
||||
// Do not recover from panics here since if this goroutine panics, the
|
||||
// application won't be able to continue listening to DoH.
|
||||
defer s.handlePanicAndExit(ctx)
|
||||
|
||||
u := &url.URL{
|
||||
Scheme: "h3",
|
||||
Host: s.addr,
|
||||
}
|
||||
log.Info("[%s]: Start listening to %s", s.name, u)
|
||||
|
||||
err := hs.ServeListener(ql)
|
||||
if err != nil {
|
||||
log.Info("[%s]: Finished listening to %s due to %v", s.name, u, err)
|
||||
}
|
||||
}
|
||||
|
||||
// httpHandler is a helper structure that implements http.Handler
|
||||
// and holds pointers to ServerHTTPS, net.Listener.
|
||||
type httpHandler struct {
|
||||
srv *ServerHTTPS
|
||||
listener net.Listener
|
||||
localAddr net.Addr
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ http.Handler = (*httpHandler)(nil)
|
||||
|
||||
// remoteAddr gets HTTP request's remote address.
|
||||
//
|
||||
// TODO(a.garipov): Add trusted proxies and real IP extraction logic. Perhaps
|
||||
// just copy from module dnsproxy if that one fits. Also, perhaps make that
|
||||
// logic pluggable and put it into a new package in module golibs.
|
||||
func (h *httpHandler) remoteAddr(r *http.Request) (addr net.Addr) {
|
||||
// Consider that the http.Request.RemoteAddr documentation is correct and
|
||||
// that it is always a valid ip:port value. Panic if it isn't so.
|
||||
ipStr, port, err := netutil.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to split host:port %s: %v", r.RemoteAddr, err))
|
||||
}
|
||||
|
||||
ip, err := netutil.ParseIP(ipStr)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to parse IP %s: %v", ipStr, err))
|
||||
}
|
||||
|
||||
if NetworkFromAddr(h.localAddr) == NetworkUDP {
|
||||
// This means that we're extracting remoteAddr from an HTTP/3 request.
|
||||
return &net.UDPAddr{IP: ip, Port: port}
|
||||
}
|
||||
|
||||
return &net.TCPAddr{IP: ip, Port: port}
|
||||
}
|
||||
|
||||
// ServeHTTP implements the http.Handler interface for *httpHandler. It reads
|
||||
// the DNS data from the request, resolves it, and sends a response.
|
||||
//
|
||||
@ -250,17 +381,18 @@ func (h *httpHandler) serveDoH(ctx context.Context, w http.ResponseWriter, r *ht
|
||||
return
|
||||
}
|
||||
|
||||
rAddr := remoteAddr(r)
|
||||
lAddr := h.listener.Addr()
|
||||
rAddr := h.remoteAddr(r)
|
||||
lAddr := h.localAddr
|
||||
rw := NewNonWriterResponseWriter(lAddr, rAddr)
|
||||
|
||||
ci := ClientInfo{
|
||||
URL: netutil.CloneURL(r.URL),
|
||||
ctx, err = httpContextWithClientInfo(ctx, r)
|
||||
if err != nil {
|
||||
log.Debug("Failed to enrich DoH context: %v", err)
|
||||
h.srv.metrics.OnInvalidMsg(ctx)
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
if r.TLS != nil {
|
||||
ci.TLSServerName = strings.ToLower(r.TLS.ServerName)
|
||||
}
|
||||
ctx = ContextWithClientInfo(ctx, ci)
|
||||
|
||||
// Serve the query
|
||||
written := h.srv.serveDNS(ctx, m, rw)
|
||||
@ -335,22 +467,83 @@ func (h *httpHandler) writeResponse(
|
||||
return err
|
||||
}
|
||||
|
||||
// listenTCP starts the TCP/TLS listener.
|
||||
// listenTCP starts the TCP/TLS listener. Note that if there's no TLS config,
|
||||
// a plain TCP listener will be started instead.
|
||||
func (s *ServerHTTPS) listenTLS(ctx context.Context) (err error) {
|
||||
l, err := listenTCP(ctx, s.addr)
|
||||
err = s.listenTCP(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s.conf.TLSConfig != nil {
|
||||
l = tls.NewListener(l, s.conf.TLSConfig)
|
||||
// Prepare the TLS configuration of the server.
|
||||
tlsConf := s.conf.TLSConfig
|
||||
if tlsConf == nil {
|
||||
return nil
|
||||
} else if len(tlsConf.NextProtos) == 0 {
|
||||
tlsConf = tlsConf.Clone()
|
||||
tlsConf.NextProtos = nextProtoDoH
|
||||
}
|
||||
|
||||
s.tcpListener = l
|
||||
s.tcpListener = tls.NewListener(s.tcpListener, tlsConf)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// listenQUIC starts a QUIC listener that will be used to serve HTTP/3 requests.
|
||||
func (s *ServerHTTPS) listenQUIC(ctx context.Context) (err error) {
|
||||
// Prepare the TLS configuration of the server.
|
||||
tlsConf := s.conf.TLSConfig
|
||||
if tlsConf != nil && len(tlsConf.NextProtos) == 0 {
|
||||
tlsConf = tlsConf.Clone()
|
||||
tlsConf.NextProtos = nextProtoDoH3
|
||||
}
|
||||
|
||||
// Do not enable OOB here as quic-go will do that on its own.
|
||||
conn, err := listenUDP(ctx, s.addr, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
qConf := newServerQUICConfig(s.metrics)
|
||||
ql, err := quic.ListenEarly(conn, tlsConf, qConf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.udpListener = conn
|
||||
s.quicListener = ql
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// httpContextWithClientInfo adds client info to the context.
|
||||
func httpContextWithClientInfo(
|
||||
parent context.Context,
|
||||
r *http.Request,
|
||||
) (ctx context.Context, err error) {
|
||||
ci := ClientInfo{
|
||||
URL: netutil.CloneURL(r.URL),
|
||||
}
|
||||
|
||||
// Due to the quic-go bug we should use Host instead of r.TLS:
|
||||
// https://github.com/lucas-clemente/quic-go/issues/3596
|
||||
//
|
||||
// TODO(ameshkov): remove this when the bug is fixed in quic-go.
|
||||
if r.ProtoAtLeast(3, 0) {
|
||||
var host string
|
||||
host, err = netutil.SplitHost(r.Host)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse Host: %w", err)
|
||||
}
|
||||
|
||||
ci.TLSServerName = host
|
||||
} else if r.TLS != nil {
|
||||
ci.TLSServerName = strings.ToLower(r.TLS.ServerName)
|
||||
}
|
||||
|
||||
return ContextWithClientInfo(parent, ci), nil
|
||||
}
|
||||
|
||||
// httpRequestToMsg reads the DNS message from http.Request.
|
||||
func httpRequestToMsg(req *http.Request) (b []byte, err error) {
|
||||
_, isJSON, _ := isDoH(req)
|
||||
@ -389,28 +582,6 @@ func httpRequestToMsgGet(req *http.Request) (b []byte, err error) {
|
||||
return base64.RawURLEncoding.DecodeString(b64[0])
|
||||
}
|
||||
|
||||
// remoteAddr gets HTTP request's remote address.
|
||||
//
|
||||
// TODO(a.garipov): Add trusted proxies and real IP extraction logic. Perhaps
|
||||
// just copy from module dnsproxy if that one fits. Also, perhaps make that
|
||||
// logic pluggable and put it into a new package in module golibs.
|
||||
func remoteAddr(r *http.Request) (addr *net.TCPAddr) {
|
||||
// Consider that the http.Request.RemoteAddr documentation is correct and
|
||||
// that it is always a valid ip:port value. Panic if it isn't so.
|
||||
ipStr, port, err := netutil.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ip, err := netutil.ParseIP(ipStr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// TODO(a.garipov): Return a *net.UDPAddr when we're HTTP/3-ready.
|
||||
return &net.TCPAddr{IP: ip, Port: port}
|
||||
}
|
||||
|
||||
// isDoH returns true if r.URL.Path contains DNS-over-HTTP paths, and also what
|
||||
// content type is desired by the user. isJSON is true if the user uses the
|
||||
// JSON API. ct can be either MimeTypeDoH or MimeTypeJSON.
|
||||
|
@ -19,6 +19,8 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/lucas-clemente/quic-go/http3"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/http2"
|
||||
@ -30,7 +32,8 @@ func TestServerHTTPS_integration_serveRequests(t *testing.T) {
|
||||
method string
|
||||
tls bool
|
||||
json bool
|
||||
requestWireformat bool
|
||||
reqWireFormat bool
|
||||
http3Enabled bool
|
||||
}{{
|
||||
name: "doh_get_wireformat",
|
||||
method: http.MethodGet,
|
||||
@ -76,23 +79,31 @@ func TestServerHTTPS_integration_serveRequests(t *testing.T) {
|
||||
method: http.MethodGet,
|
||||
tls: true,
|
||||
json: true,
|
||||
requestWireformat: true,
|
||||
reqWireFormat: true,
|
||||
}, {
|
||||
name: "doh_post_json_wireformat",
|
||||
method: http.MethodPost,
|
||||
tls: true,
|
||||
json: true,
|
||||
requestWireformat: true,
|
||||
reqWireFormat: true,
|
||||
}, {
|
||||
name: "doh3_get_wireformat",
|
||||
method: http.MethodGet,
|
||||
tls: true,
|
||||
json: false,
|
||||
http3Enabled: true,
|
||||
}, {
|
||||
name: "doh3_post_wireformat",
|
||||
method: http.MethodPost,
|
||||
tls: true,
|
||||
json: false,
|
||||
http3Enabled: true,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var tlsConfig *tls.Config
|
||||
if tc.tls {
|
||||
tlsConfig = dnsservertest.CreateServerTLSConfig("example.org")
|
||||
}
|
||||
|
||||
srv, addr, err := dnsservertest.RunLocalHTTPSServer(
|
||||
tlsConfig := dnsservertest.CreateServerTLSConfig("example.org")
|
||||
srv, err := dnsservertest.RunLocalHTTPSServer(
|
||||
dnsservertest.DefaultHandler(),
|
||||
tlsConfig,
|
||||
nil,
|
||||
@ -113,16 +124,12 @@ func TestServerHTTPS_integration_serveRequests(t *testing.T) {
|
||||
}
|
||||
|
||||
var resp *dns.Msg
|
||||
resp, err = sendDoHRequest(
|
||||
addr,
|
||||
tlsConfig,
|
||||
tc.method,
|
||||
tc.json,
|
||||
tc.requestWireformat,
|
||||
req,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
addr := srv.LocalTCPAddr()
|
||||
if tc.http3Enabled {
|
||||
addr = srv.LocalUDPAddr()
|
||||
}
|
||||
|
||||
resp = mustDoHReq(t, addr, tlsConfig, tc.method, tc.json, tc.reqWireFormat, req)
|
||||
require.True(t, resp.Response)
|
||||
})
|
||||
}
|
||||
@ -134,7 +141,7 @@ func TestServerHTTPS_integration_nonDNSHandler(t *testing.T) {
|
||||
_, _ = w.Write([]byte("OK"))
|
||||
})
|
||||
|
||||
srv, addr, err := dnsservertest.RunLocalHTTPSServer(
|
||||
srv, err := dnsservertest.RunLocalHTTPSServer(
|
||||
dnsservertest.DefaultHandler(),
|
||||
nil,
|
||||
testHandler,
|
||||
@ -146,7 +153,7 @@ func TestServerHTTPS_integration_nonDNSHandler(t *testing.T) {
|
||||
})
|
||||
|
||||
var resp *http.Response
|
||||
resp, err = http.Get(fmt.Sprintf("http://%s/test", addr))
|
||||
resp, err = http.Get(fmt.Sprintf("http://%s/test", srv.LocalTCPAddr()))
|
||||
defer log.OnCloserError(resp.Body, log.DEBUG)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
@ -307,59 +314,69 @@ func TestDNSMsgToJSONMsg(t *testing.T) {
|
||||
}}, jsonMsg.Extra)
|
||||
}
|
||||
|
||||
func sendDoHRequest(
|
||||
func mustDoHReq(
|
||||
t testing.TB,
|
||||
httpsAddr net.Addr,
|
||||
tlsConfig *tls.Config,
|
||||
method string,
|
||||
json bool,
|
||||
requestWireformat bool,
|
||||
msg *dns.Msg,
|
||||
) (resp *dns.Msg, err error) {
|
||||
var client *http.Client
|
||||
client, err = createDoHClient(httpsAddr, tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req *dns.Msg,
|
||||
) (resp *dns.Msg) {
|
||||
t.Helper()
|
||||
|
||||
client, err := createDoHClient(httpsAddr, tlsConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
proto := "https"
|
||||
if tlsConfig == nil {
|
||||
proto = "http"
|
||||
}
|
||||
|
||||
var req *http.Request
|
||||
var httpReq *http.Request
|
||||
if json {
|
||||
req, err = createJSONRequest(proto, method, requestWireformat, msg)
|
||||
httpReq, err = createJSONRequest(proto, method, requestWireformat, req)
|
||||
} else {
|
||||
req, err = createDoHRequest(proto, method, msg)
|
||||
httpReq, err = createDoHRequest(proto, method, req)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var httpResp *http.Response
|
||||
httpResp, err = client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
httpResp, err := client.Do(httpReq)
|
||||
require.NoError(t, err)
|
||||
defer log.OnCloserError(httpResp.Body, log.DEBUG)
|
||||
|
||||
var body []byte
|
||||
body, err = io.ReadAll(httpResp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if tlsConfig != nil && !httpResp.ProtoAtLeast(2, 0) {
|
||||
t.Fatal(fmt.Errorf("protocol is too old: %s", httpResp.Proto))
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(httpResp.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
if json && !requestWireformat {
|
||||
resp, err = unpackJSONMsg(body)
|
||||
} else {
|
||||
resp, err = unpackDoHMsg(body)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
|
||||
return resp, err
|
||||
return resp
|
||||
}
|
||||
|
||||
func createDoHClient(httpsAddr net.Addr, tlsConfig *tls.Config) (*http.Client, error) {
|
||||
func createDoHClient(httpsAddr net.Addr, tlsConfig *tls.Config) (client *http.Client, err error) {
|
||||
if dnsserver.NetworkFromAddr(httpsAddr) == dnsserver.NetworkUDP {
|
||||
return createDoH3Client(httpsAddr, tlsConfig)
|
||||
}
|
||||
|
||||
return createDoH2Client(httpsAddr, tlsConfig)
|
||||
}
|
||||
|
||||
func createDoH2Client(httpsAddr net.Addr, tlsConfig *tls.Config) (client *http.Client, err error) {
|
||||
if tlsConfig != nil {
|
||||
tlsConfig = tlsConfig.Clone()
|
||||
tlsConfig.NextProtos = []string{"h2", "http/1.1"}
|
||||
}
|
||||
|
||||
dialer := &net.Dialer{
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
@ -371,9 +388,10 @@ func createDoHClient(httpsAddr net.Addr, tlsConfig *tls.Config) (*http.Client, e
|
||||
TLSClientConfig: tlsConfig,
|
||||
DisableCompression: true,
|
||||
DialContext: dialContext,
|
||||
ForceAttemptHTTP2: true,
|
||||
}
|
||||
if tlsConfig != nil {
|
||||
err := http2.ConfigureTransport(transport)
|
||||
err = http2.ConfigureTransport(transport)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -384,6 +402,29 @@ func createDoHClient(httpsAddr net.Addr, tlsConfig *tls.Config) (*http.Client, e
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createDoH3Client(httpsAddr net.Addr, tlsConfig *tls.Config) (client *http.Client, err error) {
|
||||
tlsConfig = tlsConfig.Clone()
|
||||
tlsConfig.NextProtos = []string{"h3"}
|
||||
|
||||
transport := &http3.RoundTripper{
|
||||
DisableCompression: true,
|
||||
Dial: func(
|
||||
ctx context.Context,
|
||||
_ string,
|
||||
tlsCfg *tls.Config,
|
||||
cfg *quic.Config,
|
||||
) (c quic.EarlyConnection, e error) {
|
||||
return quic.DialAddrEarlyContext(ctx, httpsAddr.String(), tlsCfg, cfg)
|
||||
},
|
||||
TLSClientConfig: tlsConfig,
|
||||
}
|
||||
|
||||
return &http.Client{
|
||||
Transport: transport,
|
||||
Timeout: 5 * time.Second,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createDoHRequest(proto, method string, msg *dns.Msg) (r *http.Request, err error) {
|
||||
// Prepare message
|
||||
var buf []byte
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
@ -13,6 +14,7 @@ import (
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/bluele/gcache"
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
@ -27,6 +29,20 @@ const (
|
||||
// value in quic-go is 30, but our internal tests show that a higher
|
||||
// value works better for clients written with ngtcp2.
|
||||
maxQUICIdleTimeout = 5 * time.Minute
|
||||
|
||||
// quicAddrValidatorCacheSize is the size of the cache that we use in the QUIC
|
||||
// address validator. The value is chosen arbitrarily and we should consider
|
||||
// making it configurable.
|
||||
//
|
||||
// TODO(ameshkov): make it configurable after we analyze stats.
|
||||
quicAddrValidatorCacheSize = 10000
|
||||
|
||||
// quicAddrValidatorCacheTTL is time-to-live for cache items in the QUIC address
|
||||
// validator. The value is chosen arbitrarily and we should consider making it
|
||||
// configurable.
|
||||
//
|
||||
// TODO(ameshkov): make it configurable after we analyze stats.
|
||||
quicAddrValidatorCacheTTL = 30 * time.Minute
|
||||
)
|
||||
|
||||
const (
|
||||
@ -76,19 +92,24 @@ func NewServerQUIC(conf ConfigQUIC) (s *ServerQUIC) {
|
||||
}
|
||||
|
||||
s = &ServerQUIC{
|
||||
ServerBase: newServerBase(conf.ConfigBase),
|
||||
ServerBase: newServerBase(ProtoDoQ, conf.ConfigBase),
|
||||
conf: conf,
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// Start starts the ServerQUIC server, exits immediately if it failed to
|
||||
// start listening.
|
||||
// Start implements the dnsserver.Server interface for *ServerQUIC.
|
||||
func (s *ServerQUIC) Start(ctx context.Context) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "starting doq server: %w", err) }()
|
||||
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if s.conf.TLSConfig == nil {
|
||||
return errors.Error("tls config is required")
|
||||
}
|
||||
|
||||
if s.started {
|
||||
return ErrServerAlreadyStarted
|
||||
}
|
||||
@ -120,8 +141,10 @@ func (s *ServerQUIC) Start(ctx context.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown stops the server and waits for all active connections to close.
|
||||
// Shutdown implements the dnsserver.Server interface for *ServerQUIC.
|
||||
func (s *ServerQUIC) Shutdown(ctx context.Context) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "shutting down doq server: %w", err) }()
|
||||
|
||||
log.Info("[%s]: Stopping the server", s.Name())
|
||||
|
||||
err = s.shutdown()
|
||||
@ -164,13 +187,13 @@ func (s *ServerQUIC) startServeQUIC(ctx context.Context) {
|
||||
defer s.handlePanicAndExit(ctx)
|
||||
defer s.wg.Done()
|
||||
|
||||
log.Info("[%s]: Start listening to quic://%s", s.Name(), s.LocalAddr())
|
||||
log.Info("[%s]: Start listening to quic://%s", s.Name(), s.LocalUDPAddr())
|
||||
err := s.serveQUIC(ctx, s.quicListener)
|
||||
if err != nil {
|
||||
log.Info(
|
||||
"[%s]: Finished listening to quic://%s due to %v",
|
||||
s.Name(),
|
||||
s.LocalAddr(),
|
||||
s.LocalUDPAddr(),
|
||||
err,
|
||||
)
|
||||
}
|
||||
@ -418,27 +441,24 @@ func (s *ServerQUIC) readQUICMsg(
|
||||
return m, doqDraft, nil
|
||||
}
|
||||
|
||||
// listenQUIC creates the UDP listener for the ServerQUIC.addr
|
||||
// and also starts the QUIC listener.
|
||||
// listenQUIC creates the UDP listener for the ServerQUIC.addr and also starts
|
||||
// the QUIC listener.
|
||||
func (s *ServerQUIC) listenQUIC(ctx context.Context) (err error) {
|
||||
var l net.PacketConn
|
||||
l, err = listenUDP(ctx, s.addr)
|
||||
// Do not enable OOB here as quic-go will do that on its own.
|
||||
conn, err := listenUDP(ctx, s.addr, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
udpConn, ok := l.(*net.UDPConn)
|
||||
if !ok {
|
||||
return ErrInvalidArgument
|
||||
}
|
||||
qConf := newServerQUICConfig(s.metrics)
|
||||
|
||||
qConf := &quic.Config{MaxIdleTimeout: maxQUICIdleTimeout}
|
||||
ql, err := quic.Listen(l, s.conf.TLSConfig, qConf)
|
||||
// Do not change to quic.ListenEarly, see quicNotEarlyListener to know why.
|
||||
ql, err := quic.Listen(conn, s.conf.TLSConfig, qConf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.udpListener = udpConn
|
||||
s.udpListener = conn
|
||||
s.quicListener = ql
|
||||
|
||||
return nil
|
||||
@ -470,16 +490,10 @@ func isExpectedQUICErr(err error) (ok bool) {
|
||||
return true
|
||||
}
|
||||
|
||||
// We have to use error string since quic-go keeps it's error structs in the
|
||||
// internal package
|
||||
str := err.Error()
|
||||
|
||||
// Expected to be returned by all streams and connection methods calls when
|
||||
// the server is closed. Unfortunately, this error is not exported from
|
||||
// quic-go.
|
||||
//
|
||||
// TODO(ameshkov): Make a pull request to quic-go about this.
|
||||
if strings.Contains(str, "server closed") {
|
||||
if errors.Is(err, quic.ErrServerClosed) {
|
||||
return true
|
||||
}
|
||||
|
||||
@ -571,3 +585,74 @@ func closeQUICConn(conn quic.Connection, code quic.ApplicationErrorCode) {
|
||||
log.Debug("failed to close the QUIC connection: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// newServerQUICConfig creates *quic.Config populated with the default settings.
|
||||
// This function is supposed to be used for both DoQ and DoH3 server.
|
||||
func newServerQUICConfig(metrics MetricsListener) (conf *quic.Config) {
|
||||
v := newQUICAddrValidator(quicAddrValidatorCacheSize, quicAddrValidatorCacheTTL, metrics)
|
||||
|
||||
return &quic.Config{
|
||||
MaxIdleTimeout: maxQUICIdleTimeout,
|
||||
RequireAddressValidation: v.requiresValidation,
|
||||
MaxIncomingStreams: math.MaxUint16,
|
||||
MaxIncomingUniStreams: math.MaxUint16,
|
||||
}
|
||||
}
|
||||
|
||||
// quicAddrValidator is a helper struct that holds a small LRU cache of
|
||||
// addresses for which we do not require address validation.
|
||||
type quicAddrValidator struct {
|
||||
cache gcache.Cache
|
||||
ttl time.Duration
|
||||
metrics MetricsListener
|
||||
}
|
||||
|
||||
// newQUICAddrValidator initializes a new instance of *quicAddrValidator.
|
||||
func newQUICAddrValidator(
|
||||
cacheSize int,
|
||||
ttl time.Duration,
|
||||
metrics MetricsListener,
|
||||
) (v *quicAddrValidator) {
|
||||
return &quicAddrValidator{
|
||||
cache: gcache.New(cacheSize).LRU().Build(),
|
||||
ttl: ttl,
|
||||
metrics: metrics,
|
||||
}
|
||||
}
|
||||
|
||||
// requiresValidation determines if a QUIC Retry packet should be sent by the
|
||||
// client. This allows the server to verify the client's address but increases
|
||||
// the latency.
|
||||
//
|
||||
// TODO(ameshkov): consider caddy-like implementation here.
|
||||
func (v *quicAddrValidator) requiresValidation(addr net.Addr) (ok bool) {
|
||||
udpAddr, ok := addr.(*net.UDPAddr)
|
||||
if !ok {
|
||||
// Report this as an error as this is not the expected behavior here.
|
||||
v.metrics.OnError(
|
||||
context.Background(),
|
||||
fmt.Errorf("not a udp address: %v", addr),
|
||||
)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
key := udpAddr.IP.String()
|
||||
if v.cache.Has(key) {
|
||||
v.metrics.OnQUICAddressValidation(true)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
v.metrics.OnQUICAddressValidation(false)
|
||||
|
||||
err := v.cache.SetWithExpire(key, true, v.ttl)
|
||||
if err != nil {
|
||||
// Shouldn't happen, since we don't set a serialization function.
|
||||
panic(fmt.Errorf("quic validator: setting cache item: %w", err))
|
||||
}
|
||||
|
||||
// Address not found in the cache so return true to make sure the server
|
||||
// will require address validation.
|
||||
return true
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
|
||||
@ -30,7 +31,7 @@ var _ Server = (*ServerTLS)(nil)
|
||||
|
||||
// NewServerTLS creates a new ServerTLS instance.
|
||||
func NewServerTLS(conf ConfigTLS) (s *ServerTLS) {
|
||||
srv := newServerDNS(conf.ConfigDNS)
|
||||
srv := newServerDNS(ProtoDoT, conf.ConfigDNS)
|
||||
s = &ServerTLS{
|
||||
ServerDNS: srv,
|
||||
conf: conf,
|
||||
@ -39,11 +40,17 @@ func NewServerTLS(conf ConfigTLS) (s *ServerTLS) {
|
||||
return s
|
||||
}
|
||||
|
||||
// Start starts the TLS listener and starts processing queries.
|
||||
// Start implements the dnsserver.Server interface for *ServerTLS.
|
||||
func (s *ServerTLS) Start(ctx context.Context) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "starting dot server: %w", err) }()
|
||||
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if s.conf.TLSConfig == nil {
|
||||
return errors.Error("tls config is required")
|
||||
}
|
||||
|
||||
// TODO(ameshkov): Consider only setting s.started to true once the
|
||||
// listeners are up.
|
||||
if s.started {
|
||||
@ -75,6 +82,13 @@ func (s *ServerTLS) Start(ctx context.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown implements the dnsserver.Server interface for *ServerTLS.
|
||||
func (s *ServerTLS) Shutdown(ctx context.Context) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "shutting down dot server: %w", err) }()
|
||||
|
||||
return s.ServerDNS.Shutdown(ctx)
|
||||
}
|
||||
|
||||
// startServeTCP starts the TCP listen loop and handles errors if any.
|
||||
func (s *ServerTLS) startServeTCP(ctx context.Context) {
|
||||
// We do not recover from panics here since if this go routine panics
|
||||
|
@ -1,7 +1,6 @@
|
||||
package dnsserver_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
@ -12,23 +11,13 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
|
||||
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestServerTLS_integration_queryTLS(t *testing.T) {
|
||||
tlsConfig := dnsservertest.CreateServerTLSConfig("example.org")
|
||||
srv, addr, err := dnsservertest.RunLocalTLSServer(
|
||||
dnsservertest.DefaultHandler(),
|
||||
tlsConfig,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, dnsserver.ProtoDoT, srv.Proto())
|
||||
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return srv.Shutdown(context.Background())
|
||||
})
|
||||
addr := dnsservertest.RunTLSServer(t, dnsservertest.DefaultHandler(), tlsConfig)
|
||||
|
||||
// Create a test message
|
||||
req := new(dns.Msg)
|
||||
@ -97,16 +86,8 @@ func TestServerTLS_integration_msgIgnore(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tlsConfig := dnsservertest.CreateServerTLSConfig("example.org")
|
||||
srv, addr, err := dnsservertest.RunLocalTLSServer(
|
||||
dnsservertest.DefaultHandler(),
|
||||
tlsConfig,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, dnsserver.ProtoDoT, srv.Proto())
|
||||
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return srv.Shutdown(context.Background())
|
||||
})
|
||||
h := dnsservertest.DefaultHandler()
|
||||
addr := dnsservertest.RunTLSServer(t, h, tlsConfig)
|
||||
|
||||
conn, err := tls.Dial("tcp", addr.String(), tlsConfig)
|
||||
require.Nil(t, err)
|
||||
@ -134,12 +115,7 @@ func TestServerTLS_integration_noTruncateQuery(t *testing.T) {
|
||||
handler := dnsservertest.CreateTestHandler(64)
|
||||
|
||||
tlsConfig := dnsservertest.CreateServerTLSConfig("example.org")
|
||||
srv, addr, err := dnsservertest.RunLocalTLSServer(handler, tlsConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return srv.Shutdown(context.Background())
|
||||
})
|
||||
addr := dnsservertest.RunTLSServer(t, handler, tlsConfig)
|
||||
|
||||
// Create a test message
|
||||
req := new(dns.Msg)
|
||||
@ -167,16 +143,7 @@ func TestServerTLS_integration_queriesPipelining(t *testing.T) {
|
||||
// i.e. we should be able to process incoming queries in parallel and
|
||||
// write responses out of order.
|
||||
tlsConfig := dnsservertest.CreateServerTLSConfig("example.org")
|
||||
srv, addr, err := dnsservertest.RunLocalTLSServer(
|
||||
dnsservertest.DefaultHandler(),
|
||||
tlsConfig,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, dnsserver.ProtoDoT, srv.Proto())
|
||||
|
||||
testutil.CleanupAndRequireSuccess(t, func() (err error) {
|
||||
return srv.Shutdown(context.Background())
|
||||
})
|
||||
addr := dnsservertest.RunTLSServer(t, dnsservertest.DefaultHandler(), tlsConfig)
|
||||
|
||||
// First - establish a connection
|
||||
conn, err := tls.Dial("tcp", addr.String(), tlsConfig)
|
||||
|
@ -114,11 +114,11 @@ var _ errors.Wrapper = (*deviceIDError)(nil)
|
||||
func (err *deviceIDError) Unwrap() (unwrapped error) { return err.err }
|
||||
|
||||
// type check
|
||||
var _ errcoll.RavenReportableError = (*deviceIDError)(nil)
|
||||
var _ errcoll.SentryReportableError = (*deviceIDError)(nil)
|
||||
|
||||
// IsRavenReportable implements the errcoll.RavenReportableError interface for
|
||||
// IsSentryReportable implements the errcoll.SentryReportableError interface for
|
||||
// *deviceIDError.
|
||||
func (err *deviceIDError) IsRavenReportable() (ok bool) { return false }
|
||||
func (err *deviceIDError) IsSentryReportable() (ok bool) { return false }
|
||||
|
||||
// deviceIDFromContext extracts the device from the server name of the TLS
|
||||
// client's DoH, DoT, or DoQ request, using the provided domain name wildcards,
|
||||
|
@ -24,12 +24,12 @@ func TestService_Wrap_deviceID(t *testing.T) {
|
||||
wildcards []string
|
||||
proto agd.Protocol
|
||||
}{{
|
||||
name: "udp",
|
||||
name: "dns",
|
||||
cliSrvName: "",
|
||||
wantDeviceID: "",
|
||||
wantErrMsg: "",
|
||||
wildcards: nil,
|
||||
proto: agd.ProtoDNSUDP,
|
||||
proto: agd.ProtoDNS,
|
||||
}, {
|
||||
name: "tls_no_device_id",
|
||||
cliSrvName: "dns.example.com",
|
||||
|
@ -345,14 +345,7 @@ type listener struct {
|
||||
|
||||
// listenerName returns a standard name for a listener.
|
||||
func listenerName(srvName agd.ServerName, addr netip.AddrPort, p agd.Protocol) (name string) {
|
||||
switch p {
|
||||
case agd.ProtoDNSTCP, agd.ProtoDNSCryptTCP:
|
||||
return fmt.Sprintf("%s/tcp/%s", srvName, addr)
|
||||
case agd.ProtoDNSUDP, agd.ProtoDNSCryptUDP:
|
||||
return fmt.Sprintf("%s/udp/%s", srvName, addr)
|
||||
default:
|
||||
return fmt.Sprintf("%s/%s", srvName, addr)
|
||||
}
|
||||
return fmt.Sprintf("%s/%s/%s", srvName, p, addr)
|
||||
}
|
||||
|
||||
// NewListener returns a new Listener. It is the default DNS listener
|
||||
@ -378,16 +371,15 @@ func NewListener(
|
||||
confBase := dnsserver.ConfigBase{
|
||||
Name: name,
|
||||
Addr: addrStr,
|
||||
Proto: s.Protocol,
|
||||
Handler: h,
|
||||
BaseContext: ctxWithReqID,
|
||||
Metrics: metricsListener,
|
||||
}
|
||||
|
||||
switch p := s.Protocol; p {
|
||||
case agd.ProtoDNSTCP, agd.ProtoDNSUDP:
|
||||
case agd.ProtoDNS:
|
||||
l = dnsserver.NewServerDNS(dnsserver.ConfigDNS{ConfigBase: confBase})
|
||||
case agd.ProtoDNSCryptTCP, agd.ProtoDNSCryptUDP:
|
||||
case agd.ProtoDNSCrypt:
|
||||
l = dnsserver.NewServerDNSCrypt(dnsserver.ConfigDNSCrypt{
|
||||
ConfigBase: confBase,
|
||||
DNSCryptProviderName: dcConf.ProviderName,
|
||||
@ -443,8 +435,8 @@ func newServers(
|
||||
// validate fg.
|
||||
fg := c.FilteringGroups[srvGrp.FilteringGroup]
|
||||
|
||||
// Only apply rate-limiting logic to plain DNS over UDP.
|
||||
rlProtos := []agd.Protocol{agd.ProtoDNSUDP}
|
||||
// Only apply rate-limiting logic to plain DNS.
|
||||
rlProtos := []agd.Protocol{agd.ProtoDNS}
|
||||
|
||||
var rlm *ratelimit.Middleware
|
||||
rlm, err = ratelimit.NewMiddleware(c.RateLimit, rlProtos)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user