Sync v2.11.0

This commit is contained in:
Andrey Meshkov 2024-12-05 14:19:25 +03:00
parent 87137bddcf
commit f1791135af
184 changed files with 9258 additions and 7906 deletions

View File

@ -7,6 +7,47 @@ The format is **not** based on [Keep a Changelog][kec], since the project **does
[kec]: https://keepachangelog.com/en/1.0.0/ [kec]: https://keepachangelog.com/en/1.0.0/
[sem]: https://semver.org/spec/v2.0.0.html [sem]: https://semver.org/spec/v2.0.0.html
## AGDNS-2507 / Build 926
- Profile's file cache version was incremented. The file cache structure has been optimized, so messages like the following are to be expected:
```none
profiledb: warning: error loading fs cache err="decoding protobuf: proto: cannot parse invalid wire-format data"
```
## AGDNS-2327 / Build 916
- Profile's file cache version was incremented. The new field `BlockChromePrefetch` has been added to profile's object.
- The objects within the `filtering_groups` have a new property, `block_chrome_prefetch`. So replace this:
```yaml
filtering_groups:
-
id: default
# …
block_firefox_canary: true
block_private_relay: true
```
with this:
```yaml
filtering_groups:
-
id: default
# …
block_chrome_prefetch: true
block_firefox_canary: true
block_private_relay: true
```
## AGDNS-2514 / Build 908
- The environment variable `DNSCHECK_CACHE_KV_SIZE` has been added.
- The property `kv.type` within the `check` object now supports the `cache` value.
## AGDNS-2484/ Build 886 ## AGDNS-2484/ Build 886
- Property `type` of the `ratelimit` object has been moved to the underlying `allowlist` object. So replace this: - Property `type` of the `ratelimit` object has been moved to the underlying `allowlist` object. So replace this:

View File

@ -24,7 +24,7 @@ BRANCH = $${BRANCH:-$$(git rev-parse --abbrev-ref HEAD)}
GOAMD64 = v1 GOAMD64 = v1
GOPROXY = https://proxy.golang.org|direct GOPROXY = https://proxy.golang.org|direct
GOTELEMETRY = off GOTELEMETRY = off
GOTOOLCHAIN = go1.23.2 GOTOOLCHAIN = go1.23.4
RACE = 0 RACE = 0
REVISION = $${REVISION:-$$(git rev-parse --short HEAD)} REVISION = $${REVISION:-$$(git rev-parse --short HEAD)}
VERSION = 0 VERSION = 0

View File

@ -175,8 +175,8 @@ check:
# Domains to use for DNS checking. # Domains to use for DNS checking.
kv: kv:
# Defines the type of remote key-value storage. Allowed values are # Defines the type of remote key-value storage. Allowed values are
# "backend", "consul", and "redis". # "backend", "cache", "consul", and "redis".
type: 'consul' type: 'cache'
# For how long to keep the information about the client. # For how long to keep the information about the client.
ttl: 30s ttl: 30s
# Domains to use for DNS checking. # Domains to use for DNS checking.
@ -341,8 +341,9 @@ filtering_groups:
enabled: true enabled: true
block_dangerous_domains: true block_dangerous_domains: true
block_newly_registered_domains: false block_newly_registered_domains: false
block_private_relay: false block_chrome_prefetch: true
block_firefox_canary: true block_firefox_canary: true
block_private_relay: false
- id: 'family' - id: 'family'
parental: parental:
enabled: true enabled: true
@ -357,8 +358,9 @@ filtering_groups:
enabled: true enabled: true
block_dangerous_domains: true block_dangerous_domains: true
block_newly_registered_domains: false block_newly_registered_domains: false
block_private_relay: false block_chrome_prefetch: true
block_firefox_canary: true block_firefox_canary: true
block_private_relay: false
- id: 'non_filtering' - id: 'non_filtering'
rule_lists: rule_lists:
enabled: false enabled: false
@ -368,8 +370,9 @@ filtering_groups:
enabled: false enabled: false
block_dangerous_domains: true block_dangerous_domains: true
block_newly_registered_domains: false block_newly_registered_domains: false
block_private_relay: false block_chrome_prefetch: false
block_firefox_canary: true block_firefox_canary: true
block_private_relay: false
# The configuration for the device-listening feature. Works only on Linux with # The configuration for the device-listening feature. Works only on Linux with
# SO_BINDTODEVICE support. # SO_BINDTODEVICE support.

View File

@ -378,7 +378,7 @@ The `check` object has the following properties:
- <a href="#check_kv" id="check_kv" name="check_kv">`kv`</a>: Remote key-value storage settings. It has the following properties: - <a href="#check_kv" id="check_kv" name="check_kv">`kv`</a>: Remote key-value storage settings. It has the following properties:
- <a href="#check-kv-type" id="check-kv-type" name="check-kv-type">`type`</a>: Type of the remote KV storage. Allowed values are `backend`, `consul`, and `redis`. - <a href="#check-kv-type" id="check-kv-type" name="check-kv-type">`type`</a>: Type of the remote KV storage. Allowed values are `backend`, `cache`, `consul`, and `redis`.
**Example:** `consul`. **Example:** `consul`.
@ -386,6 +386,8 @@ The `check` object has the following properties:
For `backend`, the TTL must be greater than `0s`. For `backend`, the TTL must be greater than `0s`.
For `cache`, the TTL is not used.
For `consul`, the TTL must be between `10s` and `1d`. Note that the actual TTL can be up to twice as long. For `consul`, the TTL must be between `10s` and `1d`. Note that the actual TTL can be up to twice as long.
For `redis`, the TTL must be greater than or equal to `1ms`. For `redis`, the TTL must be greater than or equal to `1ms`.
@ -662,14 +664,18 @@ The items of the `filtering_groups` array have the following properties:
**Example:** `true`. **Example:** `true`.
- <a href="#fg-*-block_private_relay" id="fg-*-block_private_relay" name="fg-*-block_private_relay">`private_relay`</a>: If true, Apple Private Relay queries are blocked for requests using this filtering group. - <a href="#fg-*-block_chrome_prefetch" id="fg-*-block_chrome_prefetch" name="fg-*-block_chrome_prefetch">`block_chrome_prefetch`</a>: If true, Chrome prefetch domain queries are blocked for requests using this filtering group, forcing the preferch proxy into preflight mode.
**Example:** `false`. **Example:** `true`.
- <a href="#fg-*-block_firefox_canary" id="fg-*-block_firefox_canary" name="fg-*-block_firefox_canary">`block_firefox_canary`</a>: If true, Firefox canary domain queries are blocked for requests using this filtering group. - <a href="#fg-*-block_firefox_canary" id="fg-*-block_firefox_canary" name="fg-*-block_firefox_canary">`block_firefox_canary`</a>: If true, Firefox canary domain queries are blocked for requests using this filtering group.
**Example:** `true`. **Example:** `true`.
- <a href="#fg-*-block_private_relay" id="fg-*-block_private_relay" name="fg-*-block_private_relay">`private_relay`</a>: If true, Apple Private Relay queries are blocked for requests using this filtering group.
**Example:** `false`.
## <a href="#interface_listeners" id="interface_listeners" name="interface_listeners">Network interface listeners</a> ## <a href="#interface_listeners" id="interface_listeners" name="interface_listeners">Network interface listeners</a>
> [!NOTE] > [!NOTE]

View File

@ -112,6 +112,7 @@ Supported IDs:
- `profiledb` - `profiledb`
- `rulestat` - `rulestat`
- `ticket_rotator` - `ticket_rotator`
- `tlsconfig`
The special ID `*`, when used alone, causes all available refresh tasks to be performed. Use with caution. The special ID `*`, when used alone, causes all available refresh tasks to be performed. Use with caution.

View File

@ -16,6 +16,7 @@ AdGuard DNS uses [environment variables][wiki-env] to store some of the more sen
- [`CONSUL_ALLOWLIST_URL`](#CONSUL_ALLOWLIST_URL) - [`CONSUL_ALLOWLIST_URL`](#CONSUL_ALLOWLIST_URL)
- [`CONSUL_DNSCHECK_KV_URL`](#CONSUL_DNSCHECK_KV_URL) - [`CONSUL_DNSCHECK_KV_URL`](#CONSUL_DNSCHECK_KV_URL)
- [`CONSUL_DNSCHECK_SESSION_URL`](#CONSUL_DNSCHECK_SESSION_URL) - [`CONSUL_DNSCHECK_SESSION_URL`](#CONSUL_DNSCHECK_SESSION_URL)
- [`DNSCHECK_CACHE_KV_SIZE`](#DNSCHECK_CACHE_KV_SIZE)
- [`DNSCHECK_REMOTEKV_API_KEY`](#DNSCHECK_REMOTEKV_API_KEY) - [`DNSCHECK_REMOTEKV_API_KEY`](#DNSCHECK_REMOTEKV_API_KEY)
- [`DNSCHECK_REMOTEKV_URL`](#DNSCHECK_REMOTEKV_URL) - [`DNSCHECK_REMOTEKV_URL`](#DNSCHECK_REMOTEKV_URL)
- [`FILTER_CACHE_PATH`](#FILTER_CACHE_PATH) - [`FILTER_CACHE_PATH`](#FILTER_CACHE_PATH)
@ -142,6 +143,16 @@ The HTTP(S) URL of the session API of the Consul instance used as a key-value da
**Example:** `http://localhost:8500/v1/session/create` **Example:** `http://localhost:8500/v1/session/create`
## <a href="#DNSCHECK_CACHE_KV_SIZE" id="DNSCHECK_CACHE_KV_SIZE" name="DNSCHECK_CACHE_KV_SIZE">`DNSCHECK_CACHE_KV_SIZE`</a>
The maximum number of the local cache key-value database entries for the DNS server checking.
**Default:** No default value, a positive value is required if the [type][conf-dnscheck-type] of the database is set to `cache`.
**Example:** `1000`
[conf-dnscheck-type]: configuration.md#check-kv-type
## <a href="#DNSCHECK_REMOTEKV_API_KEY" id="DNSCHECK_REMOTEKV_API_KEY" name="DNSCHECK_REMOTEKV_API_KEY">`DNSCHECK_REMOTEKV_API_KEY`</a> ## <a href="#DNSCHECK_REMOTEKV_API_KEY" id="DNSCHECK_REMOTEKV_API_KEY" name="DNSCHECK_REMOTEKV_API_KEY">`DNSCHECK_REMOTEKV_API_KEY`</a>
The API key to use when authenticating queries to the backend key-value database API, if any. The API key should be valid as defined by [RFC 6750]. The API key to use when authenticating queries to the backend key-value database API, if any. The API key should be valid as defined by [RFC 6750].

34
go.mod
View File

@ -1,10 +1,10 @@
module github.com/AdguardTeam/AdGuardDNS module github.com/AdguardTeam/AdGuardDNS
go 1.23.2 go 1.23.4
require ( require (
github.com/AdguardTeam/AdGuardDNS/internal/dnsserver v0.0.0-20240607112746-5690301129fe github.com/AdguardTeam/AdGuardDNS/internal/dnsserver v0.0.0-20240607112746-5690301129fe
github.com/AdguardTeam/golibs v0.30.1 github.com/AdguardTeam/golibs v0.30.4
github.com/AdguardTeam/urlfilter v0.20.0 github.com/AdguardTeam/urlfilter v0.20.0
github.com/ameshkov/dnscrypt/v2 v2.3.0 github.com/ameshkov/dnscrypt/v2 v2.3.0
github.com/axiomhq/hyperloglog v0.2.0 github.com/axiomhq/hyperloglog v0.2.0
@ -19,15 +19,15 @@ require (
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible
github.com/prometheus/client_golang v1.20.5 github.com/prometheus/client_golang v1.20.5
github.com/prometheus/client_model v0.6.1 github.com/prometheus/client_model v0.6.1
github.com/prometheus/common v0.60.0 github.com/prometheus/common v0.60.1
github.com/quic-go/quic-go v0.48.1 github.com/quic-go/quic-go v0.48.2
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
golang.org/x/crypto v0.28.0 golang.org/x/crypto v0.30.0
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
golang.org/x/net v0.30.0 golang.org/x/net v0.32.0
golang.org/x/sys v0.26.0 golang.org/x/sys v0.28.0
golang.org/x/time v0.7.0 golang.org/x/time v0.8.0
google.golang.org/grpc v1.67.1 google.golang.org/grpc v1.68.0
google.golang.org/protobuf v1.35.1 google.golang.org/protobuf v1.35.1
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
) )
@ -41,20 +41,20 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/google/pprof v0.0.0-20241023014458-598669927662 // indirect github.com/google/pprof v0.0.0-20241203143554-1e3fdc7de467 // indirect
github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/compress v1.17.11 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/onsi/ginkgo/v2 v2.20.2 // indirect github.com/onsi/ginkgo/v2 v2.22.0 // indirect
github.com/panjf2000/ants/v2 v2.10.0 // indirect github.com/panjf2000/ants/v2 v2.10.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect
github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/qpack v0.5.1 // indirect
go.uber.org/mock v0.5.0 // indirect go.uber.org/mock v0.5.0 // indirect
golang.org/x/mod v0.21.0 // indirect golang.org/x/mod v0.22.0 // indirect
golang.org/x/sync v0.8.0 // indirect golang.org/x/sync v0.10.0 // indirect
golang.org/x/text v0.19.0 // indirect golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.26.0 // indirect golang.org/x/tools v0.28.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

70
go.sum
View File

@ -1,5 +1,5 @@
github.com/AdguardTeam/golibs v0.30.1 h1:/yv7dq2h7WXw/jTDxkE3FP9zHerRT+i03PZRHJX4fPU= github.com/AdguardTeam/golibs v0.30.4 h1:zfFX1v4hkOCz6BifkneiBW2PCwSK523kYNr+VwaFrIw=
github.com/AdguardTeam/golibs v0.30.1/go.mod h1:FkwcNQEJoGsgDGXcalrVa/4gWbE68KsmE2guXWtBQUE= github.com/AdguardTeam/golibs v0.30.4/go.mod h1:Ir9dlHfb8nRQsG3Qgo1zoGL+k1qMbcBtb8tcnsvzdAE=
github.com/AdguardTeam/urlfilter v0.20.0 h1:X32qiuVCVd8WDYCEsbdZKfXMzwdVqrdulamtUi4rmzs= github.com/AdguardTeam/urlfilter v0.20.0 h1:X32qiuVCVd8WDYCEsbdZKfXMzwdVqrdulamtUi4rmzs=
github.com/AdguardTeam/urlfilter v0.20.0/go.mod h1:gjrywLTxfJh6JOkwi9SU+frhP7kVVEZ5exFGkR99qpk= github.com/AdguardTeam/urlfilter v0.20.0/go.mod h1:gjrywLTxfJh6JOkwi9SU+frhP7kVVEZ5exFGkR99qpk=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
@ -37,12 +37,14 @@ github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/gomodule/redigo v1.9.2 h1:HrutZBLhSIU8abiSfW8pj8mPhOyMYjZT/wcA4/L9L9s= github.com/gomodule/redigo v1.9.2 h1:HrutZBLhSIU8abiSfW8pj8mPhOyMYjZT/wcA4/L9L9s=
github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw= github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20241023014458-598669927662 h1:SKMkD83p7FwUqKmBsPdLHF5dNyxq3jOWwu9w9UyH5vA= github.com/google/pprof v0.0.0-20241203143554-1e3fdc7de467 h1:keEZFtbLJugfE0qHn+Ge1JCE71spzkchQobDf3mzS/4=
github.com/google/pprof v0.0.0-20241023014458-598669927662/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/pprof v0.0.0-20241203143554-1e3fdc7de467/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg= github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
@ -59,10 +61,10 @@ github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc=
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE= github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8= github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
github.com/panjf2000/ants/v2 v2.10.0 h1:zhRg1pQUtkyRiOFo2Sbqwjp0GfBNo9cUY2/Grpx1p+8= github.com/panjf2000/ants/v2 v2.10.0 h1:zhRg1pQUtkyRiOFo2Sbqwjp0GfBNo9cUY2/Grpx1p+8=
@ -81,14 +83,14 @@ github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.48.1 h1:y/8xmfWI9qmGTc+lBr4jKRUWLGSlSigv847ULJ4hYXA= github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
github.com/quic-go/quic-go v0.48.1/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
@ -111,29 +113,29 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI= google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 h1:XVhgTWWV3kGQlwJHR3upFWZeTsei6Oks1apkZSeonIE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -1,4 +1,4 @@
go 1.23.2 go 1.23.4
use ( use (
. .

View File

@ -1,6 +1,7 @@
cel.dev/expr v0.15.0 h1:O1jzfJCQBfL5BFoYktaxwIhuttaQPsVWerH9/EEKx0w= cel.dev/expr v0.15.0 h1:O1jzfJCQBfL5BFoYktaxwIhuttaQPsVWerH9/EEKx0w=
cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg=
cel.dev/expr v0.16.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= cel.dev/expr v0.16.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg=
cel.dev/expr v0.16.1/go.mod h1:AsGA5zb3WruAEQeQng1RZdGEXmBj0jvMWh6l5SnNuC8=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 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.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.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
@ -161,6 +162,7 @@ github.com/AdguardTeam/golibs v0.25.3/go.mod h1:HaTyS2wCbxFudjht9N/+/Qf1b5cMad2B
github.com/AdguardTeam/golibs v0.30.0/go.mod h1:vjw1OVZG6BYyoqGRY88U4LCJLOMfhBFhU0UJBdaSAuQ= github.com/AdguardTeam/golibs v0.30.0/go.mod h1:vjw1OVZG6BYyoqGRY88U4LCJLOMfhBFhU0UJBdaSAuQ=
github.com/AdguardTeam/golibs v0.30.1 h1:/yv7dq2h7WXw/jTDxkE3FP9zHerRT+i03PZRHJX4fPU= github.com/AdguardTeam/golibs v0.30.1 h1:/yv7dq2h7WXw/jTDxkE3FP9zHerRT+i03PZRHJX4fPU=
github.com/AdguardTeam/golibs v0.30.1/go.mod h1:FkwcNQEJoGsgDGXcalrVa/4gWbE68KsmE2guXWtBQUE= github.com/AdguardTeam/golibs v0.30.1/go.mod h1:FkwcNQEJoGsgDGXcalrVa/4gWbE68KsmE2guXWtBQUE=
github.com/AdguardTeam/golibs v0.30.3/go.mod h1:Ir9dlHfb8nRQsG3Qgo1zoGL+k1qMbcBtb8tcnsvzdAE=
github.com/AdguardTeam/gomitmproxy v0.2.0 h1:rvCOf17pd1/CnMyMQW891zrEiIQBpQ8cIGjKN9pinUU= github.com/AdguardTeam/gomitmproxy v0.2.0 h1:rvCOf17pd1/CnMyMQW891zrEiIQBpQ8cIGjKN9pinUU=
github.com/AdguardTeam/gomitmproxy v0.2.1 h1:p9gr8Er1TYvf+7ic81Ax1sZ62UNCsMTZNbm7tC59S9o= github.com/AdguardTeam/gomitmproxy v0.2.1 h1:p9gr8Er1TYvf+7ic81Ax1sZ62UNCsMTZNbm7tC59S9o=
github.com/AdguardTeam/gomitmproxy v0.2.1/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU= github.com/AdguardTeam/gomitmproxy v0.2.1/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU=
@ -251,6 +253,7 @@ github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50/go.mod h1:5e1+Vvlzido6
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw= github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw=
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q=
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d h1:t5Wuyh53qYyg9eqn4BbnlIT+vmhyww0TatL+zT3uWgI= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d h1:t5Wuyh53qYyg9eqn4BbnlIT+vmhyww0TatL+zT3uWgI=
@ -367,8 +370,6 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@ -401,6 +402,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
@ -488,6 +491,7 @@ github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw
github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
@ -572,6 +576,8 @@ github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3Ro
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI=
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=
github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM= github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=
github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=
@ -589,12 +595,14 @@ github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/quic-go/qtls-go1-20 v0.3.3/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= github.com/quic-go/qtls-go1-20 v0.3.3/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
@ -739,6 +747,7 @@ go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8p
go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go4.org v0.0.0-20180809161055-417644f6feb5 h1:+hE86LblG4AyDgwMCLTE6FOlM9+qjHSYS+rKqxUVdsM= go4.org v0.0.0-20180809161055-417644f6feb5 h1:+hE86LblG4AyDgwMCLTE6FOlM9+qjHSYS+rKqxUVdsM=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d h1:E2M5QgjZ/Jg+ObCQAudsXxuTsLj7Nl5RV/lZcQZmKSo= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d h1:E2M5QgjZ/Jg+ObCQAudsXxuTsLj7Nl5RV/lZcQZmKSo=
@ -758,6 +767,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20221019170559-20944726eadf/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/exp v0.0.0-20221019170559-20944726eadf/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
@ -769,6 +780,8 @@ golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQ
golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-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-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -784,6 +797,7 @@ golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -820,6 +834,9 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 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-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-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -852,6 +869,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-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-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -889,6 +908,9 @@ golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240208230135-b75ee8823808 h1:+Kc94D8UVEVxJnLXp/+FMfqQARZtWHfVrcRtcG8aT3g= golang.org/x/telemetry v0.0.0-20240208230135-b75ee8823808 h1:+Kc94D8UVEVxJnLXp/+FMfqQARZtWHfVrcRtcG8aT3g=
golang.org/x/telemetry v0.0.0-20240208230135-b75ee8823808/go.mod h1:KG1lNk5ZFNssSZLrpVb4sMXKMpGwGXOxSG3rnu2gZQQ= golang.org/x/telemetry v0.0.0-20240208230135-b75ee8823808/go.mod h1:KG1lNk5ZFNssSZLrpVb4sMXKMpGwGXOxSG3rnu2gZQQ=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2 h1:IRJeR9r1pYWsHKTRe/IInb7lYvbBVIqOgsX/u0mbOWY= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2 h1:IRJeR9r1pYWsHKTRe/IInb7lYvbBVIqOgsX/u0mbOWY=
@ -925,6 +947,8 @@ golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@ -938,6 +962,9 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
@ -963,7 +990,11 @@ golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -1014,6 +1045,7 @@ google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw=
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU=
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo=
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I=
google.golang.org/genproto/googleapis/bytestream v0.0.0-20240304161311-37d4d3c04a78/go.mod h1:vh/N7795ftP0AkN1w8XKqN4w1OdUKXW5Eummda+ofv8= google.golang.org/genproto/googleapis/bytestream v0.0.0-20240304161311-37d4d3c04a78/go.mod h1:vh/N7795ftP0AkN1w8XKqN4w1OdUKXW5Eummda+ofv8=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240304161311-37d4d3c04a78/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= google.golang.org/genproto/googleapis/rpc v0.0.0-20240304161311-37d4d3c04a78/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
@ -1044,6 +1076,7 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=

View File

@ -2,17 +2,13 @@ package agd_test
import ( import (
"strings" "strings"
"testing"
"github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/golibs/testutil"
) )
func TestMain(m *testing.M) {
testutil.DiscardLogOutput(m)
}
// Common long strings for tests. // Common long strings for tests.
//
// TODO(a.garipov): Move to a new validation package.
var ( var (
testLongStr = strings.Repeat("a", 200) testLongStr = strings.Repeat("a", 200)
testLongStrUnicode = strings.Repeat("ы", 200) testLongStrUnicode = strings.Repeat("ы", 200)

View File

@ -34,7 +34,8 @@ func TestNewDeviceName(t *testing.T) {
}, { }, {
name: "too_long_unicode", name: "too_long_unicode",
in: testLongStrUnicode, in: testLongStrUnicode,
wantErrMsg: `bad device name "` + testLongStrUnicode + `": too long: got 200 runes, max 128`, wantErrMsg: `bad device name "` + testLongStrUnicode +
`": too long: got 200 runes, max 128`,
}} }}
for _, tc := range testCases { for _, tc := range testCases {

View File

@ -0,0 +1,28 @@
package agd
import "github.com/AdguardTeam/AdGuardDNS/internal/filter"
// FilteringGroup represents a set of filtering settings.
type FilteringGroup struct {
// FilterConfig is the configuration of the filters used for this filtering
// group. It must not be nil.
FilterConfig *filter.ConfigGroup
// ID is the unique ID of this filtering group. It must be set.
ID FilteringGroupID
// BlockChromePrefetch shows if the Chrome prefetch proxy feature should be
// disabled for requests using this filtering group.
BlockChromePrefetch bool
// BlockFirefoxCanary shows if Firefox canary domain is blocked for
// requests using this filtering group.
BlockFirefoxCanary bool
// BlockPrivateRelay shows if Apple Private Relay is blocked for requests
// using this filtering group.
BlockPrivateRelay bool
}
// FilteringGroupID is the ID of a filter group. It is an opaque string.
type FilteringGroupID string

View File

@ -1,173 +0,0 @@
package agd
import (
"fmt"
"unicode/utf8"
"github.com/AdguardTeam/golibs/errors"
)
// FilterListID is the ID of a filter list. It is an opaque string.
type FilterListID string
// Special FilterListID values shared across the AdGuard DNS system.
//
// DO NOT change these as other parts of the system depend on these values.
const (
// FilterListIDNone means that no filter were applied at all.
FilterListIDNone FilterListID = ""
// FilterListIDBlockedService is the shared filter-list ID used when a
// request was blocked by the service blocker.
FilterListIDBlockedService FilterListID = "blocked_service"
// FilterListIDCustom is the special shared filter-list ID used when
// a request was filtered by a custom profile rule.
FilterListIDCustom FilterListID = "custom"
// FilterListIDAdultBlocking is the special shared filter-list ID used when
// a request was filtered by the adult content blocking filter.
FilterListIDAdultBlocking FilterListID = "adult_blocking"
// FilterListIDSafeBrowsing is the special shared filter-list ID used when
// a request was filtered by the safe browsing filter.
FilterListIDSafeBrowsing FilterListID = "safe_browsing"
// FilterListIDNewRegDomains is the special shared filter-list ID used when
// a request was filtered by the newly registered domains filter.
FilterListIDNewRegDomains FilterListID = "newly_registered_domains"
// FilterListIDGeneralSafeSearch is the shared filter-list ID used when
// a request was modified by the general safe search filter.
FilterListIDGeneralSafeSearch FilterListID = "general_safe_search"
// FilterListIDYoutubeSafeSearch is the special shared filter-list ID used
// when a request was modified by the YouTube safe search filter.
FilterListIDYoutubeSafeSearch FilterListID = "youtube_safe_search"
// FilterListIDAdGuardDNS is the special filter-list ID of the main AdGuard
// DNS filtering-rule list. For this list, rule statistics are collected.
FilterListIDAdGuardDNS FilterListID = "adguard_dns_filter"
// FilterListIDAdGuardPopup is the special filter-list ID of the AdGuard DNS
// list of popup domains.
FilterListIDAdGuardPopup FilterListID = "adguard_popup_filter"
)
// The maximum and minimum lengths of a filter list ID.
const (
MaxFilterListIDLen = 128
MinFilterListIDLen = 1
)
// NewFilterListID converts a simple string into a FilterListID and makes sure
// that it's valid. This should be preferred to a simple type conversion.
func NewFilterListID(s string) (id FilterListID, err error) {
defer func() { err = errors.Annotate(err, "bad filter list id %q: %w", s) }()
err = ValidateInclusion(len(s), MaxFilterListIDLen, MinFilterListIDLen, UnitByte)
if err != nil {
return FilterListIDNone, err
}
// Allow only the printable, non-whitespace ASCII characters. Technically
// we only need to exclude carriage return, line feed, and slash characters,
// but let's be more strict just in case.
if i, r := firstNonIDRune(s, true); i != -1 {
return FilterListIDNone, fmt.Errorf("bad rune %q at index %d", r, i)
}
return FilterListID(s), nil
}
// SupportsDNSRewrite returns true if the $dnsrewrite rules in filtering-rule
// lists with this ID should be processed.
func (id FilterListID) SupportsDNSRewrite() (ok bool) {
switch id {
case
FilterListIDAdGuardPopup,
FilterListIDCustom,
FilterListIDGeneralSafeSearch,
FilterListIDYoutubeSafeSearch:
return true
default:
return false
}
}
// FilterRuleText is the text of a single rule within a filter.
type FilterRuleText string
// MaxFilterRuleTextRuneLen is the maximum length of a filter rule in runes.
const MaxFilterRuleTextRuneLen = 1024
// NewFilterRuleText converts a simple string into a FilterRuleText and makes
// sure that it's valid. This should be preferred to a simple type conversion.
func NewFilterRuleText(s string) (t FilterRuleText, err error) {
defer func() { err = errors.Annotate(err, "bad filter rule text %q: %w", s) }()
err = ValidateInclusion(utf8.RuneCountInString(s), MaxFilterRuleTextRuneLen, 0, UnitRune)
if err != nil {
return "", err
}
return FilterRuleText(s), nil
}
// FilteringGroup represents a set of filtering settings.
//
// 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.
type FilteringGroup struct {
// ID is the unique ID of this filtering group.
ID FilteringGroupID
// RuleListIDs are the filtering rule list IDs used for this filtering
// group. They are ignored if RuleListsEnabled is false.
RuleListIDs []FilterListID
// RuleListsEnabled shows whether the rule-list based filtering is enabled.
// This must be true in order for all parameters below to work.
RuleListsEnabled bool
// ParentalEnabled shows whether the parental protection functionality is
// enabled. This must be true in order for all parameters below to
// work.
ParentalEnabled bool
// BlockAdult shows whether the adult content blocking safe browsing
// filtering should be enforced.
BlockAdult bool
// SafeBrowsingEnabled shows whether the general safe browsing filtering
// should be enforced.
SafeBrowsingEnabled bool
// BlockDangerousDomains shows whether the dangerous domains safe browsing
// filtering should be enforced.
BlockDangerousDomains bool
// BlockNewlyRegisteredDomains shows whether the newly registered domains
// safe browsing filtering should be enforced.
BlockNewlyRegisteredDomains bool
// GeneralSafeSearch shows whether the general safe search filtering should
// be enforced.
GeneralSafeSearch bool
// YoutubeSafeSearch shows whether the YouTube safe search filtering should
// be enforced.
YoutubeSafeSearch bool
// BlockPrivateRelay shows if Apple Private Relay is blocked for requests
// using this filtering group.
BlockPrivateRelay bool
// BlockFirefoxCanary shows if Firefox canary domain is blocked for
// requests using this filtering group.
BlockFirefoxCanary bool
}
// FilteringGroupID is the ID of a filter group. It is an opaque string.
type FilteringGroupID string

View File

@ -2,13 +2,11 @@ package agd
import ( import (
"fmt" "fmt"
"math"
"time" "time"
"github.com/AdguardTeam/AdGuardDNS/internal/access" "github.com/AdguardTeam/AdGuardDNS/internal/access"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/AdGuardDNS/internal/filter"
) )
// Profile contains information about an AdGuard DNS profile. In other parts of // Profile contains information about an AdGuard DNS profile. In other parts of
@ -17,141 +15,62 @@ import (
// //
// NOTE: Do not change fields of this structure without incrementing // NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion]. // [internal/profiledb/internal.FileCacheVersion].
//
// 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.
type Profile struct { type Profile struct {
// Parental are the parental settings for this profile. They are ignored if // FilterConfig is the configuration of the filters used for this profile
// FilteringEnabled is set to false. // and all its devices that don't have filtering disabled. It must not be
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
Parental *ParentalProtectionSettings
// Ratelimiter is the custom ratelimiter for this profile. It must not be
// nil. // nil.
// FilterConfig *filter.ConfigClient
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
Ratelimiter Ratelimiter
// SafeBrowsing are the safe browsing settings for this profile. They are
// ignored if FilteringEnabled is set to false.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
SafeBrowsing *SafeBrowsingSettings
// Access is the access manager for this profile. It must not be nil. // Access is the access manager for this profile. It must not be nil.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
Access access.Profile Access access.Profile
// BlockingMode defines the way blocked responses are constructed. It must // BlockingMode defines the way blocked responses are constructed. It must
// not be nil. // not be nil.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
BlockingMode dnsmsg.BlockingMode BlockingMode dnsmsg.BlockingMode
// Ratelimiter is the custom ratelimiter for this profile. It must not be
// nil.
Ratelimiter Ratelimiter
// ID is the unique ID of this profile. It must not be empty. // ID is the unique ID of this profile. It must not be empty.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
ID ProfileID ID ProfileID
// UpdateTime shows the last time this profile was updated from the backend.
// This is NOT the time of update in the backend's database, since the
// backend doesn't send this information.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
UpdateTime time.Time
// DeviceIDs are the IDs of devices attached to this profile. // DeviceIDs are the IDs of devices attached to this profile.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
DeviceIDs []DeviceID DeviceIDs []DeviceID
// RuleListIDs are the IDs of the filtering rule lists enabled for this
// profile. They are ignored if FilteringEnabled or RuleListsEnabled are
// set to false.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
RuleListIDs []FilterListID
// CustomRules are the custom filtering rules for this profile. They are
// ignored if RuleListsEnabled is set to false.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
CustomRules []FilterRuleText
// FilteredResponseTTL is the time-to-live value used for responses sent to // FilteredResponseTTL is the time-to-live value used for responses sent to
// the devices of this profile. // the devices of this profile.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
FilteredResponseTTL time.Duration FilteredResponseTTL time.Duration
// FilteringEnabled defines whether queries from devices of this profile
// should be filtered in any way at all.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
FilteringEnabled bool
// RuleListsEnabled defines whether queries from devices of this profile
// should be filtered using the filtering rule lists in RuleListIDs.
// Requires FilteringEnabled to be set to true.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
RuleListsEnabled bool
// QueryLogEnabled defines whether query logs should be saved for the
// devices of this profile.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
QueryLogEnabled bool
// Deleted shows if this profile is deleted.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
Deleted bool
// BlockPrivateRelay shows if Apple Private Relay queries are blocked for
// requests from all devices in this profile.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
BlockPrivateRelay bool
// BlockFirefoxCanary shows if Firefox canary domain is blocked for
// requests from all devices in this profile.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
BlockFirefoxCanary bool
// IPLogEnabled shows if client IP addresses are logged.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
IPLogEnabled bool
// AutoDevicesEnabled shows if the automatic creation of devices using // AutoDevicesEnabled shows if the automatic creation of devices using
// HumanIDs should be enabled for this profile. // HumanIDs should be enabled for this profile.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
AutoDevicesEnabled bool AutoDevicesEnabled bool
// BlockChromePrefetch shows if the Chrome prefetch proxy feature should be
// forced into preflight mode for all devices in this profile.
BlockChromePrefetch bool
// BlockFirefoxCanary shows if Firefox canary domain is blocked for
// requests from all devices in this profile.
BlockFirefoxCanary bool
// BlockPrivateRelay shows if Apple Private Relay queries are blocked for
// requests from all devices in this profile.
BlockPrivateRelay bool
// Deleted shows if this profile is deleted.
Deleted bool
// FilteringEnabled defines whether queries from devices of this profile
// should be filtered in any way at all.
FilteringEnabled bool
// IPLogEnabled shows if client IP addresses are logged.
IPLogEnabled bool
// QueryLogEnabled defines whether query logs should be saved for the
// devices of this profile.
QueryLogEnabled bool
} }
// ProfileID is the ID of a profile. It is an opaque string. // ProfileID is the ID of a profile. It is an opaque string.
@ -179,165 +98,3 @@ func NewProfileID(s string) (id ProfileID, err error) {
return ProfileID(s), nil return ProfileID(s), nil
} }
// DayRange is a range within a single day. Start and End are minutes from the
// start of day, with 0 being 00:00:00.(0) and 1439, 23:59:59.(9).
//
// Additionally, if both Start and End are set to [math.MaxUint16], the range is
// a special zero-length range. This is needed, because when both Start and End
// are zero, such DayRange indicates one minute after midnight; as well as to
// reduce the amount of pointers and thus GC time.
//
// TODO(a.garipov): Refactor. See AGDNS-1516.
type DayRange struct {
Start uint16
End uint16
}
// MaxDayRangeMinutes is the maximum value for DayRange.Start and DayRange.End
// fields, excluding the zero-length range ones.
const MaxDayRangeMinutes = 24*60 - 1
// ZeroLengthDayRange returns a new zero-length day range.
func ZeroLengthDayRange() (r DayRange) {
return DayRange{
Start: math.MaxUint16,
End: math.MaxUint16,
}
}
// IsZeroLength returns true if r is a zero-length range.
func (r DayRange) IsZeroLength() (ok bool) {
return r.Start == math.MaxUint16 && r.End == math.MaxUint16
}
// Validate returns the day range validation errors, if any.
func (r DayRange) Validate() (err error) {
defer func() { err = errors.Annotate(err, "bad day range: %w") }()
switch {
case r.IsZeroLength():
return nil
case r.End < r.Start:
return fmt.Errorf("end %d less than start %d", r.End, r.Start)
case r.Start > MaxDayRangeMinutes:
return fmt.Errorf("start %d greater than %d", r.Start, MaxDayRangeMinutes)
case r.End > MaxDayRangeMinutes:
return fmt.Errorf("end %d greater than %d", r.End, MaxDayRangeMinutes)
default:
return nil
}
}
// WeeklySchedule is a schedule for one week. The index is the same as
// time.Weekday values. That is, 0 is Sunday, 1 is Monday, etc. An empty
// DayRange means that there is no schedule for this day.
type WeeklySchedule [7]DayRange
// ParentalProtectionSchedule is the schedule of a client's parental protection.
// All fields must not be nil.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
type ParentalProtectionSchedule struct {
// Week is the parental protection schedule for every day of the week.
Week *WeeklySchedule
// TimeZone is the profile's time zone.
TimeZone *agdtime.Location
}
// Contains returns true if t is within the allowed schedule.
func (s *ParentalProtectionSchedule) Contains(t time.Time) (ok bool) {
t = t.In(&s.TimeZone.Location)
r := s.Week[int(t.Weekday())]
if r.IsZeroLength() {
return false
}
day := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, &s.TimeZone.Location)
start := day.Add(time.Duration(r.Start) * time.Minute)
end := day.Add(time.Duration(r.End+1)*time.Minute - 1*time.Nanosecond)
return !t.Before(start) && !t.After(end)
}
// ParentalProtectionSettings are the parental protection settings of a profile.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
type ParentalProtectionSettings struct {
Schedule *ParentalProtectionSchedule
// BlockedServices are the IDs of the services blocked for this profile.
BlockedServices []BlockedServiceID
// Enabled tells whether the parental protection should be enabled at all.
// This must be true in order for all parameters below to work.
Enabled bool
// BlockAdult tells if AdGuard DNS should enforce blocking of adult content
// using the safe browsing filter.
BlockAdult bool
// GeneralSafeSearch tells if AdGuard DNS should enforce general safe search
// in most search engines.
GeneralSafeSearch bool
// YoutubeSafeSearch tells if AdGuard DNS should enforce safe search on
// YouTube.
YoutubeSafeSearch bool
}
// SafeBrowsingSettings are the safe browsing settings of a profile.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
type SafeBrowsingSettings struct {
// Enabled defines whether queries from devices of this profile should be
// filtered using the safe browsing filter. This must be true in order for
// all parameters below to work.
Enabled bool
// BlockDangerousDomains shows whether the dangerous domains safe browsing
// filtering should be enforced.
BlockDangerousDomains bool
// BlockNewlyRegisteredDomains shows whether the newly registered domains
// safe browsing filtering should be enforced.
BlockNewlyRegisteredDomains bool
}
// BlockedServiceID is the ID of a blocked service. While these are usually
// human-readable, clients should treat them as opaque strings.
//
// When a request is blocked by the service blocker, this ID is used as the
// text of the blocking rule.
type BlockedServiceID string
// The maximum and minimum lengths of a blocked service ID.
const (
MaxBlockedServiceIDLen = 64
MinBlockedServiceIDLen = 1
)
// NewBlockedServiceID converts a simple string into a BlockedServiceID and
// makes sure that it's valid. This should be preferred to a simple type
// conversion.
func NewBlockedServiceID(s string) (id BlockedServiceID, err error) {
defer func() { err = errors.Annotate(err, "bad blocked service id %q: %w", s) }()
err = ValidateInclusion(len(s), MaxBlockedServiceIDLen, MinBlockedServiceIDLen, UnitByte)
if err != nil {
return "", err
}
// Allow only the printable, non-whitespace ASCII characters. Technically
// we only need to exclude carriage return, line feed, and slash characters,
// but let's be more strict just in case.
if i, r := firstNonIDRune(s, true); i != -1 {
return "", fmt.Errorf("bad char %q at index %d", r, i)
}
return BlockedServiceID(s), nil
}

View File

@ -63,7 +63,7 @@ func (GlobalRatelimiter) Config() (_ *RatelimitConfig) { return &RatelimitConfig
// CountResponses implements the [Ratelimiter] interface for GlobalRatelimiter. // CountResponses implements the [Ratelimiter] interface for GlobalRatelimiter.
func (GlobalRatelimiter) CountResponses(_ context.Context, _ *dns.Msg, _ netip.Addr) {} func (GlobalRatelimiter) CountResponses(_ context.Context, _ *dns.Msg, _ netip.Addr) {}
// DefaultRatelimiter is the default [Ratelimiter] impelentation. // DefaultRatelimiter is the default [Ratelimiter] implementation.
// //
// TODO(a.garipov): Add tests. // TODO(a.garipov): Add tests.
type DefaultRatelimiter struct { type DefaultRatelimiter struct {

View File

@ -19,7 +19,7 @@ type Server struct {
DNSCrypt *DNSCryptConfig DNSCrypt *DNSCryptConfig
// TLS is the TLS configuration for this server, if any. // TLS is the TLS configuration for this server, if any.
TLS *tls.Config TLS *TLSConfig
// QUICConf is the QUIC configuration for this server. // QUICConf is the QUIC configuration for this server.
QUICConf *QUICConfig QUICConf *QUICConfig
@ -200,3 +200,13 @@ type QUICConfig struct {
// QUICLimitsEnabled, if true, enables QUIC limiting. // QUICLimitsEnabled, if true, enables QUIC limiting.
QUICLimitsEnabled bool QUICLimitsEnabled bool
} }
// TLSConfig is the TLS configuration of a DNS server. Metrics and ALPs must be
// set for saved configurations.
type TLSConfig struct {
// Default is the defult TLS configuration. It must not be nil.
Default *tls.Config
// H3 is the TLS configuration for DoH3.
H3 *tls.Config
}

View File

@ -1,8 +1,6 @@
package agd package agd
import ( import (
"crypto/tls"
"github.com/AdguardTeam/golibs/container" "github.com/AdguardTeam/golibs/container"
"github.com/miekg/dns" "github.com/miekg/dns"
) )
@ -14,10 +12,11 @@ type ServerGroup struct {
// Resolvers (DDR) handlers. DDR must not be nil. // Resolvers (DDR) handlers. DDR must not be nil.
DDR *DDR DDR *DDR
// TLS are the TLS settings for this server group. If Servers contains at // DeviceDomains is the list of domain names used to detect device IDs from
// least one server with a non-plain protocol (see [Protocol.IsPlain]), TLS // clients' server names.
// must not be nil. //
TLS *TLS // TODO(s.chzhen): Consider using a custom type.
DeviceDomains []string
// Name is the unique name of the server group. // Name is the unique name of the server group.
Name ServerGroupName Name ServerGroupName
@ -36,20 +35,6 @@ type ServerGroup struct {
// ServerGroupName is the name of a server group. // ServerGroupName is the name of a server group.
type ServerGroupName string type ServerGroupName string
// TLS is the TLS configuration of a DNS server group.
type TLS struct {
// Conf is the server's TLS configuration.
Conf *tls.Config
// DeviceDomains are the domain names used to detect device IDs from
// clients' server names.
DeviceDomains []string
// SessionKeys are paths to files containing the TLS session keys for this
// server.
SessionKeys []string
}
// DDR is the configuration for the server group's Discovery Of Designated // DDR is the configuration for the server group's Discovery Of Designated
// Resolvers (DDR) handlers. // Resolvers (DDR) handlers.
type DDR struct { type DDR struct {

View File

@ -10,7 +10,11 @@ import (
// LRUConfig is a configuration structure of a cache. // LRUConfig is a configuration structure of a cache.
type LRUConfig struct { type LRUConfig struct {
Size int // Count is the maximum number of elements to keep in the cache. It must be
// positive.
//
// TODO(a.garipov): Make uint64.
Count int
} }
// LRU is an [Interface] implementation. // LRU is an [Interface] implementation.
@ -21,7 +25,7 @@ type LRU[K, T any] struct {
// NewLRU returns a new initialized LRU cache. // NewLRU returns a new initialized LRU cache.
func NewLRU[K, T any](conf *LRUConfig) (c *LRU[K, T]) { func NewLRU[K, T any](conf *LRUConfig) (c *LRU[K, T]) {
return &LRU[K, T]{ return &LRU[K, T]{
cache: gcache.New(conf.Size).LRU().Build(), cache: gcache.New(conf.Count).LRU().Build(),
} }
} }

View File

@ -16,7 +16,7 @@ func TestLRU(t *testing.T) {
) )
cache := agdcache.NewLRU[string, int](&agdcache.LRUConfig{ cache := agdcache.NewLRU[string, int](&agdcache.LRUConfig{
Size: 10, Count: 10,
}) })
cache.Set(key, val) cache.Set(key, val)

View File

@ -5,16 +5,11 @@ import (
"testing" "testing"
"github.com/AdguardTeam/AdGuardDNS/internal/agdpasswd" "github.com/AdguardTeam/AdGuardDNS/internal/agdpasswd"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
func TestMain(m *testing.M) {
testutil.DiscardLogOutput(m)
}
func TestPasswordHashBcrypt_Authenticate(t *testing.T) { func TestPasswordHashBcrypt_Authenticate(t *testing.T) {
t.Parallel() t.Parallel()

View File

@ -22,6 +22,18 @@ type Refresher interface {
Refresh(ctx context.Context) (err error) Refresh(ctx context.Context) (err error)
} }
// RefresherFunc is an adapter to allow the use of ordinary functions as
// [Refresher].
type RefresherFunc func(ctx context.Context) (err error)
// type check
var _ Refresher = RefresherFunc(nil)
// Refresh implements the [Refresher] interface for RefresherFunc.
func (f RefresherFunc) Refresh(ctx context.Context) (err error) {
return f(ctx)
}
// RefreshWorker is an [Interface] implementation that updates its [Refresher] // RefreshWorker is an [Interface] implementation that updates its [Refresher]
// every tick of the provided ticker. // every tick of the provided ticker.
type RefreshWorker struct { type RefreshWorker struct {

View File

@ -96,9 +96,9 @@ func TestRefreshWorker(t *testing.T) {
}) })
t.Run("error", func(t *testing.T) { t.Run("error", func(t *testing.T) {
errRefr, syncCh := newTestRefresher(t, testError) refrWithError, syncCh := newTestRefresher(t, testError)
w := agdservice.NewRefreshWorker(newRefrConfig(t, errRefr, testIvl, false)) w := agdservice.NewRefreshWorker(newRefrConfig(t, refrWithError, testIvl, false))
err := w.Start(testutil.ContextWithTimeout(t, testTimeout)) err := w.Start(testutil.ContextWithTimeout(t, testTimeout))
require.NoError(t, err) require.NoError(t, err)
@ -110,9 +110,9 @@ func TestRefreshWorker(t *testing.T) {
}) })
t.Run("error_on_shutdown", func(t *testing.T) { t.Run("error_on_shutdown", func(t *testing.T) {
errRefr, syncCh := newTestRefresher(t, testError) refrWithError, syncCh := newTestRefresher(t, testError)
w := agdservice.NewRefreshWorker(newRefrConfig(t, errRefr, testIvlLong, true)) w := agdservice.NewRefreshWorker(newRefrConfig(t, refrWithError, testIvlLong, true))
err := w.Start(testutil.ContextWithTimeout(t, testTimeout)) err := w.Start(testutil.ContextWithTimeout(t, testTimeout))
require.NoError(t, err) require.NoError(t, err)

View File

@ -11,6 +11,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdpasswd" "github.com/AdguardTeam/AdGuardDNS/internal/agdpasswd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice" "github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/billstat" "github.com/AdguardTeam/AdGuardDNS/internal/billstat"
"github.com/AdguardTeam/AdGuardDNS/internal/dnscheck" "github.com/AdguardTeam/AdGuardDNS/internal/dnscheck"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsdb" "github.com/AdguardTeam/AdGuardDNS/internal/dnsdb"
@ -111,6 +112,21 @@ func (r *Refresher) Refresh(ctx context.Context) (err error) {
return r.OnRefresh(ctx) return r.OnRefresh(ctx)
} }
// Package agdtime
// type check
var _ agdtime.Clock = (*Clock)(nil)
// Clock is a [agdtime.Clock] for tests.
type Clock struct {
OnNow func() (now time.Time)
}
// Now implements the [agdtime.Clock] interface for *Clock.
func (c *Clock) Now() (now time.Time) {
return c.OnNow()
}
// Package billstat // Package billstat
// type check // type check
@ -220,34 +236,24 @@ var _ filter.Interface = (*Filter)(nil)
// Filter is a [filter.Interface] for tests. // Filter is a [filter.Interface] for tests.
type Filter struct { type Filter struct {
OnFilterRequest func( OnFilterRequest func(ctx context.Context, req *filter.Request) (r filter.Result, err error)
ctx context.Context, OnFilterResponse func(ctx context.Context, resp *filter.Response) (r filter.Result, err error)
req *dns.Msg,
ri *agd.RequestInfo,
) (r filter.Result, err error)
OnFilterResponse func(
ctx context.Context,
resp *dns.Msg,
ri *agd.RequestInfo,
) (r filter.Result, err error)
} }
// FilterRequest implements the [filter.Interface] interface for *Filter. // FilterRequest implements the [filter.Interface] interface for *Filter.
func (f *Filter) FilterRequest( func (f *Filter) FilterRequest(
ctx context.Context, ctx context.Context,
req *dns.Msg, req *filter.Request,
ri *agd.RequestInfo,
) (r filter.Result, err error) { ) (r filter.Result, err error) {
return f.OnFilterRequest(ctx, req, ri) return f.OnFilterRequest(ctx, req)
} }
// FilterResponse implements the [filter.Interface] interface for *Filter. // FilterResponse implements the [filter.Interface] interface for *Filter.
func (f *Filter) FilterResponse( func (f *Filter) FilterResponse(
ctx context.Context, ctx context.Context,
resp *dns.Msg, resp *filter.Response,
ri *agd.RequestInfo,
) (r filter.Result, err error) { ) (r filter.Result, err error) {
return f.OnFilterResponse(ctx, resp, ri) return f.OnFilterResponse(ctx, resp)
} }
// type check // type check
@ -274,21 +280,18 @@ var _ filter.Storage = (*FilterStorage)(nil)
// FilterStorage is a [filter.Storage] for tests. // FilterStorage is a [filter.Storage] for tests.
type FilterStorage struct { type FilterStorage struct {
OnFilterFromContext func(ctx context.Context, ri *agd.RequestInfo) (f filter.Interface) OnForConfig func(ctx context.Context, c filter.Config) (f filter.Interface)
OnHasListID func(id agd.FilterListID) (ok bool) OnHasListID func(id filter.ID) (ok bool)
} }
// FilterFromContext implements the [filter.Storage] interface for // ForConfig implements the [filter.Storage] interface for
// *FilterStorage. // *FilterStorage.
func (s *FilterStorage) FilterFromContext( func (s *FilterStorage) ForConfig(ctx context.Context, c filter.Config) (f filter.Interface) {
ctx context.Context, return s.OnForConfig(ctx, c)
ri *agd.RequestInfo,
) (f filter.Interface) {
return s.OnFilterFromContext(ctx, ri)
} }
// HasListID implements the [filter.Storage] interface for *FilterStorage. // HasListID implements the [filter.Storage] interface for *FilterStorage.
func (s *FilterStorage) HasListID(id agd.FilterListID) (ok bool) { func (s *FilterStorage) HasListID(id filter.ID) (ok bool) {
return s.OnHasListID(id) return s.OnHasListID(id)
} }
@ -522,11 +525,11 @@ var _ rulestat.Interface = (*RuleStat)(nil)
// RuleStat is a [rulestat.Interface] for tests. // RuleStat is a [rulestat.Interface] for tests.
type RuleStat struct { type RuleStat struct {
OnCollect func(ctx context.Context, id agd.FilterListID, text agd.FilterRuleText) OnCollect func(ctx context.Context, id filter.ID, text filter.RuleText)
} }
// Collect implements the [rulestat.Interface] interface for *RuleStat. // Collect implements the [rulestat.Interface] interface for *RuleStat.
func (s *RuleStat) Collect(ctx context.Context, id agd.FilterListID, text agd.FilterRuleText) { func (s *RuleStat) Collect(ctx context.Context, id filter.ID, text filter.RuleText) {
s.OnCollect(ctx, id, text) s.OnCollect(ctx, id, text)
} }

View File

@ -8,6 +8,24 @@ import (
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
) )
// Clock is an interface for time-related operations.
//
// TODO(a.garipov): Expand with operations like After or Tick.
//
// TODO(a.garipov): Move to golibs/timeutil.
type Clock interface {
Now() (now time.Time)
}
// SystemClock is a [Clock] that uses the functions from package time.
type SystemClock struct{}
// type check
var _ Clock = SystemClock{}
// Now implements the [Clock] interface for SystemClock.
func (SystemClock) Now() (now time.Time) { return time.Now() }
// Location is a wrapper around time.Location that can de/serialize itself from // Location is a wrapper around time.Location that can de/serialize itself from
// and to JSON. // and to JSON.
// //

View File

@ -8,7 +8,6 @@ import (
"fmt" "fmt"
"net/url" "net/url"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/golibs/httphdr" "github.com/AdguardTeam/golibs/httphdr"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
@ -41,11 +40,6 @@ func newClient(apiURL *url.URL) (client *grpc.ClientConn, err error) {
return conn, nil return conn, nil
} }
// reportf is a helper method for reporting non-critical errors.
func reportf(ctx context.Context, errColl errcoll.Interface, format string, args ...any) {
errcoll.Collectf(ctx, errColl, "backendpb: "+format, args...)
}
// ctxWithAuthentication adds the API key authentication header to the outgoing // ctxWithAuthentication adds the API key authentication header to the outgoing
// request context if apiKey is not empty. If it is empty, ctx is parent. // request context if apiKey is not empty. If it is empty, ctx is parent.
func ctxWithAuthentication(parent context.Context, apiKey string) (ctx context.Context) { func ctxWithAuthentication(parent context.Context, apiKey string) (ctx context.Context) {

View File

@ -2,18 +2,13 @@ package backendpb
import ( import (
"net/netip" "net/netip"
"testing"
"time" "time"
"github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/golibs/testutil" "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/c2h5oh/datasize" "github.com/c2h5oh/datasize"
) )
func TestMain(m *testing.M) {
testutil.DiscardLogOutput(m)
}
// Common IDs for tests and their string forms. // Common IDs for tests and their string forms.
// //
// TODO(a.garipov): Move all or most tests into external and unexport these. // TODO(a.garipov): Move all or most tests into external and unexport these.
@ -37,5 +32,8 @@ var TestUpdTime = time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
// TODO(a.garipov): Add to golibs/netutil. // TODO(a.garipov): Add to golibs/netutil.
var TestBind = netip.MustParsePrefix("0.0.0.0/0") var TestBind = netip.MustParsePrefix("0.0.0.0/0")
// TestLogger is the common logger for tests.
var TestLogger = slogutil.NewDiscardLogger()
// TestRespSzEst is a response-size estimate for tests. // TestRespSzEst is a response-size estimate for tests.
const TestRespSzEst datasize.ByteSize = 1 * datasize.KB const TestRespSzEst datasize.ByteSize = 1 * datasize.KB

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"io" "io"
"log/slog"
"net/url" "net/url"
"github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/agd"
@ -16,13 +17,18 @@ import (
// BillStatConfig is the configuration structure for the business logic backend // BillStatConfig is the configuration structure for the business logic backend
// billing statistics uploader. // billing statistics uploader.
type BillStatConfig struct { type BillStatConfig struct {
// Logger is used for logging the operation of the billing statistics
// uploader. It must not be nil.
Logger *slog.Logger
// GRPCMetrics is used for the collection of the protobuf communication
// statistics.
GRPCMetrics GRPCMetrics
// ErrColl is the error collector that is used to collect critical and // ErrColl is the error collector that is used to collect critical and
// non-critical errors. // non-critical errors.
ErrColl errcoll.Interface ErrColl errcoll.Interface
// Metrics is used for the collection of the protobuf errors.
Metrics Metrics
// Endpoint is the backend API URL. The scheme should be either "grpc" or // Endpoint is the backend API URL. The scheme should be either "grpc" or
// "grpcs". // "grpcs".
Endpoint *url.URL Endpoint *url.URL
@ -31,6 +37,20 @@ type BillStatConfig struct {
APIKey string APIKey string
} }
// BillStat is the implementation of the [billstat.Uploader] interface that
// uploads the billing statistics to the business logic backend. It is safe for
// concurrent use.
//
// TODO(a.garipov): Consider uniting with [ProfileStorage] into a single
// backendpb.Client.
type BillStat struct {
logger *slog.Logger
errColl errcoll.Interface
grpcMetrics GRPCMetrics
client DNSServiceClient
apiKey string
}
// NewBillStat creates a new billing statistics uploader. c must not be nil. // NewBillStat creates a new billing statistics uploader. c must not be nil.
func NewBillStat(c *BillStatConfig) (b *BillStat, err error) { func NewBillStat(c *BillStatConfig) (b *BillStat, err error) {
client, err := newClient(c.Endpoint) client, err := newClient(c.Endpoint)
@ -40,26 +60,14 @@ func NewBillStat(c *BillStatConfig) (b *BillStat, err error) {
} }
return &BillStat{ return &BillStat{
logger: c.Logger,
errColl: c.ErrColl, errColl: c.ErrColl,
metrics: c.Metrics, grpcMetrics: c.GRPCMetrics,
client: NewDNSServiceClient(client), client: NewDNSServiceClient(client),
apiKey: c.APIKey, apiKey: c.APIKey,
}, nil }, nil
} }
// BillStat is the implementation of the [billstat.Uploader] interface that
// uploads the billing statistics to the business logic backend. It is safe for
// concurrent use.
//
// TODO(a.garipov): Consider uniting with [ProfileStorage] into a single
// backendpb.Client.
type BillStat struct {
errColl errcoll.Interface
metrics Metrics
client DNSServiceClient
apiKey string
}
// type check // type check
var _ billstat.Uploader = (*BillStat)(nil) var _ billstat.Uploader = (*BillStat)(nil)
@ -72,12 +80,13 @@ func (b *BillStat) Upload(ctx context.Context, records billstat.Records) (err er
ctx = ctxWithAuthentication(ctx, b.apiKey) ctx = ctxWithAuthentication(ctx, b.apiKey)
stream, err := b.client.SaveDevicesBillingStat(ctx) stream, err := b.client.SaveDevicesBillingStat(ctx)
if err != nil { if err != nil {
return fmt.Errorf("opening stream: %w", fixGRPCError(ctx, b.metrics, err)) return fmt.Errorf("opening stream: %w", fixGRPCError(ctx, b.grpcMetrics, err))
} }
for deviceID, record := range records { for deviceID, record := range records {
if record == nil { if record == nil {
reportf(ctx, b.errColl, "device %q: null record", deviceID) err = fmt.Errorf("device %q: null record", deviceID)
errcoll.Collect(ctx, b.errColl, b.logger, "uploading records", err)
continue continue
} }
@ -87,7 +96,7 @@ func (b *BillStat) Upload(ctx context.Context, records billstat.Records) (err er
return fmt.Errorf( return fmt.Errorf(
"uploading device %q record: %w", "uploading device %q record: %w",
deviceID, deviceID,
fixGRPCError(ctx, b.metrics, sendErr), fixGRPCError(ctx, b.grpcMetrics, sendErr),
) )
} }
} }

View File

@ -101,13 +101,14 @@ func TestBillStat_Upload(t *testing.T) {
errColl := &agdtest.ErrorCollector{ errColl := &agdtest.ErrorCollector{
OnCollect: func(_ context.Context, err error) { OnCollect: func(_ context.Context, err error) {
testutil.AssertErrorMsg(t, `backendpb: device "invalid": null record`, err) testutil.AssertErrorMsg(t, `uploading records: device "invalid": null record`, err)
}, },
} }
b, err := backendpb.NewBillStat(&backendpb.BillStatConfig{ b, err := backendpb.NewBillStat(&backendpb.BillStatConfig{
Logger: backendpb.TestLogger,
ErrColl: errColl, ErrColl: errColl,
Metrics: backendpb.EmptyMetrics{}, GRPCMetrics: backendpb.EmptyGRPCMetrics{},
Endpoint: &url.URL{ Endpoint: &url.URL{
Scheme: "grpc", Scheme: "grpc",
Host: l.Addr().String(), Host: l.Addr().String(),

View File

@ -3,6 +3,7 @@ package backendpb
import ( import (
"context" "context"
"fmt" "fmt"
"log/slog"
"net/netip" "net/netip"
"github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/agd"
@ -19,7 +20,8 @@ func devicesToInternal(
ds []*DeviceSettings, ds []*DeviceSettings,
bindSet netutil.SubnetSet, bindSet netutil.SubnetSet,
errColl errcoll.Interface, errColl errcoll.Interface,
mtrc Metrics, logger *slog.Logger,
mtrc ProfileDBMetrics,
) (out []*agd.Device, ids []agd.DeviceID) { ) (out []*agd.Device, ids []agd.DeviceID) {
l := len(ds) l := len(ds)
if l == 0 { if l == 0 {
@ -35,7 +37,8 @@ func devicesToInternal(
id = d.Id id = d.Id
} }
reportf(ctx, errColl, "bad device settings for device with id %q: %w", id, err) err = fmt.Errorf("bad settings for device with id %q: %w", id, err)
errcoll.Collect(ctx, errColl, logger, "converting device", err)
// TODO(s.chzhen): Add a return result structure and move the // TODO(s.chzhen): Add a return result structure and move the
// metrics collection to the layer above. // metrics collection to the layer above.

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.35.1 // protoc-gen-go v1.35.2
// protoc v5.28.3 // protoc v5.28.3
// source: dns.proto // source: dns.proto
@ -247,6 +247,7 @@ type DNSProfile struct {
Access *AccessSettings `protobuf:"bytes,18,opt,name=access,proto3" json:"access,omitempty"` Access *AccessSettings `protobuf:"bytes,18,opt,name=access,proto3" json:"access,omitempty"`
AutoDevicesEnabled bool `protobuf:"varint,19,opt,name=auto_devices_enabled,json=autoDevicesEnabled,proto3" json:"auto_devices_enabled,omitempty"` AutoDevicesEnabled bool `protobuf:"varint,19,opt,name=auto_devices_enabled,json=autoDevicesEnabled,proto3" json:"auto_devices_enabled,omitempty"`
RateLimit *RateLimitSettings `protobuf:"bytes,20,opt,name=rate_limit,json=rateLimit,proto3" json:"rate_limit,omitempty"` RateLimit *RateLimitSettings `protobuf:"bytes,20,opt,name=rate_limit,json=rateLimit,proto3" json:"rate_limit,omitempty"`
BlockChromePrefetch bool `protobuf:"varint,21,opt,name=block_chrome_prefetch,json=blockChromePrefetch,proto3" json:"block_chrome_prefetch,omitempty"`
} }
func (x *DNSProfile) Reset() { func (x *DNSProfile) Reset() {
@ -426,6 +427,13 @@ func (x *DNSProfile) GetRateLimit() *RateLimitSettings {
return nil return nil
} }
func (x *DNSProfile) GetBlockChromePrefetch() bool {
if x != nil {
return x.BlockChromePrefetch
}
return false
}
type isDNSProfile_BlockingMode interface { type isDNSProfile_BlockingMode interface {
isDNSProfile_BlockingMode() isDNSProfile_BlockingMode()
} }
@ -2001,7 +2009,7 @@ var file_dns_proto_rawDesc = []byte{
0x09, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x09, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x73, 0x79, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x73, 0x79,
0x6e, 0x63, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xad, 0x08, 0x0a, 0x0a, 0x44, 0x4e, 0x53, 0x50, 0x72, 0x6e, 0x63, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xe1, 0x08, 0x0a, 0x0a, 0x44, 0x4e, 0x53, 0x50, 0x72,
0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x6e, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x6e, 0x73, 0x5f, 0x69, 0x64, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x64, 0x6e, 0x73, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x64, 0x6e, 0x73, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x11,
0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65,
@ -2067,223 +2075,226 @@ var file_dns_proto_rawDesc = []byte{
0x65, 0x64, 0x12, 0x31, 0x0a, 0x0a, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x12, 0x31, 0x0a, 0x0a, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74,
0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d,
0x69, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x09, 0x72, 0x61, 0x74, 0x65, 0x69, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x09, 0x72, 0x61, 0x74, 0x65,
0x4c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x0f, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63,
0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x85, 0x01, 0x0a, 0x14, 0x53, 0x61, 0x66, 0x65, 0x42, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x74, 0x63, 0x68, 0x18, 0x15,
0x72, 0x6f, 0x77, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x68, 0x72, 0x6f, 0x6d,
0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x74, 0x63, 0x68, 0x42, 0x0f, 0x0a, 0x0d, 0x62, 0x6c, 0x6f,
0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x36, 0x0a, 0x17, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x85, 0x01, 0x0a, 0x14, 0x53,
0x63, 0x6b, 0x5f, 0x64, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x66, 0x65, 0x42, 0x72, 0x6f, 0x77, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69,
0x61, 0x69, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x62, 0x6c, 0x6f, 0x63, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01,
0x6b, 0x44, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x36, 0x0a,
0x73, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x72, 0x64, 0x18, 0x03, 0x17, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73,
0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x72, 0x64, 0x22, 0x8a, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15,
0x02, 0x0a, 0x0e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73, 0x44, 0x6f,
0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e,
0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e,
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69, 0x72, 0x64, 0x22, 0x8a, 0x02, 0x0a, 0x0e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65, 0x74,
0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x52, 0x10, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x5f, 0x69, 0x70, 0x18, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x69, 0x6c,
0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x49, 0x70, 0x12, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03,
0x23, 0x0a, 0x0d, 0x64, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x70, 0x73, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x45,
0x18, 0x05, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x64, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64,
0x64, 0x49, 0x70, 0x73, 0x12, 0x3f, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x5f, 0x69, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x65,
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x41, 0x64, 0x49, 0x70, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64,
0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x5f, 0x69, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x64, 0x65, 0x64, 0x69,
0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x63, 0x61, 0x74, 0x65, 0x64, 0x49, 0x70, 0x73, 0x12, 0x3f, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x68,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x68, 0x75, 0x6d, 0x61, 0x6e, 0x5f, 0x69, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b,
0x64, 0x5f, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x68, 0x32, 0x17, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
0x75, 0x6d, 0x61, 0x6e, 0x49, 0x64, 0x4c, 0x6f, 0x77, 0x65, 0x72, 0x22, 0x87, 0x02, 0x0a, 0x10, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x65,
0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x68, 0x75, 0x6d,
0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x5f, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28,
0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x6c, 0x09, 0x52, 0x0c, 0x68, 0x75, 0x6d, 0x61, 0x6e, 0x49, 0x64, 0x4c, 0x6f, 0x77, 0x65, 0x72, 0x22,
0x6f, 0x63, 0x6b, 0x5f, 0x61, 0x64, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x87, 0x02, 0x0a, 0x10, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x53, 0x65, 0x74, 0x74,
0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x64, 0x75, 0x6c, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x67, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18,
0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1f,
0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x61, 0x64, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20,
0x6c, 0x53, 0x61, 0x66, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x2e, 0x0a, 0x13, 0x79, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x64, 0x75, 0x6c, 0x74, 0x12,
0x6f, 0x75, 0x74, 0x75, 0x62, 0x65, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x2e, 0x0a, 0x13, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f,
0x63, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x79, 0x6f, 0x75, 0x74, 0x75, 0x62, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x67, 0x65,
0x65, 0x53, 0x61, 0x66, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x29, 0x0a, 0x10, 0x62, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x53, 0x61, 0x66, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12,
0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x2e, 0x0a, 0x13, 0x79, 0x6f, 0x75, 0x74, 0x75, 0x62, 0x65, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f,
0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x53, 0x65, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x79, 0x6f,
0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x75, 0x74, 0x75, 0x62, 0x65, 0x53, 0x61, 0x66, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12,
0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x29, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69,
0x75, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x73, 0x63, 0x68, 0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
0x65, 0x64, 0x75, 0x6c, 0x65, 0x22, 0x54, 0x0a, 0x10, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x08, 0x73, 0x63,
0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x6d, 0x7a, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x53,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x6d, 0x7a, 0x12, 0x2e, 0x0a, 0x0b, 0x77, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52,
0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x22, 0x54, 0x0a, 0x10, 0x53, 0x63, 0x68,
0x32, 0x0c, 0x2e, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0b, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x10, 0x0a,
0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x22, 0xd8, 0x01, 0x0a, 0x0b, 0x03, 0x74, 0x6d, 0x7a, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x6d, 0x7a, 0x12,
0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x03, 0x6d, 0x2e, 0x0a, 0x0b, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02,
0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e,
0x6e, 0x67, 0x65, 0x52, 0x03, 0x6d, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x03, 0x74, 0x75, 0x65, 0x18, 0x67, 0x65, 0x52, 0x0b, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x22,
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0xd8, 0x01, 0x0a, 0x0b, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12,
0x52, 0x03, 0x74, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x03, 0x77, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x1b, 0x0a, 0x03, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44,
0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x77, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x6d, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x03,
0x65, 0x64, 0x12, 0x1b, 0x0a, 0x03, 0x74, 0x68, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x74, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52,
0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x74, 0x68, 0x75, 0x12, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x74, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x03, 0x77, 0x65, 0x64,
0x1b, 0x0a, 0x03, 0x66, 0x72, 0x69, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67,
0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x66, 0x72, 0x69, 0x12, 0x1b, 0x0a, 0x03, 0x65, 0x52, 0x03, 0x77, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x03, 0x74, 0x68, 0x75, 0x18, 0x04, 0x20,
0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03,
0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x73, 0x61, 0x74, 0x12, 0x1b, 0x0a, 0x03, 0x73, 0x75, 0x6e, 0x74, 0x68, 0x75, 0x12, 0x1b, 0x0a, 0x03, 0x66, 0x72, 0x69, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b,
0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x66, 0x72, 0x69,
0x65, 0x52, 0x03, 0x73, 0x75, 0x6e, 0x22, 0x68, 0x0a, 0x08, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x12, 0x1b, 0x0a, 0x03, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e,
0x67, 0x65, 0x12, 0x2f, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x73, 0x61, 0x74, 0x12, 0x1b, 0x0a,
0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x03, 0x73, 0x75, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79,
0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x73, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x73, 0x75, 0x6e, 0x22, 0x68, 0x0a, 0x08, 0x44, 0x61,
0x61, 0x72, 0x74, 0x12, 0x2b, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x2f, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18,
0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x22, 0x3f, 0x0a, 0x11, 0x52, 0x75, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x53, 0x65, 0x74, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2b, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02,
0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x3f, 0x0a, 0x11, 0x52, 0x75, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74,
0x73, 0x22, 0x3e, 0x0a, 0x14, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61,
0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x49, 0x50, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62,
0x34, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x70, 0x76, 0x34, 0x12, 0x12, 0x0a, 0x6c, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
0x04, 0x69, 0x70, 0x76, 0x36, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x70, 0x76, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x3e, 0x0a, 0x14, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e,
0x36, 0x22, 0x16, 0x0a, 0x14, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x49, 0x50, 0x12, 0x12, 0x0a,
0x65, 0x4e, 0x58, 0x44, 0x4f, 0x4d, 0x41, 0x49, 0x4e, 0x22, 0x14, 0x0a, 0x12, 0x42, 0x6c, 0x6f, 0x04, 0x69, 0x70, 0x76, 0x34, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x70, 0x76,
0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x75, 0x6c, 0x6c, 0x49, 0x50, 0x22, 0x34, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x15, 0x0a, 0x13, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x69, 0x70, 0x76, 0x36, 0x22, 0x16, 0x0a, 0x14, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e,
0x45, 0x46, 0x55, 0x53, 0x45, 0x44, 0x22, 0xe3, 0x01, 0x0a, 0x11, 0x44, 0x65, 0x76, 0x69, 0x63, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x58, 0x44, 0x4f, 0x4d, 0x41, 0x49, 0x4e, 0x22, 0x14, 0x0a,
0x65, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x12, 0x48, 0x0a, 0x12, 0x12, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x75, 0x6c,
0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x69, 0x6c, 0x49, 0x50, 0x22, 0x15, 0x0a, 0x13, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d,
0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x6f, 0x64, 0x65, 0x52, 0x45, 0x46, 0x55, 0x53, 0x45, 0x44, 0x22, 0xe3, 0x01, 0x0a, 0x11, 0x44,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x65, 0x76, 0x69, 0x63, 0x65, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74,
0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x12, 0x48, 0x0a, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74,
0x74, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67,
0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54,
0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x63,
0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6c, 0x69, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65,
0x65, 0x6e, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64,
0x6f, 0x74, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6c, 0x69, 0x65, 0x6e,
0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x61, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x73, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x14,
0x01, 0x28, 0x0d, 0x52, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x22, 0x90, 0x02, 0x0a, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x70,
0x0e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28,
0x31, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x0d, 0x52, 0x03, 0x61, 0x73, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65,
0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73,
0x6e, 0x67, 0x65, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x43, 0x69, 0x22, 0x90, 0x02, 0x0a, 0x0e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69,
0x64, 0x72, 0x12, 0x31, 0x0a, 0x0e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x6e, 0x67, 0x73, 0x12, 0x31, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74,
0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69, 0x64, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69,
0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69,
0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x31, 0x0a, 0x0e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c,
0x73, 0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x61, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a,
0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x6c, 0x2e, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63,
0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c,
0x0d, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x6e, 0x12, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d,
0x34, 0x0a, 0x16, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x64, 0x6f, 0x6d, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x6e, 0x12, 0x23,
0x61, 0x69, 0x6e, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18,
0x14, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x04, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74,
0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x41, 0x73, 0x6e, 0x12, 0x34, 0x0a, 0x16, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74,
0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20,
0x3d, 0x0a, 0x09, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x03, 0x28, 0x09, 0x52, 0x14, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x44, 0x6f,
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61,
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0x85, 0x6c, 0x65, 0x64, 0x22, 0x3d, 0x0a, 0x09, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65,
0x01, 0x0a, 0x16, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x64, 0x6f, 0x68, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72,
0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66,
0x52, 0x0b, 0x64, 0x6f, 0x68, 0x41, 0x75, 0x74, 0x68, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x32, 0x0a, 0x69, 0x78, 0x22, 0x85, 0x01, 0x0a, 0x16, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63,
0x14, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x62, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x22, 0x0a,
0x63, 0x72, 0x79, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x12, 0x70, 0x0d, 0x64, 0x6f, 0x68, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01,
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x48, 0x61, 0x73, 0x68, 0x42, 0x63, 0x72, 0x79, 0x70, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x64, 0x6f, 0x68, 0x41, 0x75, 0x74, 0x68, 0x4f, 0x6e, 0x6c,
0x74, 0x42, 0x13, 0x0a, 0x11, 0x64, 0x6f, 0x68, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x79, 0x12, 0x32, 0x0a, 0x14, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61,
0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x22, 0x75, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x73, 0x68, 0x5f, 0x62, 0x63, 0x72, 0x79, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48,
0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x00, 0x52, 0x12, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x48, 0x61, 0x73, 0x68, 0x42,
0x06, 0x64, 0x6e, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x64, 0x63, 0x72, 0x79, 0x70, 0x74, 0x42, 0x13, 0x0a, 0x11, 0x64, 0x6f, 0x68, 0x5f, 0x70, 0x61, 0x73,
0x6e, 0x73, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x68, 0x75, 0x6d, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x22, 0x75, 0x0a, 0x13, 0x43, 0x72,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x68, 0x75, 0x6d, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x2c, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x6e, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x0b, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x09, 0x52, 0x05, 0x64, 0x6e, 0x73, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x68, 0x75, 0x6d, 0x61,
0x65, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3f, 0x0a, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x68, 0x75, 0x6d, 0x61,
0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x6e, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x74, 0x79,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0b, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63,
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70,
0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x22, 0x68, 0x65, 0x22, 0x3f, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63,
0x0a, 0x10, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x45, 0x72, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x06, 0x64, 0x65, 0x76,
0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x44, 0x65, 0x76, 0x69,
0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3a, 0x0a, 0x0b, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x06, 0x64, 0x65, 0x76, 0x69,
0x72, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x63, 0x65, 0x22, 0x68, 0x0a, 0x10, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65,
0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x72, 0x65, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x74, 0x72, 0x79, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x22, 0x34, 0x0a, 0x18, 0x44, 0x65, 0x76, 0x69, 0x12, 0x3a, 0x0a, 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18,
0x63, 0x65, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x45, 0x78, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x45, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x2b, 0x52, 0x0a, 0x72, 0x65, 0x74, 0x72, 0x79, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x22, 0x34, 0x0a, 0x18,
0x0a, 0x0f, 0x42, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x45, 0x78, 0x63, 0x65, 0x65,
0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x64, 0x65, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73,
0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x35, 0x0a, 0x19, 0x41,
0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x61, 0x69,
0x6c, 0x65, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x22, 0x6c, 0x0a, 0x11, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x53, 0x67, 0x65, 0x22, 0x2b, 0x0a, 0x0f, 0x42, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22,
0x64, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x70, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x35, 0x0a, 0x19, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
0x72, 0x70, 0x73, 0x12, 0x2b, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x69, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07,
0x64, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69, 0x64, 0x72, 0x52, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d,
0x61, 0x6e, 0x67, 0x65, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x69, 0x64, 0x72, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x6c, 0x0a, 0x11, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69,
0x22, 0x26, 0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x47, 0x65, 0x74, 0x52, 0x6d, 0x69, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e,
0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x64, 0x0a, 0x13, 0x52, 0x65, 0x6d, 0x6f, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x70, 0x73, 0x18, 0x02, 0x20, 0x01,
0x74, 0x65, 0x4b, 0x56, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0d, 0x52, 0x03, 0x72, 0x70, 0x73, 0x12, 0x2b, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e,
0x14, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43,
0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2e, 0x0a, 0x05, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x02, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x43, 0x69, 0x64, 0x72, 0x22, 0x26, 0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x48, 0x00, 0x52, 0x05, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65,
0x65, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x64, 0x0a, 0x13,
0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2b, 0x0a, 0x03, 0x74, 0x74,
0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x22, 0x15, 0x0a, 0x13, 0x52, 0x65, 0x6d, 0x6f, 0x74,
0x65, 0x4b, 0x56, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x87,
0x01, 0x0a, 0x0a, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a,
0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x57, 0x49,
0x4e, 0x44, 0x4f, 0x57, 0x53, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4e, 0x44, 0x52, 0x4f,
0x49, 0x44, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x4d, 0x41, 0x43, 0x10, 0x03, 0x12, 0x07, 0x0a,
0x03, 0x49, 0x4f, 0x53, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x49, 0x4e, 0x55, 0x58, 0x10,
0x05, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x52, 0x10, 0x06, 0x12, 0x0c, 0x0a,
0x08, 0x53, 0x4d, 0x41, 0x52, 0x54, 0x5f, 0x54, 0x56, 0x10, 0x07, 0x12, 0x10, 0x0a, 0x0c, 0x47,
0x41, 0x4d, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x53, 0x4f, 0x4c, 0x45, 0x10, 0x08, 0x12, 0x09, 0x0a,
0x05, 0x4f, 0x54, 0x48, 0x45, 0x52, 0x10, 0x09, 0x32, 0xd0, 0x01, 0x0a, 0x0a, 0x44, 0x4e, 0x53,
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x0e, 0x67, 0x65, 0x74, 0x44, 0x4e,
0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x13, 0x2e, 0x44, 0x4e, 0x53, 0x50,
0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0b,
0x2e, 0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x30, 0x01, 0x12, 0x46, 0x0a,
0x16, 0x73, 0x61, 0x76, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x42, 0x69, 0x6c, 0x6c,
0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x12, 0x12, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65,
0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
0x70, 0x74, 0x79, 0x28, 0x01, 0x12, 0x44, 0x0a, 0x15, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44,
0x65, 0x76, 0x69, 0x63, 0x65, 0x42, 0x79, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x14,
0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76,
0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x61, 0x0a, 0x10, 0x52,
0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12,
0x4d, 0x0a, 0x14, 0x67, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x53,
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x19, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69,
0x6d, 0x69, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x53, 0x65,
0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x75,
0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x12, 0x30, 0x0a, 0x03, 0x67, 0x65, 0x74, 0x12, 0x13, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74,
0x65, 0x4b, 0x56, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e,
0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x03, 0x73, 0x65, 0x74, 0x12, 0x13, 0x2e, 0x52, 0x65, 0x6d, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28,
0x6f, 0x74, 0x65, 0x4b, 0x56, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x48, 0x00, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2e, 0x0a, 0x05, 0x65, 0x6d, 0x70,
0x14, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3d, 0x0a, 0x21, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
0x75, 0x61, 0x72, 0x64, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x64, 0x6e, 0x73, 0x48, 0x00, 0x52, 0x05, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c,
0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x42, 0x10, 0x44, 0x4e, 0x53, 0x50, 0x75, 0x65, 0x22, 0x67, 0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x53, 0x65,
0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0xa2, 0x02, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
0x03, 0x44, 0x4e, 0x53, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61,
0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2b,
0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75,
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x22, 0x15, 0x0a, 0x13, 0x52,
0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x2a, 0x87, 0x01, 0x0a, 0x0a, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70,
0x65, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x0b,
0x0a, 0x07, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x53, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x41,
0x4e, 0x44, 0x52, 0x4f, 0x49, 0x44, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x4d, 0x41, 0x43, 0x10,
0x03, 0x12, 0x07, 0x0a, 0x03, 0x49, 0x4f, 0x53, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x49,
0x4e, 0x55, 0x58, 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x52, 0x10,
0x06, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x4d, 0x41, 0x52, 0x54, 0x5f, 0x54, 0x56, 0x10, 0x07, 0x12,
0x10, 0x0a, 0x0c, 0x47, 0x41, 0x4d, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x53, 0x4f, 0x4c, 0x45, 0x10,
0x08, 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x54, 0x48, 0x45, 0x52, 0x10, 0x09, 0x32, 0xd0, 0x01, 0x0a,
0x0a, 0x44, 0x4e, 0x53, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x0e, 0x67,
0x65, 0x74, 0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x13, 0x2e,
0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x30,
0x01, 0x12, 0x46, 0x0a, 0x16, 0x73, 0x61, 0x76, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73,
0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x12, 0x12, 0x2e, 0x44, 0x65,
0x76, 0x69, 0x63, 0x65, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x1a,
0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, 0x01, 0x12, 0x44, 0x0a, 0x15, 0x63, 0x72, 0x65,
0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x42, 0x79, 0x48, 0x75, 0x6d, 0x61, 0x6e,
0x49, 0x64, 0x12, 0x14, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63,
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74,
0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32,
0x61, 0x0a, 0x10, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x53, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x12, 0x4d, 0x0a, 0x14, 0x67, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69,
0x6d, 0x69, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x19, 0x2e, 0x52, 0x61,
0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d,
0x69, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x32, 0x75, 0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x53, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x30, 0x0a, 0x03, 0x67, 0x65, 0x74, 0x12, 0x13, 0x2e, 0x52,
0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x14, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x47, 0x65, 0x74, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x03, 0x73, 0x65, 0x74, 0x12, 0x13,
0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x53, 0x65,
0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3d, 0x0a, 0x21, 0x63, 0x6f, 0x6d,
0x2e, 0x61, 0x64, 0x67, 0x75, 0x61, 0x72, 0x64, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64,
0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x42, 0x10,
0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f,
0x50, 0x01, 0xa2, 0x02, 0x03, 0x44, 0x4e, 0x53, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (

View File

@ -108,6 +108,7 @@ message DNSProfile {
AccessSettings access = 18; AccessSettings access = 18;
bool auto_devices_enabled = 19; bool auto_devices_enabled = 19;
RateLimitSettings rate_limit = 20; RateLimitSettings rate_limit = 20;
bool block_chrome_prefetch = 21;
} }
message SafeBrowsingSettings { message SafeBrowsingSettings {

View File

@ -9,30 +9,15 @@ import (
status "google.golang.org/grpc/status" status "google.golang.org/grpc/status"
) )
// GRPCError is a type alias for string that contains the gRPC error type.
//
// TODO(s.chzhen): Rewrite as soon as the import cycle is resolved.
type GRPCError = string
// gRPC errors of [GRPCError] type.
const (
GRPCErrAuthentication GRPCError = "auth"
GRPCErrBadRequest GRPCError = "bad_req"
GRPCErrDeviceQuota GRPCError = "dev_quota"
GRPCErrOther GRPCError = "other"
GRPCErrRateLimit GRPCError = "rate_limit"
GRPCErrTimeout GRPCError = "timeout"
)
// fixGRPCError converts a gRPC error into an application error, if necessary. // fixGRPCError converts a gRPC error into an application error, if necessary.
// That includes gRPC deadlines, which do not match [context.DeadlineExceeded] // That includes gRPC deadlines, which do not match [context.DeadlineExceeded]
// correctly. // correctly.
// //
// It also updates the backend gRPC metrics depending on the type, see // It also updates the backend gRPC metrics depending on the type, see
// [Metrics.IncrementGRPCErrorCount]. // [GRPCMetrics.IncrementErrorCount].
func fixGRPCError(ctx context.Context, mtrc Metrics, err error) (res error) { func fixGRPCError(ctx context.Context, mtrc GRPCMetrics, err error) (res error) {
metricsType := GRPCErrOther metricsType := GRPCErrOther
defer func() { mtrc.IncrementGRPCErrorCount(ctx, metricsType) }() defer func() { mtrc.IncrementErrorCount(ctx, metricsType) }()
s, ok := status.FromError(err) s, ok := status.FromError(err)
if !ok { if !ok {

View File

@ -5,13 +5,47 @@ import (
"time" "time"
) )
// Metrics is an interface that is used for the collection of the protobuf // GRPCError is a type alias for string that contains the gRPC error type.
// errors statistics. //
type Metrics interface { // See [GRPCMetrics.IncrementErrorCount].
// IncrementGRPCErrorCount increments the gRPC error count of errType. //
// errType must be one of GRPCError values. // TODO(s.chzhen): Rewrite as soon as the import cycle is resolved.
IncrementGRPCErrorCount(ctx context.Context, errType GRPCError) type GRPCError = string
// gRPC errors of [GRPCError] type.
//
// NOTE: Keep in sync with [metrics.GRPCError].
const (
GRPCErrAuthentication GRPCError = "auth"
GRPCErrBadRequest GRPCError = "bad_req"
GRPCErrDeviceQuota GRPCError = "dev_quota"
GRPCErrOther GRPCError = "other"
GRPCErrRateLimit GRPCError = "rate_limit"
GRPCErrTimeout GRPCError = "timeout"
)
// GRPCMetrics is an interface that is used for the collection of the protobuf
// communication statistics.
type GRPCMetrics interface {
// IncrementErrorCount increments the gRPC error count of errType. errType
// must be one of [GRPCError] values.
IncrementErrorCount(ctx context.Context, errType GRPCError)
}
// EmptyGRPCMetrics is the implementation of the [GRPCMetrics] interface that
// does nothing.
type EmptyGRPCMetrics struct{}
// type check
var _ GRPCMetrics = EmptyGRPCMetrics{}
// IncrementErrorCount implements the [GRPCMetrics] interface for
// EmptyGRPCMetrics.
func (EmptyGRPCMetrics) IncrementErrorCount(_ context.Context, _ GRPCError) {}
// ProfileDBMetrics is an interface that is used for the collection of the
// profile database statistics.
type ProfileDBMetrics interface {
// IncrementInvalidDevicesCount increments the number of invalid devices. // IncrementInvalidDevicesCount increments the number of invalid devices.
IncrementInvalidDevicesCount(ctx context.Context) IncrementInvalidDevicesCount(ctx context.Context)
@ -19,18 +53,61 @@ type Metrics interface {
UpdateStats(ctx context.Context, avgRecv, avgDec time.Duration) UpdateStats(ctx context.Context, avgRecv, avgDec time.Duration)
} }
// EmptyMetrics is the implementation of the [Metrics] interface that does // EmptyProfileDBMetrics is the implementation of the [ProfileDBMetrics]
// nothing. // interface that does nothing.
type EmptyMetrics struct{} type EmptyProfileDBMetrics struct{}
// type check // type check
var _ Metrics = EmptyMetrics{} var _ ProfileDBMetrics = EmptyProfileDBMetrics{}
// IncrementGRPCErrorCount implements the [Metrics] interface for EmptyMetrics. // IncrementInvalidDevicesCount implements the [ProfileDBMetrics] interface for
func (EmptyMetrics) IncrementGRPCErrorCount(_ context.Context, errType GRPCError) {} // EmptyProfileDBMetrics.
func (EmptyProfileDBMetrics) IncrementInvalidDevicesCount(_ context.Context) {}
// IncrementInvalidDevicesCount implements the [Metrics] interface for EmptyMetrics. // UpdateStats implements the [ProfileDBMetrics] interface for
func (EmptyMetrics) IncrementInvalidDevicesCount(_ context.Context) {} // EmptyProfileDBMetrics.
func (EmptyProfileDBMetrics) UpdateStats(_ context.Context, _, _ time.Duration) {}
// UpdateStats implements the [Metrics] interface for EmptyMetrics. // RemoteKVOp is the type alias for string that contains remote key-value
func (EmptyMetrics) UpdateStats(_ context.Context, _, _ time.Duration) {} // storage operation name.
//
// See [RemoteKVMetrics.ObserveOperation].
type RemoteKVOp = string
// Remote key-value storage operation names for [RemoteKVOp].
//
// NOTE: Keep in sync with [metrics.RemoteKVOp].
const (
RemoteKVOpGet RemoteKVOp = "get"
RemoteKVOpSet RemoteKVOp = "set"
)
// RemoteKVMetrics is an interface that is used for the collection of the remote
// key-value storage statistics.
//
// TODO(e.burkov): It may actually describe metrics for any remote key-value
// storage implementation, consider declaring it in the [remotekv] package.
type RemoteKVMetrics interface {
// ObserveOperation updates the remote key-value storage statistics. op
// must be one of [RemoteKVOp] values.
ObserveOperation(ctx context.Context, op string, dur time.Duration)
// IncrementLookups increments the number of lookups. hit is true if the
// lookup returned a value.
IncrementLookups(ctx context.Context, hit bool)
}
// EmptyRemoteKVMetrics is the implementation of the [RemoteKVMetrics] interface
// that does nothing.
type EmptyRemoteKVMetrics struct{}
// type check
var _ RemoteKVMetrics = EmptyRemoteKVMetrics{}
// ObserveOperation implements the [RemoteKVMetrics] interface for
// EmptyRemoteKVMetrics.
func (EmptyRemoteKVMetrics) ObserveOperation(_ context.Context, _ string, _ time.Duration) {}
// IncrementLookups implements the [RemoteKVMetrics] interface for
// EmptyRemoteKVMetrics.
func (EmptyRemoteKVMetrics) IncrementLookups(_ context.Context, _ bool) {}

View File

@ -3,6 +3,7 @@ package backendpb
import ( import (
"context" "context"
"fmt" "fmt"
"log/slog"
"net/netip" "net/netip"
"time" "time"
@ -11,6 +12,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime" "github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll" "github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip" "github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/netutil"
@ -20,20 +22,21 @@ import (
// toInternal converts the protobuf-encoded data into a profile structure and // toInternal converts the protobuf-encoded data into a profile structure and
// its device structures. // its device structures.
// //
// TODO(a.garipov): Refactor into a method of [*ProfileStorage]? // TODO(a.garipov): Refactor into methods of [*ProfileStorage].
func (x *DNSProfile) toInternal( func (x *DNSProfile) toInternal(
ctx context.Context, ctx context.Context,
updTime time.Time, updTime time.Time,
bindSet netutil.SubnetSet, bindSet netutil.SubnetSet,
errColl errcoll.Interface, errColl errcoll.Interface,
mtrc Metrics, logger *slog.Logger,
mtrc ProfileDBMetrics,
respSzEst datasize.ByteSize, respSzEst datasize.ByteSize,
) (profile *agd.Profile, devices []*agd.Device, err error) { ) (profile *agd.Profile, devices []*agd.Device, err error) {
if x == nil { if x == nil {
return nil, nil, fmt.Errorf("profile is nil") return nil, nil, fmt.Errorf("profile is nil")
} }
parental, err := x.Parental.toInternal(ctx, errColl) parental, err := x.Parental.toInternal(ctx, errColl, logger)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("parental: %w", err) return nil, nil, fmt.Errorf("parental: %w", err)
} }
@ -43,8 +46,7 @@ func (x *DNSProfile) toInternal(
return nil, nil, fmt.Errorf("blocking mode: %w", err) return nil, nil, fmt.Errorf("blocking mode: %w", err)
} }
devices, deviceIds := devicesToInternal(ctx, x.Devices, bindSet, errColl, mtrc) devices, deviceIds := devicesToInternal(ctx, x.Devices, bindSet, errColl, logger, mtrc)
listsEnabled, listIDs := x.RuleLists.toInternal(ctx, errColl)
profID, err := agd.NewProfileID(x.DnsId) profID, err := agd.NewProfileID(x.DnsId)
if err != nil { if err != nil {
@ -56,52 +58,63 @@ func (x *DNSProfile) toInternal(
fltRespTTL = respTTL.AsDuration() fltRespTTL = respTTL.AsDuration()
} }
return &agd.Profile{ customRules := rulesToInternal(ctx, x.CustomRules, errColl, logger)
Parental: parental, custom := &filter.ConfigCustom{
BlockingMode: m, ID: string(x.DnsId),
ID: profID,
UpdateTime: updTime, UpdateTime: updTime,
DeviceIDs: deviceIds, Rules: customRules,
RuleListIDs: listIDs, // TODO(a.garipov): Consider adding an explicit flag to the protocol.
CustomRules: rulesToInternal(ctx, x.CustomRules, errColl), Enabled: len(customRules) > 0,
FilteredResponseTTL: fltRespTTL, }
FilteringEnabled: x.FilteringEnabled,
Ratelimiter: x.RateLimit.toInternal(ctx, errColl, respSzEst), return &agd.Profile{
FilterConfig: &filter.ConfigClient{
Custom: custom,
Parental: parental,
RuleList: x.RuleLists.toInternal(ctx, errColl, logger),
SafeBrowsing: x.SafeBrowsing.toInternal(), SafeBrowsing: x.SafeBrowsing.toInternal(),
Access: x.Access.toInternal(ctx, errColl), },
RuleListsEnabled: listsEnabled, Access: x.Access.toInternal(ctx, errColl, logger),
QueryLogEnabled: x.QueryLogEnabled, BlockingMode: m,
Deleted: x.Deleted, Ratelimiter: x.RateLimit.toInternal(ctx, errColl, logger, respSzEst),
BlockPrivateRelay: x.BlockPrivateRelay, ID: profID,
BlockFirefoxCanary: x.BlockFirefoxCanary, DeviceIDs: deviceIds,
IPLogEnabled: x.IpLogEnabled, FilteredResponseTTL: fltRespTTL,
AutoDevicesEnabled: x.AutoDevicesEnabled, AutoDevicesEnabled: x.AutoDevicesEnabled,
BlockChromePrefetch: x.BlockChromePrefetch,
BlockFirefoxCanary: x.BlockFirefoxCanary,
BlockPrivateRelay: x.BlockPrivateRelay,
Deleted: x.Deleted,
FilteringEnabled: x.FilteringEnabled,
IPLogEnabled: x.IpLogEnabled,
QueryLogEnabled: x.QueryLogEnabled,
}, devices, nil }, devices, nil
} }
// toInternal converts a protobuf parental-settings structure to an internal // toInternal converts a protobuf parental-protection settings structure to an
// one. If x is nil, toInternal returns nil. // internal one. If x is nil, toInternal returns a disabled configuration.
func (x *ParentalSettings) toInternal( func (x *ParentalSettings) toInternal(
ctx context.Context, ctx context.Context,
errColl errcoll.Interface, errColl errcoll.Interface,
) (s *agd.ParentalProtectionSettings, err error) { logger *slog.Logger,
) (c *filter.ConfigParental, err error) {
c = &filter.ConfigParental{}
if x == nil { if x == nil {
return nil, nil return c, nil
} }
schedule, err := x.Schedule.toInternal() c.AdultBlockingEnabled = x.BlockAdult
c.BlockedServices = blockedSvcsToInternal(ctx, errColl, logger, x.BlockedServices)
c.Enabled = x.Enabled
c.SafeSearchGeneralEnabled = x.GeneralSafeSearch
c.SafeSearchYouTubeEnabled = x.YoutubeSafeSearch
c.PauseSchedule, err = x.Schedule.toInternal()
if err != nil { if err != nil {
return nil, fmt.Errorf("schedule: %w", err) return nil, fmt.Errorf("pause schedule: %w", err)
} }
return &agd.ParentalProtectionSettings{ return c, nil
Schedule: schedule,
BlockedServices: blockedSvcsToInternal(ctx, errColl, x.BlockedServices),
Enabled: x.Enabled,
BlockAdult: x.BlockAdult,
GeneralSafeSearch: x.GeneralSafeSearch,
YoutubeSafeSearch: x.YoutubeSafeSearch,
}, nil
} }
// toInternal converts protobuf rate-limiting settings to an internal structure. // toInternal converts protobuf rate-limiting settings to an internal structure.
@ -109,6 +122,7 @@ func (x *ParentalSettings) toInternal(
func (x *RateLimitSettings) toInternal( func (x *RateLimitSettings) toInternal(
ctx context.Context, ctx context.Context,
errColl errcoll.Interface, errColl errcoll.Interface,
logger *slog.Logger,
respSzEst datasize.ByteSize, respSzEst datasize.ByteSize,
) (r agd.Ratelimiter) { ) (r agd.Ratelimiter) {
if x == nil || !x.Enabled { if x == nil || !x.Enabled {
@ -116,24 +130,26 @@ func (x *RateLimitSettings) toInternal(
} }
return agd.NewDefaultRatelimiter(&agd.RatelimitConfig{ return agd.NewDefaultRatelimiter(&agd.RatelimitConfig{
ClientSubnets: cidrRangeToInternal(ctx, errColl, x.ClientCidr), ClientSubnets: cidrRangeToInternal(ctx, errColl, logger, x.ClientCidr),
RPS: x.Rps, RPS: x.Rps,
Enabled: x.Enabled, Enabled: x.Enabled,
}, respSzEst) }, respSzEst)
} }
// toInternal converts protobuf safe-browsing settings to an internal structure. // toInternal converts protobuf safe-browsing settings to an internal
// If x is nil, toInternal returns nil. // safe-browsing configuration. If x is nil, toInternal returns a disabled
func (x *SafeBrowsingSettings) toInternal() (sb *agd.SafeBrowsingSettings) { // configuration.
func (x *SafeBrowsingSettings) toInternal() (c *filter.ConfigSafeBrowsing) {
c = &filter.ConfigSafeBrowsing{}
if x == nil { if x == nil {
return nil return c
} }
return &agd.SafeBrowsingSettings{ c.Enabled = x.Enabled
Enabled: x.Enabled, c.DangerousDomainsEnabled = x.BlockDangerousDomains
BlockDangerousDomains: x.BlockDangerousDomains, c.NewlyRegisteredDomainsEnabled = x.BlockNrd
BlockNewlyRegisteredDomains: x.BlockNrd,
} return c
} }
// toInternal converts protobuf access settings to an internal structure. If x // toInternal converts protobuf access settings to an internal structure. If x
@ -141,14 +157,15 @@ func (x *SafeBrowsingSettings) toInternal() (sb *agd.SafeBrowsingSettings) {
func (x *AccessSettings) toInternal( func (x *AccessSettings) toInternal(
ctx context.Context, ctx context.Context,
errColl errcoll.Interface, errColl errcoll.Interface,
logger *slog.Logger,
) (a access.Profile) { ) (a access.Profile) {
if x == nil || !x.Enabled { if x == nil || !x.Enabled {
return access.EmptyProfile{} return access.EmptyProfile{}
} }
return access.NewDefaultProfile(&access.ProfileConfig{ return access.NewDefaultProfile(&access.ProfileConfig{
AllowedNets: cidrRangeToInternal(ctx, errColl, x.AllowlistCidr), AllowedNets: cidrRangeToInternal(ctx, errColl, logger, x.AllowlistCidr),
BlockedNets: cidrRangeToInternal(ctx, errColl, x.BlocklistCidr), BlockedNets: cidrRangeToInternal(ctx, errColl, logger, x.BlocklistCidr),
AllowedASN: asnToInternal(x.AllowlistAsn), AllowedASN: asnToInternal(x.AllowlistAsn),
BlockedASN: asnToInternal(x.BlocklistAsn), BlockedASN: asnToInternal(x.BlocklistAsn),
BlocklistDomainRules: x.BlocklistDomainRules, BlocklistDomainRules: x.BlocklistDomainRules,
@ -160,12 +177,14 @@ func (x *AccessSettings) toInternal(
func cidrRangeToInternal( func cidrRangeToInternal(
ctx context.Context, ctx context.Context,
errColl errcoll.Interface, errColl errcoll.Interface,
logger *slog.Logger,
cidrs []*CidrRange, cidrs []*CidrRange,
) (out []netip.Prefix) { ) (out []netip.Prefix) {
for i, c := range cidrs { for i, c := range cidrs {
addr, ok := netip.AddrFromSlice(c.Address) addr, ok := netip.AddrFromSlice(c.Address)
if !ok { if !ok {
reportf(ctx, errColl, "bad cidr at index %d: %v", i, c.Address) err := fmt.Errorf("bad cidr at index %d: %v", i, c.Address)
errcoll.Collect(ctx, errColl, logger, "converting cidrs", err)
continue continue
} }
@ -187,71 +206,71 @@ func asnToInternal(asns []uint32) (out []geoip.ASN) {
} }
// blockedSvcsToInternal is a helper that converts the blocked service IDs from // blockedSvcsToInternal is a helper that converts the blocked service IDs from
// the backend response to AdGuard DNS blocked service IDs. // the backend response to AdGuard DNS blocked-service IDs.
func blockedSvcsToInternal( func blockedSvcsToInternal(
ctx context.Context, ctx context.Context,
errColl errcoll.Interface, errColl errcoll.Interface,
logger *slog.Logger,
respSvcs []string, respSvcs []string,
) (svcs []agd.BlockedServiceID) { ) (ids []filter.BlockedServiceID) {
l := len(respSvcs) l := len(respSvcs)
if l == 0 { if l == 0 {
return nil return nil
} }
svcs = make([]agd.BlockedServiceID, 0, l) ids = make([]filter.BlockedServiceID, 0, l)
for i, s := range respSvcs { for i, idStr := range respSvcs {
id, err := agd.NewBlockedServiceID(s) id, err := filter.NewBlockedServiceID(idStr)
if err != nil { if err != nil {
reportf(ctx, errColl, "blocked service at index %d: %w", i, err) err = fmt.Errorf("at index %d: %w", i, err)
errcoll.Collect(ctx, errColl, logger, "converting blocked services", err)
continue continue
} }
svcs = append(svcs, id) ids = append(ids, id)
} }
return svcs return ids
} }
// toInternal converts a protobuf protection-schedule structure to an internal // toInternal converts a protobuf protection-schedule structure to an internal
// one. If x is nil, toInternal returns nil. // one. If x is nil, toInternal returns nil.
func (x *ScheduleSettings) toInternal() (sch *agd.ParentalProtectionSchedule, err error) { func (x *ScheduleSettings) toInternal() (c *filter.ConfigSchedule, err error) {
if x == nil { if x == nil {
return nil, nil return nil, nil
} }
sch = &agd.ParentalProtectionSchedule{} c = &filter.ConfigSchedule{
Week: &filter.WeeklySchedule{},
}
sch.TimeZone, err = agdtime.LoadLocation(x.Tmz) c.TimeZone, err = agdtime.LoadLocation(x.Tmz)
if err != nil { if err != nil {
return nil, fmt.Errorf("loading timezone: %w", err) return nil, fmt.Errorf("loading timezone: %w", err)
} }
sch.Week = &agd.WeeklySchedule{}
w := x.WeeklyRange w := x.WeeklyRange
days := []*DayRange{w.Sun, w.Mon, w.Tue, w.Wed, w.Thu, w.Fri, w.Sat} days := []*DayRange{w.Sun, w.Mon, w.Tue, w.Wed, w.Thu, w.Fri, w.Sat}
for i, d := range days { for i, d := range days {
if d == nil { if d == nil {
sch.Week[i] = agd.ZeroLengthDayRange()
continue continue
} }
sch.Week[i] = agd.DayRange{ ivl := &filter.DayInterval{
Start: uint16(d.Start.AsDuration().Minutes()), Start: uint16(d.Start.AsDuration().Minutes()),
End: uint16(d.End.AsDuration().Minutes()), End: uint16(d.End.AsDuration().Minutes() + 1),
}
} }
for i, r := range sch.Week { err = ivl.Validate()
err = r.Validate()
if err != nil { if err != nil {
return nil, fmt.Errorf("weekday %s: %w", time.Weekday(i), err) return nil, fmt.Errorf("weekday %s: %w", time.Weekday(i), err)
} }
c.Week[i] = ivl
} }
return sch, nil return c, nil
} }
// toInternal converts a protobuf custom blocking-mode to an internal one. // toInternal converts a protobuf custom blocking-mode to an internal one.
@ -312,17 +331,19 @@ func rulesToInternal(
ctx context.Context, ctx context.Context,
respRules []string, respRules []string,
errColl errcoll.Interface, errColl errcoll.Interface,
) (rules []agd.FilterRuleText) { logger *slog.Logger,
) (rules []filter.RuleText) {
l := len(respRules) l := len(respRules)
if l == 0 { if l == 0 {
return nil return nil
} }
rules = make([]agd.FilterRuleText, 0, l) rules = make([]filter.RuleText, 0, l)
for i, r := range respRules { for i, r := range respRules {
text, err := agd.NewFilterRuleText(r) text, err := filter.NewRuleText(r)
if err != nil { if err != nil {
reportf(ctx, errColl, "rule at index %d: %w", i, err) err = fmt.Errorf("at index %d: %w", i, err)
errcoll.Collect(ctx, errColl, logger, "converting rules", err)
continue continue
} }
@ -334,32 +355,32 @@ func rulesToInternal(
} }
// toInternal is a helper that converts the filter lists from the backend // toInternal is a helper that converts the filter lists from the backend
// response to AdGuard DNS filter list ids. If x is nil, toInternal returns // response to AdGuard DNS rule-list configuration. If x is nil, toInternal
// false and nil. // returns a disabled configuration.
func (x *RuleListsSettings) toInternal( func (x *RuleListsSettings) toInternal(
ctx context.Context, ctx context.Context,
errColl errcoll.Interface, errColl errcoll.Interface,
) (enabled bool, filterLists []agd.FilterListID) { logger *slog.Logger,
) (c *filter.ConfigRuleList) {
c = &filter.ConfigRuleList{}
if x == nil { if x == nil {
return false, nil return c
} }
l := len(x.Ids) c.Enabled = x.Enabled
if l == 0 { c.IDs = make([]filter.ID, 0, len(x.Ids))
return x.Enabled, nil
}
filterLists = make([]agd.FilterListID, 0, l) for i, idStr := range x.Ids {
for i, f := range x.Ids { id, err := filter.NewID(idStr)
id, err := agd.NewFilterListID(f)
if err != nil { if err != nil {
reportf(ctx, errColl, "filter id: at index %d: %w", i, err) err = fmt.Errorf("at index %d: %w", i, err)
errcoll.Collect(ctx, errColl, logger, "converting filter id", err)
continue continue
} }
filterLists = append(filterLists, id) c.IDs = append(c.IDs, id)
} }
return x.Enabled, filterLists return c
} }

View File

@ -12,6 +12,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest" "github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime" "github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip" "github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/golibs/testutil" "github.com/AdguardTeam/golibs/testutil"
"github.com/c2h5oh/datasize" "github.com/c2h5oh/datasize"
@ -35,7 +36,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime, TestUpdTime,
TestBind, TestBind,
errColl, errColl,
EmptyMetrics{}, TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst, TestRespSzEst,
) )
require.NoError(t, err) require.NoError(t, err)
@ -58,14 +60,15 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime, TestUpdTime,
TestBind, TestBind,
savingErrColl, savingErrColl,
EmptyMetrics{}, TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst, TestRespSzEst,
) )
require.NoError(t, err) require.NoError(t, err)
testutil.AssertErrorMsg( testutil.AssertErrorMsg(
t, t,
`backendpb: bad device settings for device with id "inv-d-ip":`+ `converting device: bad settings for device with id "inv-d-ip":`+
" dedicated ips: ip at index 0: unexpected slice size", ` dedicated ips: ip at index 0: unexpected slice size`,
errCollErr, errCollErr,
) )
@ -89,13 +92,14 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime, TestUpdTime,
bindSet, bindSet,
savingErrColl, savingErrColl,
EmptyMetrics{}, TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst, TestRespSzEst,
) )
require.NoError(t, err) require.NoError(t, err)
testutil.AssertErrorMsg( testutil.AssertErrorMsg(
t, t,
`backendpb: bad device settings for device with id "`+TestDeviceIDStr+`":`+ `converting device: bad settings for device with id "`+TestDeviceIDStr+`":`+
" dedicated ips: at index 0: \"1.1.1.2\" is not in bind data", " dedicated ips: at index 0: \"1.1.1.2\" is not in bind data",
errCollErr, errCollErr,
) )
@ -114,7 +118,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime, TestUpdTime,
TestBind, TestBind,
errColl, errColl,
EmptyMetrics{}, TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst, TestRespSzEst,
) )
testutil.AssertErrorMsg(t, "profile is nil", err) testutil.AssertErrorMsg(t, "profile is nil", err)
@ -133,7 +138,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime, TestUpdTime,
TestBind, TestBind,
errColl, errColl,
EmptyMetrics{}, TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst, TestRespSzEst,
) )
require.NoError(t, err) require.NoError(t, err)
@ -155,12 +161,13 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime, TestUpdTime,
TestBind, TestBind,
errColl, errColl,
EmptyMetrics{}, TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst, TestRespSzEst,
) )
testutil.AssertErrorMsg( testutil.AssertErrorMsg(
t, t,
"parental: schedule: loading timezone: unknown time zone invalid", "parental: pause schedule: loading timezone: unknown time zone invalid",
err, err,
) )
}) })
@ -179,12 +186,13 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime, TestUpdTime,
TestBind, TestBind,
errColl, errColl,
EmptyMetrics{}, TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst, TestRespSzEst,
) )
testutil.AssertErrorMsg( testutil.AssertErrorMsg(
t, t,
"parental: schedule: weekday Sunday: bad day range: end 0 less than start 16", "parental: pause schedule: weekday Sunday: end: out of range: 1 is less than start 16",
err, err,
) )
}) })
@ -201,7 +209,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime, TestUpdTime,
TestBind, TestBind,
errColl, errColl,
EmptyMetrics{}, TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst, TestRespSzEst,
) )
testutil.AssertErrorMsg(t, "blocking mode: bad custom ipv4: unexpected slice size", err) testutil.AssertErrorMsg(t, "blocking mode: bad custom ipv4: unexpected slice size", err)
@ -219,7 +228,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime, TestUpdTime,
TestBind, TestBind,
errColl, errColl,
EmptyMetrics{}, TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst, TestRespSzEst,
) )
testutil.AssertErrorMsg(t, "blocking mode: bad custom ipv6: unexpected slice size", err) testutil.AssertErrorMsg(t, "blocking mode: bad custom ipv6: unexpected slice size", err)
@ -238,7 +248,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime, TestUpdTime,
TestBind, TestBind,
errColl, errColl,
EmptyMetrics{}, TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst, TestRespSzEst,
) )
testutil.AssertErrorMsg(t, "blocking mode: no valid custom ips found", err) testutil.AssertErrorMsg(t, "blocking mode: no valid custom ips found", err)
@ -255,7 +266,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime, TestUpdTime,
TestBind, TestBind,
errColl, errColl,
EmptyMetrics{}, TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst, TestRespSzEst,
) )
require.NoError(t, err) require.NoError(t, err)
@ -279,7 +291,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime, TestUpdTime,
TestBind, TestBind,
errColl, errColl,
EmptyMetrics{}, TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst, TestRespSzEst,
) )
require.NoError(t, err) require.NoError(t, err)
@ -302,7 +315,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime, TestUpdTime,
TestBind, TestBind,
errColl, errColl,
EmptyMetrics{}, TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst, TestRespSzEst,
) )
require.NoError(t, err) require.NoError(t, err)
@ -464,6 +478,7 @@ func NewTestDNSProfile(tb testing.TB) (dp *DNSProfile) {
Prefix: 24, Prefix: 24,
}}, }},
}, },
BlockChromePrefetch: true,
} }
} }
@ -484,35 +499,37 @@ func newProfile(tb testing.TB) (p *agd.Profile) {
wantLoc, err := agdtime.LoadLocation("GMT") wantLoc, err := agdtime.LoadLocation("GMT")
require.NoError(tb, err) require.NoError(tb, err)
dayRange := agd.DayRange{ dayIvl := &filter.DayInterval{
Start: 0, Start: 0,
End: 59, End: 60,
} }
wantParental := &agd.ParentalProtectionSettings{ wantParental := &filter.ConfigParental{
Schedule: &agd.ParentalProtectionSchedule{ PauseSchedule: &filter.ConfigSchedule{
Week: &agd.WeeklySchedule{ Week: &filter.WeeklySchedule{
agd.ZeroLengthDayRange(), nil,
dayRange, dayIvl,
dayRange, dayIvl,
dayRange, dayIvl,
dayRange, dayIvl,
dayRange, dayIvl,
agd.ZeroLengthDayRange(), nil,
}, },
TimeZone: wantLoc, TimeZone: wantLoc,
}, },
BlockedServices: []agd.BlockedServiceID{"youtube"}, BlockedServices: []filter.BlockedServiceID{
"youtube",
},
Enabled: false, Enabled: false,
BlockAdult: false, AdultBlockingEnabled: false,
GeneralSafeSearch: false, SafeSearchGeneralEnabled: false,
YoutubeSafeSearch: false, SafeSearchYouTubeEnabled: false,
} }
wantSafeBrowsing := &agd.SafeBrowsingSettings{ wantSafeBrowsing := &filter.ConfigSafeBrowsing{
Enabled: true, Enabled: true,
BlockDangerousDomains: true, DangerousDomainsEnabled: true,
BlockNewlyRegisteredDomains: false, NewlyRegisteredDomainsEnabled: false,
} }
wantBlockingMode := &dnsmsg.BlockingModeCustomIP{ wantBlockingMode := &dnsmsg.BlockingModeCustomIP{
@ -535,30 +552,39 @@ func newProfile(tb testing.TB) (p *agd.Profile) {
}, 1*datasize.KB) }, 1*datasize.KB)
return &agd.Profile{ return &agd.Profile{
Parental: wantParental, FilterConfig: &filter.ConfigClient{
BlockingMode: wantBlockingMode, Custom: &filter.ConfigCustom{
ID: TestProfileID, ID: TestProfileIDStr,
UpdateTime: TestUpdTime, UpdateTime: TestUpdTime,
Rules: []filter.RuleText{"||example.org^"},
Enabled: true,
},
Parental: wantParental,
RuleList: &filter.ConfigRuleList{
IDs: []filter.ID{"1"},
Enabled: true,
},
SafeBrowsing: wantSafeBrowsing,
},
Access: wantAccess,
BlockingMode: wantBlockingMode,
Ratelimiter: wantRateLimiter,
ID: TestProfileID,
DeviceIDs: []agd.DeviceID{ DeviceIDs: []agd.DeviceID{
TestDeviceID, TestDeviceID,
"2222bbbb", "2222bbbb",
"3333cccc", "3333cccc",
"4444dddd", "4444dddd",
}, },
RuleListIDs: []agd.FilterListID{"1"},
CustomRules: []agd.FilterRuleText{"||example.org^"},
FilteredResponseTTL: 10 * time.Second, FilteredResponseTTL: 10 * time.Second,
Ratelimiter: wantRateLimiter,
SafeBrowsing: wantSafeBrowsing,
Access: wantAccess,
RuleListsEnabled: true,
FilteringEnabled: true,
QueryLogEnabled: true,
Deleted: false,
BlockPrivateRelay: true,
BlockFirefoxCanary: true,
IPLogEnabled: true,
AutoDevicesEnabled: true, AutoDevicesEnabled: true,
BlockChromePrefetch: true,
BlockFirefoxCanary: true,
BlockPrivateRelay: true,
Deleted: false,
FilteringEnabled: true,
IPLogEnabled: true,
QueryLogEnabled: true,
} }
} }
@ -631,7 +657,8 @@ func BenchmarkDNSProfile_ToInternal(b *testing.B) {
TestUpdTime, TestUpdTime,
TestBind, TestBind,
errColl, errColl,
EmptyMetrics{}, TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst, TestRespSzEst,
) )
} }
@ -639,10 +666,10 @@ func BenchmarkDNSProfile_ToInternal(b *testing.B) {
require.NotNil(b, profSink) require.NotNil(b, profSink)
require.NoError(b, errSink) require.NoError(b, errSink)
// Most recent result, on a ThinkPad X13: // Most recent results:
// goos: linux // goos: linux
// goarch: amd64 // goarch: amd64
// pkg: github.com/AdguardTeam/AdGuardDNS/internal/backendpb // pkg: github.com/AdguardTeam/AdGuardDNS/internal/backendpb
// cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics // cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
// BenchmarkDNSProfile_ToInternal-16 67160 22130 ns/op 3048 B/op 51 allocs/op // BenchmarkDNSProfile_ToInternal-16 79732 14686 ns/op 3160 B/op 59 allocs/op
} }

View File

@ -31,12 +31,16 @@ type ProfileStorageConfig struct {
// non-critical errors. It must not be nil. // non-critical errors. It must not be nil.
ErrColl errcoll.Interface ErrColl errcoll.Interface
// Logger is used as the base logger for the profile storage. It must not // Logger is used for logging the operation of the profile storage. It must
// be nil. // not be nil.
Logger *slog.Logger Logger *slog.Logger
// Metrics is used for the collection of the protobuf errors. // GRPCMetrics is used for the collection of the protobuf communication
Metrics Metrics // statistics.
GRPCMetrics GRPCMetrics
// Metrics is used for the collection of the profiles storage statistics.
Metrics ProfileDBMetrics
// Endpoint is the backend API URL. The scheme should be either "grpc" or // Endpoint is the backend API URL. The scheme should be either "grpc" or
// "grpcs". It must not be nil. // "grpcs". It must not be nil.
@ -63,7 +67,8 @@ type ProfileStorage struct {
errColl errcoll.Interface errColl errcoll.Interface
client DNSServiceClient client DNSServiceClient
logger *slog.Logger logger *slog.Logger
metrics Metrics grpcMetrics GRPCMetrics
metrics ProfileDBMetrics
apiKey string apiKey string
respSzEst datasize.ByteSize respSzEst datasize.ByteSize
maxProfSize datasize.ByteSize maxProfSize datasize.ByteSize
@ -83,6 +88,7 @@ func NewProfileStorage(c *ProfileStorageConfig) (s *ProfileStorage, err error) {
errColl: c.ErrColl, errColl: c.ErrColl,
client: NewDNSServiceClient(client), client: NewDNSServiceClient(client),
logger: c.Logger, logger: c.Logger,
grpcMetrics: c.GRPCMetrics,
metrics: c.Metrics, metrics: c.Metrics,
apiKey: c.APIKey, apiKey: c.APIKey,
respSzEst: c.ResponseSizeEstimate, respSzEst: c.ResponseSizeEstimate,
@ -115,7 +121,7 @@ func (s *ProfileStorage) CreateAutoDevice(
DeviceType: DeviceType(req.DeviceType), DeviceType: DeviceType(req.DeviceType),
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("calling backend: %w", fixGRPCError(ctx, s.metrics, err)) return nil, fmt.Errorf("calling backend: %w", fixGRPCError(ctx, s.grpcMetrics, err))
} }
d, err := backendResp.Device.toInternal(s.bindSet) d, err := backendResp.Device.toInternal(s.bindSet)
@ -140,7 +146,7 @@ func (s *ProfileStorage) Profiles(
respSzOpt := grpc.MaxCallRecvMsgSize(int(s.maxProfSize.Bytes())) respSzOpt := grpc.MaxCallRecvMsgSize(int(s.maxProfSize.Bytes()))
stream, err := s.client.GetDNSProfiles(ctx, toProtobuf(req), respSzOpt) stream, err := s.client.GetDNSProfiles(ctx, toProtobuf(req), respSzOpt)
if err != nil { if err != nil {
return nil, fmt.Errorf("loading profiles: %w", fixGRPCError(ctx, s.metrics, err)) return nil, fmt.Errorf("loading profiles: %w", fixGRPCError(ctx, s.grpcMetrics, err))
} }
defer func() { err = errors.WithDeferred(err, stream.CloseSend()) }() defer func() { err = errors.WithDeferred(err, stream.CloseSend()) }()
@ -165,7 +171,7 @@ func (s *ProfileStorage) Profiles(
return nil, fmt.Errorf( return nil, fmt.Errorf(
"receiving profile #%d: %w", "receiving profile #%d: %w",
n, n,
fixGRPCError(ctx, s.metrics, profErr), fixGRPCError(ctx, s.grpcMetrics, profErr),
) )
} }
stats.endRecv() stats.endRecv()
@ -176,11 +182,12 @@ func (s *ProfileStorage) Profiles(
time.Now(), time.Now(),
s.bindSet, s.bindSet,
s.errColl, s.errColl,
s.logger,
s.metrics, s.metrics,
s.respSzEst, s.respSzEst,
) )
if profErr != nil { if profErr != nil {
reportf(ctx, s.errColl, "loading profile: %w", profErr) errcoll.Collect(ctx, s.errColl, s.logger, "loading profile", profErr)
continue continue
} }

View File

@ -13,7 +13,6 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest" "github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/AdGuardDNS/internal/backendpb" "github.com/AdguardTeam/AdGuardDNS/internal/backendpb"
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb" "github.com/AdguardTeam/AdGuardDNS/internal/profiledb"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil" "github.com/AdguardTeam/golibs/testutil"
"github.com/c2h5oh/datasize" "github.com/c2h5oh/datasize"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -69,8 +68,9 @@ func TestProfileStorage_CreateAutoDevice(t *testing.T) {
s, err := backendpb.NewProfileStorage(&backendpb.ProfileStorageConfig{ s, err := backendpb.NewProfileStorage(&backendpb.ProfileStorageConfig{
BindSet: backendpb.TestBind, BindSet: backendpb.TestBind,
ErrColl: agdtest.NewErrorCollector(), ErrColl: agdtest.NewErrorCollector(),
Logger: slogutil.NewDiscardLogger(), Logger: backendpb.TestLogger,
Metrics: backendpb.EmptyMetrics{}, GRPCMetrics: backendpb.EmptyGRPCMetrics{},
Metrics: backendpb.EmptyProfileDBMetrics{},
Endpoint: &url.URL{ Endpoint: &url.URL{
Scheme: "grpc", Scheme: "grpc",
Host: l.Addr().String(), Host: l.Addr().String(),
@ -159,8 +159,9 @@ func BenchmarkProfileStorage_Profiles(b *testing.B) {
s, err := backendpb.NewProfileStorage(&backendpb.ProfileStorageConfig{ s, err := backendpb.NewProfileStorage(&backendpb.ProfileStorageConfig{
BindSet: netip.MustParsePrefix("0.0.0.0/0"), BindSet: netip.MustParsePrefix("0.0.0.0/0"),
ErrColl: agdtest.NewErrorCollector(), ErrColl: agdtest.NewErrorCollector(),
Logger: slogutil.NewDiscardLogger(), Logger: backendpb.TestLogger,
Metrics: backendpb.EmptyMetrics{}, GRPCMetrics: backendpb.EmptyGRPCMetrics{},
Metrics: backendpb.EmptyProfileDBMetrics{},
Endpoint: &url.URL{ Endpoint: &url.URL{
Scheme: "grpc", Scheme: "grpc",
Host: l.Addr().String(), Host: l.Addr().String(),
@ -195,8 +196,10 @@ func BenchmarkProfileStorage_Profiles(b *testing.B) {
require.NoError(b, errSink) require.NoError(b, errSink)
require.NotNil(b, respSink) require.NotNil(b, respSink)
// goos: darwin // Most recent results:
// goarch: arm64 // goos: linux
// goarch: amd64
// pkg: github.com/AdguardTeam/AdGuardDNS/internal/backendpb // pkg: github.com/AdguardTeam/AdGuardDNS/internal/backendpb
// BenchmarkProfileStorage_Profiles-8 11599 104725 ns/op 18281 B/op 328 allocs/op // cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
// BenchmarkProfileStorage_Profiles-16 4501 258657 ns/op 20020 B/op 350 allocs/op
} }

View File

@ -19,8 +19,9 @@ type RateLimiterConfig struct {
// not be nil. // not be nil.
Logger *slog.Logger Logger *slog.Logger
// GRPCMetrics is used for the collection of the protobuf errors. // GRPCMetrics is used for the collection of the protobuf communication
GRPCMetrics Metrics // statistics.
GRPCMetrics GRPCMetrics
// Metrics is used to collect allowlist statistics. // Metrics is used to collect allowlist statistics.
Metrics consul.Metrics Metrics consul.Metrics
@ -44,7 +45,7 @@ type RateLimiterConfig struct {
// that retrieves the rate limit settings from the business logic backend. // that retrieves the rate limit settings from the business logic backend.
type RateLimiter struct { type RateLimiter struct {
logger *slog.Logger logger *slog.Logger
grpcMetrics Metrics grpcMetrics GRPCMetrics
metrics consul.Metrics metrics consul.Metrics
allowlist *ratelimit.DynamicAllowlist allowlist *ratelimit.DynamicAllowlist
errColl errcoll.Interface errColl errcoll.Interface
@ -92,7 +93,7 @@ func (l *RateLimiter) Refresh(ctx context.Context) (err error) {
} }
allowedSubnets := backendResp.AllowedSubnets allowedSubnets := backendResp.AllowedSubnets
prefixes := cidrRangeToInternal(ctx, l.errColl, allowedSubnets) prefixes := cidrRangeToInternal(ctx, l.errColl, l.logger, allowedSubnets)
l.allowlist.Update(prefixes) l.allowlist.Update(prefixes)
l.logger.InfoContext(ctx, "refresh successful", "num_records", len(prefixes)) l.logger.InfoContext(ctx, "refresh successful", "num_records", len(prefixes))

View File

@ -11,7 +11,6 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/backendpb" "github.com/AdguardTeam/AdGuardDNS/internal/backendpb"
"github.com/AdguardTeam/AdGuardDNS/internal/consul" "github.com/AdguardTeam/AdGuardDNS/internal/consul"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil" "github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -83,9 +82,9 @@ func TestRateLimiter_Refresh(t *testing.T) {
allowlist := ratelimit.NewDynamicAllowlist(nil, nil) allowlist := ratelimit.NewDynamicAllowlist(nil, nil)
l, err := backendpb.NewRateLimiter(&backendpb.RateLimiterConfig{ l, err := backendpb.NewRateLimiter(&backendpb.RateLimiterConfig{
Logger: slogutil.NewDiscardLogger(), Logger: backendpb.TestLogger,
Metrics: consul.EmptyMetrics{}, Metrics: consul.EmptyMetrics{},
GRPCMetrics: backendpb.EmptyMetrics{}, GRPCMetrics: backendpb.EmptyGRPCMetrics{},
Allowlist: allowlist, Allowlist: allowlist,
Endpoint: &url.URL{ Endpoint: &url.URL{
Scheme: "grpc", Scheme: "grpc",

View File

@ -7,18 +7,20 @@ import (
"time" "time"
"github.com/AdguardTeam/AdGuardDNS/internal/remotekv" "github.com/AdguardTeam/AdGuardDNS/internal/remotekv"
"github.com/AdguardTeam/golibs/errors"
"google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/durationpb"
) )
// RemoteKVConfig is the configuration for the business logic backend key-value // RemoteKVConfig is the configuration for the business logic backend key-value
// storage. // storage.
type RemoteKVConfig struct { type RemoteKVConfig struct {
// Metrics is used for the collection of the remote key-value storage // Metrics is used for the collection of the backend remote key-value
// storage statistics.
Metrics RemoteKVMetrics
// GRPCMetrics is used for the collection of the protobuf communication
// statistics. // statistics.
// GRPCMetrics GRPCMetrics
// TODO(e.burkov): Perhaps, it worths of a separate metrics interface,
// since it's only used for the collection of the protobuf errors.
Metrics Metrics
// Endpoint is the backend API URL. The scheme should be either "grpc" or // Endpoint is the backend API URL. The scheme should be either "grpc" or
// "grpcs". // "grpcs".
@ -35,7 +37,8 @@ type RemoteKVConfig struct {
// uses the business logic backend as the key-value storage. It is safe for // uses the business logic backend as the key-value storage. It is safe for
// concurrent use. // concurrent use.
type RemoteKV struct { type RemoteKV struct {
metrics Metrics grpcMetrics GRPCMetrics
metrics RemoteKVMetrics
client RemoteKVServiceClient client RemoteKVServiceClient
apiKey string apiKey string
ttl time.Duration ttl time.Duration
@ -51,6 +54,7 @@ func NewRemoteKV(c *RemoteKVConfig) (kv *RemoteKV, err error) {
} }
return &RemoteKV{ return &RemoteKV{
grpcMetrics: c.GRPCMetrics,
metrics: c.Metrics, metrics: c.Metrics,
client: NewRemoteKVServiceClient(client), client: NewRemoteKVServiceClient(client),
apiKey: c.APIKey, apiKey: c.APIKey,
@ -68,14 +72,34 @@ func (kv *RemoteKV) Get(ctx context.Context, key string) (val []byte, ok bool, e
} }
ctx = ctxWithAuthentication(ctx, kv.apiKey) ctx = ctxWithAuthentication(ctx, kv.apiKey)
start := time.Now()
resp, err := kv.client.Get(ctx, req) resp, err := kv.client.Get(ctx, req)
if err != nil { if err != nil {
return nil, false, fmt.Errorf("getting %q key: %w", key, fixGRPCError(ctx, kv.metrics, err)) err = fmt.Errorf("getting %q key: %w", key, fixGRPCError(ctx, kv.grpcMetrics, err))
return nil, false, err
} }
val = resp.GetData() kv.metrics.ObserveOperation(ctx, RemoteKVOpGet, time.Since(start))
return val, val != nil, nil defer func() { kv.metrics.IncrementLookups(ctx, ok) }()
received := resp.GetValue()
switch received := received.(type) {
case *RemoteKVGetResponse_Data:
return received.Data, true, nil
case *RemoteKVGetResponse_Empty:
return nil, false, nil
default:
return nil, false, fmt.Errorf(
"getting %q key: response type: %w: %T(%[3]v)",
key,
errors.ErrBadEnumValue,
received,
)
}
} }
// Set implements the [remotekv.Interface] interface for *RemoteKV. // Set implements the [remotekv.Interface] interface for *RemoteKV.
@ -87,10 +111,14 @@ func (kv *RemoteKV) Set(ctx context.Context, key string, val []byte) (err error)
} }
ctx = ctxWithAuthentication(ctx, kv.apiKey) ctx = ctxWithAuthentication(ctx, kv.apiKey)
start := time.Now()
_, err = kv.client.Set(ctx, req) _, err = kv.client.Set(ctx, req)
if err != nil { if err != nil {
return fmt.Errorf("setting %q key: %w", key, fixGRPCError(ctx, kv.metrics, err)) return fmt.Errorf("setting %q key: %w", key, fixGRPCError(ctx, kv.grpcMetrics, err))
} }
kv.metrics.ObserveOperation(ctx, RemoteKVOpSet, time.Since(start))
return nil return nil
} }

View File

@ -65,7 +65,8 @@ func TestRemoteKV_Get(t *testing.T) {
t.Cleanup(grpcSrv.GracefulStop) t.Cleanup(grpcSrv.GracefulStop)
kv, err := backendpb.NewRemoteKV(&backendpb.RemoteKVConfig{ kv, err := backendpb.NewRemoteKV(&backendpb.RemoteKVConfig{
Metrics: backendpb.EmptyMetrics{}, GRPCMetrics: backendpb.EmptyGRPCMetrics{},
Metrics: backendpb.EmptyRemoteKVMetrics{},
Endpoint: &url.URL{ Endpoint: &url.URL{
Scheme: "grpc", Scheme: "grpc",
Host: l.Addr().String(), Host: l.Addr().String(),

View File

@ -54,7 +54,7 @@ func (s *profilesCallStats) endDec() {
} }
// report writes the statistics to the log and the metrics. // report writes the statistics to the log and the metrics.
func (s *profilesCallStats) report(ctx context.Context, mtrc Metrics) { func (s *profilesCallStats) report(ctx context.Context, mtrc ProfileDBMetrics) {
lvl := slog.LevelDebug lvl := slog.LevelDebug
if s.isFullSync { if s.isFullSync {
lvl = slog.LevelInfo lvl = slog.LevelInfo

View File

@ -54,7 +54,9 @@ func newChanPacketConn(
sessions: sessions, sessions: sessions,
writeRequests: writeRequests, writeRequests: writeRequests,
sessionsGauge: metrics.BindToDeviceUDPSessionsChanSize.WithLabelValues(subnet.String()), sessionsGauge: metrics.BindToDeviceUDPSessionsChanSize.WithLabelValues(
subnet.String(),
),
writeRequestsGauge: writeRequestsGauge, writeRequestsGauge: writeRequestsGauge,
deadlineMu: &sync.RWMutex{}, deadlineMu: &sync.RWMutex{},

View File

@ -70,7 +70,12 @@ func listenControlWithSO(
opts, opts,
newIntSetSockOptFunc("IP_RECVORIGDSTADDR", unix.IPPROTO_IP, unix.IP_RECVORIGDSTADDR, 1), newIntSetSockOptFunc("IP_RECVORIGDSTADDR", unix.IPPROTO_IP, unix.IP_RECVORIGDSTADDR, 1),
newIntSetSockOptFunc("IP_FREEBIND", unix.IPPROTO_IP, unix.IP_FREEBIND, 1), newIntSetSockOptFunc("IP_FREEBIND", unix.IPPROTO_IP, unix.IP_FREEBIND, 1),
newIntSetSockOptFunc("IPV6_RECVORIGDSTADDR", unix.IPPROTO_IPV6, unix.IPV6_RECVORIGDSTADDR, 1), newIntSetSockOptFunc(
"IPV6_RECVORIGDSTADDR",
unix.IPPROTO_IPV6,
unix.IPV6_RECVORIGDSTADDR,
1,
),
newIntSetSockOptFunc("IPV6_FREEBIND", unix.IPPROTO_IPV6, unix.IPV6_FREEBIND, 1), newIntSetSockOptFunc("IPV6_FREEBIND", unix.IPPROTO_IPV6, unix.IPV6_FREEBIND, 1),
) )
default: default:

View File

@ -6,14 +6,9 @@ import (
"log/slog" "log/slog"
"time" "time"
"github.com/AdguardTeam/AdGuardDNS/internal/backendpb"
"github.com/AdguardTeam/AdGuardDNS/internal/billstat"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb" "github.com/AdguardTeam/AdGuardDNS/internal/profiledb"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/AdguardTeam/golibs/timeutil" "github.com/AdguardTeam/golibs/timeutil"
) )
@ -91,24 +86,3 @@ func initProfDB(
return nil return nil
} }
// newBillStatUploader creates and returns a billstat uploader depending on the
// provided API URL.
func newBillStatUploader(
envs *environment,
errColl errcoll.Interface,
mtrc backendpb.Metrics,
) (s billstat.Uploader, err error) {
apiURL := netutil.CloneURL(&envs.BillStatURL.URL)
err = urlutil.ValidateGRPCURL(apiURL)
if err != nil {
return nil, fmt.Errorf("billstat api url: %w", err)
}
return backendpb.NewBillStat(&backendpb.BillStatConfig{
ErrColl: errColl,
Metrics: mtrc,
Endpoint: apiURL,
APIKey: envs.BillStatAPIKey,
})
}

View File

@ -16,6 +16,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdcache" "github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice" "github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/backendpb" "github.com/AdguardTeam/AdGuardDNS/internal/backendpb"
"github.com/AdguardTeam/AdGuardDNS/internal/billstat" "github.com/AdguardTeam/AdGuardDNS/internal/billstat"
"github.com/AdguardTeam/AdGuardDNS/internal/bindtodevice" "github.com/AdguardTeam/AdGuardDNS/internal/bindtodevice"
@ -32,6 +33,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnssvc" "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll" "github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/filter" "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/filterstorage"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix" "github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip" "github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics" "github.com/AdguardTeam/AdGuardDNS/internal/metrics"
@ -57,6 +59,7 @@ const (
debugIDProfileDB = "profiledb" debugIDProfileDB = "profiledb"
debugIDRuleStat = "rulestat" debugIDRuleStat = "rulestat"
debugIDTicketRotator = "ticket_rotator" debugIDTicketRotator = "ticket_rotator"
debugIDTLSConfig = "tlsconfig"
debugIDWebSvc = "websvc" debugIDWebSvc = "websvc"
) )
@ -76,6 +79,7 @@ type builder struct {
cacheManager *agdcache.DefaultManager cacheManager *agdcache.DefaultManager
cloner *dnsmsg.Cloner cloner *dnsmsg.Cloner
conf *configuration conf *configuration
debugRefrs debugsvc.Refreshers
env *environment env *environment
errColl errcoll.Interface errColl errcoll.Interface
geoIPError chan error geoIPError chan error
@ -91,7 +95,7 @@ type builder struct {
access *access.Global access *access.Global
adultBlocking *hashprefix.Filter adultBlocking *hashprefix.Filter
adultBlockingHashes *hashprefix.Storage adultBlockingHashes *hashprefix.Storage
backendGRPCMtrc *metrics.BackendPB backendGRPCMtrc *metrics.BackendGRPC
billStat billstat.Recorder billStat billstat.Recorder
bindSet netutil.SubnetSet bindSet netutil.SubnetSet
btdManager *bindtodevice.Manager btdManager *bindtodevice.Manager
@ -100,7 +104,8 @@ type builder struct {
dnsCheck dnscheck.Interface dnsCheck dnscheck.Interface
dnsDB dnsdb.Interface dnsDB dnsdb.Interface
dnsSvc *dnssvc.Service dnsSvc *dnssvc.Service
filterStorage *filter.DefaultStorage filterMtrc filter.Metrics
filterStorage *filterstorage.Default
filteringGroups map[agd.FilteringGroupID]*agd.FilteringGroup filteringGroups map[agd.FilteringGroupID]*agd.FilteringGroup
fwdHandler *forward.Handler fwdHandler *forward.Handler
geoIP *geoip.File geoIP *geoip.File
@ -110,12 +115,11 @@ type builder struct {
newRegDomainsHashes *hashprefix.Storage newRegDomainsHashes *hashprefix.Storage
profileDB profiledb.Interface profileDB profiledb.Interface
rateLimit *ratelimit.Backoff rateLimit *ratelimit.Backoff
debugRefrs debugsvc.Refreshers
ruleStat rulestat.Interface ruleStat rulestat.Interface
safeBrowsing *hashprefix.Filter safeBrowsing *hashprefix.Filter
safeBrowsingHashes *hashprefix.Storage safeBrowsingHashes *hashprefix.Storage
sdeConf *dnsmsg.StructuredDNSErrorsConfig sdeConf *dnsmsg.StructuredDNSErrorsConfig
tlsMtrc tlsconfig.Metrics tlsManager *tlsconfig.DefaultManager
webSvc *websvc.Service webSvc *websvc.Service
// The fields below are initialized later, just like with the fields above, // The fields below are initialized later, just like with the fields above,
@ -201,8 +205,8 @@ func (b *builder) initGeoIP(ctx context.Context) {
CacheManager: b.cacheManager, CacheManager: b.cacheManager,
ASNPath: asn, ASNPath: asn,
CountryPath: ctry, CountryPath: ctry,
HostCacheSize: c.HostCacheSize, HostCacheCount: c.HostCacheSize,
IPCacheSize: c.IPCacheSize, IPCacheCount: c.IPCacheSize,
AllTopASNs: geoip.DefaultTopASNs, AllTopASNs: geoip.DefaultTopASNs,
CountryTopASNs: geoip.DefaultCountryTopASNs, CountryTopASNs: geoip.DefaultCountryTopASNs,
}) })
@ -225,6 +229,11 @@ func (b *builder) initHashPrefixFilters(ctx context.Context) (err error) {
matchers := map[string]*hashprefix.Storage{} matchers := map[string]*hashprefix.Storage{}
b.filterMtrc, err = metrics.NewFilter(b.mtrcNamespace, b.promRegisterer)
if err != nil {
return fmt.Errorf("registering filter metrics: %w", err)
}
// TODO(a.garipov): Merge the three functions below together. // TODO(a.garipov): Merge the three functions below together.
err = b.initAdultBlocking(ctx, matchers, maxSize, cacheDir) err = b.initAdultBlocking(ctx, matchers, maxSize, cacheDir)
@ -251,8 +260,7 @@ func (b *builder) initHashPrefixFilters(ctx context.Context) (err error) {
// initAdultBlocking initializes the adult-blocking filter and hash storage. It // initAdultBlocking initializes the adult-blocking filter and hash storage. It
// also adds the refresher with ID // also adds the refresher with ID
// [hashprefix.IDPrefix]/[agd.FilterListIDAdultBlocking] to the debug // [hashprefix.IDPrefix]/[filter.IDAdultBlocking] to the debug refreshers.
// refreshers.
// //
// It must be called from [builder.initHashPrefixFilters]. // It must be called from [builder.initHashPrefixFilters].
func (b *builder) initAdultBlocking( func (b *builder) initAdultBlocking(
@ -272,7 +280,7 @@ func (b *builder) initAdultBlocking(
} }
c := b.conf.AdultBlocking c := b.conf.AdultBlocking
id := agd.FilterListIDAdultBlocking id := filter.IDAdultBlocking
prefix := path.Join(hashprefix.IDPrefix, string(id)) prefix := path.Join(hashprefix.IDPrefix, string(id))
b.adultBlocking, err = hashprefix.NewFilter(&hashprefix.FilterConfig{ b.adultBlocking, err = hashprefix.NewFilter(&hashprefix.FilterConfig{
Logger: b.baseLogger.With(slogutil.KeyPrefix, prefix), Logger: b.baseLogger.With(slogutil.KeyPrefix, prefix),
@ -281,6 +289,7 @@ func (b *builder) initAdultBlocking(
Hashes: b.adultBlockingHashes, Hashes: b.adultBlockingHashes,
URL: &b.env.AdultBlockingURL.URL, URL: &b.env.AdultBlockingURL.URL,
ErrColl: b.errColl, ErrColl: b.errColl,
Metrics: b.filterMtrc,
ID: id, ID: id,
CachePath: filepath.Join(cacheDir, string(id)), CachePath: filepath.Join(cacheDir, string(id)),
ReplacementHost: c.BlockHost, ReplacementHost: c.BlockHost,
@ -289,7 +298,7 @@ func (b *builder) initAdultBlocking(
CacheTTL: c.CacheTTL.Duration, CacheTTL: c.CacheTTL.Duration,
// TODO(a.garipov): Make all sizes [datasize.ByteSize] and rename cache // TODO(a.garipov): Make all sizes [datasize.ByteSize] and rename cache
// entity counts to fooCount. // entity counts to fooCount.
CacheSize: c.CacheSize, CacheCount: c.CacheSize,
MaxSize: maxSize, MaxSize: maxSize,
}) })
if err != nil { if err != nil {
@ -327,8 +336,7 @@ func (b *builder) initAdultBlocking(
// initNewRegDomains initializes the newly-registered domain filter and hash // initNewRegDomains initializes the newly-registered domain filter and hash
// storage. It also adds the refresher with ID // storage. It also adds the refresher with ID
// [hashprefix.IDPrefix]/[agd.FilterListIDNewRegDomains] to the debug // [hashprefix.IDPrefix]/[filter.IDNewRegDomains] to the debug refreshers.
// refreshers.
// //
// It must be called from [builder.initHashPrefixFilters]. // It must be called from [builder.initHashPrefixFilters].
func (b *builder) initNewRegDomains( func (b *builder) initNewRegDomains(
@ -349,7 +357,7 @@ func (b *builder) initNewRegDomains(
// Reuse the general safe-browsing filter configuration with a new URL and // Reuse the general safe-browsing filter configuration with a new URL and
// ID. // ID.
c := b.conf.SafeBrowsing c := b.conf.SafeBrowsing
id := agd.FilterListIDNewRegDomains id := filter.IDNewRegDomains
prefix := path.Join(hashprefix.IDPrefix, string(id)) prefix := path.Join(hashprefix.IDPrefix, string(id))
b.newRegDomains, err = hashprefix.NewFilter(&hashprefix.FilterConfig{ b.newRegDomains, err = hashprefix.NewFilter(&hashprefix.FilterConfig{
Logger: b.baseLogger.With(slogutil.KeyPrefix, prefix), Logger: b.baseLogger.With(slogutil.KeyPrefix, prefix),
@ -358,13 +366,14 @@ func (b *builder) initNewRegDomains(
Hashes: b.newRegDomainsHashes, Hashes: b.newRegDomainsHashes,
URL: &b.env.NewRegDomainsURL.URL, URL: &b.env.NewRegDomainsURL.URL,
ErrColl: b.errColl, ErrColl: b.errColl,
Metrics: b.filterMtrc,
ID: id, ID: id,
CachePath: filepath.Join(cacheDir, string(id)), CachePath: filepath.Join(cacheDir, string(id)),
ReplacementHost: c.BlockHost, ReplacementHost: c.BlockHost,
Staleness: c.RefreshIvl.Duration, Staleness: c.RefreshIvl.Duration,
RefreshTimeout: c.RefreshTimeout.Duration, RefreshTimeout: c.RefreshTimeout.Duration,
CacheTTL: c.CacheTTL.Duration, CacheTTL: c.CacheTTL.Duration,
CacheSize: c.CacheSize, CacheCount: c.CacheSize,
MaxSize: maxSize, MaxSize: maxSize,
}) })
if err != nil { if err != nil {
@ -399,8 +408,8 @@ func (b *builder) initNewRegDomains(
} }
// initSafeBrowsing initializes the safe-browsing filter and hash storage. It // initSafeBrowsing initializes the safe-browsing filter and hash storage. It
// also adds the refresher with ID // also adds the refresher with ID [hashprefix.IDPrefix]/[filter.IDSafeBrowsing]
// [hashprefix.IDPrefix]/[agd.FilterListIDSafeBrowsing] to the debug refreshers. // to the debug refreshers.
// //
// It must be called from [builder.initHashPrefixFilters]. // It must be called from [builder.initHashPrefixFilters].
func (b *builder) initSafeBrowsing( func (b *builder) initSafeBrowsing(
@ -420,7 +429,7 @@ func (b *builder) initSafeBrowsing(
} }
c := b.conf.SafeBrowsing c := b.conf.SafeBrowsing
id := agd.FilterListIDSafeBrowsing id := filter.IDSafeBrowsing
prefix := path.Join(hashprefix.IDPrefix, string(id)) prefix := path.Join(hashprefix.IDPrefix, string(id))
b.safeBrowsing, err = hashprefix.NewFilter(&hashprefix.FilterConfig{ b.safeBrowsing, err = hashprefix.NewFilter(&hashprefix.FilterConfig{
Logger: b.baseLogger.With(slogutil.KeyPrefix, prefix), Logger: b.baseLogger.With(slogutil.KeyPrefix, prefix),
@ -429,13 +438,14 @@ func (b *builder) initSafeBrowsing(
Hashes: b.safeBrowsingHashes, Hashes: b.safeBrowsingHashes,
URL: &b.env.SafeBrowsingURL.URL, URL: &b.env.SafeBrowsingURL.URL,
ErrColl: b.errColl, ErrColl: b.errColl,
Metrics: b.filterMtrc,
ID: id, ID: id,
CachePath: filepath.Join(cacheDir, string(id)), CachePath: filepath.Join(cacheDir, string(id)),
ReplacementHost: c.BlockHost, ReplacementHost: c.BlockHost,
Staleness: c.RefreshIvl.Duration, Staleness: c.RefreshIvl.Duration,
RefreshTimeout: c.RefreshTimeout.Duration, RefreshTimeout: c.RefreshTimeout.Duration,
CacheTTL: c.CacheTTL.Duration, CacheTTL: c.CacheTTL.Duration,
CacheSize: c.CacheSize, CacheCount: c.CacheSize,
MaxSize: maxSize, MaxSize: maxSize,
}) })
if err != nil { if err != nil {
@ -477,26 +487,78 @@ func (b *builder) initSafeBrowsing(
// [builder.initHashPrefixFilters] must be called before this method. // [builder.initHashPrefixFilters] must be called before this method.
func (b *builder) initFilterStorage(ctx context.Context) (err error) { func (b *builder) initFilterStorage(ctx context.Context) (err error) {
c := b.conf.Filters c := b.conf.Filters
b.filterStorage = filter.NewDefaultStorage(c.toInternal( refrIvl := c.RefreshIvl.Duration
b.baseLogger, refrTimeout := c.RefreshTimeout.Duration
b.errColl,
b.cacheManager,
b.env,
b.safeBrowsing,
b.adultBlocking,
b.newRegDomains,
))
err = b.filterStorage.RefreshInitial(ctx) b.filterStorage, err = filterstorage.New(&filterstorage.Config{
BaseLogger: b.baseLogger,
Logger: b.baseLogger.With(slogutil.KeyPrefix, filter.StoragePrefix),
BlockedServices: &filterstorage.ConfigBlockedServices{
IndexURL: &b.env.BlockedServiceIndexURL.URL,
// TODO(a.garipov): Consider adding a separate parameter here.
IndexMaxSize: c.MaxSize,
// TODO(a.garipov): Consider making configurable.
IndexRefreshTimeout: 3 * time.Minute,
// TODO(a.garipov): Consider adding a separate parameter here.
IndexStaleness: refrIvl,
// TODO(a.garipov): Consider adding a separate parameter here.
ResultCacheCount: c.RuleListCache.Size,
// TODO(a.garipov): Consider adding a separate parameter here.
ResultCacheEnabled: c.RuleListCache.Enabled,
Enabled: bool(b.env.BlockedServiceEnabled),
},
Custom: &filterstorage.ConfigCustom{
CacheCount: c.CustomFilterCacheSize,
},
HashPrefix: &filterstorage.ConfigHashPrefix{
Adult: b.adultBlocking,
Dangerous: b.safeBrowsing,
NewlyRegistered: b.newRegDomains,
},
RuleLists: &filterstorage.ConfigRuleLists{
IndexURL: &b.env.FilterIndexURL.URL,
// TODO(a.garipov): Consider adding a separate parameter here.
IndexMaxSize: c.MaxSize,
MaxSize: c.MaxSize,
IndexRefreshTimeout: c.IndexRefreshTimeout.Duration,
// TODO(a.garipov): Consider adding a separate parameter here.
IndexStaleness: refrIvl,
RefreshTimeout: refrTimeout,
// TODO(a.garipov): Consider adding a separate parameter here.
Staleness: refrIvl,
ResultCacheCount: c.RuleListCache.Size,
ResultCacheEnabled: c.RuleListCache.Enabled,
},
SafeSearchGeneral: b.newSafeSearchConfig(
b.env.GeneralSafeSearchURL,
filter.IDGeneralSafeSearch,
bool(b.env.GeneralSafeSearchEnabled),
),
SafeSearchYouTube: b.newSafeSearchConfig(
b.env.YoutubeSafeSearchURL,
filter.IDYoutubeSafeSearch,
bool(b.env.YoutubeSafeSearchEnabled),
),
CacheManager: b.cacheManager,
Clock: agdtime.SystemClock{},
ErrColl: b.errColl,
Metrics: b.filterMtrc,
CacheDir: b.env.FilterCachePath,
})
if err != nil { if err != nil {
return fmt.Errorf("creating default filter storage: %w", err) return fmt.Errorf("creating default filter storage: %w", err)
} }
err = b.filterStorage.RefreshInitial(ctx)
if err != nil {
return fmt.Errorf("refreshing default filter storage: %w", err)
}
refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{ refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
Context: newCtxWithTimeoutCons(c.RefreshIvl.Duration), Context: newCtxWithTimeoutCons(refrTimeout),
Refresher: b.filterStorage, Refresher: b.filterStorage,
Logger: b.baseLogger.With(slogutil.KeyPrefix, "filters/storage_refresh"), Logger: b.baseLogger.With(slogutil.KeyPrefix, "filters/storage_refresh"),
Interval: c.RefreshIvl.Duration, Interval: refrIvl,
RefreshOnShutdown: false, RefreshOnShutdown: false,
RandomizeStart: false, RandomizeStart: false,
}) })
@ -514,6 +576,35 @@ func (b *builder) initFilterStorage(ctx context.Context) (err error) {
return nil return nil
} }
// newSafeSearchConfig returns a new safe-search configuration for the given URL
// and ID if enabled; otherwise, it returns an empty configuration.
func (b *builder) newSafeSearchConfig(
u *urlutil.URL,
id filter.ID,
enabled bool,
) (c *filterstorage.ConfigSafeSearch) {
if !enabled {
return &filterstorage.ConfigSafeSearch{}
}
fltConf := b.conf.Filters
return &filterstorage.ConfigSafeSearch{
URL: &u.URL,
ID: id,
// TODO(a.garipov): Consider adding a separate parameter here.
MaxSize: fltConf.MaxSize,
// TODO(a.garipov): Consider making this configurable.
ResultCacheTTL: 1 * time.Hour,
// TODO(a.garipov): Consider adding a separate parameter here.
RefreshTimeout: fltConf.RefreshTimeout.Duration,
// TODO(a.garipov): Consider adding a separate parameter here.
Staleness: fltConf.RefreshIvl.Duration,
ResultCacheCount: fltConf.SafeSearchCacheSize,
Enabled: true,
}
}
// initFilteringGroups initializes the filtering groups. // initFilteringGroups initializes the filtering groups.
// //
// [builder.initFilterStorage] must be called before this method. // [builder.initFilterStorage] must be called before this method.
@ -606,26 +697,54 @@ func (b *builder) initMsgConstructor(ctx context.Context) (err error) {
return nil return nil
} }
// initTLSManager initializes the TLS manager and the TLS-related metrics. It
// also adds the refresher with ID [debugIDTLSConfig] to the debug refreshers.
func (b *builder) initTLSManager(ctx context.Context) (err error) {
mtrc, err := metrics.NewTLSConfig(b.mtrcNamespace, b.promRegisterer)
if err != nil {
return fmt.Errorf("registering tls metrics: %w", err)
}
logFile := b.env.SSLKeyLogFile
if logFile != "" {
b.logger.WarnContext(ctx, "tls key logging is enabled", "file", logFile)
}
ticketPaths := b.conf.ServerGroups.collectSessTicketPaths()
mgr, err := tlsconfig.NewDefaultManager(&tlsconfig.DefaultManagerConfig{
Logger: b.baseLogger.With(slogutil.KeyPrefix, "tlsconfig"),
ErrColl: b.errColl,
Metrics: mtrc,
KeyLogFilename: logFile,
SessionTicketPaths: ticketPaths,
})
if err != nil {
return fmt.Errorf("initializing tls manager: %w", err)
}
b.tlsManager = mgr
b.debugRefrs[debugIDTLSConfig] = mgr
b.logger.DebugContext(ctx, "initialized tls manager")
return nil
}
// initServerGroups initializes the server groups. // initServerGroups initializes the server groups.
// //
// The following methods must be called before this one: // The following methods must be called before this one:
// - [builder.initBindToDevice] // - [builder.initBindToDevice]
// - [builder.initFilteringGroups] // - [builder.initFilteringGroups]
// - [builder.initMsgConstructor] // - [builder.initMsgConstructor]
// - [builder.initTLSManager]
func (b *builder) initServerGroups(ctx context.Context) (err error) { func (b *builder) initServerGroups(ctx context.Context) (err error) {
mtrc, err := metrics.NewTLSConfig(b.mtrcNamespace, b.promRegisterer)
if err != nil {
return fmt.Errorf("registering tls metrics: %w", err)
}
b.tlsMtrc = mtrc
c := b.conf c := b.conf
b.serverGroups, err = c.ServerGroups.toInternal( b.serverGroups, err = c.ServerGroups.toInternal(
ctx, ctx,
mtrc,
b.messages, b.messages,
b.btdManager, b.btdManager,
b.tlsManager,
b.filteringGroups, b.filteringGroups,
c.RateLimit, c.RateLimit,
c.DNS, c.DNS,
@ -699,22 +818,12 @@ func (b *builder) startBindToDevice(ctx context.Context) (err error) {
return nil return nil
} }
// initTLS initializes the optional TLS key logging and session-ticket rotation. // initTicketRotator initializes the TLS session ticket rotator. It also adds
// It also adds the refresher with ID [debugIDTicketRotator] to the debug // the refresher with ID [debugIDTicketRotator] to the debug refreshers.
// refreshers.
// //
// [builder.initServerGroups] must be called before this method. // [builder.initTLSManager] must be called before this method.
func (b *builder) initTLS(ctx context.Context) (err error) { func (b *builder) initTicketRotator(ctx context.Context) (err error) {
if f := b.env.SSLKeyLogFile; f != "" { tickRot := agdservice.RefresherFunc(b.tlsManager.RotateTickets)
b.logger.WarnContext(ctx, "IMPORTANT: TLS KEY LOGGING IS ENABLED", "ssl_key_log_file", f)
err = enableTLSKeyLogging(b.serverGroups, f)
if err != nil {
return fmt.Errorf("enabling tls key logging: %w", err)
}
}
tickRot := newTicketRotator(b.baseLogger, b.errColl, b.tlsMtrc, b.serverGroups)
err = tickRot.Refresh(ctx) err = tickRot.Refresh(ctx)
if err != nil { if err != nil {
return fmt.Errorf("initial session ticket refresh: %w", err) return fmt.Errorf("initial session ticket refresh: %w", err)
@ -744,6 +853,7 @@ func (b *builder) initTLS(ctx context.Context) (err error) {
} }
// initGRPCMetrics initializes the gRPC metrics if necessary. // initGRPCMetrics initializes the gRPC metrics if necessary.
// [builder.initServerGroups] must be called before this method.
func (b *builder) initGRPCMetrics(ctx context.Context) (err error) { func (b *builder) initGRPCMetrics(ctx context.Context) (err error) {
switch { switch {
case case
@ -756,18 +866,19 @@ func (b *builder) initGRPCMetrics(ctx context.Context) (err error) {
return nil return nil
} }
b.backendGRPCMtrc, err = metrics.NewBackendPB(b.mtrcNamespace, b.promRegisterer) b.backendGRPCMtrc, err = metrics.NewBackendGRPC(b.mtrcNamespace, b.promRegisterer)
if err != nil { if err != nil {
return fmt.Errorf("registering backendbp metrics: %w", err) return fmt.Errorf("registering backend grpc metrics: %w", err)
} }
b.logger.DebugContext(ctx, "initialized grpc metrics") b.logger.DebugContext(ctx, "initialized backend grpc metrics")
return nil return nil
} }
// initBillStat initializes the billing-statistics recorder if necessary. It // initBillStat initializes the billing-statistics recorder if necessary. It
// also adds the refresher with ID [debugIDBillStat] to the debug refreshers. // also adds the refresher with ID [debugIDBillStat] to the debug refreshers.
// [builder.initGRPCMetrics] must be called before this method.
func (b *builder) initBillStat(ctx context.Context) (err error) { func (b *builder) initBillStat(ctx context.Context) (err error) {
if !b.profilesEnabled { if !b.profilesEnabled {
b.billStat = billstat.EmptyRecorder{} b.billStat = billstat.EmptyRecorder{}
@ -775,7 +886,7 @@ func (b *builder) initBillStat(ctx context.Context) (err error) {
return nil return nil
} }
upl, err := newBillStatUploader(b.env, b.errColl, b.backendGRPCMtrc) upl, err := b.newBillStatUploader()
if err != nil { if err != nil {
return fmt.Errorf("creating billstat uploader: %w", err) return fmt.Errorf("creating billstat uploader: %w", err)
} }
@ -819,11 +930,28 @@ func (b *builder) initBillStat(ctx context.Context) (err error) {
return nil return nil
} }
// newBillStatUploader creates and returns a billstat uploader depending on the
// provided API URL.
func (b *builder) newBillStatUploader() (s billstat.Uploader, err error) {
apiURL := netutil.CloneURL(&b.env.BillStatURL.URL)
err = urlutil.ValidateGRPCURL(apiURL)
if err != nil {
return nil, fmt.Errorf("billstat api url: %w", err)
}
return backendpb.NewBillStat(&backendpb.BillStatConfig{
Logger: b.baseLogger.With(slogutil.KeyPrefix, "billstat_uploader"),
ErrColl: b.errColl,
GRPCMetrics: b.backendGRPCMtrc,
Endpoint: apiURL,
APIKey: b.env.BillStatAPIKey,
})
}
// initProfileDB initializes the profile database if necessary. // initProfileDB initializes the profile database if necessary.
// //
// [builder.initGRPCMetrics] and [builder.initServerGroups] must be called // [builder.initGRPCMetrics] must be called before this method. It also adds
// before this method. It also adds the refresher with ID [debugIDProfileDB] to // the refresher with ID [debugIDProfileDB] to the debug refreshers.
// the debug refreshers.
func (b *builder) initProfileDB(ctx context.Context) (err error) { func (b *builder) initProfileDB(ctx context.Context) (err error) {
if !b.profilesEnabled { if !b.profilesEnabled {
b.profileDB = &profiledb.Disabled{} b.profileDB = &profiledb.Disabled{}
@ -837,12 +965,18 @@ func (b *builder) initProfileDB(ctx context.Context) (err error) {
return fmt.Errorf("profile api url: %w", err) return fmt.Errorf("profile api url: %w", err)
} }
backendProfileDBMtrc, err := metrics.NewBackendProfileDB(b.mtrcNamespace, b.promRegisterer)
if err != nil {
return fmt.Errorf("registering backend grpc profile metrics: %w", err)
}
respSzEst := b.conf.RateLimit.ResponseSizeEstimate respSzEst := b.conf.RateLimit.ResponseSizeEstimate
strg, err := backendpb.NewProfileStorage(&backendpb.ProfileStorageConfig{ strg, err := backendpb.NewProfileStorage(&backendpb.ProfileStorageConfig{
BindSet: b.bindSet, BindSet: b.bindSet,
ErrColl: b.errColl, ErrColl: b.errColl,
Logger: b.baseLogger.With(slogutil.KeyPrefix, "backendpb"), Logger: b.baseLogger.With(slogutil.KeyPrefix, "profilestorage"),
Metrics: b.backendGRPCMtrc, GRPCMetrics: b.backendGRPCMtrc,
Metrics: backendProfileDBMtrc,
Endpoint: apiURL, Endpoint: apiURL,
APIKey: b.env.ProfilesAPIKey, APIKey: b.env.ProfilesAPIKey,
ResponseSizeEstimate: respSzEst, ResponseSizeEstimate: respSzEst,
@ -917,6 +1051,7 @@ func (b *builder) initDNSCheck(ctx context.Context) (err error) {
c := b.conf.Check c := b.conf.Check
checkConf, err := c.toInternal( checkConf, err := c.toInternal(
b.baseLogger,
b.env, b.env,
b.messages, b.messages,
b.errColl, b.errColl,
@ -1051,12 +1186,10 @@ func (b *builder) initRateLimiter(ctx context.Context) (err error) {
} }
// initWeb initializes the web service, starts it, and registers it in the // initWeb initializes the web service, starts it, and registers it in the
// signal handler. // signal handler. [builder.initDNSCheck] must be call before this method.
//
// [builder.initServerGroups] must be called before this method.
func (b *builder) initWeb(ctx context.Context) (err error) { func (b *builder) initWeb(ctx context.Context) (err error) {
c := b.conf.Web c := b.conf.Web
webConf, err := c.toInternal(ctx, b.env, b.dnsCheck, b.errColl, b.tlsMtrc) webConf, err := c.toInternal(ctx, b.env, b.dnsCheck, b.errColl, b.tlsManager)
if err != nil { if err != nil {
return fmt.Errorf("converting web configuration: %w", err) return fmt.Errorf("converting web configuration: %w", err)
} }
@ -1142,7 +1275,6 @@ func (b *builder) waitGeoIP(ctx context.Context) (err error) {
// - [builder.initAccess] // - [builder.initAccess]
// - [builder.initBillStat] // - [builder.initBillStat]
// - [builder.initBindToDevice] // - [builder.initBindToDevice]
// - [builder.initDNSCheck]
// - [builder.initFilterStorage] // - [builder.initFilterStorage]
// - [builder.initFilteringGroups] // - [builder.initFilteringGroups]
// - [builder.initMsgConstructor] // - [builder.initMsgConstructor]
@ -1153,7 +1285,7 @@ func (b *builder) waitGeoIP(ctx context.Context) (err error) {
// - [builder.waitGeoIP] // - [builder.waitGeoIP]
func (b *builder) initDNS(ctx context.Context) (err error) { func (b *builder) initDNS(ctx context.Context) (err error) {
b.fwdHandler = forward.NewHandler(b.conf.Upstream.toInternal(b.baseLogger)) b.fwdHandler = forward.NewHandler(b.conf.Upstream.toInternal(b.baseLogger))
b.dnsDB = b.conf.DNSDB.toInternal(b.errColl) b.dnsDB = b.conf.DNSDB.toInternal(b.baseLogger, b.errColl)
dnsHdlrsConf := &dnssvc.HandlersConfig{ dnsHdlrsConf := &dnssvc.HandlersConfig{
BaseLogger: b.baseLogger, BaseLogger: b.baseLogger,

View File

@ -2,10 +2,12 @@ package cmd
import ( import (
"fmt" "fmt"
"log/slog"
"net/netip" "net/netip"
"strings" "strings"
"time" "time"
"github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp" "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
"github.com/AdguardTeam/AdGuardDNS/internal/backendpb" "github.com/AdguardTeam/AdGuardDNS/internal/backendpb"
"github.com/AdguardTeam/AdGuardDNS/internal/dnscheck" "github.com/AdguardTeam/AdGuardDNS/internal/dnscheck"
@ -17,6 +19,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/remotekv/rediskv" "github.com/AdguardTeam/AdGuardDNS/internal/remotekv/rediskv"
"github.com/AdguardTeam/golibs/container" "github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/timeutil" "github.com/AdguardTeam/golibs/timeutil"
"github.com/c2h5oh/datasize" "github.com/c2h5oh/datasize"
@ -50,14 +53,15 @@ type checkConfig struct {
// toInternal converts c to the DNS server check configuration for the DNS // toInternal converts c to the DNS server check configuration for the DNS
// server. c must be valid. // server. c must be valid.
func (c *checkConfig) toInternal( func (c *checkConfig) toInternal(
baseLogger *slog.Logger,
envs *environment, envs *environment,
messages *dnsmsg.Constructor, messages *dnsmsg.Constructor,
errColl errcoll.Interface, errColl errcoll.Interface,
namespace string, namespace string,
reg prometheus.Registerer, reg prometheus.Registerer,
backendMtrc backendpb.Metrics, grpcMtrc backendpb.GRPCMetrics,
) (conf *dnscheck.RemoteKVConfig, err error) { ) (conf *dnscheck.RemoteKVConfig, err error) {
kv, err := newRemoteKV(c.RemoteKV, envs, namespace, reg, backendMtrc) kv, err := c.RemoteKV.newRemoteKV(envs, namespace, reg, grpcMtrc)
if err != nil { if err != nil {
// Don't wrap the error, because it's informative enough as is. // Don't wrap the error, because it's informative enough as is.
return nil, err return nil, err
@ -69,6 +73,7 @@ func (c *checkConfig) toInternal(
} }
return &dnscheck.RemoteKVConfig{ return &dnscheck.RemoteKVConfig{
Logger: baseLogger.With(slogutil.KeyPrefix, "dnscheck"),
Messages: messages, Messages: messages,
RemoteKV: kv, RemoteKV: kv,
ErrColl: errColl, ErrColl: errColl,
@ -87,33 +92,42 @@ const maxRespSize = 1 * datasize.MB
// [remotekv.KeyNamespace]. // [remotekv.KeyNamespace].
const keyNamespaceCheck = "check" const keyNamespaceCheck = "check"
// newRemoteKV returns a new properly initialized remote key-value storage. // newRemoteKV returns a new properly initialized remote key-value storage. c
func newRemoteKV( // must be valid. grpcMtrc should be registered before calling this method.
c *remoteKVConfig, func (c *remoteKVConfig) newRemoteKV(
envs *environment, envs *environment,
namespace string, namespace string,
reg prometheus.Registerer, reg prometheus.Registerer,
backendMtrc backendpb.Metrics, grpcMtrc backendpb.GRPCMetrics,
) (kv remotekv.Interface, err error) { ) (kv remotekv.Interface, err error) {
switch c.Type { switch c.Type {
case kvModeBackend: case kvModeBackend:
kv, err = backendpb.NewRemoteKV(&backendpb.RemoteKVConfig{ var backendKVMtrc *metrics.BackendRemoteKV
Metrics: backendMtrc, backendKVMtrc, err = metrics.NewBackendRemoteKV(namespace, reg)
Endpoint: &envs.DNSCheckRemoteKVURL.URL, if err != nil {
APIKey: envs.DNSCheckRemoteKVAPIKey, return nil, fmt.Errorf("registering backend kv metrics: %w", err)
TTL: c.TTL.Duration, }
})
kv, err = c.newBackendRemoteKV(envs, backendKVMtrc, grpcMtrc)
if err != nil { if err != nil {
return nil, fmt.Errorf("initializing backend dnscheck kv: %w", err) return nil, fmt.Errorf("initializing backend dnscheck kv: %w", err)
} }
case kvModeCache:
// TODO(e.burkov): The local cache in [dnscheck.RemoteKV] becomes
// pointless with this mode.
return remotekv.NewCache(&remotekv.CacheConfig{
Cache: agdcache.NewLRU[string, []byte](&agdcache.LRUConfig{
Count: envs.DNSCheckCacheKVSize,
}),
}), nil
case kvModeRedis: case kvModeRedis:
var redisKVMtrc rediskv.Metrics var redisKVMtrc *metrics.RedisKV
redisKVMtrc, err = metrics.NewRedisKV(namespace, reg) redisKVMtrc, err = metrics.NewRedisKV(namespace, reg)
if err != nil { if err != nil {
return nil, fmt.Errorf("registering redis kv metrics: %w", err) return nil, fmt.Errorf("registering redis kv metrics: %w", err)
} }
redisKV := rediskv.NewRedisKV(&rediskv.RedisKVConfig{ kv = rediskv.NewRedisKV(&rediskv.RedisKVConfig{
Metrics: redisKVMtrc, Metrics: redisKVMtrc,
Addr: &netutil.HostPort{ Addr: &netutil.HostPort{
Host: envs.RedisAddr, Host: envs.RedisAddr,
@ -124,12 +138,59 @@ func newRemoteKV(
IdleTimeout: envs.RedisIdleTimeout.Duration, IdleTimeout: envs.RedisIdleTimeout.Duration,
TTL: c.TTL.Duration, TTL: c.TTL.Duration,
}) })
kv = remotekv.NewKeyNamespace(&remotekv.KeyNamespaceConfig{
KV: redisKV,
Prefix: fmt.Sprintf("%s:%s:", envs.RedisKeyPrefix, keyNamespaceCheck),
})
case kvModeConsul: case kvModeConsul:
kv, err = c.newConsulRemoteKV(envs)
if err != nil {
return nil, fmt.Errorf("initializing consul dnscheck kv: %w", err)
}
default:
panic(fmt.Errorf("dnscheck kv type: %w: %q", errors.ErrBadEnumValue, c.Type))
}
return remotekv.NewKeyNamespace(&remotekv.KeyNamespaceConfig{
KV: kv,
Prefix: newRemoveKVPrefix(envs, c.Type),
}), nil
}
// newBackendRemoteKV returns a new properly initialized backend remote
// key-value storage. c must be valid.
//
// TODO(e.burkov): Add key namespace.
func (c *remoteKVConfig) newBackendRemoteKV(
envs *environment,
backendKVMtrc *metrics.BackendRemoteKV,
grpcMtrc backendpb.GRPCMetrics,
) (kv *backendpb.RemoteKV, err error) {
kv, err = backendpb.NewRemoteKV(&backendpb.RemoteKVConfig{
GRPCMetrics: grpcMtrc,
Metrics: backendKVMtrc,
Endpoint: &envs.DNSCheckRemoteKVURL.URL,
APIKey: envs.DNSCheckRemoteKVAPIKey,
TTL: c.TTL.Duration,
})
if err != nil {
return nil, fmt.Errorf("initializing backend dnscheck kv: %w", err)
}
return kv, nil
}
// newRemoveKVPrefix returns a remote KV custom prefix for the keys.
func newRemoveKVPrefix(envs *environment, kvType string) (pref string) {
switch kvType {
case kvModeBackend, kvModeCache, kvModeConsul:
return fmt.Sprintf("%s:%s:", kvType, keyNamespaceCheck)
case kvModeRedis:
return fmt.Sprintf("%s:%s:", envs.RedisKeyPrefix, keyNamespaceCheck)
default:
panic(fmt.Errorf("dnscheck kv type: %w: %q", errors.ErrBadEnumValue, kvType))
}
}
// newConsulRemoteKV returns a new properly initialized Consul-based remote
// key-value storage. c must be valid.
func (c *remoteKVConfig) newConsulRemoteKV(envs *environment) (kv remotekv.Interface, err error) {
consulKVURL := envs.ConsulDNSCheckKVURL consulKVURL := envs.ConsulDNSCheckKVURL
consulSessionURL := envs.ConsulDNSCheckSessionURL consulSessionURL := envs.ConsulDNSCheckSessionURL
if consulKVURL == nil || consulSessionURL == nil { if consulKVURL == nil || consulSessionURL == nil {
@ -151,9 +212,6 @@ func newRemoteKV(
if err != nil { if err != nil {
return nil, fmt.Errorf("initializing consul dnscheck kv: %w", err) return nil, fmt.Errorf("initializing consul dnscheck kv: %w", err)
} }
default:
return remotekv.Empty{}, nil
}
return kv, nil return kv, nil
} }
@ -238,6 +296,7 @@ func validateNonNilIPs(ips []netip.Addr, fam netutil.AddrFamily) (err error) {
// DNSCheck key-value database modes. // DNSCheck key-value database modes.
const ( const (
kvModeBackend = "backend" kvModeBackend = "backend"
kvModeCache = "cache"
kvModeConsul = "consul" kvModeConsul = "consul"
kvModeRedis = "redis" kvModeRedis = "redis"
) )
@ -246,7 +305,7 @@ const (
// checking. // checking.
type remoteKVConfig struct { type remoteKVConfig struct {
// Type defines the type of remote key-value store. Allowed values are // Type defines the type of remote key-value store. Allowed values are
// [kvModeBackend], [kvModeConsul] and [kvModeRedis]. // [kvModeBackend], [kvModeCache], [kvModeConsul] and [kvModeRedis].
Type string `yaml:"type"` Type string `yaml:"type"`
// TTL defines, for how long to keep the information about a single client. // TTL defines, for how long to keep the information about a single client.
@ -269,6 +328,8 @@ func (c *remoteKVConfig) validate() (err error) {
if ttl.Duration <= 0 { if ttl.Duration <= 0 {
return newNotPositiveError("ttl", ttl) return newNotPositiveError("ttl", ttl)
} }
case kvModeCache:
// Go on.
case kvModeConsul: case kvModeConsul:
if ttl.Duration < consulkv.MinTTL || ttl.Duration > consulkv.MaxTTL { if ttl.Duration < consulkv.MinTTL || ttl.Duration > consulkv.MaxTTL {
return fmt.Errorf( return fmt.Errorf(
@ -288,8 +349,6 @@ func (c *remoteKVConfig) validate() (err error) {
ttl, ttl,
) )
} }
case "":
return fmt.Errorf("type: %w", errors.ErrEmptyValue)
default: default:
return fmt.Errorf("type: %w: %q", errors.ErrBadEnumValue, c.Type) return fmt.Errorf("type: %w: %q", errors.ErrBadEnumValue, c.Type)
} }

View File

@ -94,11 +94,13 @@ func Main(plugins *plugin.Registry) {
errors.Check(b.initMsgConstructor(ctx)) errors.Check(b.initMsgConstructor(ctx))
errors.Check(b.initTLSManager(ctx))
errors.Check(b.initServerGroups(ctx)) errors.Check(b.initServerGroups(ctx))
errors.Check(b.startBindToDevice(ctx)) errors.Check(b.startBindToDevice(ctx))
errors.Check(b.initTLS(ctx)) errors.Check(b.initTicketRotator(ctx))
errors.Check(b.initGRPCMetrics(ctx)) errors.Check(b.initGRPCMetrics(ctx))

View File

@ -48,8 +48,8 @@ func connectivityCheck(srvGrps []*agd.ServerGroup, connCheck *connCheckConfig) (
} }
defer func() { defer func() {
errClose := errors.Annotate(conn4.Close(), "connectivity check: closing ipv4: %w") closeErr := errors.Annotate(conn4.Close(), "connectivity check: closing ipv4: %w")
err = errors.WithDeferred(err, errClose) err = errors.WithDeferred(err, closeErr)
}() }()
if !requireIPv6ConnCheck(srvGrps) { if !requireIPv6ConnCheck(srvGrps) {
@ -69,8 +69,8 @@ func connectivityCheck(srvGrps []*agd.ServerGroup, connCheck *connCheckConfig) (
} }
defer func() { defer func() {
errClose := errors.Annotate(conn6.Close(), "connectivity check: closing ipv6: %w") closeErr := errors.Annotate(conn6.Close(), "connectivity check: closing ipv6: %w")
err = errors.WithDeferred(err, errClose) err = errors.WithDeferred(err, closeErr)
}() }()
return nil return nil

View File

@ -1,9 +1,12 @@
package cmd package cmd
import ( import (
"log/slog"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsdb" "github.com/AdguardTeam/AdGuardDNS/internal/dnsdb"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll" "github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
) )
// dnsDBConfig is the configuration of the DNSDB module. // dnsDBConfig is the configuration of the DNSDB module.
@ -34,12 +37,16 @@ func (c *dnsDBConfig) validate() (err error) {
// toInternal builds and returns an anonymous statistics collector. c must be // toInternal builds and returns an anonymous statistics collector. c must be
// valid. // valid.
func (c *dnsDBConfig) toInternal(errColl errcoll.Interface) (d dnsdb.Interface) { func (c *dnsDBConfig) toInternal(
baseLogger *slog.Logger,
errColl errcoll.Interface,
) (d dnsdb.Interface) {
if !c.Enabled { if !c.Enabled {
return dnsdb.Empty{} return dnsdb.Empty{}
} }
db := dnsdb.New(&dnsdb.DefaultConfig{ db := dnsdb.New(&dnsdb.DefaultConfig{
Logger: baseLogger.With(slogutil.KeyPrefix, "dnsdb"),
ErrColl: errColl, ErrColl: errColl,
MaxSize: c.MaxSize, MaxSize: c.MaxSize,
}) })

View File

@ -68,6 +68,8 @@ type environment struct {
RedisIdleTimeout timeutil.Duration `env:"REDIS_IDLE_TIMEOUT" envDefault:"30s"` RedisIdleTimeout timeutil.Duration `env:"REDIS_IDLE_TIMEOUT" envDefault:"30s"`
// TODO(a.garipov): Rename to DNSCHECK_CACHE_KV_COUNT?
DNSCheckCacheKVSize int `env:"DNSCHECK_CACHE_KV_SIZE"`
RedisMaxActive int `env:"REDIS_MAX_ACTIVE" envDefault:"10"` RedisMaxActive int `env:"REDIS_MAX_ACTIVE" envDefault:"10"`
RedisMaxIdle int `env:"REDIS_MAX_IDLE" envDefault:"3"` RedisMaxIdle int `env:"REDIS_MAX_IDLE" envDefault:"3"`
@ -233,10 +235,12 @@ func (envs *environment) validateFromValidConfig(conf *configuration) (err error
var errs []error var errs []error
switch typ := conf.Check.RemoteKV.Type; typ { switch typ := conf.Check.RemoteKV.Type; typ {
case kvModeRedis:
errs = envs.validateRedis(errs)
case kvModeBackend: case kvModeBackend:
errs = envs.validateBackendKV(errs) errs = envs.validateBackendKV(errs)
case kvModeCache:
errs = envs.validateCache(errs)
case kvModeRedis:
errs = envs.validateRedis(errs)
default: default:
// Probably consul. // Probably consul.
} }
@ -259,38 +263,51 @@ func (envs *environment) validateFromValidConfig(conf *configuration) (err error
return errors.Join(errs...) return errors.Join(errs...)
} }
// validateCache appends validation errors to the given errs if environment
// variables for KV Cache contain errors.
func (envs *environment) validateCache(errs []error) (res []error) {
res = errs
if envs.DNSCheckCacheKVSize <= 0 {
err := newNotPositiveError("DNSCHECK_CACHE_KV_SIZE", envs.DNSCheckCacheKVSize)
res = append(res, err)
}
return res
}
// validateRedis appends validation errors to the given errs if environment // validateRedis appends validation errors to the given errs if environment
// variables for Redis contain errors. // variables for Redis contain errors.
func (envs *environment) validateRedis(errs []error) (withRedis []error) { func (envs *environment) validateRedis(errs []error) (res []error) {
withRedis = errs res = errs
if envs.RedisAddr == "" { if envs.RedisAddr == "" {
err := fmt.Errorf("REDIS_ADDR: %q", errors.ErrEmptyValue) err := fmt.Errorf("REDIS_ADDR: %w", errors.ErrEmptyValue)
withRedis = append(withRedis, err) res = append(res, err)
} }
if envs.RedisIdleTimeout.Duration <= 0 { if envs.RedisIdleTimeout.Duration <= 0 {
err := newNotPositiveError("REDIS_IDLE_TIMEOUT", envs.RedisIdleTimeout) err := newNotPositiveError("REDIS_IDLE_TIMEOUT", envs.RedisIdleTimeout)
withRedis = append(withRedis, err) res = append(res, err)
} }
if envs.RedisMaxActive < 0 { if envs.RedisMaxActive < 0 {
err := newNegativeError("REDIS_MAX_ACTIVE", envs.RedisMaxActive) err := newNegativeError("REDIS_MAX_ACTIVE", envs.RedisMaxActive)
withRedis = append(withRedis, err) res = append(res, err)
} }
if envs.RedisMaxIdle < 0 { if envs.RedisMaxIdle < 0 {
err := newNegativeError("REDIS_MAX_IDLE", envs.RedisMaxIdle) err := newNegativeError("REDIS_MAX_IDLE", envs.RedisMaxIdle)
withRedis = append(withRedis, err) res = append(res, err)
} }
return withRedis return res
} }
// validateBackendKV appends validation errors to the given errs if environment // validateBackendKV appends validation errors to the given errs if environment
// variables for a backend key-value store contain errors. // variables for a backend key-value store contain errors.
func (envs *environment) validateBackendKV(errs []error) (withKV []error) { func (envs *environment) validateBackendKV(errs []error) (res []error) {
withKV = errs res = errs
var u *url.URL var u *url.URL
if envs.DNSCheckRemoteKVURL != nil { if envs.DNSCheckRemoteKVURL != nil {
@ -299,16 +316,16 @@ func (envs *environment) validateBackendKV(errs []error) (withKV []error) {
err := urlutil.ValidateGRPCURL(u) err := urlutil.ValidateGRPCURL(u)
if err != nil { if err != nil {
withKV = append(withKV, fmt.Errorf("env DNSCHECK_REMOTEKV_URL: %w", err)) res = append(res, fmt.Errorf("env DNSCHECK_REMOTEKV_URL: %w", err))
} }
return withKV return res
} }
// validateProfilesURLs appends validation errors to the given errs if profiles // validateProfilesURLs appends validation errors to the given errs if profiles
// URLs in environment variables are invalid. // URLs in environment variables are invalid.
func (envs *environment) validateProfilesURLs(errs []error) (withURLs []error) { func (envs *environment) validateProfilesURLs(errs []error) (res []error) {
withURLs = errs res = errs
grpcOnlyURLs := []*urlEnvData{{ grpcOnlyURLs := []*urlEnvData{{
url: envs.BillStatURL, url: envs.BillStatURL,
@ -332,11 +349,11 @@ func (envs *environment) validateProfilesURLs(errs []error) (withURLs []error) {
err := urlutil.ValidateGRPCURL(u) err := urlutil.ValidateGRPCURL(u)
if err != nil { if err != nil {
withURLs = append(withURLs, fmt.Errorf("env %s: %w", urlData.name, err)) res = append(res, fmt.Errorf("env %s: %w", urlData.name, err))
} }
} }
return withURLs return res
} }
// validateRateLimitURLs appends validation errors to the given errs if rate // validateRateLimitURLs appends validation errors to the given errs if rate

View File

@ -2,16 +2,8 @@ package cmd
import ( import (
"fmt" "fmt"
"log/slog"
"net/url"
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/timeutil" "github.com/AdguardTeam/golibs/timeutil"
"github.com/c2h5oh/datasize" "github.com/c2h5oh/datasize"
) )
@ -27,9 +19,13 @@ type filtersConfig struct {
// CustomFilterCacheSize is the size of the LRU cache of compiled filtering // CustomFilterCacheSize is the size of the LRU cache of compiled filtering
// engines for profiles with custom filtering rules. // engines for profiles with custom filtering rules.
//
// TODO(a.garipov): Rename to "custom_filter_cache_count"?
CustomFilterCacheSize int `yaml:"custom_filter_cache_size"` CustomFilterCacheSize int `yaml:"custom_filter_cache_size"`
// SafeSearchCacheSize is the size of the LRU cache of safe-search results. // SafeSearchCacheSize is the size of the LRU cache of safe-search results.
//
// TODO(a.garipov): Rename to "safe_search_cache_count"?
SafeSearchCacheSize int `yaml:"safe_search_cache_size"` SafeSearchCacheSize int `yaml:"safe_search_cache_size"`
// ResponseTTL is the TTL to set for DNS responses to requests for filtered // ResponseTTL is the TTL to set for DNS responses to requests for filtered
@ -66,58 +62,6 @@ type filtersConfig struct {
SDEEnabled bool `yaml:"sde_enabled"` SDEEnabled bool `yaml:"sde_enabled"`
} }
// toInternal converts c to the filter storage configuration for the DNS server.
// cacheDir must exist. c must be valid.
func (c *filtersConfig) toInternal(
logger *slog.Logger,
errColl errcoll.Interface,
cacheManager agdcache.Manager,
envs *environment,
safeBrowsing *hashprefix.Filter,
adultBlocking *hashprefix.Filter,
newRegDomains *hashprefix.Filter,
) (conf *filter.DefaultStorageConfig) {
var blockedServiceIndexURL *url.URL
if envs.BlockedServiceEnabled {
blockedServiceIndexURL = netutil.CloneURL(&envs.BlockedServiceIndexURL.URL)
}
var generalSafeSearchRulesURL *url.URL
if envs.GeneralSafeSearchEnabled {
generalSafeSearchRulesURL = netutil.CloneURL(&envs.GeneralSafeSearchURL.URL)
}
var youtubeSafeSearchRulesURL *url.URL
if envs.YoutubeSafeSearchEnabled {
youtubeSafeSearchRulesURL = netutil.CloneURL(&envs.YoutubeSafeSearchURL.URL)
}
return &filter.DefaultStorageConfig{
BaseLogger: logger,
FilterIndexURL: netutil.CloneURL(&envs.FilterIndexURL.URL),
BlockedServiceIndexURL: blockedServiceIndexURL,
GeneralSafeSearchRulesURL: generalSafeSearchRulesURL,
YoutubeSafeSearchRulesURL: youtubeSafeSearchRulesURL,
SafeBrowsing: safeBrowsing,
AdultBlocking: adultBlocking,
NewRegDomains: newRegDomains,
Now: time.Now,
ErrColl: errColl,
CacheManager: cacheManager,
CacheDir: envs.FilterCachePath,
CustomFilterCacheSize: c.CustomFilterCacheSize,
SafeSearchCacheSize: c.SafeSearchCacheSize,
// TODO(a.garipov): Consider making this configurable.
SafeSearchCacheTTL: 1 * time.Hour,
RuleListCacheSize: c.RuleListCache.Size,
RefreshIvl: c.RefreshIvl.Duration,
IndexRefreshTimeout: c.IndexRefreshTimeout.Duration,
RuleListRefreshTimeout: c.RuleListRefreshTimeout.Duration,
UseRuleListCache: c.RuleListCache.Enabled,
MaxRuleListSize: c.MaxSize,
}
}
// type check // type check
var _ validator = (*filtersConfig)(nil) var _ validator = (*filtersConfig)(nil)
@ -153,6 +97,8 @@ func (c *filtersConfig) validate() (err error) {
// fltRuleListCache contains filtering rule-list cache configuration. // fltRuleListCache contains filtering rule-list cache configuration.
type fltRuleListCache struct { type fltRuleListCache struct {
// Size defines the size of the LRU cache of rule-list filtering results. // Size defines the size of the LRU cache of rule-list filtering results.
//
// TODO(a.garipov): Rename to "count"?
Size int `yaml:"size"` Size int `yaml:"size"`
// Enabled shows if the rule-list cache is enabled. If it is false, the // Enabled shows if the rule-list cache is enabled. If it is false, the

View File

@ -24,13 +24,17 @@ type filteringGroup struct {
// ID is a filtering group ID. Must be unique. // ID is a filtering group ID. Must be unique.
ID string `yaml:"id"` ID string `yaml:"id"`
// BlockPrivateRelay shows if Apple Private Relay queries are blocked for // BlockChromePrefetch shows if the Chrome prefetch proxy feature should be
// requests using this filtering group. // disabled for requests using this filtering group.
BlockPrivateRelay bool `yaml:"block_private_relay"` BlockChromePrefetch bool `yaml:"block_chrome_prefetch"`
// BlockFirefoxCanary shows if Firefox canary domain is blocked for // BlockFirefoxCanary shows if Firefox canary domain is blocked for
// requests using this filtering group. // requests using this filtering group.
BlockFirefoxCanary bool `yaml:"block_firefox_canary"` BlockFirefoxCanary bool `yaml:"block_firefox_canary"`
// BlockPrivateRelay shows if Apple Private Relay queries are blocked for
// requests using this filtering group.
BlockPrivateRelay bool `yaml:"block_private_relay"`
} }
// fltGrpRuleLists contains filter rule lists configuration for a filtering // fltGrpRuleLists contains filter rule lists configuration for a filtering
@ -44,6 +48,15 @@ type fltGrpRuleLists struct {
Enabled bool `yaml:"enabled"` Enabled bool `yaml:"enabled"`
} }
// toInternal converts c to the rule-list configuration for the filtering group.
// c must be valid.
func (c *fltGrpRuleLists) toInternal(ids []filter.ID) (fltConf *filter.ConfigRuleList) {
return &filter.ConfigRuleList{
IDs: ids,
Enabled: c.Enabled,
}
}
// fltGrpParental contains parental protection configuration for a filtering // fltGrpParental contains parental protection configuration for a filtering
// group. // group.
type fltGrpParental struct { type fltGrpParental struct {
@ -64,6 +77,19 @@ type fltGrpParental struct {
YoutubeSafeSearch bool `yaml:"youtube_safe_search"` YoutubeSafeSearch bool `yaml:"youtube_safe_search"`
} }
// toInternal converts c to the parental-control configuration for the filtering
// group. c must be valid.
func (c *fltGrpParental) toInternal() (fltConf *filter.ConfigParental) {
return &filter.ConfigParental{
PauseSchedule: nil,
BlockedServices: nil,
Enabled: c.Enabled,
AdultBlockingEnabled: c.BlockAdult,
SafeSearchGeneralEnabled: c.GeneralSafeSearch,
SafeSearchYouTubeEnabled: c.YoutubeSafeSearch,
}
}
// fltGrpSafeBrowsing contains general safe browsing configuration for // fltGrpSafeBrowsing contains general safe browsing configuration for
// a filtering group. // a filtering group.
type fltGrpSafeBrowsing struct { type fltGrpSafeBrowsing struct {
@ -79,6 +105,16 @@ type fltGrpSafeBrowsing struct {
BlockNewlyRegisteredDomains bool `yaml:"block_newly_registered_domains"` BlockNewlyRegisteredDomains bool `yaml:"block_newly_registered_domains"`
} }
// toInternal converts c to the safe-browsing configuration for the filtering
// group. c must be valid.
func (c *fltGrpSafeBrowsing) toInternal() (fltConf *filter.ConfigSafeBrowsing) {
return &filter.ConfigSafeBrowsing{
Enabled: c.Enabled,
DangerousDomainsEnabled: c.BlockDangerousDomains,
NewlyRegisteredDomainsEnabled: c.BlockNewlyRegisteredDomains,
}
}
// type check // type check
var _ validator = (*filteringGroup)(nil) var _ validator = (*filteringGroup)(nil)
@ -87,21 +123,23 @@ func (g *filteringGroup) validate() (err error) {
switch { switch {
case g == nil: case g == nil:
return errors.ErrNoValue return errors.ErrNoValue
case g.RuleLists == nil:
return fmt.Errorf("rule_lists: %w", errors.ErrNoValue)
case g.ID == "":
return fmt.Errorf("id: %w", errors.ErrEmptyValue)
case g.Parental == nil: case g.Parental == nil:
return fmt.Errorf("parental: %w", errors.ErrNoValue) return fmt.Errorf("parental: %w", errors.ErrNoValue)
case g.RuleLists == nil:
return fmt.Errorf("rule_lists: %w", errors.ErrNoValue)
case g.SafeBrowsing == nil:
return fmt.Errorf("safe_browsing: %w", errors.ErrNoValue)
case g.ID == "":
return fmt.Errorf("id: %w", errors.ErrEmptyValue)
} }
fltIDs := container.NewMapSet[string]() fltIDs := container.NewMapSet[string]()
for i, fltID := range g.RuleLists.IDs { for i, fltID := range g.RuleLists.IDs {
if fltIDs.Has(fltID) { if fltIDs.Has(fltID) {
return fmt.Errorf("rule_lists: at index %d: duplicate id %q", i, fltID) return fmt.Errorf("rule_lists: at index %d: id: %w: %q", i, errors.ErrDuplicated, fltID)
} }
_, err = agd.NewFilterListID(fltID) _, err = filter.NewID(fltID)
if err != nil { if err != nil {
return fmt.Errorf("rule_lists: at index %d: %w", i, err) return fmt.Errorf("rule_lists: at index %d: %w", i, err)
} }
@ -123,32 +161,29 @@ func (groups filteringGroups) toInternal(
) (fltGrps map[agd.FilteringGroupID]*agd.FilteringGroup, err error) { ) (fltGrps map[agd.FilteringGroupID]*agd.FilteringGroup, err error) {
fltGrps = make(map[agd.FilteringGroupID]*agd.FilteringGroup, len(groups)) fltGrps = make(map[agd.FilteringGroupID]*agd.FilteringGroup, len(groups))
for _, g := range groups { for _, g := range groups {
filterIDs := make([]agd.FilterListID, len(g.RuleLists.IDs)) filterIDs := make([]filter.ID, len(g.RuleLists.IDs))
for i, fltID := range g.RuleLists.IDs { for i, fltID := range g.RuleLists.IDs {
// Assume that these have already been validated in // Assume that these have already been validated in
// [filteringGroup.validate]. // [filteringGroup.validate].
id := agd.FilterListID(fltID) id := filter.ID(fltID)
if !s.HasListID(id) { if !s.HasListID(id) {
return nil, fmt.Errorf("filter list id %q is not in the index", id) return nil, fmt.Errorf("filter list id %q is not in the index", id)
} }
filterIDs[i] = agd.FilterListID(fltID) filterIDs[i] = filter.ID(fltID)
} }
id := agd.FilteringGroupID(g.ID) id := agd.FilteringGroupID(g.ID)
fltGrps[id] = &agd.FilteringGroup{ fltGrps[id] = &agd.FilteringGroup{
FilterConfig: &filter.ConfigGroup{
Parental: g.Parental.toInternal(),
RuleList: g.RuleLists.toInternal(filterIDs),
SafeBrowsing: g.SafeBrowsing.toInternal(),
},
ID: id, ID: id,
RuleListsEnabled: g.RuleLists.Enabled, BlockChromePrefetch: g.BlockChromePrefetch,
RuleListIDs: filterIDs,
ParentalEnabled: g.Parental.Enabled,
BlockAdult: g.Parental.BlockAdult,
SafeBrowsingEnabled: g.SafeBrowsing.Enabled,
BlockDangerousDomains: g.SafeBrowsing.BlockDangerousDomains,
BlockNewlyRegisteredDomains: g.SafeBrowsing.BlockNewlyRegisteredDomains,
GeneralSafeSearch: g.Parental.GeneralSafeSearch,
YoutubeSafeSearch: g.Parental.YoutubeSafeSearch,
BlockPrivateRelay: g.BlockPrivateRelay,
BlockFirefoxCanary: g.BlockFirefoxCanary, BlockFirefoxCanary: g.BlockFirefoxCanary,
BlockPrivateRelay: g.BlockPrivateRelay,
} }
} }
@ -172,7 +207,7 @@ func (groups filteringGroups) validate() (err error) {
} }
if ids.Has(string(g.ID)) { if ids.Has(string(g.ID)) {
return fmt.Errorf("at index %d: duplicate id %q", i, g.ID) return fmt.Errorf("at index %d: id: %w: %q", i, errors.ErrDuplicated, g.ID)
} }
ids.Add(g.ID) ids.Add(g.ID)

View File

@ -8,9 +8,13 @@ import (
// geoIPConfig is the GeoIP database configuration. // geoIPConfig is the GeoIP database configuration.
type geoIPConfig struct { type geoIPConfig struct {
// HostCacheSize is the size of the hostname lookup cache, in entries. // HostCacheSize is the size of the hostname lookup cache, in entries.
//
// TODO(a.garipov): Rename to "host_cache_count"?
HostCacheSize int `yaml:"host_cache_size"` HostCacheSize int `yaml:"host_cache_size"`
// IPCacheSize is the size of the IP lookup cache, in entries. // IPCacheSize is the size of the IP lookup cache, in entries.
//
// TODO(a.garipov): Rename to "ip_cache_count"?
IPCacheSize int `yaml:"ip_cache_size"` IPCacheSize int `yaml:"ip_cache_size"`
// RefreshIvl defines how often AdGuard DNS reopens the GeoIP database // RefreshIvl defines how often AdGuard DNS reopens the GeoIP database

View File

@ -1,11 +1,14 @@
package cmd package cmd
import ( import (
"crypto/tls"
"fmt" "fmt"
"net/netip" "net/netip"
"slices"
"github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/bindtodevice" "github.com/AdguardTeam/AdGuardDNS/internal/bindtodevice"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig" "github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig"
"github.com/AdguardTeam/golibs/container" "github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
@ -14,11 +17,11 @@ import (
// toInternal returns the configuration of DNS servers for a single server // toInternal returns the configuration of DNS servers for a single server
// group. srvs and other parts of the configuration must be valid. // group. srvs and other parts of the configuration must be valid.
func (srvs servers) toInternal( func (srvs servers) toInternal(
mtrc tlsconfig.Metrics,
tlsConfig *agd.TLS,
btdMgr *bindtodevice.Manager, btdMgr *bindtodevice.Manager,
tlsMgr tlsconfig.Manager,
ratelimitConf *rateLimitConfig, ratelimitConf *rateLimitConfig,
dnsConf *dnsConfig, dnsConf *dnsConfig,
deviceDomains []string,
) (dnsSrvs []*agd.Server, err error) { ) (dnsSrvs []*agd.Server, err error) {
dnsSrvs = make([]*agd.Server, 0, len(srvs)) dnsSrvs = make([]*agd.Server, 0, len(srvs))
for _, srv := range srvs { for _, srv := range srvs {
@ -66,18 +69,7 @@ func (srvs servers) toInternal(
QUICLimitsEnabled: ratelimitConf.QUIC.Enabled, QUICLimitsEnabled: ratelimitConf.QUIC.Enabled,
} }
tlsConf := tlsConfig.Conf.Clone() dnsSrv.TLS = newTLSConfig(dnsSrv, tlsMgr, deviceDomains, srv)
// Attach the functions that will count TLS handshake metrics.
tlsConf.GetConfigForClient = mtrc.BeforeHandshake(string(srv.Protocol))
tlsConf.VerifyConnection = mtrc.AfterHandshake(
string(srv.Protocol),
srv.Name,
tlsConfig.DeviceDomains,
tlsConf.Certificates,
)
dnsSrv.TLS = tlsConf
} }
dnsSrv.SetBindData(bindData) dnsSrv.SetBindData(bindData)
@ -88,6 +80,35 @@ func (srvs servers) toInternal(
return dnsSrvs, nil return dnsSrvs, nil
} }
// newTLSConfig returns the TLS configuration with metrics and ALPs set.
//
// TODO(s.chzhen): Consider moving to agd package as soon as the import cycle
// is resolved.
func newTLSConfig(
dnsSrv *agd.Server,
tlsMgr tlsconfig.Manager,
deviceDomains []string,
srv *server,
) (c *agd.TLSConfig) {
tlsConf := tlsMgr.CloneWithMetrics(string(srv.Protocol), srv.Name, deviceDomains)
var tlsConfH3 *tls.Config
switch dnsSrv.Protocol {
case agd.ProtoDoH:
tlsConfH3 = tlsMgr.CloneWithMetrics(string(srv.Protocol), srv.Name, deviceDomains)
tlsConf.NextProtos = slices.Clone(dnsserver.NextProtoDoH)
tlsConfH3.NextProtos = slices.Clone(dnsserver.NextProtoDoH3)
case agd.ProtoDoQ:
tlsConf.NextProtos = slices.Clone(dnsserver.NextProtoDoQ)
}
return &agd.TLSConfig{
Default: tlsConf,
H3: tlsConfH3,
}
}
// servers is a slice of server settings. A valid instance of servers has no // servers is a slice of server settings. A valid instance of servers has no
// nil items. // nil items.
type servers []*server type servers []*server
@ -110,7 +131,7 @@ func (srvs servers) validate() (needsTLS bool, err error) {
} }
if names.Has(s.Name) { if names.Has(s.Name) {
return false, fmt.Errorf("at index %d: duplicate name %q", i, s.Name) return false, fmt.Errorf("at index %d: name: %w: %q", i, errors.ErrDuplicated, s.Name)
} }
names.Add(s.Name) names.Add(s.Name)
@ -337,11 +358,11 @@ func (c *serverBindInterface) validate() (err error) {
set := container.NewMapSet[netip.Prefix]() set := container.NewMapSet[netip.Prefix]()
for i, subnet := range c.Subnets { for i, subnet := range c.Subnets {
if !subnet.IsValid() { if !subnet.IsValid() {
return fmt.Errorf("bad subnet at index %d", i) return fmt.Errorf("subnets: at index %d: bad subnet", i)
} }
if set.Has(subnet) { if set.Has(subnet) {
return fmt.Errorf("duplicate subnet %s at index %d", subnet, i) return fmt.Errorf("subnets: at index %d: %w: %s", i, errors.ErrDuplicated, subnet)
} }
set.Add(subnet) set.Add(subnet)

View File

@ -20,9 +20,9 @@ type serverGroups []*serverGroup
// service. srvGrps and other parts of the configuration must be valid. // service. srvGrps and other parts of the configuration must be valid.
func (srvGrps serverGroups) toInternal( func (srvGrps serverGroups) toInternal(
ctx context.Context, ctx context.Context,
mtrc tlsconfig.Metrics,
messages *dnsmsg.Constructor, messages *dnsmsg.Constructor,
btdMgr *bindtodevice.Manager, btdMgr *bindtodevice.Manager,
tlsMgr tlsconfig.Manager,
fltGrps map[agd.FilteringGroupID]*agd.FilteringGroup, fltGrps map[agd.FilteringGroupID]*agd.FilteringGroup,
ratelimitConf *rateLimitConfig, ratelimitConf *rateLimitConfig,
dnsConf *dnsConfig, dnsConf *dnsConfig,
@ -35,26 +35,26 @@ func (srvGrps serverGroups) toInternal(
return nil, fmt.Errorf("server group %q: unknown filtering group %q", g.Name, fltGrpID) return nil, fmt.Errorf("server group %q: unknown filtering group %q", g.Name, fltGrpID)
} }
var tlsConf *agd.TLS var deviceDomains []string
tlsConf, err = g.TLS.toInternal(ctx, mtrc) deviceDomains, err = g.TLS.toInternal(ctx, tlsMgr)
if err != nil { if err != nil {
return nil, fmt.Errorf("tls: %w", err) return nil, fmt.Errorf("tls %q: %w", g.Name, err)
} }
svcSrvGrps[i] = &agd.ServerGroup{ svcSrvGrps[i] = &agd.ServerGroup{
DDR: g.DDR.toInternal(messages), DDR: g.DDR.toInternal(messages),
TLS: tlsConf, DeviceDomains: deviceDomains,
Name: agd.ServerGroupName(g.Name), Name: agd.ServerGroupName(g.Name),
FilteringGroup: fltGrpID, FilteringGroup: fltGrpID,
ProfilesEnabled: g.ProfilesEnabled, ProfilesEnabled: g.ProfilesEnabled,
} }
svcSrvGrps[i].Servers, err = g.Servers.toInternal( svcSrvGrps[i].Servers, err = g.Servers.toInternal(
mtrc,
tlsConf,
btdMgr, btdMgr,
tlsMgr,
ratelimitConf, ratelimitConf,
dnsConf, dnsConf,
deviceDomains,
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("server group %q: %w", g.Name, err) return nil, fmt.Errorf("server group %q: %w", g.Name, err)
@ -81,7 +81,7 @@ func (srvGrps serverGroups) validate() (err error) {
} }
if names.Has(g.Name) { if names.Has(g.Name) {
return fmt.Errorf("at index %d: duplicate name %q", i, g.Name) return fmt.Errorf("at index %d: name: %w: %q", i, errors.ErrDuplicated, g.Name)
} }
names.Add(g.Name) names.Add(g.Name)
@ -148,3 +148,16 @@ func (g *serverGroup) validate() (err error) {
return nil return nil
} }
// collectSessTicketPaths returns the list of unique session ticket file paths
// for all server groups.
func (srvGrps serverGroups) collectSessTicketPaths() (paths []string) {
set := container.NewSortedSliceSet[string]()
for _, g := range srvGrps {
for _, k := range g.TLS.SessionKeys {
set.Add(k)
}
}
return set.Values()
}

View File

@ -1,121 +0,0 @@
package cmd
import (
"context"
"crypto/tls"
"fmt"
"log/slog"
"os"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig"
"github.com/AdguardTeam/golibs/logutil/slogutil"
)
// ticketRotator is a refresh worker that rereads and resets TLS session
// tickets. It should be initially refreshed before use.
type ticketRotator struct {
logger *slog.Logger
errColl errcoll.Interface
mtrc tlsconfig.Metrics
confs map[*tls.Config][]string
}
// newTicketRotator creates a new TLS session ticket rotator that rotates
// tickets for the TLS configurations of all servers in grps.
//
// grps must be valid.
func newTicketRotator(
logger *slog.Logger,
errColl errcoll.Interface,
mtrc tlsconfig.Metrics,
grps []*agd.ServerGroup,
) (tr *ticketRotator) {
confs := map[*tls.Config][]string{}
for _, g := range grps {
t := g.TLS
if t == nil || len(t.SessionKeys) == 0 {
continue
}
for _, srv := range g.Servers {
if srv.TLS != nil {
confs[srv.TLS] = t.SessionKeys
}
}
}
return &ticketRotator{
logger: logger.With(slogutil.KeyPrefix, "tickrot"),
errColl: errColl,
mtrc: mtrc,
confs: confs,
}
}
// sessTickLen is the length of a single TLS session ticket key in bytes.
//
// NOTE: Unlike Nginx, Go's crypto/tls doesn't use the random bytes from the
// session ticket keys as-is, but instead hashes these bytes and uses the first
// 48 bytes of the hashed data as the key name, the AES key, and the HMAC key.
const sessTickLen = 32
// type check
var _ agdservice.Refresher = (*ticketRotator)(nil)
// Refresh implements the [agdservice.Refresher] interface for *ticketRotator.
func (r *ticketRotator) Refresh(ctx context.Context) (err error) {
r.logger.DebugContext(ctx, "refresh started")
defer r.logger.DebugContext(ctx, "refresh finished")
defer func() {
if err != nil {
errcoll.Collect(ctx, r.errColl, r.logger, "ticket rotation failed", err)
}
}()
for conf, files := range r.confs {
keys := make([][sessTickLen]byte, 0, len(files))
for _, fileName := range files {
var key [sessTickLen]byte
key, err = readSessionTicketKey(fileName)
if err != nil {
r.mtrc.SetSessionTicketRotationStatus(ctx, false)
return fmt.Errorf("session ticket for srv %s: %w", conf.ServerName, err)
}
keys = append(keys, key)
}
if len(keys) == 0 {
return fmt.Errorf("no session tickets for srv %s in %q", conf.ServerName, files)
}
conf.SetSessionTicketKeys(keys)
}
r.mtrc.SetSessionTicketRotationStatus(ctx, true)
return nil
}
// readSessionTicketKey reads a single TLS session ticket key from a file.
func readSessionTicketKey(fn string) (key [sessTickLen]byte, err error) {
// #nosec G304 -- Trust the file paths that are given to us in the
// configuration file.
b, err := os.ReadFile(fn)
if err != nil {
return key, fmt.Errorf("reading session ticket: %w", err)
}
if len(b) < sessTickLen {
return key, fmt.Errorf("session ticket in %s: bad len %d, want %d", fn, len(b), sessTickLen)
}
return [sessTickLen]byte(b), nil
}

View File

@ -3,13 +3,9 @@ package cmd
import ( import (
"context" "context"
"crypto/tls" "crypto/tls"
"crypto/x509"
"fmt" "fmt"
"os"
"path/filepath"
"strings" "strings"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig" "github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig"
"github.com/AdguardTeam/golibs/container" "github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
@ -39,27 +35,22 @@ type tlsConfig struct {
// valid. // valid.
func (c *tlsConfig) toInternal( func (c *tlsConfig) toInternal(
ctx context.Context, ctx context.Context,
mtrc tlsconfig.Metrics, tlsMgr tlsconfig.Manager,
) (conf *agd.TLS, err error) { ) (deviceDomains []string, err error) {
if c == nil { if c == nil {
return nil, nil return nil, nil
} }
tlsConf, err := c.Certificates.toInternal(ctx, mtrc) err = c.Certificates.store(ctx, tlsMgr)
if err != nil { if err != nil {
return nil, fmt.Errorf("certificates: %w", err) return nil, fmt.Errorf("certificates: %w", err)
} }
var deviceDomains []string
for _, w := range c.DeviceIDWildcards { for _, w := range c.DeviceIDWildcards {
deviceDomains = append(deviceDomains, strings.TrimPrefix(w, "*.")) deviceDomains = append(deviceDomains, strings.TrimPrefix(w, "*."))
} }
return &agd.TLS{ return deviceDomains, nil
Conf: tlsConf,
DeviceDomains: deviceDomains,
SessionKeys: c.SessionKeys,
}, nil
} }
// validate returns an error if the TLS configuration is invalid for the given // validate returns an error if the TLS configuration is invalid for the given
@ -101,9 +92,9 @@ func validateDeviceIDWildcards(wildcards []string) (err error) {
for i, w := range wildcards { for i, w := range wildcards {
// TODO(e.burkov): Consider removing this requirement. // TODO(e.burkov): Consider removing this requirement.
if !strings.HasPrefix(w, "*.") { if !strings.HasPrefix(w, "*.") {
return fmt.Errorf("at index %d: not a wildcard", i) return fmt.Errorf("at index %d: not a wildcard: %q", i, w)
} else if s.Has(w) { } else if s.Has(w) {
return fmt.Errorf("at index %d: duplicated wildcard", i) return fmt.Errorf("at index %d: wildcard: %w: %q", i, errors.ErrDuplicated, w)
} }
s.Add(w) s.Add(w)
@ -125,42 +116,40 @@ type tlsConfigCert struct {
// no nil items. // no nil items.
type tlsConfigCerts []*tlsConfigCert type tlsConfigCerts []*tlsConfigCert
// toInternal converts certs to a TLS configuration. certs must be valid. // store stores the TLS certificates in the TLS manager. certs must be valid.
func (certs tlsConfigCerts) store(ctx context.Context, tlsMgr tlsconfig.Manager) (err error) {
var errs []error
for i, c := range certs {
err = tlsMgr.Add(ctx, c.Certificate, c.Key)
if err != nil {
errs = append(errs, fmt.Errorf("adding certificate at index %d: %w", i, err))
}
}
if len(errs) != 0 {
return errors.Join(errs...)
}
return nil
}
// toInternal is like [tlsConfigCerts.store] but it also returns the TLS
// configuration. certs must be valid.
func (certs tlsConfigCerts) toInternal( func (certs tlsConfigCerts) toInternal(
ctx context.Context, ctx context.Context,
mtrc tlsconfig.Metrics, tlsMgr tlsconfig.Manager,
) (conf *tls.Config, err error) { ) (conf *tls.Config, err error) {
if len(certs) == 0 { if len(certs) == 0 {
return nil, nil return nil, nil
} }
tlsCerts := make([]tls.Certificate, len(certs)) err = certs.store(ctx, tlsMgr)
for i, c := range certs {
var cert tls.Certificate
cert, err = tls.LoadX509KeyPair(c.Certificate, c.Key)
if err != nil { if err != nil {
return nil, fmt.Errorf("certificate at index %d: %w", i, err) // Don't wrap the error, because it's informative enough as is.
return nil, err
} }
var leaf *x509.Certificate return tlsMgr.Clone(), nil
leaf, err = x509.ParseCertificate(cert.Certificate[0])
if err != nil {
return nil, fmt.Errorf("invalid leaf, certificate at index %d: %w", i, err)
}
cert.Leaf = leaf
tlsCerts[i] = cert
authAlgo, subj := leaf.PublicKeyAlgorithm.String(), leaf.Subject.String()
mtrc.SetCertificateInfo(ctx, authAlgo, subj, leaf.NotAfter)
}
return &tls.Config{
Certificates: tlsCerts,
MinVersion: tls.VersionTLS12,
MaxVersion: tls.VersionTLS13,
}, nil
} }
// type check // type check
@ -181,24 +170,3 @@ func (certs tlsConfigCerts) validate() (err error) {
return nil return nil
} }
// enableTLSKeyLogging enables TLS key logging (use for debug purposes only).
func enableTLSKeyLogging(grps []*agd.ServerGroup, keyLogFileName string) (err error) {
path := filepath.Clean(keyLogFileName)
// TODO(a.garipov): Consider closing the file when we add SIGHUP support.
kl, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
if err != nil {
return fmt.Errorf("opening SSL_KEY_LOG_FILE: %w", err)
}
for _, g := range grps {
for _, s := range g.Servers {
if s.TLS != nil {
s.TLS.KeyLogWriter = kl
}
}
}
return nil
}

View File

@ -41,7 +41,10 @@ func (c *upstreamConfig) toInternal(logger *slog.Logger) (fwdConf *forward.Handl
upsConfs := toUpstreamConfigs(upstreams) upsConfs := toUpstreamConfigs(upstreams)
fallbackConfs := toUpstreamConfigs(fallbacks) fallbackConfs := toUpstreamConfigs(fallbacks)
metricsListener := prometheus.NewForwardMetricsListener(metrics.Namespace(), len(upstreams)+len(fallbacks)) metricsListener := prometheus.NewForwardMetricsListener(
metrics.Namespace(),
len(upstreams)+len(fallbacks),
)
var hcInit time.Duration var hcInit time.Duration
if c.Healthcheck.Enabled { if c.Healthcheck.Enabled {

View File

@ -68,7 +68,7 @@ func (c *webConfig) toInternal(
envs *environment, envs *environment,
dnsCk dnscheck.Interface, dnsCk dnscheck.Interface,
errColl errcoll.Interface, errColl errcoll.Interface,
mtrc tlsconfig.Metrics, tlsMgr tlsconfig.Manager,
) (conf *websvc.Config, err error) { ) (conf *websvc.Config, err error) {
if c == nil { if c == nil {
return nil, nil return nil, nil
@ -87,7 +87,7 @@ func (c *webConfig) toInternal(
conf.RootRedirectURL = netutil.CloneURL(&c.RootRedirectURL.URL) conf.RootRedirectURL = netutil.CloneURL(&c.RootRedirectURL.URL)
} }
conf.LinkedIP, err = c.LinkedIP.toInternal(ctx, mtrc, envs.LinkedIPTargetURL) conf.LinkedIP, err = c.LinkedIP.toInternal(ctx, tlsMgr, envs.LinkedIPTargetURL)
if err != nil { if err != nil {
return nil, fmt.Errorf("converting linked_ip: %w", err) return nil, fmt.Errorf("converting linked_ip: %w", err)
} }
@ -111,7 +111,7 @@ func (c *webConfig) toInternal(
}} }}
for _, bp := range blockPages { for _, bp := range blockPages {
*bp.webConfPtr, err = bp.conf.toInternal(ctx, mtrc) *bp.webConfPtr, err = bp.conf.toInternal(ctx, tlsMgr)
if err != nil { if err != nil {
return nil, fmt.Errorf("%s: %w", bp.name, err) return nil, fmt.Errorf("%s: %w", bp.name, err)
} }
@ -123,7 +123,7 @@ func (c *webConfig) toInternal(
return nil, err return nil, err
} }
conf.NonDoHBind, err = c.NonDoHBind.toInternal(ctx, mtrc) conf.NonDoHBind, err = c.NonDoHBind.toInternal(ctx, tlsMgr)
if err != nil { if err != nil {
return nil, fmt.Errorf("converting non_doh_bind: %w", err) return nil, fmt.Errorf("converting non_doh_bind: %w", err)
} }
@ -230,7 +230,7 @@ type linkedIPServer struct {
// toInternal converts s to a linkedIP server configuration. s must be valid. // toInternal converts s to a linkedIP server configuration. s must be valid.
func (s *linkedIPServer) toInternal( func (s *linkedIPServer) toInternal(
ctx context.Context, ctx context.Context,
mtrc tlsconfig.Metrics, tlsMgr tlsconfig.Manager,
targetURL *urlutil.URL, targetURL *urlutil.URL,
) (srv *websvc.LinkedIPServer, err error) { ) (srv *websvc.LinkedIPServer, err error) {
if s == nil { if s == nil {
@ -238,7 +238,7 @@ func (s *linkedIPServer) toInternal(
} }
srv = &websvc.LinkedIPServer{} srv = &websvc.LinkedIPServer{}
srv.Bind, err = s.Bind.toInternal(ctx, mtrc) srv.Bind, err = s.Bind.toInternal(ctx, tlsMgr)
if err != nil { if err != nil {
return nil, fmt.Errorf("converting bind: %w", err) return nil, fmt.Errorf("converting bind: %w", err)
} }
@ -288,7 +288,7 @@ type blockPageServer struct {
// toInternal converts s to a block page server configuration. s must be valid. // toInternal converts s to a block page server configuration. s must be valid.
func (s *blockPageServer) toInternal( func (s *blockPageServer) toInternal(
ctx context.Context, ctx context.Context,
mtrc tlsconfig.Metrics, tlsMgr tlsconfig.Manager,
) (conf *websvc.BlockPageServerConfig, err error) { ) (conf *websvc.BlockPageServerConfig, err error) {
if s == nil { if s == nil {
return nil, nil return nil, nil
@ -298,7 +298,7 @@ func (s *blockPageServer) toInternal(
ContentFilePath: s.BlockPage, ContentFilePath: s.BlockPage,
} }
conf.Bind, err = s.Bind.toInternal(ctx, mtrc) conf.Bind, err = s.Bind.toInternal(ctx, tlsMgr)
if err != nil { if err != nil {
return nil, fmt.Errorf("converting bind: %w", err) return nil, fmt.Errorf("converting bind: %w", err)
} }
@ -337,12 +337,12 @@ type bindData []*bindItem
// be valid. // be valid.
func (bd bindData) toInternal( func (bd bindData) toInternal(
ctx context.Context, ctx context.Context,
mtrc tlsconfig.Metrics, tlsMgr tlsconfig.Manager,
) (data []*websvc.BindData, err error) { ) (data []*websvc.BindData, err error) {
data = make([]*websvc.BindData, len(bd)) data = make([]*websvc.BindData, len(bd))
for i, d := range bd { for i, d := range bd {
data[i], err = d.toInternal(ctx, mtrc) data[i], err = d.toInternal(ctx, tlsMgr)
if err != nil { if err != nil {
return nil, fmt.Errorf("bind data at index %d: %w", i, err) return nil, fmt.Errorf("bind data at index %d: %w", i, err)
} }
@ -383,9 +383,9 @@ type bindItem struct {
// be valid. // be valid.
func (i *bindItem) toInternal( func (i *bindItem) toInternal(
ctx context.Context, ctx context.Context,
mtrc tlsconfig.Metrics, tlsMgr tlsconfig.Manager,
) (data *websvc.BindData, err error) { ) (data *websvc.BindData, err error) {
tlsConf, err := i.Certificates.toInternal(ctx, mtrc) tlsConf, err := i.Certificates.toInternal(ctx, tlsMgr)
if err != nil { if err != nil {
return nil, fmt.Errorf("certificates: %w", err) return nil, fmt.Errorf("certificates: %w", err)
} }

View File

@ -17,27 +17,27 @@ import (
func TestListenConfig(t *testing.T) { func TestListenConfig(t *testing.T) {
pc := &fakenet.PacketConn{ pc := &fakenet.PacketConn{
OnClose: func() (err error) { panic("not implemented") }, OnClose: func() (_ error) { panic("not implemented") },
OnLocalAddr: func() (laddr net.Addr) { panic("not implemented") }, OnLocalAddr: func() (_ net.Addr) { panic("not implemented") },
OnReadFrom: func(b []byte) (n int, addr net.Addr, err error) { panic("not implemented") }, OnReadFrom: func(_ []byte) (_ int, _ net.Addr, _ error) {
OnSetDeadline: func(t time.Time) (err error) { panic("not implemented") }, panic("not implemented")
OnSetReadDeadline: func(t time.Time) (err error) { panic("not implemented") }, },
OnSetWriteDeadline: func(t time.Time) (err error) { panic("not implemented") }, OnSetDeadline: func(_ time.Time) (_ error) { panic("not implemented") },
OnWriteTo: func(b []byte, addr net.Addr) (n int, err error) { panic("not implemented") }, OnSetReadDeadline: func(_ time.Time) (_ error) { panic("not implemented") },
OnSetWriteDeadline: func(_ time.Time) (_ error) { panic("not implemented") },
OnWriteTo: func(_ []byte, _ net.Addr) (_ int, _ error) {
panic("not implemented")
},
} }
lsnr := &fakenet.Listener{ lsnr := &fakenet.Listener{
OnAccept: func() (c net.Conn, err error) { panic("not implemented") }, OnAccept: func() (_ net.Conn, _ error) { panic("not implemented") },
OnAddr: func() (addr net.Addr) { panic("not implemented") }, OnAddr: func() (_ net.Addr) { panic("not implemented") },
OnClose: func() (err error) { return nil }, OnClose: func() (_ error) { return nil },
} }
c := &agdtest.ListenConfig{ c := &agdtest.ListenConfig{
OnListen: func( OnListen: func(ctx context.Context, network, address string) (l net.Listener, err error) {
ctx context.Context,
network string,
address string,
) (l net.Listener, err error) {
return lsnr, nil return lsnr, nil
}, },
OnListenPacket: func( OnListenPacket: func(

View File

@ -162,7 +162,7 @@ func TestService_Start(t *testing.T) {
resp, err = client.Post(ctx, refreshURL, agdhttp.HdrValApplicationJSON, reqBody) resp, err = client.Post(ctx, refreshURL, agdhttp.HdrValApplicationJSON, reqBody)
require.NoError(t, err) require.NoError(t, err)
assert.Len(t, refreshed, 0) assert.Empty(t, refreshed)
assert.Equal(t, http.StatusBadRequest, resp.StatusCode) assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
respBody = readRespBody(t, resp) respBody = readRespBody(t, resp)

View File

@ -2,15 +2,8 @@ package dnscheck_test
import ( import (
"net/netip" "net/netip"
"testing"
"github.com/AdguardTeam/golibs/testutil"
) )
func TestMain(m *testing.M) {
testutil.DiscardLogOutput(m)
}
// Test data. // Test data.
var ( var (
testRemoteIP = netip.MustParseAddr("1.2.3.4") testRemoteIP = netip.MustParseAddr("1.2.3.4")

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log/slog"
"net/http" "net/http"
"net/netip" "net/netip"
"slices" "slices"
@ -19,7 +20,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/remotekv/consulkv" "github.com/AdguardTeam/AdGuardDNS/internal/remotekv/consulkv"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/httphdr" "github.com/AdguardTeam/golibs/httphdr"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/netutil"
"github.com/miekg/dns" "github.com/miekg/dns"
cache "github.com/patrickmn/go-cache" cache "github.com/patrickmn/go-cache"
@ -28,8 +29,10 @@ import (
// RemoteKV is the RemoteKV KV based DNS checker. // RemoteKV is the RemoteKV KV based DNS checker.
type RemoteKV struct { type RemoteKV struct {
// mu protects cache. Don't use an RWMutex here, since the ratio of read logger *slog.Logger
// and write access is expected to be approximately equal.
// mu protects cache. Don't use an RWMutex here, since it is expected that
// there are about as many reads as there are writes.
mu *sync.Mutex mu *sync.Mutex
cache *cache.Cache cache *cache.Cache
@ -49,6 +52,9 @@ type RemoteKV struct {
// RemoteKVConfig is the configuration structure for remote KV based DNS // RemoteKVConfig is the configuration structure for remote KV based DNS
// checker. All fields must be non-empty. // checker. All fields must be non-empty.
type RemoteKVConfig struct { type RemoteKVConfig struct {
// Logger is used to log the operation of the DNS checker.
Logger *slog.Logger
// Messages is the message constructor used to create DNS responses with // Messages is the message constructor used to create DNS responses with
// IPv4 and IPv6 IPs. // IPv4 and IPv6 IPs.
Messages *dnsmsg.Constructor Messages *dnsmsg.Constructor
@ -85,8 +91,9 @@ const (
) )
// NewRemoteKV creates a new remote KV based DNS checker. c must be non-nil. // NewRemoteKV creates a new remote KV based DNS checker. c must be non-nil.
func NewRemoteKV(c *RemoteKVConfig) (cc *RemoteKV) { func NewRemoteKV(c *RemoteKVConfig) (dc *RemoteKV) {
return &RemoteKV{ return &RemoteKV{
logger: c.Logger,
mu: &sync.Mutex{}, mu: &sync.Mutex{},
cache: cache.New(defaultCacheExp, defaultCacheGC), cache: cache.New(defaultCacheExp, defaultCacheGC),
kv: c.RemoteKV, kv: c.RemoteKV,
@ -104,7 +111,7 @@ func NewRemoteKV(c *RemoteKVConfig) (cc *RemoteKV) {
var _ Interface = (*RemoteKV)(nil) var _ Interface = (*RemoteKV)(nil)
// Check implements the Interface interface for *RemoteKV. // Check implements the Interface interface for *RemoteKV.
func (cc *RemoteKV) Check( func (dc *RemoteKV) Check(
ctx context.Context, ctx context.Context,
req *dns.Msg, req *dns.Msg,
ri *agd.RequestInfo, ri *agd.RequestInfo,
@ -124,7 +131,7 @@ func (cc *RemoteKV) Check(
}() }()
var randomID string var randomID string
randomID, matched, err = randomIDFromDomain(ri.Host, cc.domains) randomID, matched, err = randomIDFromDomain(ri.Host, dc.domains)
if err != nil { if err != nil {
// Don't wrap the error, because it's informative enough as is. // Don't wrap the error, because it's informative enough as is.
return nil, err return nil, err
@ -132,32 +139,32 @@ func (cc *RemoteKV) Check(
// Not a dnscheck domain, just ignore the request. // Not a dnscheck domain, just ignore the request.
return nil, nil return nil, nil
} else if randomID == "" { } else if randomID == "" {
return cc.resp(ri, req) return dc.resp(ri, req)
} }
inf := cc.newInfo(ri) inf := dc.newInfo(ri)
b, err := json.Marshal(inf) b, err := json.Marshal(inf)
if err != nil { if err != nil {
return nil, fmt.Errorf("encoding value for key %q for remote kv: %w", randomID, err) return nil, fmt.Errorf("encoding value for key %q for remote kv: %w", randomID, err)
} }
cc.addToCache(randomID, b) dc.addToCache(randomID, b)
err = cc.kv.Set(ctx, randomID, b) err = dc.kv.Set(ctx, randomID, b)
if err != nil { if err != nil {
errcoll.Collectf(ctx, cc.errColl, "dnscheck: remote kv setting: %w", err) errcoll.Collect(ctx, dc.errColl, dc.logger, "remote kv setting", err)
} }
return cc.resp(ri, req) return dc.resp(ri, req)
} }
// addToCache adds inf into cache using randomID as key. It's safe for // addToCache adds inf into cache using randomID as key. It's safe for
// concurrent use. // concurrent use.
func (cc *RemoteKV) addToCache(randomID string, inf []byte) { func (dc *RemoteKV) addToCache(randomID string, inf []byte) {
cc.mu.Lock() dc.mu.Lock()
defer cc.mu.Unlock() defer dc.mu.Unlock()
cc.cache.SetDefault(randomID, inf) dc.cache.SetDefault(randomID, inf)
} }
// serverType is a type for the enum of server types in the DNS checker HTTP // serverType is a type for the enum of server types in the DNS checker HTTP
@ -172,7 +179,7 @@ const (
// newInfo returns an information record with all available data about the // newInfo returns an information record with all available data about the
// server and the request. ri must not be nil. // server and the request. ri must not be nil.
func (cc *RemoteKV) newInfo(ri *agd.RequestInfo) (inf *info) { func (dc *RemoteKV) newInfo(ri *agd.RequestInfo) (inf *info) {
g := ri.ServerGroup g := ri.ServerGroup
srvType := serverTypePublic srvType := serverTypePublic
@ -186,8 +193,8 @@ func (cc *RemoteKV) newInfo(ri *agd.RequestInfo) (inf *info) {
ServerType: srvType, ServerType: srvType,
Protocol: ri.Proto.String(), Protocol: ri.Proto.String(),
NodeLocation: cc.nodeLocation, NodeLocation: dc.nodeLocation,
NodeName: cc.nodeName, NodeName: dc.nodeName,
ClientIP: ri.RemoteIP, ClientIP: ri.RemoteIP,
} }
@ -204,7 +211,7 @@ func (cc *RemoteKV) newInfo(ri *agd.RequestInfo) (inf *info) {
// //
// TODO(e.burkov): Inspect the reason for using different message constructors // TODO(e.burkov): Inspect the reason for using different message constructors
// for different DNS types, and consider using only one of them. // for different DNS types, and consider using only one of them.
func (cc *RemoteKV) resp(ri *agd.RequestInfo, req *dns.Msg) (resp *dns.Msg, err error) { func (dc *RemoteKV) resp(ri *agd.RequestInfo, req *dns.Msg) (resp *dns.Msg, err error) {
qt := ri.QType qt := ri.QType
if qt != dns.TypeA && qt != dns.TypeAAAA { if qt != dns.TypeA && qt != dns.TypeAAAA {
@ -212,24 +219,23 @@ func (cc *RemoteKV) resp(ri *agd.RequestInfo, req *dns.Msg) (resp *dns.Msg, err
} }
if qt == dns.TypeA { if qt == dns.TypeA {
return cc.messages.NewRespIP(req, cc.ipv4...) return dc.messages.NewRespIP(req, dc.ipv4...)
} }
return cc.messages.NewRespIP(req, cc.ipv6...) return dc.messages.NewRespIP(req, dc.ipv6...)
} }
// type check // type check
var _ http.Handler = (*RemoteKV)(nil) var _ http.Handler = (*RemoteKV)(nil)
// ServeHTTP implements the http.Handler interface for *RemoteKV. // ServeHTTP implements the http.Handler interface for *RemoteKV.
func (cc *RemoteKV) ServeHTTP(w http.ResponseWriter, r *http.Request) { //
m, p, raddr := r.Method, r.URL.Path, r.RemoteAddr // TODO(a.garipov): Consider using the websvc logger once it switches to
log.Debug("dnscheck: http req %s %s from %s", m, p, raddr) // log/slog.
defer log.Debug("dnscheck: finished http req %s %s from %s", m, p, raddr) func (dc *RemoteKV) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// TODO(a.garipov): Put this into constant here and in package dnssvc. // TODO(a.garipov): Put this into constant here and in package dnssvc.
if r.URL.Path == "/dnscheck/test" { if r.URL.Path == "/dnscheck/test" {
cc.serveCheckTest(r.Context(), w, r) dc.serveCheckTest(r.Context(), w, r)
return return
} }
@ -241,48 +247,48 @@ func (cc *RemoteKV) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// //
// TODO(a.garipov): Refactor this and other HTTP handlers to return wrapped // TODO(a.garipov): Refactor this and other HTTP handlers to return wrapped
// errors and centralize the error handling. // errors and centralize the error handling.
func (cc *RemoteKV) serveCheckTest(ctx context.Context, w http.ResponseWriter, r *http.Request) { func (dc *RemoteKV) serveCheckTest(ctx context.Context, w http.ResponseWriter, r *http.Request) {
raddr := r.RemoteAddr l := dc.logger.With("raddr", r.RemoteAddr)
name, err := netutil.SplitHost(r.Host) host, err := netutil.SplitHost(r.Host)
if err != nil { if err != nil {
log.Debug("dnscheck: http req from %s: bad host %q: %s", raddr, r.Host, err) l.DebugContext(ctx, "bad host", "hostport", r.Host, slogutil.KeyError, err)
http.NotFound(w, r) http.NotFound(w, r)
return return
} }
randomID, matched, err := randomIDFromDomain(name, cc.domains) randomID, matched, err := randomIDFromDomain(host, dc.domains)
if err != nil { if err != nil {
log.Debug("dnscheck: http req from %s: id: %s", raddr, err) l.DebugContext(ctx, "bad request", "host", host, slogutil.KeyError, err)
http.NotFound(w, r) http.NotFound(w, r)
return return
} else if !matched || randomID == "" { } else if !matched || randomID == "" {
// We expect dnscheck requests to have a unique ID in the domain name. // We expect dnscheck requests to have a unique ID in the domain name.
log.Debug("dnscheck: http req from %s: bad domain %q", raddr, name) l.DebugContext(ctx, "bad domain", "host", host, slogutil.KeyError, err)
http.NotFound(w, r) http.NotFound(w, r)
return return
} }
inf, ok, err := cc.info(ctx, randomID) inf, ok, err := dc.info(ctx, randomID)
// TODO(s.chzhen): Use error interface instead of error value. // TODO(s.chzhen): Use error interface instead of error value.
if errors.Is(err, consulkv.ErrRateLimited) { if errors.Is(err, consulkv.ErrRateLimited) {
http.Error(w, err.Error(), http.StatusTooManyRequests) http.Error(w, err.Error(), http.StatusTooManyRequests)
return return
} else if err != nil { } else if err != nil {
log.Debug("dnscheck: http req from %s: getting info: %s", raddr, err) l.DebugContext(ctx, "getting info", "random_id", randomID, slogutil.KeyError, err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} else if !ok { } else if !ok {
log.Debug("dnscheck: http req from %s: no info for %q", raddr, randomID) l.DebugContext(ctx, "no info", "random_id", randomID, slogutil.KeyError, err)
http.NotFound(w, r) http.NotFound(w, r)
@ -295,12 +301,12 @@ func (cc *RemoteKV) serveCheckTest(ctx context.Context, w http.ResponseWriter, r
_, err = w.Write(inf) _, err = w.Write(inf)
if err != nil { if err != nil {
errcoll.Collectf(ctx, cc.errColl, "dnscheck: http resp write error: %w", err) errcoll.Collect(ctx, dc.errColl, dc.logger, "http resp write", err)
} }
} }
// info returns an information record by the random request ID. // info returns an information record by the random request ID.
func (cc *RemoteKV) info(ctx context.Context, randomID string) (inf []byte, ok bool, err error) { func (dc *RemoteKV) info(ctx context.Context, randomID string) (inf []byte, ok bool, err error) {
defer func() { defer func() {
metrics.DNSCheckRequestTotal.With(prometheus.Labels{ metrics.DNSCheckRequestTotal.With(prometheus.Labels{
"type": "http", "type": "http",
@ -312,17 +318,17 @@ func (cc *RemoteKV) info(ctx context.Context, randomID string) (inf []byte, ok b
defer func() { err = errors.Annotate(err, "getting from remote kv: %w") }() defer func() { err = errors.Annotate(err, "getting from remote kv: %w") }()
cc.mu.Lock() dc.mu.Lock()
defer cc.mu.Unlock() defer dc.mu.Unlock()
infoVal, ok := cc.cache.Get(randomID) infoVal, ok := dc.cache.Get(randomID)
if ok { if ok {
return infoVal.([]byte), true, nil return infoVal.([]byte), true, nil
} }
inf, ok, err = cc.kv.Get(ctx, randomID) inf, ok, err = dc.kv.Get(ctx, randomID)
if err != nil { if err != nil {
errcoll.Collectf(ctx, cc.errColl, "dnscheck: remote kv getting: %w", err) errcoll.Collect(ctx, dc.errColl, dc.logger, "remote kv getting", err)
// Don't wrap the error, as it will get annotated. // Don't wrap the error, as it will get annotated.
return nil, false, err return nil, false, err

View File

@ -17,6 +17,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnscheck" "github.com/AdguardTeam/AdGuardDNS/internal/dnscheck"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/remotekv" "github.com/AdguardTeam/AdGuardDNS/internal/remotekv"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil/urlutil" "github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -46,6 +47,7 @@ func TestConsul_ServeHTTP(t *testing.T) {
} }
conf := &dnscheck.RemoteKVConfig{ conf := &dnscheck.RemoteKVConfig{
Logger: slogutil.NewDiscardLogger(),
Messages: &dnsmsg.Constructor{}, Messages: &dnsmsg.Constructor{},
RemoteKV: remotekv.Empty{}, RemoteKV: remotekv.Empty{},
ErrColl: agdtest.NewErrorCollector(), ErrColl: agdtest.NewErrorCollector(),
@ -171,6 +173,7 @@ func TestConsul_Check(t *testing.T) {
msgs := agdtest.NewConstructorWithTTL(t, ttl*time.Second) msgs := agdtest.NewConstructorWithTTL(t, ttl*time.Second)
conf := &dnscheck.RemoteKVConfig{ conf := &dnscheck.RemoteKVConfig{
Logger: slogutil.NewDiscardLogger(),
Messages: msgs, Messages: msgs,
RemoteKV: remotekv.Empty{}, RemoteKV: remotekv.Empty{},
Domains: []string{checkDomain}, Domains: []string{checkDomain},

View File

@ -6,6 +6,7 @@ package dnsdb
import ( import (
"context" "context"
"log/slog"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
@ -35,6 +36,7 @@ func (Empty) Record(_ context.Context, _ *dns.Msg, _ *agd.RequestInfo) {}
// Default is the default DNSDB implementation. // Default is the default DNSDB implementation.
type Default struct { type Default struct {
logger *slog.Logger
buffer *atomic.Pointer[buffer] buffer *atomic.Pointer[buffer]
errColl errcoll.Interface errColl errcoll.Interface
maxSize int maxSize int
@ -42,6 +44,9 @@ type Default struct {
// DefaultConfig is the default DNS database configuration structure. // DefaultConfig is the default DNS database configuration structure.
type DefaultConfig struct { type DefaultConfig struct {
// Logger is used to log the operation of the DNS database.
Logger *slog.Logger
// ErrColl is used to collect HTTP errors. // ErrColl is used to collect HTTP errors.
ErrColl errcoll.Interface ErrColl errcoll.Interface
@ -52,6 +57,7 @@ type DefaultConfig struct {
// New creates a new default DNS database. c must not be nil. // New creates a new default DNS database. c must not be nil.
func New(c *DefaultConfig) (db *Default) { func New(c *DefaultConfig) (db *Default) {
db = &Default{ db = &Default{
logger: c.Logger,
buffer: &atomic.Pointer[buffer]{}, buffer: &atomic.Pointer[buffer]{},
errColl: c.ErrColl, errColl: c.ErrColl,
maxSize: c.MaxSize, maxSize: c.MaxSize,

View File

@ -1,11 +0,0 @@
package dnsdb_test
import (
"testing"
"github.com/AdguardTeam/golibs/testutil"
)
func TestMain(m *testing.M) {
testutil.DiscardLogOutput(m)
}

View File

@ -31,7 +31,7 @@ func (db *Default) ServeHTTP(w http.ResponseWriter, r *http.Request) {
defer func() { defer func() {
if err != nil { if err != nil {
h.Set(httphdr.XError, err.Error()) h.Set(httphdr.XError, err.Error())
errcoll.Collectf(ctx, db.errColl, "dnsdb: http handler error: %w", err) errcoll.Collect(ctx, db.errColl, db.logger, "handling http", err)
} }
}() }()

View File

@ -18,6 +18,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsdb" "github.com/AdguardTeam/AdGuardDNS/internal/dnsdb"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
"github.com/AdguardTeam/golibs/httphdr" "github.com/AdguardTeam/golibs/httphdr"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil/urlutil" "github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -120,6 +121,7 @@ func TestDefault_ServeHTTP(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
db := dnsdb.New(&dnsdb.DefaultConfig{ db := dnsdb.New(&dnsdb.DefaultConfig{
Logger: slogutil.NewDiscardLogger(),
ErrColl: agdtest.NewErrorCollector(), ErrColl: agdtest.NewErrorCollector(),
MaxSize: 100, MaxSize: 100,
}) })

View File

@ -42,9 +42,9 @@ type MiddlewareConfig struct {
// performance metrics. If not set, EmptyMetricsListener is used. // performance metrics. If not set, EmptyMetricsListener is used.
MetricsListener MetricsListener MetricsListener MetricsListener
// Size is the number of entities to hold in the cache. It must be greater // Count is the number of entities to hold in the cache. It must be
// than zero. // positive.
Size int Count int
// MinTTL is the minimum supported TTL for cache items. // MinTTL is the minimum supported TTL for cache items.
MinTTL time.Duration MinTTL time.Duration
@ -64,7 +64,7 @@ func NewMiddleware(c *MiddlewareConfig) (m *Middleware) {
return &Middleware{ return &Middleware{
metrics: metrics, metrics: metrics,
cache: gcache.New(c.Size).LRU().Build(), cache: gcache.New(c.Count).LRU().Build(),
cacheMinTTL: c.MinTTL, cacheMinTTL: c.MinTTL,
overrideTTL: c.OverrideTTL, overrideTTL: c.OverrideTTL,
} }

View File

@ -19,8 +19,8 @@ func TestMiddleware_Wrap(t *testing.T) {
const ( const (
servFailMaxCacheTTL = 30 servFailMaxCacheTTL = 30
reqHostname = "example.com" reqHost = "example.com"
reqCname = "cname.example.com" reqCNAME = "cname.example.com"
reqNs1 = "ns1.example.com" reqNs1 = "ns1.example.com"
reqNs2 = "ns2.example.com" reqNs2 = "ns2.example.com"
@ -30,10 +30,10 @@ func TestMiddleware_Wrap(t *testing.T) {
reqAddr := netip.MustParseAddr("1.2.3.4") reqAddr := netip.MustParseAddr("1.2.3.4")
testTTL := 60 * time.Second testTTL := 60 * time.Second
aReq := dnsservertest.NewReq(reqHostname, dns.TypeA, dns.ClassINET) aReq := dnsservertest.NewReq(reqHost, dns.TypeA, dns.ClassINET)
cnameReq := dnsservertest.NewReq(reqHostname, dns.TypeCNAME, dns.ClassINET) cnameReq := dnsservertest.NewReq(reqHost, dns.TypeCNAME, dns.ClassINET)
cnameAns := dnsservertest.SectionAnswer{dnsservertest.NewCNAME(reqHostname, defaultTTL, reqCname)} cnameAns := dnsservertest.SectionAnswer{dnsservertest.NewCNAME(reqHost, defaultTTL, reqCNAME)}
soaNs := dnsservertest.SectionNs{dnsservertest.NewSOA(reqHostname, defaultTTL, reqNs1, reqNs2)} soaNs := dnsservertest.SectionNs{dnsservertest.NewSOA(reqHost, defaultTTL, reqNs1, reqNs2)}
const N = 5 const N = 5
testCases := []struct { testCases := []struct {
@ -46,7 +46,7 @@ func TestMiddleware_Wrap(t *testing.T) {
}{{ }{{
req: aReq, req: aReq,
resp: dnsservertest.NewResp(dns.RcodeSuccess, aReq, dnsservertest.SectionAnswer{ resp: dnsservertest.NewResp(dns.RcodeSuccess, aReq, dnsservertest.SectionAnswer{
dnsservertest.NewA(reqHostname, defaultTTL, reqAddr), dnsservertest.NewA(reqHost, defaultTTL, reqAddr),
}), }),
name: "simple_a", name: "simple_a",
wantNumReq: 1, wantNumReq: 1,
@ -83,7 +83,7 @@ func TestMiddleware_Wrap(t *testing.T) {
}, { }, {
req: aReq, req: aReq,
resp: dnsservertest.NewResp(dns.RcodeNameError, aReq, dnsservertest.SectionNs{ resp: dnsservertest.NewResp(dns.RcodeNameError, aReq, dnsservertest.SectionNs{
dnsservertest.NewNS(reqHostname, defaultTTL, reqNs1), dnsservertest.NewNS(reqHost, defaultTTL, reqNs1),
}), }),
name: "non_authoritative_nxdomain", name: "non_authoritative_nxdomain",
// TODO(ameshkov): Consider https://datatracker.ietf.org/doc/html/rfc2308#section-3. // TODO(ameshkov): Consider https://datatracker.ietf.org/doc/html/rfc2308#section-3.
@ -107,7 +107,7 @@ func TestMiddleware_Wrap(t *testing.T) {
}, { }, {
req: cnameReq, req: cnameReq,
resp: dnsservertest.NewResp(dns.RcodeSuccess, cnameReq, dnsservertest.SectionAnswer{ resp: dnsservertest.NewResp(dns.RcodeSuccess, cnameReq, dnsservertest.SectionAnswer{
dnsservertest.NewCNAME(reqHostname, defaultTTL, reqCname), dnsservertest.NewCNAME(reqHost, defaultTTL, reqCNAME),
}), }),
name: "simple_cname_ans", name: "simple_cname_ans",
wantNumReq: 1, wantNumReq: 1,
@ -116,7 +116,7 @@ func TestMiddleware_Wrap(t *testing.T) {
}, { }, {
req: aReq, req: aReq,
resp: dnsservertest.NewResp(dns.RcodeSuccess, aReq, dnsservertest.SectionAnswer{ resp: dnsservertest.NewResp(dns.RcodeSuccess, aReq, dnsservertest.SectionAnswer{
dnsservertest.NewA(reqHostname, 0, reqAddr), dnsservertest.NewA(reqHost, 0, reqAddr),
}), }),
name: "expired_one", name: "expired_one",
wantNumReq: N, wantNumReq: N,
@ -125,7 +125,7 @@ func TestMiddleware_Wrap(t *testing.T) {
}, { }, {
req: aReq, req: aReq,
resp: dnsservertest.NewResp(dns.RcodeSuccess, aReq, dnsservertest.SectionAnswer{ resp: dnsservertest.NewResp(dns.RcodeSuccess, aReq, dnsservertest.SectionAnswer{
dnsservertest.NewA(reqHostname, 10, reqAddr), dnsservertest.NewA(reqHost, 10, reqAddr),
}), }),
name: "override_ttl_ok", name: "override_ttl_ok",
wantNumReq: 1, wantNumReq: 1,
@ -134,7 +134,7 @@ func TestMiddleware_Wrap(t *testing.T) {
}, { }, {
req: aReq, req: aReq,
resp: dnsservertest.NewResp(dns.RcodeSuccess, aReq, dnsservertest.SectionAnswer{ resp: dnsservertest.NewResp(dns.RcodeSuccess, aReq, dnsservertest.SectionAnswer{
dnsservertest.NewA(reqHostname, 1000, reqAddr), dnsservertest.NewA(reqHost, 1000, reqAddr),
}), }),
name: "override_ttl_max", name: "override_ttl_max",
wantNumReq: 1, wantNumReq: 1,
@ -143,7 +143,7 @@ func TestMiddleware_Wrap(t *testing.T) {
}, { }, {
req: aReq, req: aReq,
resp: dnsservertest.NewResp(dns.RcodeSuccess, aReq, dnsservertest.SectionAnswer{ resp: dnsservertest.NewResp(dns.RcodeSuccess, aReq, dnsservertest.SectionAnswer{
dnsservertest.NewA(reqHostname, 0, reqAddr), dnsservertest.NewA(reqHost, 0, reqAddr),
}), }),
name: "override_ttl_zero", name: "override_ttl_zero",
wantNumReq: N, wantNumReq: N,
@ -152,7 +152,7 @@ func TestMiddleware_Wrap(t *testing.T) {
}, { }, {
req: aReq, req: aReq,
resp: dnsservertest.NewResp(dns.RcodeServerFailure, aReq, dnsservertest.SectionAnswer{ resp: dnsservertest.NewResp(dns.RcodeServerFailure, aReq, dnsservertest.SectionAnswer{
dnsservertest.NewA(reqHostname, servFailMaxCacheTTL, reqAddr), dnsservertest.NewA(reqHost, servFailMaxCacheTTL, reqAddr),
}), }),
name: "override_ttl_servfail", name: "override_ttl_servfail",
wantNumReq: 1, wantNumReq: 1,
@ -186,7 +186,7 @@ func TestMiddleware_Wrap(t *testing.T) {
withCache := dnsserver.WithMiddlewares( withCache := dnsserver.WithMiddlewares(
handler, handler,
cache.NewMiddleware(&cache.MiddlewareConfig{ cache.NewMiddleware(&cache.MiddlewareConfig{
Size: 100, Count: 100,
MinTTL: minTTL, MinTTL: minTTL,
OverrideTTL: tc.minTTL != nil, OverrideTTL: tc.minTTL != nil,
}), }),

View File

@ -158,6 +158,14 @@ func RunLocalHTTPSServer(
network = dnsserver.NetworkTCP network = dnsserver.NetworkTCP
} }
var tlsConfigH3 *tls.Config
if tlsConfig != nil {
tlsConfigH3 = tlsConfig.Clone()
tlsConfig.NextProtos = dnsserver.NextProtoDoH
tlsConfigH3.NextProtos = dnsserver.NextProtoDoH3
}
conf := dnsserver.ConfigHTTPS{ conf := dnsserver.ConfigHTTPS{
ConfigBase: dnsserver.ConfigBase{ ConfigBase: dnsserver.ConfigBase{
Name: "test", Name: "test",
@ -165,7 +173,8 @@ func RunLocalHTTPSServer(
Handler: h, Handler: h,
Network: network, Network: network,
}, },
TLSConfig: tlsConfig, TLSConfDefault: tlsConfig,
TLSConfH3: tlsConfigH3,
NonDNSHandler: nonDNSHandler, NonDNSHandler: nonDNSHandler,
} }

View File

@ -38,20 +38,30 @@ func CreateServerTLSConfig(tlsServerName string) (tlsConfig *tls.Config) {
NotBefore: notBefore, NotBefore: notBefore,
NotAfter: notAfter, NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature |
x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true, BasicConstraintsValid: true,
IsCA: true, IsCA: true,
} }
template.DNSNames = append(template.DNSNames, tlsServerName) template.DNSNames = append(template.DNSNames, tlsServerName)
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(privateKey), privateKey) derBytes, err := x509.CreateCertificate(
rand.Reader,
&template,
&template,
publicKey(privateKey),
privateKey,
)
if err != nil { if err != nil {
panic(fmt.Sprintf("failed to create certificate: %v", err)) panic(fmt.Sprintf("failed to create certificate: %v", err))
} }
certPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) certPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
keyPem := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)}) keyPem := pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
})
cert, err := tls.X509KeyPair(certPem, keyPem) cert, err := tls.X509KeyPair(certPem, keyPem)
if err != nil { if err != nil {

View File

@ -1,9 +1,9 @@
module github.com/AdguardTeam/AdGuardDNS/internal/dnsserver module github.com/AdguardTeam/AdGuardDNS/internal/dnsserver
go 1.23.2 go 1.23.4
require ( require (
github.com/AdguardTeam/golibs v0.30.1 github.com/AdguardTeam/golibs v0.30.4
github.com/ameshkov/dnscrypt/v2 v2.3.0 github.com/ameshkov/dnscrypt/v2 v2.3.0
github.com/ameshkov/dnsstamps v1.0.3 github.com/ameshkov/dnsstamps v1.0.3
github.com/bluele/gcache v0.0.2 github.com/bluele/gcache v0.0.2
@ -12,11 +12,11 @@ require (
github.com/panjf2000/ants/v2 v2.10.0 github.com/panjf2000/ants/v2 v2.10.0
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible
github.com/prometheus/client_golang v1.20.5 github.com/prometheus/client_golang v1.20.5
github.com/quic-go/quic-go v0.48.1 github.com/quic-go/quic-go v0.48.2
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
golang.org/x/net v0.30.0 golang.org/x/net v0.32.0
golang.org/x/sys v0.26.0 golang.org/x/sys v0.28.0
) )
require ( require (
@ -26,23 +26,23 @@ require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/google/pprof v0.0.0-20241023014458-598669927662 // indirect github.com/google/pprof v0.0.0-20241203143554-1e3fdc7de467 // indirect
github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/compress v1.17.11 // indirect
github.com/kr/text v0.2.0 // indirect github.com/kr/text v0.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/onsi/ginkgo/v2 v2.20.2 // indirect github.com/onsi/ginkgo/v2 v2.22.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.60.0 // indirect github.com/prometheus/common v0.60.1 // indirect
github.com/prometheus/procfs v0.15.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect
github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/qpack v0.5.1 // indirect
go.uber.org/mock v0.5.0 // indirect go.uber.org/mock v0.5.0 // indirect
golang.org/x/crypto v0.28.0 // indirect golang.org/x/crypto v0.30.0 // indirect
golang.org/x/mod v0.21.0 // indirect golang.org/x/mod v0.22.0 // indirect
golang.org/x/sync v0.8.0 // indirect golang.org/x/sync v0.10.0 // indirect
golang.org/x/text v0.19.0 // indirect golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.7.0 // indirect golang.org/x/time v0.8.0 // indirect
golang.org/x/tools v0.26.0 // indirect golang.org/x/tools v0.28.0 // indirect
google.golang.org/protobuf v1.35.1 // indirect google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

View File

@ -1,5 +1,4 @@
github.com/AdguardTeam/golibs v0.30.1 h1:/yv7dq2h7WXw/jTDxkE3FP9zHerRT+i03PZRHJX4fPU= github.com/AdguardTeam/golibs v0.30.4 h1:zfFX1v4hkOCz6BifkneiBW2PCwSK523kYNr+VwaFrIw=
github.com/AdguardTeam/golibs v0.30.1/go.mod h1:FkwcNQEJoGsgDGXcalrVa/4gWbE68KsmE2guXWtBQUE=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw= github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
@ -26,8 +25,7 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20241023014458-598669927662 h1:SKMkD83p7FwUqKmBsPdLHF5dNyxq3jOWwu9w9UyH5vA= github.com/google/pprof v0.0.0-20241203143554-1e3fdc7de467 h1:keEZFtbLJugfE0qHn+Ge1JCE71spzkchQobDf3mzS/4=
github.com/google/pprof v0.0.0-20241023014458-598669927662/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@ -38,10 +36,8 @@ github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/panjf2000/ants/v2 v2.10.0 h1:zhRg1pQUtkyRiOFo2Sbqwjp0GfBNo9cUY2/Grpx1p+8= github.com/panjf2000/ants/v2 v2.10.0 h1:zhRg1pQUtkyRiOFo2Sbqwjp0GfBNo9cUY2/Grpx1p+8=
github.com/panjf2000/ants/v2 v2.10.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I= github.com/panjf2000/ants/v2 v2.10.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I=
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 h1:IWzUvJ72xMjmrjR9q3H1PF+jwdN0uNQiR2t1BLNalyo=
@ -52,14 +48,12 @@ github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.48.1 h1:y/8xmfWI9qmGTc+lBr4jKRUWLGSlSigv847ULJ4hYXA= github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
github.com/quic-go/quic-go v0.48.1/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -72,25 +66,16 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -19,7 +19,7 @@ import (
func TestCacheMetricsListener_integration_cache(t *testing.T) { func TestCacheMetricsListener_integration_cache(t *testing.T) {
cacheMiddleware := cache.NewMiddleware(&cache.MiddlewareConfig{ cacheMiddleware := cache.NewMiddleware(&cache.MiddlewareConfig{
MetricsListener: prometheus.NewCacheMetricsListener(testNamespace), MetricsListener: prometheus.NewCacheMetricsListener(testNamespace),
Size: 100, Count: 100,
}) })
handlerWithMiddleware := dnsserver.WithMiddlewares( handlerWithMiddleware := dnsserver.WithMiddlewares(

View File

@ -61,7 +61,7 @@ func (p Protocol) ALPN() (alpn []string) {
case ProtoDoQ: case ProtoDoQ:
return []string{nextProtoDoQ} return []string{nextProtoDoQ}
case ProtoDoH: case ProtoDoH:
return slices.Clone(nextProtoDoH3) return slices.Clone(NextProtoDoH3)
default: default:
return nil return nil
} }

View File

@ -43,15 +43,15 @@ const (
httpIdleTimeout = 120 * time.Second httpIdleTimeout = 120 * time.Second
) )
// nextProtoDoH is a list of ALPN that we would add by default to the server's // NextProtoDoH is a list of ALPN protocols added by default to the server's
// *tls.Config if no NextProto is specified there. Note, that with this order, // *tls.Config if no NextProto is specified there. Note that with this order,
// we prioritize HTTP/2 over HTTP/1.1. // HTTP/2 is prioritized over HTTP/1.1.
var nextProtoDoH = []string{http2.NextProtoTLS, "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 // NextProtoDoH3 is a list of ALPN protocols added by default to the server's
// *tls.Config if no NextProto is specified there and DoH3 is supposed to be // *tls.Config if no NextProto is specified there and DoH3 is supposed to be
// used. // used. Note that with this order, HTTP/2 is prioritized over HTTP/1.1.
var nextProtoDoH3 = []string{http3.NextProtoH3, http2.NextProtoTLS, "http/1.1"} var NextProtoDoH3 = []string{http3.NextProtoH3, http2.NextProtoTLS, "http/1.1"}
// ConfigHTTPS is a struct that needs to be passed to NewServerHTTPS to // ConfigHTTPS is a struct that needs to be passed to NewServerHTTPS to
// initialize a new ServerHTTPS instance. You can choose whether HTTP/3 is // initialize a new ServerHTTPS instance. You can choose whether HTTP/3 is
@ -59,10 +59,14 @@ var nextProtoDoH3 = []string{http3.NextProtoH3, http2.NextProtoTLS, "http/1.1"}
// will listen to both HTTP/2 and HTTP/3, but if you set it to NetworkTCP, the // 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. // server will only use HTTP/2 and NetworkUDP will mean HTTP/3 only.
type ConfigHTTPS struct { type ConfigHTTPS struct {
// TLSConfig is the TLS configuration for HTTPS. If not set and // TLSConfDefault is the TLS configuration for HTTPS. If not set and
// [ConfigBase.Network] is set to NetworkTCP the server will listen to // [ConfigBase.Network] is set to NetworkTCP the server will listen to plain
// plain HTTP. // HTTP. If it is not nil, it must be set to [NextProtoDoH].
TLSConfig *tls.Config TLSConfDefault *tls.Config
// TLSConfH3 is the TLS configuration for DoH3. If it is not nil, it must
// be set to [NextProtoDoH3].
TLSConfH3 *tls.Config
// NonDNSHandler handles requests with the path not equal to /dns-query. // NonDNSHandler handles requests with the path not equal to /dns-query.
// If it is empty, the server will return 404 for requests like that. // If it is empty, the server will return 404 for requests like that.
@ -306,7 +310,7 @@ func (s *ServerHTTPS) serveHTTPS(ctx context.Context, hs *http.Server, l net.Lis
defer s.handlePanicAndExit(ctx) defer s.handlePanicAndExit(ctx)
scheme := urlutil.SchemeHTTPS scheme := urlutil.SchemeHTTPS
if s.conf.TLSConfig == nil { if s.conf.TLSConfDefault == nil {
scheme = urlutil.SchemeHTTP scheme = urlutil.SchemeHTTP
} }
@ -516,12 +520,9 @@ func (s *ServerHTTPS) listenTLS(ctx context.Context) (err error) {
} }
// Prepare the TLS configuration of the server. // Prepare the TLS configuration of the server.
tlsConf := s.conf.TLSConfig tlsConf := s.conf.TLSConfDefault
if tlsConf == nil { if tlsConf == nil {
return nil return nil
} else if len(tlsConf.NextProtos) == 0 {
tlsConf = tlsConf.Clone()
tlsConf.NextProtos = nextProtoDoH
} }
s.tcpListener = tls.NewListener(s.tcpListener, tlsConf) s.tcpListener = tls.NewListener(s.tcpListener, tlsConf)
@ -532,11 +533,7 @@ func (s *ServerHTTPS) listenTLS(ctx context.Context) (err error) {
// listenQUIC starts a QUIC listener that will be used to serve HTTP/3 requests. // listenQUIC starts a QUIC listener that will be used to serve HTTP/3 requests.
func (s *ServerHTTPS) listenQUIC(ctx context.Context) (err error) { func (s *ServerHTTPS) listenQUIC(ctx context.Context) (err error) {
// Prepare the TLS configuration of the server. // Prepare the TLS configuration of the server.
tlsConf := s.conf.TLSConfig tlsConf := s.conf.TLSConfH3
if tlsConf != nil && len(tlsConf.NextProtos) == 0 {
tlsConf = tlsConf.Clone()
tlsConf.NextProtos = nextProtoDoH3
}
conn, err := s.listenConfig.ListenPacket(ctx, "udp", s.addr) conn, err := s.listenConfig.ListenPacket(ctx, "udp", s.addr)
if err != nil { if err != nil {

View File

@ -307,7 +307,8 @@ func TestDNSMsgToJSONMsg(t *testing.T) {
Type: dns.TypeHTTPS, Type: dns.TypeHTTPS,
Class: dns.ClassINET, Class: dns.ClassINET,
TTL: 100, TTL: 100,
Data: `0 example.com alpn="h2,h3" ech="AQI=" ipv4hint="127.0.0.1,127.0.0.2" ipv6hint="2000::,2001::"`, Data: `0 example.com alpn="h2,h3" ech="AQI=" ipv4hint="127.0.0.1,127.0.0.2" ` +
`ipv6hint="2000::,2001::"`,
}}, jsonMsg.Answer) }}, jsonMsg.Answer)
require.Equal(t, []dnsserver.JSONAnswer{{ require.Equal(t, []dnsserver.JSONAnswer{{
Name: "example.org", Name: "example.org",

View File

@ -62,10 +62,16 @@ const (
// compatProtoDQ are ALPNs for backwards compatibility. // compatProtoDQ are ALPNs for backwards compatibility.
var compatProtoDQ = []string{"doq-i00", "doq-i02", "doq-i03", "dq"} var compatProtoDQ = []string{"doq-i00", "doq-i02", "doq-i03", "dq"}
// NextProtoDoQ is a list of ALPN protocols added by default to the server's
// *tls.Config if no NextProto is specified there and DoQ is supposed to be
// used.
var NextProtoDoQ = append([]string{nextProtoDoQ}, compatProtoDQ...)
// ConfigQUIC is a struct that needs to be passed to NewServerQUIC to // ConfigQUIC is a struct that needs to be passed to NewServerQUIC to
// initialize a new ServerQUIC instance. // initialize a new ServerQUIC instance.
type ConfigQUIC struct { type ConfigQUIC struct {
// TLSConfig is the TLS configuration for QUIC. // TLSConfig is the TLS configuration for QUIC. If it is not nil, it must
// be set to [NextProtoDoQ].
TLSConfig *tls.Config TLSConfig *tls.Config
ConfigBase ConfigBase
@ -111,12 +117,6 @@ const quicBytePoolSize = dns.MaxMsgSize
// NewServerQUIC creates a new ServerQUIC instance. // NewServerQUIC creates a new ServerQUIC instance.
func NewServerQUIC(conf ConfigQUIC) (s *ServerQUIC) { func NewServerQUIC(conf ConfigQUIC) (s *ServerQUIC) {
// Make sure DOQ ALPNs are enabled in the TLS config.
tlsConfig := conf.TLSConfig
if len(tlsConfig.NextProtos) == 0 {
tlsConfig.NextProtos = append([]string{nextProtoDoQ}, compatProtoDQ...)
}
if conf.ListenConfig == nil { if conf.ListenConfig == nil {
// Do not enable OOB here as quic-go will do that on its own. // Do not enable OOB here as quic-go will do that on its own.
conf.ListenConfig = netext.DefaultListenConfig(nil) conf.ListenConfig = netext.DefaultListenConfig(nil)

View File

@ -107,6 +107,7 @@ func TestServerQUIC_integration_ENDS0Padding(t *testing.T) {
func TestServerQUIC_integration_0RTT(t *testing.T) { func TestServerQUIC_integration_0RTT(t *testing.T) {
tlsConfig := dnsservertest.CreateServerTLSConfig("example.org") tlsConfig := dnsservertest.CreateServerTLSConfig("example.org")
tlsConfig.NextProtos = dnsserver.NextProtoDoQ
srv, addr, err := dnsservertest.RunLocalQUICServer( srv, addr, err := dnsservertest.RunLocalQUICServer(
dnsservertest.NewDefaultHandler(), dnsservertest.NewDefaultHandler(),
tlsConfig, tlsConfig,

View File

@ -347,14 +347,15 @@ func NewListener(
case agd.ProtoDoH: case agd.ProtoDoH:
l = dnsserver.NewServerHTTPS(dnsserver.ConfigHTTPS{ l = dnsserver.NewServerHTTPS(dnsserver.ConfigHTTPS{
ConfigBase: baseConf, ConfigBase: baseConf,
TLSConfig: s.TLS, TLSConfDefault: s.TLS.Default,
TLSConfH3: s.TLS.H3,
NonDNSHandler: nonDNS, NonDNSHandler: nonDNS,
MaxStreamsPerPeer: quicConf.MaxStreamsPerPeer, MaxStreamsPerPeer: quicConf.MaxStreamsPerPeer,
QUICLimitsEnabled: quicConf.QUICLimitsEnabled, QUICLimitsEnabled: quicConf.QUICLimitsEnabled,
}) })
case agd.ProtoDoQ: case agd.ProtoDoQ:
l = dnsserver.NewServerQUIC(dnsserver.ConfigQUIC{ l = dnsserver.NewServerQUIC(dnsserver.ConfigQUIC{
TLSConfig: s.TLS, TLSConfig: s.TLS.Default,
ConfigBase: baseConf, ConfigBase: baseConf,
MaxStreamsPerPeer: quicConf.MaxStreamsPerPeer, MaxStreamsPerPeer: quicConf.MaxStreamsPerPeer,
QUICLimitsEnabled: quicConf.QUICLimitsEnabled, QUICLimitsEnabled: quicConf.QUICLimitsEnabled,
@ -369,7 +370,7 @@ func NewListener(
MaxPipelineCount: tcpConf.MaxPipelineCount, MaxPipelineCount: tcpConf.MaxPipelineCount,
TCPIdleTimeout: tcpConf.IdleTimeout, TCPIdleTimeout: tcpConf.IdleTimeout,
}, },
TLSConfig: s.TLS, TLSConfig: s.TLS.Default,
}) })
default: default:
return nil, fmt.Errorf("protocol: %w: %d", errors.ErrBadEnumValue, p) return nil, fmt.Errorf("protocol: %w: %d", errors.ErrBadEnumValue, p)

View File

@ -88,7 +88,7 @@ func wrapPreUpstreamMw(ctx context.Context, c *HandlersConfig) (wrapped dnsserve
cacheMw := cache.NewMiddleware(&cache.MiddlewareConfig{ cacheMw := cache.NewMiddleware(&cache.MiddlewareConfig{
// TODO(a.garipov): Do not use promauto and refactor. // TODO(a.garipov): Do not use promauto and refactor.
MetricsListener: dnssrvprom.NewCacheMetricsListener(metrics.Namespace()), MetricsListener: dnssrvprom.NewCacheMetricsListener(metrics.Namespace()),
Size: conf.NoECSCount, Count: conf.NoECSCount,
MinTTL: conf.MinTTL, MinTTL: conf.MinTTL,
OverrideTTL: conf.OverrideCacheTTL, OverrideTTL: conf.OverrideCacheTTL,
}) })
@ -146,7 +146,10 @@ func newMainMiddlewareMetrics(c *HandlersConfig) (mainMwMtrc MainMiddlewareMetri
// newHandlersForServers returns a handler map for each server group and each // newHandlersForServers returns a handler map for each server group and each
// server. // server.
func newHandlersForServers(c *HandlersConfig, h dnsserver.Handler) (handlers Handlers, err error) { func newHandlersForServers(c *HandlersConfig, h dnsserver.Handler) (handlers Handlers, err error) {
rlMwMtrc, err := metrics.NewDefaultRatelimitMiddleware(c.MetricsNamespace, c.PrometheusRegisterer) rlMwMtrc, err := metrics.NewDefaultRatelimitMiddleware(
c.MetricsNamespace,
c.PrometheusRegisterer,
)
if err != nil { if err != nil {
return nil, fmt.Errorf("ratelimit middleware metrics: %w", err) return nil, fmt.Errorf("ratelimit middleware metrics: %w", err)
} }
@ -206,6 +209,6 @@ func newDeviceFinder(c *HandlersConfig, g *agd.ServerGroup, s *agd.Server) (df a
ProfileDB: c.ProfileDB, ProfileDB: c.ProfileDB,
HumanIDParser: c.HumanIDParser, HumanIDParser: c.HumanIDParser,
Server: s, Server: s,
DeviceDomains: g.TLS.DeviceDomains, DeviceDomains: g.DeviceDomains,
}) })
} }

View File

@ -60,19 +60,27 @@ func TestNewHandlers(t *testing.T) {
}, },
} }
fltGrps := map[agd.FilteringGroupID]*agd.FilteringGroup{ fltGrp := &agd.FilteringGroup{
dnssvctest.FilteringGroupID: { FilterConfig: &filter.ConfigGroup{
ID: dnssvctest.FilteringGroupID, Parental: &filter.ConfigParental{},
RuleListIDs: []agd.FilterListID{dnssvctest.FilterListID1}, RuleList: &filter.ConfigRuleList{
RuleListsEnabled: true, IDs: []filter.ID{dnssvctest.FilterListID1},
Enabled: true,
}, },
SafeBrowsing: &filter.ConfigSafeBrowsing{},
},
ID: dnssvctest.FilteringGroupID,
}
fltGrps := map[agd.FilteringGroupID]*agd.FilteringGroup{
dnssvctest.FilteringGroupID: fltGrp,
} }
fltStrg := &agdtest.FilterStorage{ fltStrg := &agdtest.FilterStorage{
OnFilterFromContext: func(_ context.Context, _ *agd.RequestInfo) (f filter.Interface) { OnForConfig: func(_ context.Context, _ filter.Config) (f filter.Interface) {
panic("not implemented") panic("not implemented")
}, },
OnHasListID: func(_ agd.FilterListID) (ok bool) { panic("not implemented") }, OnHasListID: func(_ filter.ID) (ok bool) { panic("not implemented") },
} }
hashMatcher := &agdtest.HashMatcher{ hashMatcher := &agdtest.HashMatcher{
@ -91,7 +99,7 @@ func TestNewHandlers(t *testing.T) {
} }
ruleStat := &agdtest.RuleStat{ ruleStat := &agdtest.RuleStat{
OnCollect: func(_ context.Context, _ agd.FilterListID, _ agd.FilterRuleText) { OnCollect: func(_ context.Context, _ filter.ID, _ filter.RuleText) {
panic("not implemented") panic("not implemented")
}, },
} }
@ -104,7 +112,6 @@ func TestNewHandlers(t *testing.T) {
DDR: &agd.DDR{ DDR: &agd.DDR{
Enabled: true, Enabled: true,
}, },
TLS: &agd.TLS{},
Name: dnssvctest.ServerGroupName, Name: dnssvctest.ServerGroupName,
FilteringGroup: dnssvctest.FilteringGroupID, FilteringGroup: dnssvctest.FilteringGroupID,
Servers: []*agd.Server{srv}, Servers: []*agd.Server{srv},

View File

@ -57,7 +57,7 @@ func newTestService(
querylogCh chan<- *querylog.Entry, querylogCh chan<- *querylog.Entry,
geoIPCh chan<- string, geoIPCh chan<- string,
dnsDBCh chan<- *agd.RequestInfo, dnsDBCh chan<- *agd.RequestInfo,
ruleStatCh chan<- agd.FilterRuleText, ruleStatCh chan<- filter.RuleText,
) (svc *dnssvc.Service, srvAddr netip.AddrPort) { ) (svc *dnssvc.Service, srvAddr netip.AddrPort) {
t.Helper() t.Helper()
@ -73,11 +73,19 @@ func newTestService(
} }
prof := &agd.Profile{ prof := &agd.Profile{
FilterConfig: &filter.ConfigClient{
Custom: &filter.ConfigCustom{},
Parental: &filter.ConfigParental{},
RuleList: &filter.ConfigRuleList{
IDs: []filter.ID{dnssvctest.FilterListID1},
Enabled: true,
},
SafeBrowsing: &filter.ConfigSafeBrowsing{},
},
Access: access.EmptyProfile{}, Access: access.EmptyProfile{},
BlockingMode: &dnsmsg.BlockingModeNullIP{}, BlockingMode: &dnsmsg.BlockingModeNullIP{},
ID: dnssvctest.ProfileID, ID: dnssvctest.ProfileID,
DeviceIDs: []agd.DeviceID{dnssvctest.DeviceID}, DeviceIDs: []agd.DeviceID{dnssvctest.DeviceID},
RuleListIDs: []agd.FilterListID{dnssvctest.FilterListID1},
FilteredResponseTTL: agdtest.FilteredResponseTTL, FilteredResponseTTL: agdtest.FilteredResponseTTL,
FilteringEnabled: true, FilteringEnabled: true,
QueryLogEnabled: true, QueryLogEnabled: true,
@ -124,10 +132,10 @@ func newTestService(
} }
fltStrg := &agdtest.FilterStorage{ fltStrg := &agdtest.FilterStorage{
OnFilterFromContext: func(_ context.Context, _ *agd.RequestInfo) (f filter.Interface) { OnForConfig: func(_ context.Context, _ filter.Config) (f filter.Interface) {
return flt return flt
}, },
OnHasListID: func(_ agd.FilterListID) (ok bool) { panic("not implemented") }, OnHasListID: func(_ filter.ID) (ok bool) { panic("not implemented") },
} }
var ql querylog.Interface = &agdtest.QueryLog{ var ql querylog.Interface = &agdtest.QueryLog{
@ -164,7 +172,7 @@ func newTestService(
} }
ruleStat := &agdtest.RuleStat{ ruleStat := &agdtest.RuleStat{
OnCollect: func(_ context.Context, _ agd.FilterListID, text agd.FilterRuleText) { OnCollect: func(_ context.Context, _ filter.ID, text filter.RuleText) {
testutil.RequireSend(pt, ruleStatCh, text, dnssvctest.Timeout) testutil.RequireSend(pt, ruleStatCh, text, dnssvctest.Timeout)
}, },
} }
@ -182,15 +190,25 @@ func newTestService(
DDR: &agd.DDR{ DDR: &agd.DDR{
Enabled: true, Enabled: true,
}, },
TLS: &agd.TLS{
DeviceDomains: []string{dnssvctest.DomainForDevices}, DeviceDomains: []string{dnssvctest.DomainForDevices},
},
Name: dnssvctest.ServerGroupName, Name: dnssvctest.ServerGroupName,
FilteringGroup: dnssvctest.FilteringGroupID, FilteringGroup: dnssvctest.FilteringGroupID,
Servers: []*agd.Server{srv}, Servers: []*agd.Server{srv},
ProfilesEnabled: true, ProfilesEnabled: true,
}} }}
fltGrp := &agd.FilteringGroup{
FilterConfig: &filter.ConfigGroup{
Parental: &filter.ConfigParental{},
RuleList: &filter.ConfigRuleList{
IDs: []filter.ID{dnssvctest.FilterListID1},
Enabled: true,
},
SafeBrowsing: &filter.ConfigSafeBrowsing{},
},
ID: dnssvctest.FilteringGroupID,
}
hdlrConf := &dnssvc.HandlersConfig{ hdlrConf := &dnssvc.HandlersConfig{
BaseLogger: slogutil.NewDiscardLogger(), BaseLogger: slogutil.NewDiscardLogger(),
Cache: &dnssvc.CacheConfig{ Cache: &dnssvc.CacheConfig{
@ -227,11 +245,7 @@ func newTestService(
RuleStat: ruleStat, RuleStat: ruleStat,
MetricsNamespace: path.Base(t.Name()), MetricsNamespace: path.Base(t.Name()),
FilteringGroups: map[agd.FilteringGroupID]*agd.FilteringGroup{ FilteringGroups: map[agd.FilteringGroupID]*agd.FilteringGroup{
dnssvctest.FilteringGroupID: { dnssvctest.FilteringGroupID: fltGrp,
ID: dnssvctest.FilteringGroupID,
RuleListIDs: []agd.FilterListID{dnssvctest.FilterListID1},
RuleListsEnabled: true,
},
}, },
ServerGroups: srvGrps, ServerGroups: srvGrps,
EDEEnabled: true, EDEEnabled: true,
@ -271,7 +285,7 @@ func TestService_Wrap(t *testing.T) {
querylogCh := make(chan *querylog.Entry, 1) querylogCh := make(chan *querylog.Entry, 1)
geoIPCh := make(chan string, 2) geoIPCh := make(chan string, 2)
dnsDBCh := make(chan *agd.RequestInfo, 1) dnsDBCh := make(chan *agd.RequestInfo, 1)
ruleStatCh := make(chan agd.FilterRuleText, 1) ruleStatCh := make(chan filter.RuleText, 1)
errCollCh := make(chan error, 1) errCollCh := make(chan error, 1)
go func() { go func() {
@ -291,21 +305,27 @@ func TestService_Wrap(t *testing.T) {
}) })
t.Run("simple_success", func(t *testing.T) { t.Run("simple_success", func(t *testing.T) {
noMatch := func( flt := &agdtest.Filter{
OnFilterRequest: func(
_ context.Context, _ context.Context,
m *dns.Msg, fltReq *filter.Request,
_ *agd.RequestInfo,
) (r filter.Result, err error) { ) (r filter.Result, err error) {
pt := testutil.PanicT{} pt := testutil.PanicT{}
require.NotEmpty(pt, m.Question) require.NotEmpty(pt, fltReq.DNS.Question)
require.Equal(pt, dnssvctest.DomainFQDN, m.Question[0].Name) require.Equal(pt, dnssvctest.DomainFQDN, fltReq.DNS.Question[0].Name)
return nil, nil return nil, nil
} },
OnFilterResponse: func(
_ context.Context,
fltResp *filter.Response,
) (r filter.Result, err error) {
pt := testutil.PanicT{}
require.NotEmpty(pt, fltResp.DNS.Question)
require.Equal(pt, dnssvctest.DomainFQDN, fltResp.DNS.Question[0].Name)
flt := &agdtest.Filter{ return nil, nil
OnFilterRequest: noMatch, },
OnFilterResponse: noMatch,
} }
svc, srvAddr := newTestService( svc, srvAddr := newTestService(
@ -346,13 +366,13 @@ func TestService_Wrap(t *testing.T) {
dnsDBReqInfo := <-dnsDBCh dnsDBReqInfo := <-dnsDBCh
assert.NotNil(t, dnsDBReqInfo) assert.NotNil(t, dnsDBReqInfo)
assert.Equal(t, agd.FilterRuleText(""), <-ruleStatCh) assert.Equal(t, filter.RuleText(""), <-ruleStatCh)
}) })
t.Run("request_cname", func(t *testing.T) { t.Run("request_cname", func(t *testing.T) {
const ( const (
cname = "cname.example.org" cname = "cname.example.org"
cnameRule agd.FilterRuleText = "||" + dnssvctest.Domain + "^$dnsrewrite=" + cname cnameRule filter.RuleText = "||" + dnssvctest.Domain + "^$dnsrewrite=" + cname
) )
cnameFQDN := dns.Fqdn(cname) cnameFQDN := dns.Fqdn(cname)
@ -360,11 +380,10 @@ func TestService_Wrap(t *testing.T) {
flt := &agdtest.Filter{ flt := &agdtest.Filter{
OnFilterRequest: func( OnFilterRequest: func(
_ context.Context, _ context.Context,
m *dns.Msg, fltReq *filter.Request,
_ *agd.RequestInfo,
) (r filter.Result, err error) { ) (r filter.Result, err error) {
// Pretend a CNAME rewrite matched the request. // Pretend a CNAME rewrite matched the request.
mod := dnsmsg.Clone(m) mod := dnsmsg.Clone(fltReq.DNS)
mod.Question[0].Name = cnameFQDN mod.Question[0].Name = cnameFQDN
return &filter.ResultModifiedRequest{ return &filter.ResultModifiedRequest{
@ -373,11 +392,7 @@ func TestService_Wrap(t *testing.T) {
Rule: cnameRule, Rule: cnameRule,
}, nil }, nil
}, },
OnFilterResponse: func( OnFilterResponse: func(_ context.Context, _ *filter.Response) (filter.Result, error) {
_ context.Context,
_ *dns.Msg,
_ *agd.RequestInfo,
) (filter.Result, error) {
panic("not implemented") panic("not implemented")
}, },
} }

View File

@ -199,7 +199,8 @@ func TestDefault_Find_byHumanID(t *testing.T) {
// Use uppercase versions to make sure that the device finder recognizes the // Use uppercase versions to make sure that the device finder recognizes the
// device-type and profile data regardless of the case. // device-type and profile data regardless of the case.
extIDStr := "OTR-" + strings.ToUpper(dnssvctest.ProfileIDStr) + "-" + dnssvctest.HumanIDStr + "-!!!" extIDStr := "OTR-" + strings.ToUpper(dnssvctest.ProfileIDStr) + "-" +
dnssvctest.HumanIDStr + "-!!!"
profDB := agdtest.NewProfileDB() profDB := agdtest.NewProfileDB()
profDB.OnCreateAutoDevice = func( profDB.OnCreateAutoDevice = func(

View File

@ -408,7 +408,12 @@ func BenchmarkDefault(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
for range b.N { for range b.N {
sinkDevResult = df.Find(ctx, bc.req, dnssvctest.ClientAddrPort, dnssvctest.ServerAddrPort) sinkDevResult = df.Find(
ctx,
bc.req,
dnssvctest.ClientAddrPort,
dnssvctest.ServerAddrPort,
)
} }
_ = testutil.RequireTypeAssert[*agd.DeviceResultOK](b, sinkDevResult) _ = testutil.RequireTypeAssert[*agd.DeviceResultOK](b, sinkDevResult)

View File

@ -9,6 +9,7 @@ import (
"time" "time"
"github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/miekg/dns" "github.com/miekg/dns"
) )
@ -36,8 +37,8 @@ const (
// Common filtering-rule list ID for tests. // Common filtering-rule list ID for tests.
const ( const (
FilterListID1 agd.FilterListID = FilterListID1Str FilterListID1 filter.ID = FilterListID1Str
FilterListID2 agd.FilterListID = FilterListID2Str FilterListID2 filter.ID = FilterListID2Str
) )
// Common domains and FQDNs for tests. // Common domains and FQDNs for tests.
@ -144,7 +145,9 @@ func NewServer(
if proto.IsStdEncrypted() { if proto.IsStdEncrypted() {
// #nosec G402 -- This is a test helper. // #nosec G402 -- This is a test helper.
srv.TLS = &tls.Config{} srv.TLS = &agd.TLSConfig{
Default: &tls.Config{},
}
} }
switch proto { switch proto {

View File

@ -29,6 +29,12 @@ const (
// Resolvers for querying the resolver with unknown or absent name. // Resolvers for querying the resolver with unknown or absent name.
DDRDomain = DDRLabel + "." + ResolverARPADomain DDRDomain = DDRLabel + "." + ResolverARPADomain
// ChromePrefetchHost is the hostname that Chrome uses to check if it should
// use the Chrome Private Prefetch Proxy feature.
//
// See https://developer.chrome.com/docs/privacy-security/private-prefetch-proxy-for-network-admins.
ChromePrefetchHost = "dns-tunnel-check.googlezip.net"
// FirefoxCanaryHost is the hostname that Firefox uses to check if it should // FirefoxCanaryHost is the hostname that Firefox uses to check if it should
// use its own DNS-over-HTTPS settings. // use its own DNS-over-HTTPS settings.
// //
@ -251,6 +257,10 @@ func (mw *Middleware) specialDomainHandler(
if shouldBlockPrivateRelay(ri, prof) { if shouldBlockPrivateRelay(ri, prof) {
return mw.handlePrivateRelay, "apple_private_relay" return mw.handlePrivateRelay, "apple_private_relay"
} }
case ChromePrefetchHost:
if shouldBlockChromePrefetch(ri, prof) {
return mw.handleChromePrefetch, "chrome_prefetch"
}
case FirefoxCanaryHost: case FirefoxCanaryHost:
if shouldBlockFirefoxCanary(ri, prof) { if shouldBlockFirefoxCanary(ri, prof) {
return mw.handleFirefoxCanary, "firefox" return mw.handleFirefoxCanary, "firefox"
@ -262,36 +272,34 @@ func (mw *Middleware) specialDomainHandler(
return nil, "" return nil, ""
} }
// shouldBlockPrivateRelay returns true if the query is for an Apple Private // shouldBlockChromePrefetch returns true request information or profile
// Relay check domain and the request information or profile indicates that // indicate that the Chrome prefetch domain should be blocked.
// Apple Private Relay should be blocked. func shouldBlockChromePrefetch(ri *agd.RequestInfo, prof *agd.Profile) (ok bool) {
func shouldBlockPrivateRelay(ri *agd.RequestInfo, prof *agd.Profile) (ok bool) {
if prof != nil { if prof != nil {
return prof.BlockPrivateRelay return prof.BlockChromePrefetch
} }
return ri.FilteringGroup.BlockPrivateRelay return ri.FilteringGroup.BlockChromePrefetch
} }
// handlePrivateRelay responds to Apple Private Relay queries with an NXDOMAIN // handleChromePrefetch responds to Chrome prefetch domain queries with an
// response. // NXDOMAIN response.
func (mw *Middleware) handlePrivateRelay( func (mw *Middleware) handleChromePrefetch(
ctx context.Context, ctx context.Context,
rw dnsserver.ResponseWriter, rw dnsserver.ResponseWriter,
req *dns.Msg, req *dns.Msg,
ri *agd.RequestInfo, ri *agd.RequestInfo,
) (err error) { ) (err error) {
metrics.DNSSvcApplePrivateRelayRequestsTotal.Inc() metrics.DNSSvcChromePrefetchRequestsTotal.Inc()
resp := ri.Messages.NewRespRCode(req, dns.RcodeNameError) resp := ri.Messages.NewRespRCode(req, dns.RcodeNameError)
err = rw.WriteMsg(ctx, req, resp) err = rw.WriteMsg(ctx, req, resp)
return errors.Annotate(err, "writing private relay resp: %w") return errors.Annotate(err, "writing chrome prefetch resp: %w")
} }
// shouldBlockFirefoxCanary returns true if the query is for a Firefox canary // shouldBlockFirefoxCanary returns true request information or profile indicate
// domain and the request information or profile indicates that Firefox canary // that the Firefox canary domain should be blocked.
// domain should be blocked.
func shouldBlockFirefoxCanary(ri *agd.RequestInfo, prof *agd.Profile) (ok bool) { func shouldBlockFirefoxCanary(ri *agd.RequestInfo, prof *agd.Profile) (ok bool) {
if prof != nil { if prof != nil {
return prof.BlockFirefoxCanary return prof.BlockFirefoxCanary
@ -315,3 +323,29 @@ func (mw *Middleware) handleFirefoxCanary(
return errors.Annotate(err, "writing firefox canary resp: %w") return errors.Annotate(err, "writing firefox canary resp: %w")
} }
// shouldBlockPrivateRelay returns true request information or profile indicate
// that the Apple Private Relay domain should be blocked.
func shouldBlockPrivateRelay(ri *agd.RequestInfo, prof *agd.Profile) (ok bool) {
if prof != nil {
return prof.BlockPrivateRelay
}
return ri.FilteringGroup.BlockPrivateRelay
}
// handlePrivateRelay responds to Apple Private Relay queries with an NXDOMAIN
// response.
func (mw *Middleware) handlePrivateRelay(
ctx context.Context,
rw dnsserver.ResponseWriter,
req *dns.Msg,
ri *agd.RequestInfo,
) (err error) {
metrics.DNSSvcApplePrivateRelayRequestsTotal.Inc()
resp := ri.Messages.NewRespRCode(req, dns.RcodeNameError)
err = rw.WriteMsg(ctx, req, resp)
return errors.Annotate(err, "writing private relay resp: %w")
}

View File

@ -24,31 +24,36 @@ func TestMiddleware_Wrap_specialDomain(t *testing.T) {
var ( var (
profAllowed = &agd.Profile{ profAllowed = &agd.Profile{
Access: access.EmptyProfile{}, Access: access.EmptyProfile{},
BlockPrivateRelay: false, BlockChromePrefetch: false,
BlockFirefoxCanary: false, BlockFirefoxCanary: false,
BlockPrivateRelay: false,
} }
profBlocked = &agd.Profile{ profBlocked = &agd.Profile{
Access: access.EmptyProfile{}, Access: access.EmptyProfile{},
BlockPrivateRelay: true, BlockChromePrefetch: true,
BlockFirefoxCanary: true, BlockFirefoxCanary: true,
BlockPrivateRelay: true,
} }
) )
var ( var (
fltGrpAllowed = &agd.FilteringGroup{ fltGrpAllowed = &agd.FilteringGroup{
BlockPrivateRelay: false, BlockChromePrefetch: false,
BlockFirefoxCanary: false, BlockFirefoxCanary: false,
BlockPrivateRelay: false,
} }
fltGrpBlocked = &agd.FilteringGroup{ fltGrpBlocked = &agd.FilteringGroup{
BlockPrivateRelay: true, BlockChromePrefetch: true,
BlockFirefoxCanary: true, BlockFirefoxCanary: true,
BlockPrivateRelay: true,
} }
) )
const ( const (
appleHost = initial.ApplePrivateRelayMaskHost appleHost = initial.ApplePrivateRelayMaskHost
chromeHost = initial.ChromePrefetchHost
firefoxHost = initial.FirefoxCanaryHost firefoxHost = initial.FirefoxCanaryHost
) )
@ -100,29 +105,27 @@ func TestMiddleware_Wrap_specialDomain(t *testing.T) {
reqInfo: newSpecDomReqInfo(t, nil, fltGrpBlocked, firefoxHost, dns.TypeA), reqInfo: newSpecDomReqInfo(t, nil, fltGrpBlocked, firefoxHost, dns.TypeA),
name: "firefox_canary_blocked_by_fltgrp", name: "firefox_canary_blocked_by_fltgrp",
wantRCode: dns.RcodeRefused, wantRCode: dns.RcodeRefused,
}, {
reqInfo: newSpecDomReqInfo(t, profAllowed, fltGrpAllowed, chromeHost, dns.TypeA),
name: "chrome_prefetch_allowed_by_both",
wantRCode: dns.RcodeSuccess,
}, {
reqInfo: newSpecDomReqInfo(t, profBlocked, fltGrpAllowed, chromeHost, dns.TypeA),
name: "chrome_prefetch_blocked_by_prof",
wantRCode: dns.RcodeNameError,
}, {
reqInfo: newSpecDomReqInfo(t, nil, fltGrpBlocked, chromeHost, dns.TypeA),
name: "chrome_prefetch_blocked_by_fltgrp",
wantRCode: dns.RcodeNameError,
}} }}
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
var handler dnsserver.Handler = dnsserver.HandlerFunc(func(
ctx context.Context,
rw dnsserver.ResponseWriter,
req *dns.Msg,
) (err error) {
if tc.wantRCode != dns.RcodeSuccess {
return errors.Error("unexpectedly reached handler")
}
resp := (&dns.Msg{}).SetReply(req)
return rw.WriteMsg(ctx, req, resp)
})
mw := initial.New(&initial.Config{ mw := initial.New(&initial.Config{
Logger: slogutil.NewDiscardLogger(), Logger: slogutil.NewDiscardLogger(),
}) })
h := mw.Wrap(handler) h := mw.Wrap(newSpecDomHandler(tc.wantRCode == dns.RcodeSuccess))
ctx := testutil.ContextWithTimeout(t, dnssvctest.Timeout) ctx := testutil.ContextWithTimeout(t, dnssvctest.Timeout)
ctx = agd.ContextWithRequestInfo(ctx, tc.reqInfo) ctx = agd.ContextWithRequestInfo(ctx, tc.reqInfo)
@ -185,3 +188,21 @@ func newSpecDomReqInfo(
return ri return ri
} }
// newSpecDomHandler is a helper that creates a DNS handler for the
// special-domain tests. The handler returns an error if wantReach is false.
func newSpecDomHandler(wantReach bool) (h dnsserver.Handler) {
return dnsserver.HandlerFunc(func(
ctx context.Context,
rw dnsserver.ResponseWriter,
req *dns.Msg,
) (err error) {
if !wantReach {
return errors.Error("unexpectedly reached handler")
}
resp := (&dns.Msg{}).SetReply(req)
return rw.WriteMsg(ctx, req, resp)
})
}

View File

@ -221,7 +221,10 @@ func TestMiddleware_writeDebugResponse(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
rw := dnsserver.NewNonWriterResponseWriter(dnssvctest.ServerTCPAddr, dnssvctest.ClientTCPAddr) rw := dnsserver.NewNonWriterResponseWriter(
dnssvctest.ServerTCPAddr,
dnssvctest.ClientTCPAddr,
)
ctx := agd.ContextWithRequestInfo(context.Background(), tc.reqInfo) ctx := agd.ContextWithRequestInfo(context.Background(), tc.reqInfo)

View File

@ -7,6 +7,7 @@ import (
"time" "time"
"github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/filter" "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/miekg/dns" "github.com/miekg/dns"
) )
@ -57,9 +58,12 @@ func (mw *Middleware) filterRequest(
) { ) {
start := time.Now() start := time.Now()
reqRes, err := f.FilterRequest(ctx, fctx.originalRequest, ri) fltReq := mw.reqInfoToFltReq(fctx.originalRequest, ri)
defer mw.putFltReq(fltReq)
reqRes, err := f.FilterRequest(ctx, fltReq)
if err != nil { if err != nil {
mw.reportf(ctx, "filtering request: %w", err) errcoll.Collect(ctx, mw.errColl, mw.logger, "filtering request", err)
} }
if mod, ok := reqRes.(*filter.ResultModifiedRequest); ok { if mod, ok := reqRes.(*filter.ResultModifiedRequest); ok {
@ -70,6 +74,38 @@ func (mw *Middleware) filterRequest(
fctx.elapsed += time.Since(start) fctx.elapsed += time.Since(start)
} }
// reqInfoToFltReq converts data from a DNS request and request info into a
// *filter.Request. The returned request data should be put back into the pool
// by using [Middleware.putFltReq].
func (mw *Middleware) reqInfoToFltReq(req *dns.Msg, ri *agd.RequestInfo) (fltReq *filter.Request) {
fltReq = mw.fltReqPool.Get()
// NOTE: Fill all fields of fltReq since it is reused from the pool.
fltReq.DNS = req
fltReq.Messages = ri.Messages
fltReq.RemoteIP = ri.RemoteIP
if _, d := ri.DeviceData(); d != nil {
fltReq.ClientName = string(d.Name)
} else {
fltReq.ClientName = ""
}
fltReq.Host = ri.Host
fltReq.QType = ri.QType
fltReq.QClass = ri.QClass
return fltReq
}
// putFltReq sets req.DNS to nil, to prevent the message being contained in the
// pool, which can lead to conflicts with the cloner of the middleware, and puts
// req back into the pool.
func (mw *Middleware) putFltReq(req *filter.Request) {
req.DNS = nil
mw.fltReqPool.Put(req)
}
// filterResponse applies f to resp and sets the result of filtering in fctx. // filterResponse applies f to resp and sets the result of filtering in fctx.
// If origReq has a different question name than resp, the request assumed being // If origReq has a different question name than resp, the request assumed being
// CNAME-rewritten and no filtering performed on resp, the CNAME is prepended to // CNAME-rewritten and no filtering performed on resp, the CNAME is prepended to
@ -95,9 +131,12 @@ func (mw *Middleware) filterResponse(
var rr dns.RR = ri.Messages.NewAnswerCNAME(origReq, modReq.Question[0].Name) var rr dns.RR = ri.Messages.NewAnswerCNAME(origReq, modReq.Question[0].Name)
origResp.Answer = slices.Insert(origResp.Answer, 0, rr) origResp.Answer = slices.Insert(origResp.Answer, 0, rr)
} else { } else {
respRes, err := f.FilterResponse(ctx, fctx.originalResponse, ri) fltResp := mw.reqInfoToFltResp(fctx.originalResponse, ri)
defer mw.putFltResp(fltResp)
respRes, err := f.FilterResponse(ctx, fltResp)
if err != nil { if err != nil {
mw.reportf(ctx, "filtering response: %w", err) errcoll.Collect(ctx, mw.errColl, mw.logger, "filtering response", err)
} }
fctx.responseResult = respRes fctx.responseResult = respRes
@ -106,11 +145,41 @@ func (mw *Middleware) filterResponse(
fctx.elapsed += time.Since(start) fctx.elapsed += time.Since(start)
} }
// reqInfoToFltResp converts data from a DNS response and request info into a
// *filter.Response. The returned response data should be put back into
// the pool by using [Middleware.putFltResp].
func (mw *Middleware) reqInfoToFltResp(
resp *dns.Msg,
ri *agd.RequestInfo,
) (fltResp *filter.Response) {
fltResp = mw.fltRespPool.Get()
// NOTE: Fill all fields of fltResp since it is reused from the pool.
fltResp.DNS = resp
fltResp.RemoteIP = ri.RemoteIP
if _, d := ri.DeviceData(); d != nil {
fltResp.ClientName = string(d.Name)
} else {
fltResp.ClientName = ""
}
return fltResp
}
// putFltResp sets resp.DNS to nil, to prevent the message being contained in
// the pool, which can lead to conflicts with the cloner of the middleware, and
// puts resp back into the pool.
func (mw *Middleware) putFltResp(resp *filter.Response) {
resp.DNS = nil
mw.fltRespPool.Put(resp)
}
// filteringData returns the data necessary for request information recording // filteringData returns the data necessary for request information recording
// from the filtering context. // from the filtering context.
func filteringData( func filteringData(
fctx *filteringContext, fctx *filteringContext,
) (id agd.FilterListID, text agd.FilterRuleText, blocked bool) { ) (id filter.ID, text filter.RuleText, blocked bool) {
if fctx.requestResult != nil { if fctx.requestResult != nil {
return resultData(fctx.requestResult, "reqRes") return resultData(fctx.requestResult, "reqRes")
} }
@ -123,9 +192,9 @@ func filteringData(
func resultData( func resultData(
res filter.Result, res filter.Result,
argName string, argName string,
) (id agd.FilterListID, text agd.FilterRuleText, blocked bool) { ) (id filter.ID, text filter.RuleText, blocked bool) {
if res == nil { if res == nil {
return agd.FilterListIDNone, "", false return filter.IDNone, "", false
} }
id, text = res.MatchedRule() id, text = res.MatchedRule()
@ -164,7 +233,13 @@ func (mw *Middleware) setFilteredResponse(
var err error var err error
fctx.filteredResponse, err = ri.Messages.NewBlockedResp(fctx.originalRequest) fctx.filteredResponse, err = ri.Messages.NewBlockedResp(fctx.originalRequest)
if err != nil { if err != nil {
mw.reportf(ctx, "creating blocked resp for filtered req: %w", err) errcoll.Collect(
ctx,
mw.errColl,
mw.logger,
"creating blocked resp for filtered req",
err,
)
fctx.filteredResponse = fctx.originalResponse fctx.filteredResponse = fctx.originalResponse
} }
case *filter.ResultAllowed, *filter.ResultModifiedRequest: case *filter.ResultAllowed, *filter.ResultModifiedRequest:
@ -201,7 +276,13 @@ func (mw *Middleware) setFilteredResponseNoReq(
var err error var err error
fctx.filteredResponse, err = ri.Messages.NewBlockedResp(fctx.originalRequest) fctx.filteredResponse, err = ri.Messages.NewBlockedResp(fctx.originalRequest)
if err != nil { if err != nil {
mw.reportf(ctx, "creating blocked resp for filtered resp: %w", err) errcoll.Collect(
ctx,
mw.errColl,
mw.logger,
"creating blocked resp for filtered resp",
err,
)
fctx.filteredResponse = fctx.originalResponse fctx.filteredResponse = fctx.originalResponse
} }
default: default:

View File

@ -27,6 +27,8 @@ import (
type Middleware struct { type Middleware struct {
cloner *dnsmsg.Cloner cloner *dnsmsg.Cloner
fltCtxPool *syncutil.Pool[filteringContext] fltCtxPool *syncutil.Pool[filteringContext]
fltReqPool *syncutil.Pool[filter.Request]
fltRespPool *syncutil.Pool[filter.Response]
logger *slog.Logger logger *slog.Logger
messages *dnsmsg.Constructor messages *dnsmsg.Constructor
billStat billstat.Recorder billStat billstat.Recorder
@ -84,6 +86,12 @@ func New(c *Config) (mw *Middleware) {
fltCtxPool: syncutil.NewPool(func() (v *filteringContext) { fltCtxPool: syncutil.NewPool(func() (v *filteringContext) {
return &filteringContext{} return &filteringContext{}
}), }),
fltReqPool: syncutil.NewPool(func() (v *filter.Request) {
return &filter.Request{}
}),
fltRespPool: syncutil.NewPool(func() (v *filter.Response) {
return &filter.Response{}
}),
logger: c.Logger, logger: c.Logger,
messages: c.Messages, messages: c.Messages,
billStat: c.BillStat, billStat: c.BillStat,
@ -125,7 +133,7 @@ func (mw *Middleware) Wrap(next dnsserver.Handler) (wrapped dnsserver.Handler) {
"remote_ip", ri.RemoteIP, "remote_ip", ri.RemoteIP,
) )
flt := mw.fltStrg.FilterFromContext(ctx, ri) flt := mw.filter(ctx, ri)
mw.filterRequest(ctx, fctx, flt, ri) mw.filterRequest(ctx, fctx, flt, ri)
// Check the context error here, since the context could have already // Check the context error here, since the context could have already
@ -171,6 +179,20 @@ func (mw *Middleware) Wrap(next dnsserver.Handler) (wrapped dnsserver.Handler) {
return dnsserver.HandlerFunc(f) return dnsserver.HandlerFunc(f)
} }
// filter returns a filter based on the request information.
func (mw *Middleware) filter(ctx context.Context, ri *agd.RequestInfo) (f filter.Interface) {
p, d := ri.DeviceData()
if p == nil {
return mw.fltStrg.ForConfig(ctx, ri.FilteringGroup.FilterConfig)
}
if p.FilteringEnabled && d.FilteringEnabled {
return mw.fltStrg.ForConfig(ctx, p.FilterConfig)
}
return mw.fltStrg.ForConfig(ctx, nil)
}
// nextParams is a helper that returns the parameters to call the next handler // nextParams is a helper that returns the parameters to call the next handler
// with taking the filtering context into account. // with taking the filtering context into account.
func (mw *Middleware) nextParams( func (mw *Middleware) nextParams(
@ -239,8 +261,3 @@ func (mw *Middleware) reportMetrics(
IsBlocked: isBlocked, IsBlocked: isBlocked,
}) })
} }
// reportf is a helper method for reporting non-critical errors.
func (mw *Middleware) reportf(ctx context.Context, format string, args ...any) {
errcoll.Collectf(ctx, mw.errColl, "mainmw: "+format, args...)
}

View File

@ -34,12 +34,12 @@ const (
testRespAddr4Str = "3.4.5.6" testRespAddr4Str = "3.4.5.6"
testRewriteAddrStr = "7.8.9.0" testRewriteAddrStr = "7.8.9.0"
testRuleAllow agd.FilterRuleText = "@@||" + dnssvctest.DomainAllowed + "^" testRuleAllow filter.RuleText = "@@||" + dnssvctest.DomainAllowed + "^"
testRuleBlockReq agd.FilterRuleText = "||" + dnssvctest.DomainBlocked + "^" testRuleBlockReq filter.RuleText = "||" + dnssvctest.DomainBlocked + "^"
testRuleBlockResp agd.FilterRuleText = "||" + testRespAddr4Str + "^" testRuleBlockResp filter.RuleText = "||" + testRespAddr4Str + "^"
testRuleRewrite agd.FilterRuleText = "||" + dnssvctest.DomainRewritten + testRuleRewrite filter.RuleText = "||" + dnssvctest.DomainRewritten +
"^$dnsrewrite=NOERROR;A;" + testRewriteAddrStr "^$dnsrewrite=NOERROR;A;" + testRewriteAddrStr
testRuleRewriteCNAME agd.FilterRuleText = "||" + dnssvctest.DomainRewritten + testRuleRewriteCNAME filter.RuleText = "||" + dnssvctest.DomainRewritten +
"^$dnsrewrite=NOERROR;CNAME;" + dnssvctest.DomainRewrittenCNAME "^$dnsrewrite=NOERROR;CNAME;" + dnssvctest.DomainRewrittenCNAME
) )
@ -96,25 +96,23 @@ func TestMiddleware_Wrap(t *testing.T) {
flt := &agdtest.Filter{ flt := &agdtest.Filter{
OnFilterRequest: func( OnFilterRequest: func(
_ context.Context, _ context.Context,
_ *dns.Msg, _ *filter.Request,
_ *agd.RequestInfo,
) (r filter.Result, err error) { ) (r filter.Result, err error) {
return nil, nil return nil, nil
}, },
OnFilterResponse: func( OnFilterResponse: func(
_ context.Context, _ context.Context,
_ *dns.Msg, _ *filter.Response,
_ *agd.RequestInfo,
) (r filter.Result, err error) { ) (r filter.Result, err error) {
return nil, nil return nil, nil
}, },
} }
fltStrg := &agdtest.FilterStorage{ fltStrg := &agdtest.FilterStorage{
OnFilterFromContext: func(_ context.Context, _ *agd.RequestInfo) (f filter.Interface) { OnForConfig: func(_ context.Context, _ filter.Config) (f filter.Interface) {
return flt return flt
}, },
OnHasListID: func(_ agd.FilterListID) (ok bool) { panic("not implemented") }, OnHasListID: func(_ filter.ID) (ok bool) { panic("not implemented") },
} }
geoIP := agdtest.NewGeoIP() geoIP := agdtest.NewGeoIP()
@ -131,10 +129,10 @@ func TestMiddleware_Wrap(t *testing.T) {
} }
ruleStat := &agdtest.RuleStat{ ruleStat := &agdtest.RuleStat{
OnCollect: func(_ context.Context, id agd.FilterListID, text agd.FilterRuleText) { OnCollect: func(_ context.Context, id filter.ID, text filter.RuleText) {
pt := testutil.PanicT{} pt := testutil.PanicT{}
require.Equal(pt, agd.FilterListID(""), id) require.Equal(pt, filter.ID(""), id)
require.Equal(pt, agd.FilterRuleText(""), text) require.Equal(pt, filter.RuleText(""), text)
}, },
} }
@ -238,7 +236,10 @@ func TestMiddleware_Wrap(t *testing.T) {
h := mw.Wrap(newSimpleHandler(t, tc.req, wantResp)) h := mw.Wrap(newSimpleHandler(t, tc.req, wantResp))
ctx := newContext(t, tc.device, tc.profile, reqHost, reqQType, reqStart) ctx := newContext(t, tc.device, tc.profile, reqHost, reqQType, reqStart)
rw := dnsserver.NewNonWriterResponseWriter(dnssvctest.ServerTCPAddr, dnssvctest.ClientTCPAddr) rw := dnsserver.NewNonWriterResponseWriter(
dnssvctest.ServerTCPAddr,
dnssvctest.ClientTCPAddr,
)
serveErr := h.ServeDNS(ctx, rw, tc.req) serveErr := h.ServeDNS(ctx, rw, tc.req)
testutil.AssertErrorMsg(t, tc.wantErrMsg, serveErr) testutil.AssertErrorMsg(t, tc.wantErrMsg, serveErr)
@ -332,6 +333,19 @@ func newContext(
ctx = dnsserver.ContextWithRequestInfo(ctx, &dnsserver.RequestInfo{ ctx = dnsserver.ContextWithRequestInfo(ctx, &dnsserver.RequestInfo{
StartTime: start, StartTime: start,
}) })
fltConf := &filter.ConfigGroup{
Parental: &filter.ConfigParental{},
RuleList: &filter.ConfigRuleList{
IDs: []filter.ID{
dnssvctest.FilterListID1,
dnssvctest.FilterListID2,
},
Enabled: true,
},
SafeBrowsing: &filter.ConfigSafeBrowsing{},
}
ctx = agd.ContextWithRequestInfo(ctx, &agd.RequestInfo{ ctx = agd.ContextWithRequestInfo(ctx, &agd.RequestInfo{
DeviceResult: &agd.DeviceResultOK{ DeviceResult: &agd.DeviceResultOK{
Device: d, Device: d,
@ -342,11 +356,7 @@ func newContext(
ASN: testASN, ASN: testASN,
}, },
FilteringGroup: &agd.FilteringGroup{ FilteringGroup: &agd.FilteringGroup{
RuleListIDs: []agd.FilterListID{ FilterConfig: fltConf,
dnssvctest.FilterListID1,
dnssvctest.FilterListID2,
},
RuleListsEnabled: true,
}, },
Messages: agdtest.NewConstructor(tb), Messages: agdtest.NewConstructor(tb),
RemoteIP: dnssvctest.ClientAddr, RemoteIP: dnssvctest.ClientAddr,
@ -540,7 +550,7 @@ func TestMiddleware_Wrap_filtering(t *testing.T) {
respRes filter.Result respRes filter.Result
name string name string
wantErrMsg string wantErrMsg string
wantRule agd.FilterRuleText wantRule filter.RuleText
}{{ }{{
req: reqAllow, req: reqAllow,
device: nil, device: nil,
@ -641,28 +651,23 @@ func TestMiddleware_Wrap_filtering(t *testing.T) {
flt := &agdtest.Filter{ flt := &agdtest.Filter{
OnFilterRequest: func( OnFilterRequest: func(
_ context.Context, _ context.Context,
_ *dns.Msg, _ *filter.Request,
_ *agd.RequestInfo,
) (r filter.Result, err error) { ) (r filter.Result, err error) {
return tc.reqRes, nil return tc.reqRes, nil
}, },
OnFilterResponse: func( OnFilterResponse: func(
_ context.Context, _ context.Context,
_ *dns.Msg, _ *filter.Response,
_ *agd.RequestInfo,
) (r filter.Result, err error) { ) (r filter.Result, err error) {
return tc.respRes, nil return tc.respRes, nil
}, },
} }
fltStrg := &agdtest.FilterStorage{ fltStrg := &agdtest.FilterStorage{
OnFilterFromContext: func( OnForConfig: func(_ context.Context, _ filter.Config) (f filter.Interface) {
_ context.Context,
_ *agd.RequestInfo,
) (f filter.Interface) {
return flt return flt
}, },
OnHasListID: func(_ agd.FilterListID) (ok bool) { panic("not implemented") }, OnHasListID: func(_ filter.ID) (ok bool) { panic("not implemented") },
} }
q := tc.req.Question[0] q := tc.req.Question[0]
@ -680,7 +685,7 @@ func TestMiddleware_Wrap_filtering(t *testing.T) {
} }
ruleStat := &agdtest.RuleStat{ ruleStat := &agdtest.RuleStat{
OnCollect: func(_ context.Context, id agd.FilterListID, text agd.FilterRuleText) { OnCollect: func(_ context.Context, id filter.ID, text filter.RuleText) {
pt := testutil.PanicT{} pt := testutil.PanicT{}
require.Equal(pt, dnssvctest.FilterListID1, id) require.Equal(pt, dnssvctest.FilterListID1, id)
require.Equal(pt, tc.wantRule, text) require.Equal(pt, tc.wantRule, text)
@ -705,7 +710,10 @@ func TestMiddleware_Wrap_filtering(t *testing.T) {
h := mw.Wrap(newSimpleHandler(t, tc.wantUpsReq, tc.upsResp)) h := mw.Wrap(newSimpleHandler(t, tc.wantUpsReq, tc.upsResp))
ctx := newContext(t, tc.device, tc.profile, reqHost, reqQType, reqStart) ctx := newContext(t, tc.device, tc.profile, reqHost, reqQType, reqStart)
rw := dnsserver.NewNonWriterResponseWriter(dnssvctest.ServerTCPAddr, dnssvctest.ClientTCPAddr) rw := dnsserver.NewNonWriterResponseWriter(
dnssvctest.ServerTCPAddr,
dnssvctest.ClientTCPAddr,
)
serveErr := h.ServeDNS(ctx, rw, tc.req) serveErr := h.ServeDNS(ctx, rw, tc.req)
testutil.AssertErrorMsg(t, tc.wantErrMsg, serveErr) testutil.AssertErrorMsg(t, tc.wantErrMsg, serveErr)

View File

@ -11,6 +11,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet" "github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip" "github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/AdGuardDNS/internal/optslog" "github.com/AdguardTeam/AdGuardDNS/internal/optslog"
"github.com/AdguardTeam/AdGuardDNS/internal/querylog" "github.com/AdguardTeam/AdGuardDNS/internal/querylog"
@ -87,7 +88,7 @@ func (mw *Middleware) recordQueryInfo(
err := mw.queryLog.Write(ctx, e) err := mw.queryLog.Write(ctx, e)
if err != nil { if err != nil {
// Consider query logging errors non-critical. // Consider query logging errors non-critical.
mw.reportf(ctx, "writing query log: %w", err) errcoll.Collect(ctx, mw.errColl, mw.logger, "writing query log", err)
} }
} }
@ -138,7 +139,7 @@ func (mw *Middleware) responseData(
ip, err := ipFromAnswer(resp.Answer) ip, err := ipFromAnswer(resp.Answer)
if err != nil { if err != nil {
mw.reportf(ctx, "getting response data: %w", err) errcoll.Collect(ctx, mw.errColl, mw.logger, "getting response data", err)
} }
return rcode, ip, dnssec return rcode, ip, dnssec
@ -228,7 +229,7 @@ func (mw *Middleware) country(ctx context.Context, host string, ip netip.Addr) (
l, err := mw.geoIP.Data(host, ip) l, err := mw.geoIP.Data(host, ip)
if err != nil { if err != nil {
// Consider GeoIP errors non-critical. // Consider GeoIP errors non-critical.
mw.reportf(ctx, "getting geoip data: %w", err) errcoll.Collect(ctx, mw.errColl, mw.logger, "getting geoip data", err)
} }
if l != nil { if l != nil {

View File

@ -42,7 +42,7 @@ func TestMiddleware_Wrap_access(t *testing.T) {
blockedClient2Prefix = netip.MustParsePrefix("2001:db8::/120") blockedClient2Prefix = netip.MustParsePrefix("2001:db8::/120")
) )
accessMgr, errAccess := access.NewGlobal( accessMgr, accessErr := access.NewGlobal(
[]string{ []string{
domainBlockedNormal, domainBlockedNormal,
domainBlockedUppercase, domainBlockedUppercase,
@ -53,7 +53,7 @@ func TestMiddleware_Wrap_access(t *testing.T) {
blockedClient2Prefix, blockedClient2Prefix,
}, },
) )
require.NoError(t, errAccess) require.NoError(t, accessErr)
geoIP := agdtest.NewGeoIP() geoIP := agdtest.NewGeoIP()
geoIP.OnData = func(_ string, _ netip.Addr) (l *geoip.Location, err error) { geoIP.OnData = func(_ string, _ netip.Addr) (l *geoip.Location, err error) {
@ -154,9 +154,11 @@ func TestMiddleware_Wrap_access(t *testing.T) {
qtype: dns.TypeAAAA, qtype: dns.TypeAAAA,
}} }}
handler := dnsserver.HandlerFunc(func(ctx context.Context, rw dnsserver.ResponseWriter, req *dns.Msg) (_ error) { handler := dnsserver.HandlerFunc(
func(ctx context.Context, rw dnsserver.ResponseWriter, req *dns.Msg) (err error) {
return rw.WriteMsg(ctx, req, dnsservertest.NewResp(dns.RcodeSuccess, req)) return rw.WriteMsg(ctx, req, dnsservertest.NewResp(dns.RcodeSuccess, req))
}) },
)
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {

View File

@ -15,10 +15,10 @@ var msgSink *dns.Msg
func BenchmarkMiddleware_Get(b *testing.B) { func BenchmarkMiddleware_Get(b *testing.B) {
mw := &Middleware{ mw := &Middleware{
cache: agdcache.NewLRU[uint64, *cacheItem](&agdcache.LRUConfig{ cache: agdcache.NewLRU[uint64, *cacheItem](&agdcache.LRUConfig{
Size: 10, Count: 10,
}), }),
ecsCache: agdcache.NewLRU[uint64, *cacheItem](&agdcache.LRUConfig{ ecsCache: agdcache.NewLRU[uint64, *cacheItem](&agdcache.LRUConfig{
Size: 10, Count: 10,
}), }),
} }

File diff suppressed because it is too large Load Diff

View File

@ -91,10 +91,10 @@ type MiddlewareConfig struct {
// manager. c must not be nil. // manager. c must not be nil.
func NewMiddleware(c *MiddlewareConfig) (m *Middleware) { func NewMiddleware(c *MiddlewareConfig) (m *Middleware) {
cache := agdcache.NewLRU[uint64, *cacheItem](&agdcache.LRUConfig{ cache := agdcache.NewLRU[uint64, *cacheItem](&agdcache.LRUConfig{
Size: c.NoECSCount, Count: c.NoECSCount,
}) })
ecsCache := agdcache.NewLRU[uint64, *cacheItem](&agdcache.LRUConfig{ ecsCache := agdcache.NewLRU[uint64, *cacheItem](&agdcache.LRUConfig{
Size: c.ECSCount, Count: c.ECSCount,
}) })
c.CacheManager.Add(cacheIDNoECS, cache) c.CacheManager.Add(cacheIDNoECS, cache)

Some files were not shown because too many files have changed in this diff Show More