Sync v2.1.1

This commit is contained in:
Andrey Meshkov 2022-11-07 10:21:24 +03:00
parent 328512e850
commit 5de1a253fc
138 changed files with 2756 additions and 2647 deletions

1
.gitignore vendored
View File

@ -18,3 +18,4 @@ config.yml
country.mmdb
dnsdb.bolt
querylog.jsonl
profilecache.json

View File

@ -11,26 +11,64 @@ 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
```yaml
static_content:
'/favicon.ico':
allow_origin: '*'
```
```
## 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:
test_key: 'test_value'
```
```yaml
additional_metrics_info:
test_key: 'test_value'
```

View File

@ -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

View File

@ -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:**

View File

@ -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' \

View File

@ -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

View File

@ -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>

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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=

View File

@ -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.

View File

@ -20,14 +20,12 @@ const (
// packages and modules may depend on the numerical values. These numerical
// values are a part of the API.
ProtoInvalid = dnsserver.ProtoInvalid
ProtoDNSTCP = dnsserver.ProtoDNSTCP
ProtoDNSUDP = dnsserver.ProtoDNSUDP
ProtoDoH = dnsserver.ProtoDoH
ProtoDoQ = dnsserver.ProtoDoQ
ProtoDoT = dnsserver.ProtoDoT
ProtoDNSCryptTCP = dnsserver.ProtoDNSCryptTCP
ProtoDNSCryptUDP = dnsserver.ProtoDNSCryptUDP
ProtoInvalid = dnsserver.ProtoInvalid
ProtoDNS = dnsserver.ProtoDNS
ProtoDoH = dnsserver.ProtoDoH
ProtoDoQ = dnsserver.ProtoDoQ
ProtoDoT = dnsserver.ProtoDoT
ProtoDNSCrypt = dnsserver.ProtoDNSCrypt
)
// Resolver is the DNS resolver interface.

View File

@ -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.

View File

@ -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()

View File

@ -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

View File

@ -13,73 +13,77 @@ 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 (
testIvl = 5 * time.Millisecond
testIvlLong = 1 * time.Hour
name = "test refresher"
testError errors.Error = "test error"
)
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
},
}
// newRefrConf returns worker configuration.
func newRefrConf(
t *testing.T,
refr agd.Refresher,
ivl time.Duration,
refrOnShutDown bool,
errCh chan sig,
) (conf *agd.RefreshWorkerConfig) {
t.Helper()
pt := testutil.PanicT{}
errCh := make(chan agdtest.Signal, 1)
errColl := &agdtest.ErrorCollector{
OnCollect: func(_ context.Context, _ error) {
agdtest.RequireSend(pt, errCh, testTimeout)
testutil.RequireSend(pt, errCh, sig{}, testTimeout)
},
}
// Test Helpers
refrConf := func(
refr agd.Refresher,
ivl time.Duration,
refrOnShutDown bool,
) (conf *agd.RefreshWorkerConfig) {
return &agd.RefreshWorkerConfig{
Context: func() (ctx context.Context, cancel context.CancelFunc) {
return context.WithTimeout(context.Background(), testTimeout)
},
Refresher: refr,
ErrColl: errColl,
Name: name,
Interval: ivl,
RefreshOnShutdown: refrOnShutDown,
RoutineLogsAreDebug: false,
}
return &agd.RefreshWorkerConfig{
Context: func() (ctx context.Context, cancel context.CancelFunc) {
return context.WithTimeout(context.Background(), testTimeout)
},
Refresher: refr,
ErrColl: errColl,
Name: name,
Interval: ivl,
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)
})
}

View File

@ -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

View File

@ -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))

View File

@ -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
View 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"
}

View 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)
})
}
}

View File

@ -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())
}

View File

@ -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)

View File

@ -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()
}
}

View File

@ -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)
}

View File

@ -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.

View File

@ -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
}

View File

@ -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.

View File

@ -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())

View File

@ -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),
}
}

View File

@ -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))

View File

@ -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,

View File

@ -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
@ -34,14 +34,15 @@ type environments struct {
YoutubeSafeSearchURL *agdhttp.URL `env:"YOUTUBE_SAFE_SEARCH_URL,notEmpty"`
RuleStatURL *agdhttp.URL `env:"RULESTAT_URL"`
ConfPath string `env:"CONFIG_PATH" envDefault:"./config.yml"`
DNSDBPath string `env:"DNSDB_PATH" envDefault:"./dnsdb.bolt"`
FilterCachePath string `env:"FILTER_CACHE_PATH" envDefault:"./filters/"`
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"`
SentryDSN string `env:"SENTRY_DSN" envDefault:"stderr"`
SSLKeyLogFile string `env:"SSL_KEY_LOG_FILE"`
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"`
SentryDSN string `env:"SENTRY_DSN" envDefault:"stderr"`
SSLKeyLogFile string `env:"SSL_KEY_LOG_FILE"`
ListenAddr net.IP `env:"LISTEN_ADDR" envDefault:"127.0.0.1"`
@ -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

View File

@ -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.

View File

@ -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:

View File

@ -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 {

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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",

View File

@ -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)
}

View File

@ -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))

View File

@ -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 {

View File

@ -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)
}

View File

@ -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",

View File

@ -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() {

View File

@ -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
}

View File

@ -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)

View File

@ -20,17 +20,18 @@ type Handler interface {
// specified ResponseWriter.
//
// It accepts context.Context argument which has some additional info
// attached to it. This context always contains ServerInfo which can be
// 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

View File

@ -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)
}

View File

@ -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())
}

View 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)
}

View 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)
}

View File

@ -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{

View File

@ -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
}

View File

@ -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

View File

@ -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,
},
}

View File

@ -12,11 +12,11 @@ import (
func ExampleNewHandler() {
conf := dnsserver.ConfigDNS{
ConfigBase: dnsserver.ConfigBase{
Name: "srv",
Addr: "127.0.0.1:0",
Proto: dnsserver.ProtoDNSUDP,
Name: "srv",
Addr: "127.0.0.1:0",
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"),
},

View File

@ -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,

View File

@ -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()

View File

@ -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))

View File

@ -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)

View File

@ -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

View File

@ -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=

View File

@ -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.

View File

@ -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.

View File

@ -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) {
}

View File

@ -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)
}

View File

@ -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.

View File

@ -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"

View File

@ -14,22 +14,25 @@ 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.
- "dns_server_request_total" is the number of processed DNS requests. Labels
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.
- "dns_server_panic_total" is the number of panics occurred in the DNS server.
- "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
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:

View File

@ -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

View File

@ -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"})
)

View File

@ -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)

View File

@ -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")
)

View File

@ -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",

View File

@ -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

View File

@ -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))

View File

@ -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(),

View File

@ -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

View File

@ -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)

View File

@ -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 {

View File

@ -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,101 +21,90 @@ 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
m := new(dns.Msg)
m.SetQuestion("example.org.", dns.TypeA)
msg, _ := m.Pack()
// Prepare a test message.
m := new(dns.Msg)
m.SetQuestion("example.org.", dns.TypeA)
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))
require.NoError(b, err)
// 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
resBuf := make([]byte, 512)
// 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)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err = conn.Write(msg)
require.NoError(b, err)
var n int
n, err = conn.Read(resBuf)
require.NoError(b, err)
require.GreaterOrEqual(b, n, dnsserver.DNSHeaderSize)
err = readMsg(resBuf, tc.network, conn)
require.NoError(b, err)
}
b.StopTimer()
})
}
b.StopTimer()
}
func BenchmarkServeTCP(b *testing.B) {
srv, _, err := dnsservertest.RunLocalDNSServer(
dnsservertest.DefaultHandler(),
dnsserver.ProtoDNSTCP)
require.NoError(b, 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) }()
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 n int
if network == dnsserver.NetworkTCP {
var length uint16
if err = binary.Read(conn, binary.BigEndian, &length); err != nil {
b.Fatalf("failed to read the DNS query response: %v", err)
return 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)
return err
}
} else {
n, err = conn.Read(resBuf)
if err != nil {
return err
}
require.GreaterOrEqual(b, n, dnsserver.DNSHeaderSize)
}
b.StopTimer()
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,103 +148,142 @@ func BenchmarkServeTLS(b *testing.B) {
b.StopTimer()
}
func BenchmarkServeHTTPS(b *testing.B) {
proto := "https"
tlsConfig := dnsservertest.CreateServerTLSConfig("example.org")
srv, addr, err := dnsservertest.RunLocalHTTPSServer(
dnsservertest.DefaultHandler(),
tlsConfig,
nil,
)
require.NoError(b, err)
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,
}}
testutil.CleanupAndRequireSuccess(b, func() (err error) {
return srv.Shutdown(context.Background())
})
for _, tc := range testCases {
b.Run(tc.name, func(b *testing.B) {
proto := "https"
if !tc.https {
proto = "http"
}
// 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)
var tlsConfig *tls.Config
if tc.https {
tlsConfig = dnsservertest.CreateServerTLSConfig("example.org")
}
srv, err := dnsservertest.RunLocalHTTPSServer(
dnsservertest.DefaultHandler(),
tlsConfig,
nil,
)
require.NoError(b, err)
// Prepare client
client, err := createDoHClient(addr, tlsConfig)
require.NoError(b, err)
testutil.CleanupAndRequireSuccess(b, func() (err error) {
return srv.Shutdown(context.Background())
})
// Prepare http.Request
req, err := createDoHRequest(proto, http.MethodPost, m)
require.NoError(b, err)
// 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)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var res *http.Response
res, err = client.Do(req)
require.NoError(b, err)
// Prepare client.
addr := srv.LocalTCPAddr()
if tc.http3Enabled {
addr = srv.LocalUDPAddr()
}
var buf []byte
buf, err = io.ReadAll(res.Body)
_ = res.Body.Close()
require.NoError(b, err)
require.GreaterOrEqual(b, len(buf), dnsserver.DNSHeaderSize)
client, err := createDoHClient(addr, tlsConfig)
require.NoError(b, err)
// Prepare http.Request.
req, err := createDoHRequest(proto, http.MethodPost, m)
require.NoError(b, err)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var res *http.Response
res, err = client.Do(req)
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)
}
b.StopTimer()
})
}
b.StopTimer()
}
func BenchmarkServePlainHTTP(b *testing.B) {
proto := "http"
srv, addr, err := dnsservertest.RunLocalHTTPSServer(
dnsservertest.DefaultHandler(),
nil,
nil,
)
require.NoError(b, err)
func BenchmarkServeDNSCrypt(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) {
// 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},
}
// 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)
client := &dnscrypt.Client{
Timeout: 1 * time.Second,
Net: string(tc.network),
}
// Prepare client
client, err := createDoHClient(addr, nil)
require.NoError(b, err)
s := dnsservertest.RunDNSCryptServer(b, dnsservertest.DefaultHandler())
stamp := dnsstamps.ServerStamp{
ServerAddrStr: s.ServerAddr,
ServerPk: s.ResolverPk,
ProviderName: s.ProviderName,
Proto: dnsstamps.StampProtoTypeDNSCrypt,
}
// Prepare http.Request
req, err := createDoHRequest(proto, http.MethodPost, m)
require.NoError(b, err)
// Load server info
ri, err := client.DialStamp(stamp)
require.NoError(b, err)
require.NotNil(b, ri)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var res *http.Response
res, err = client.Do(req)
require.NoError(b, err)
// Open a single connection
conn, err := net.Dial(string(tc.network), stamp.ServerAddrStr)
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)
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()
})
}
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()
}

View File

@ -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)

View File

@ -12,29 +12,19 @@ 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
req *dns.Msg
name string
network dnsserver.Network
req *dns.Msg
// if nil, use defaultTestHandler
handler dnsserver.Handler
expectedRecordsCount int
@ -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{
@ -178,8 +168,8 @@ 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,
name: "udp_truncate_response",
network: dnsserver.NetworkUDP,
// Set a handler that generates a large response
handler: dnsservertest.CreateTestHandler(64),
expectedRecordsCount: 0,
@ -194,8 +184,8 @@ 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,
name: "udp_edns0_no_truncate",
network: dnsserver.NetworkUDP,
// Set a handler that generates a large response
handler: dnsservertest.CreateTestHandler(64),
expectedRecordsCount: 64,
@ -219,8 +209,8 @@ 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,
name: "tcp_no_truncate_response",
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)

View File

@ -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,23 +78,9 @@ func (s *ServerDNSCrypt) Start(ctx context.Context) (err error) {
},
}
switch s.proto {
case ProtoDNSCryptUDP:
err = s.listenUDP(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
err = s.startServe(ctx)
if err != nil {
return err
}
log.Info("[%s]: Server has been started", s.Name())
@ -96,8 +88,10 @@ func (s *ServerDNSCrypt) 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 *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
}

View File

@ -1,7 +1,6 @@
package dnsserver_test
import (
"context"
"testing"
"time"
@ -15,9 +14,9 @@ import (
func TestServerDNSCrypt_integration_query(t *testing.T) {
testCases := []struct {
name string
proto dnsserver.Protocol
req *dns.Msg
name string
network dnsserver.Network
req *dns.Msg
// if nil, use DefaultTestHandler
handler dnsserver.Handler
expectedRecordsCount int
@ -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{
@ -48,8 +47,8 @@ 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,
name: "udp_truncate_response",
network: dnsserver.NetworkUDP,
// Set a handler that generates a large response
handler: dnsservertest.CreateTestHandler(64),
// DNSCrypt server removes all records from a truncated response
@ -65,8 +64,8 @@ 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,
name: "udp_edns0_no_truncate",
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
}

View File

@ -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
}

View File

@ -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)

View File

@ -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 err != nil {
return err
if s.proto != ProtoDoH {
return ErrInvalidArgument
}
// Prepare and run the HTTP server
handler := &httpHandler{
srv: s,
listener: s.tcpListener,
if s.network.CanTCP() {
err = s.startHTTPSServer(ctx)
if err != nil {
return err
}
}
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
srv *ServerHTTPS
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.

View File

@ -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"
@ -26,11 +28,12 @@ import (
func TestServerHTTPS_integration_serveRequests(t *testing.T) {
testCases := []struct {
name string
method string
tls bool
json bool
requestWireformat bool
name string
method string
tls bool
json bool
reqWireFormat bool
http3Enabled bool
}{{
name: "doh_get_wireformat",
method: http.MethodGet,
@ -72,27 +75,35 @@ func TestServerHTTPS_integration_serveRequests(t *testing.T) {
tls: false,
json: true,
}, {
name: "doh_get_json_wireformat",
method: http.MethodGet,
tls: true,
json: true,
requestWireformat: true,
name: "doh_get_json_wireformat",
method: http.MethodGet,
tls: true,
json: true,
reqWireFormat: true,
}, {
name: "doh_post_json_wireformat",
method: http.MethodPost,
tls: true,
json: true,
requestWireformat: true,
name: "doh_post_json_wireformat",
method: http.MethodPost,
tls: true,
json: 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

View File

@ -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
}

View File

@ -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

View File

@ -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)

View File

@ -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,

View File

@ -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",

View File

@ -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