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/
[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
- 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
GOPROXY = https://proxy.golang.org|direct
GOTELEMETRY = off
GOTOOLCHAIN = go1.23.2
GOTOOLCHAIN = go1.23.4
RACE = 0
REVISION = $${REVISION:-$$(git rev-parse --short HEAD)}
VERSION = 0

View File

@ -175,8 +175,8 @@ check:
# Domains to use for DNS checking.
kv:
# Defines the type of remote key-value storage. Allowed values are
# "backend", "consul", and "redis".
type: 'consul'
# "backend", "cache", "consul", and "redis".
type: 'cache'
# For how long to keep the information about the client.
ttl: 30s
# Domains to use for DNS checking.
@ -341,8 +341,9 @@ filtering_groups:
enabled: true
block_dangerous_domains: true
block_newly_registered_domains: false
block_private_relay: false
block_chrome_prefetch: true
block_firefox_canary: true
block_private_relay: false
- id: 'family'
parental:
enabled: true
@ -357,8 +358,9 @@ filtering_groups:
enabled: true
block_dangerous_domains: true
block_newly_registered_domains: false
block_private_relay: false
block_chrome_prefetch: true
block_firefox_canary: true
block_private_relay: false
- id: 'non_filtering'
rule_lists:
enabled: false
@ -368,8 +370,9 @@ filtering_groups:
enabled: false
block_dangerous_domains: true
block_newly_registered_domains: false
block_private_relay: false
block_chrome_prefetch: false
block_firefox_canary: true
block_private_relay: false
# The configuration for the device-listening feature. Works only on Linux with
# 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-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`.
@ -386,6 +386,8 @@ The `check` object has the following properties:
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 `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`.
- <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.
**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>
> [!NOTE]

View File

@ -112,6 +112,7 @@ Supported IDs:
- `profiledb`
- `rulestat`
- `ticket_rotator`
- `tlsconfig`
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_DNSCHECK_KV_URL`](#CONSUL_DNSCHECK_KV_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_URL`](#DNSCHECK_REMOTEKV_URL)
- [`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`
## <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>
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
go 1.23.2
go 1.23.4
require (
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/ameshkov/dnscrypt/v2 v2.3.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/prometheus/client_golang v1.20.5
github.com/prometheus/client_model v0.6.1
github.com/prometheus/common v0.60.0
github.com/quic-go/quic-go v0.48.1
github.com/prometheus/common v0.60.1
github.com/quic-go/quic-go v0.48.2
github.com/stretchr/testify v1.9.0
golang.org/x/crypto v0.28.0
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
golang.org/x/net v0.30.0
golang.org/x/sys v0.26.0
golang.org/x/time v0.7.0
google.golang.org/grpc v1.67.1
golang.org/x/crypto v0.30.0
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
golang.org/x/net v0.32.0
golang.org/x/sys v0.28.0
golang.org/x/time v0.8.0
google.golang.org/grpc v1.68.0
google.golang.org/protobuf v1.35.1
gopkg.in/yaml.v2 v2.4.0
)
@ -41,20 +41,20 @@ require (
github.com/davecgh/go-spew v1.1.1 // 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/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/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/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
go.uber.org/mock v0.5.0 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/tools v0.26.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.28.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // 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.1/go.mod h1:FkwcNQEJoGsgDGXcalrVa/4gWbE68KsmE2guXWtBQUE=
github.com/AdguardTeam/golibs v0.30.4 h1:zfFX1v4hkOCz6BifkneiBW2PCwSK523kYNr+VwaFrIw=
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/go.mod h1:gjrywLTxfJh6JOkwi9SU+frhP7kVVEZ5exFGkR99qpk=
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-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/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/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/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20241023014458-598669927662 h1:SKMkD83p7FwUqKmBsPdLHF5dNyxq3jOWwu9w9UyH5vA=
github.com/google/pprof v0.0.0-20241023014458-598669927662/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/pprof v0.0.0-20241203143554-1e3fdc7de467 h1:keEZFtbLJugfE0qHn+Ge1JCE71spzkchQobDf3mzS/4=
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/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
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/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/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
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/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
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_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
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.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
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/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/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.1/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
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/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
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=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
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.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
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/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY=
golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
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.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
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/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 h1:XVhgTWWV3kGQlwJHR3upFWZeTsei6Oks1apkZSeonIE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
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/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
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 (
.

View File

@ -1,6 +1,7 @@
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.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.31.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.1 h1:/yv7dq2h7WXw/jTDxkE3FP9zHerRT+i03PZRHJX4fPU=
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.1 h1:p9gr8Er1TYvf+7ic81Ax1sZ62UNCsMTZNbm7tC59S9o=
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/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/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
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.2/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/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
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-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-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/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
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/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
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.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.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.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=
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 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.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-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
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.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
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.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
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/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
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/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
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/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
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-20221019170559-20944726eadf/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
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-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-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/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=
@ -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.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.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-20180826012351-8a410e7b638d/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.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.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-20181017192945-9dcd33a902f4/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.6.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-20180909124046-d0be0721c37e/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.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.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/go.mod h1:KG1lNk5ZFNssSZLrpVb4sMXKMpGwGXOxSG3rnu2gZQQ=
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/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
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.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
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.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
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-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
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.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.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.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-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
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/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-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/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=
@ -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.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.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
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/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=

View File

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

View File

@ -34,7 +34,8 @@ func TestNewDeviceName(t *testing.T) {
}, {
name: "too_long_unicode",
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 {

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 (
"fmt"
"math"
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/access"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"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
@ -17,141 +15,62 @@ import (
//
// NOTE: Do not change fields of this structure without incrementing
// [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 {
// Parental are the parental 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].
Parental *ParentalProtectionSettings
// Ratelimiter is the custom ratelimiter for this profile. It must not be
// FilterConfig is the configuration of the filters used for this profile
// and all its devices that don't have filtering disabled. It must not be
// nil.
//
// 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
FilterConfig *filter.ConfigClient
// 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
// BlockingMode defines the way blocked responses are constructed. It must
// not be nil.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
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.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
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.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
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
// the devices of this profile.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
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
// HumanIDs should be enabled for this profile.
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
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.
@ -179,165 +98,3 @@ func NewProfileID(s string) (id ProfileID, err error) {
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.
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.
type DefaultRatelimiter struct {

View File

@ -19,7 +19,7 @@ type Server struct {
DNSCrypt *DNSCryptConfig
// TLS is the TLS configuration for this server, if any.
TLS *tls.Config
TLS *TLSConfig
// QUICConf is the QUIC configuration for this server.
QUICConf *QUICConfig
@ -200,3 +200,13 @@ type QUICConfig struct {
// QUICLimitsEnabled, if true, enables QUIC limiting.
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
import (
"crypto/tls"
"github.com/AdguardTeam/golibs/container"
"github.com/miekg/dns"
)
@ -14,10 +12,11 @@ type ServerGroup struct {
// Resolvers (DDR) handlers. DDR must not be nil.
DDR *DDR
// TLS are the TLS settings for this server group. If Servers contains at
// least one server with a non-plain protocol (see [Protocol.IsPlain]), TLS
// must not be nil.
TLS *TLS
// DeviceDomains is the list of domain names used to detect device IDs from
// clients' server names.
//
// TODO(s.chzhen): Consider using a custom type.
DeviceDomains []string
// Name is the unique name of the server group.
Name ServerGroupName
@ -36,20 +35,6 @@ type ServerGroup struct {
// ServerGroupName is the name of a server group.
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
// Resolvers (DDR) handlers.
type DDR struct {

View File

@ -10,7 +10,11 @@ import (
// LRUConfig is a configuration structure of a cache.
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.
@ -21,7 +25,7 @@ type LRU[K, T any] struct {
// NewLRU returns a new initialized LRU cache.
func NewLRU[K, T any](conf *LRUConfig) (c *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{
Size: 10,
Count: 10,
})
cache.Set(key, val)

View File

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

View File

@ -22,6 +22,18 @@ type Refresher interface {
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]
// every tick of the provided ticker.
type RefreshWorker struct {

View File

@ -96,9 +96,9 @@ func TestRefreshWorker(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))
require.NoError(t, err)
@ -110,9 +110,9 @@ func TestRefreshWorker(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))
require.NoError(t, err)

View File

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

View File

@ -8,6 +8,24 @@ import (
"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
// and to JSON.
//

View File

@ -8,7 +8,6 @@ import (
"fmt"
"net/url"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/golibs/httphdr"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
@ -41,11 +40,6 @@ func newClient(apiURL *url.URL) (client *grpc.ClientConn, err error) {
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
// request context if apiKey is not empty. If it is empty, ctx is parent.
func ctxWithAuthentication(parent context.Context, apiKey string) (ctx context.Context) {

View File

@ -2,18 +2,13 @@ package backendpb
import (
"net/netip"
"testing"
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/c2h5oh/datasize"
)
func TestMain(m *testing.M) {
testutil.DiscardLogOutput(m)
}
// Common IDs for tests and their string forms.
//
// 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.
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.
const TestRespSzEst datasize.ByteSize = 1 * datasize.KB

View File

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"io"
"log/slog"
"net/url"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
@ -16,13 +17,18 @@ import (
// BillStatConfig is the configuration structure for the business logic backend
// billing statistics uploader.
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
// non-critical errors.
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
// "grpcs".
Endpoint *url.URL
@ -31,6 +37,20 @@ type BillStatConfig struct {
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.
func NewBillStat(c *BillStatConfig) (b *BillStat, err error) {
client, err := newClient(c.Endpoint)
@ -40,26 +60,14 @@ func NewBillStat(c *BillStatConfig) (b *BillStat, err error) {
}
return &BillStat{
logger: c.Logger,
errColl: c.ErrColl,
metrics: c.Metrics,
grpcMetrics: c.GRPCMetrics,
client: NewDNSServiceClient(client),
apiKey: c.APIKey,
}, 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
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)
stream, err := b.client.SaveDevicesBillingStat(ctx)
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 {
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
}
@ -87,7 +96,7 @@ func (b *BillStat) Upload(ctx context.Context, records billstat.Records) (err er
return fmt.Errorf(
"uploading device %q record: %w",
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{
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{
Logger: backendpb.TestLogger,
ErrColl: errColl,
Metrics: backendpb.EmptyMetrics{},
GRPCMetrics: backendpb.EmptyGRPCMetrics{},
Endpoint: &url.URL{
Scheme: "grpc",
Host: l.Addr().String(),

View File

@ -3,6 +3,7 @@ package backendpb
import (
"context"
"fmt"
"log/slog"
"net/netip"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
@ -19,7 +20,8 @@ func devicesToInternal(
ds []*DeviceSettings,
bindSet netutil.SubnetSet,
errColl errcoll.Interface,
mtrc Metrics,
logger *slog.Logger,
mtrc ProfileDBMetrics,
) (out []*agd.Device, ids []agd.DeviceID) {
l := len(ds)
if l == 0 {
@ -35,7 +37,8 @@ func devicesToInternal(
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
// metrics collection to the layer above.

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.35.1
// protoc-gen-go v1.35.2
// protoc v5.28.3
// source: dns.proto
@ -247,6 +247,7 @@ type DNSProfile struct {
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"`
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() {
@ -426,6 +427,13 @@ func (x *DNSProfile) GetRateLimit() *RateLimitSettings {
return nil
}
func (x *DNSProfile) GetBlockChromePrefetch() bool {
if x != nil {
return x.BlockChromePrefetch
}
return false
}
type isDNSProfile_BlockingMode interface {
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,
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,
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,
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,
@ -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,
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,
0x4c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x0f, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e,
0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x85, 0x01, 0x0a, 0x14, 0x53, 0x61, 0x66, 0x65, 0x42,
0x72, 0x6f, 0x77, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12,
0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x36, 0x0a, 0x17, 0x62, 0x6c, 0x6f,
0x63, 0x6b, 0x5f, 0x64, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73, 0x5f, 0x64, 0x6f, 0x6d,
0x61, 0x69, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x62, 0x6c, 0x6f, 0x63,
0x6b, 0x44, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
0x73, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x72, 0x64, 0x18, 0x03,
0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x72, 0x64, 0x22, 0x8a,
0x02, 0x0a, 0x0e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69,
0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69,
0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08,
0x52, 0x10, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c,
0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x5f, 0x69, 0x70, 0x18,
0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x49, 0x70, 0x12,
0x23, 0x0a, 0x0d, 0x64, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x70, 0x73,
0x18, 0x05, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x64, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65,
0x64, 0x49, 0x70, 0x73, 0x12, 0x3f, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69,
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x41,
0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x74,
0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x68, 0x75, 0x6d, 0x61, 0x6e, 0x5f, 0x69,
0x64, 0x5f, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x68,
0x75, 0x6d, 0x61, 0x6e, 0x49, 0x64, 0x4c, 0x6f, 0x77, 0x65, 0x72, 0x22, 0x87, 0x02, 0x0a, 0x10,
0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x6c,
0x6f, 0x63, 0x6b, 0x5f, 0x61, 0x64, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52,
0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x64, 0x75, 0x6c, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x67,
0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72,
0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61,
0x6c, 0x53, 0x61, 0x66, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x2e, 0x0a, 0x13, 0x79,
0x6f, 0x75, 0x74, 0x75, 0x62, 0x65, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72,
0x63, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x79, 0x6f, 0x75, 0x74, 0x75, 0x62,
0x65, 0x53, 0x61, 0x66, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x29, 0x0a, 0x10, 0x62,
0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18,
0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x53, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75,
0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64,
0x75, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x73, 0x63, 0x68,
0x65, 0x64, 0x75, 0x6c, 0x65, 0x22, 0x54, 0x0a, 0x10, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c,
0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x6d, 0x7a,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x6d, 0x7a, 0x12, 0x2e, 0x0a, 0x0b, 0x77,
0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x0c, 0x2e, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0b,
0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x22, 0xd8, 0x01, 0x0a, 0x0b,
0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x03, 0x6d,
0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61,
0x6e, 0x67, 0x65, 0x52, 0x03, 0x6d, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x03, 0x74, 0x75, 0x65, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65,
0x52, 0x03, 0x74, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x03, 0x77, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x77,
0x65, 0x64, 0x12, 0x1b, 0x0a, 0x03, 0x74, 0x68, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x74, 0x68, 0x75, 0x12,
0x1b, 0x0a, 0x03, 0x66, 0x72, 0x69, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44,
0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x66, 0x72, 0x69, 0x12, 0x1b, 0x0a, 0x03,
0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52,
0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x73, 0x61, 0x74, 0x12, 0x1b, 0x0a, 0x03, 0x73, 0x75, 0x6e,
0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67,
0x65, 0x52, 0x03, 0x73, 0x75, 0x6e, 0x22, 0x68, 0x0a, 0x08, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e,
0x67, 0x65, 0x12, 0x2f, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 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, 0x05, 0x73, 0x74,
0x61, 0x72, 0x74, 0x12, 0x2b, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 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, 0x65, 0x6e, 0x64,
0x22, 0x3f, 0x0a, 0x11, 0x52, 0x75, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x53, 0x65, 0x74,
0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12,
0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64,
0x73, 0x22, 0x3e, 0x0a, 0x14, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64,
0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x49, 0x50, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76,
0x34, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x70, 0x76, 0x34, 0x12, 0x12, 0x0a,
0x04, 0x69, 0x70, 0x76, 0x36, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x70, 0x76,
0x36, 0x22, 0x16, 0x0a, 0x14, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64,
0x65, 0x4e, 0x58, 0x44, 0x4f, 0x4d, 0x41, 0x49, 0x4e, 0x22, 0x14, 0x0a, 0x12, 0x42, 0x6c, 0x6f,
0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x75, 0x6c, 0x6c, 0x49, 0x50, 0x22,
0x15, 0x0a, 0x13, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x52,
0x45, 0x46, 0x55, 0x53, 0x45, 0x44, 0x22, 0xe3, 0x01, 0x0a, 0x11, 0x44, 0x65, 0x76, 0x69, 0x63,
0x65, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x12, 0x48, 0x0a, 0x12,
0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 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, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69,
0x74, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,
0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63,
0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f,
0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6c, 0x69,
0x65, 0x6e, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x61,
0x73, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20,
0x01, 0x28, 0x0d, 0x52, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x22, 0x90, 0x02, 0x0a,
0x0e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12,
0x31, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x63, 0x69, 0x64,
0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61,
0x6e, 0x67, 0x65, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x43, 0x69,
0x64, 0x72, 0x12, 0x31, 0x0a, 0x0e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f,
0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69, 0x64,
0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73,
0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69,
0x73, 0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x61, 0x6c,
0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x6c,
0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28,
0x0d, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x6e, 0x12,
0x34, 0x0a, 0x16, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x64, 0x6f, 0x6d,
0x61, 0x69, 0x6e, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52,
0x14, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22,
0x3d, 0x0a, 0x09, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07,
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61,
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0x85,
0x01, 0x0a, 0x16, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x64, 0x6f, 0x68,
0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
0x52, 0x0b, 0x64, 0x6f, 0x68, 0x41, 0x75, 0x74, 0x68, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x32, 0x0a,
0x14, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x62,
0x63, 0x72, 0x79, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x12, 0x70,
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x48, 0x61, 0x73, 0x68, 0x42, 0x63, 0x72, 0x79, 0x70,
0x74, 0x42, 0x13, 0x0a, 0x11, 0x64, 0x6f, 0x68, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x22, 0x75, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a,
0x06, 0x64, 0x6e, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x64,
0x6e, 0x73, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x68, 0x75, 0x6d, 0x61, 0x6e, 0x5f, 0x69, 0x64,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x68, 0x75, 0x6d, 0x61, 0x6e, 0x49, 0x64, 0x12,
0x2c, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x0b, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70,
0x65, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3f, 0x0a,
0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65,
0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x22, 0x68,
0x0a, 0x10, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 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, 0x67, 0x65, 0x12, 0x3a, 0x0a, 0x0b,
0x72, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 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, 0x0a, 0x72, 0x65,
0x74, 0x72, 0x79, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x22, 0x34, 0x0a, 0x18, 0x44, 0x65, 0x76, 0x69,
0x63, 0x65, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x45, 0x78, 0x63, 0x65, 0x65, 0x64, 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, 0x67, 0x65, 0x22, 0x2b,
0x0a, 0x0f, 0x42, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 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, 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,
0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63,
0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x74, 0x63, 0x68, 0x18, 0x15,
0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x68, 0x72, 0x6f, 0x6d,
0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x74, 0x63, 0x68, 0x42, 0x0f, 0x0a, 0x0d, 0x62, 0x6c, 0x6f,
0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x85, 0x01, 0x0a, 0x14, 0x53,
0x61, 0x66, 0x65, 0x42, 0x72, 0x6f, 0x77, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69,
0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x36, 0x0a,
0x17, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73,
0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15,
0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73, 0x44, 0x6f,
0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e,
0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e,
0x72, 0x64, 0x22, 0x8a, 0x02, 0x0a, 0x0e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65, 0x74,
0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x69, 0x6c,
0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03,
0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x45,
0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64,
0x5f, 0x69, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x65,
0x64, 0x49, 0x70, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64,
0x5f, 0x69, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x64, 0x65, 0x64, 0x69,
0x63, 0x61, 0x74, 0x65, 0x64, 0x49, 0x70, 0x73, 0x12, 0x3f, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x68,
0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x17, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x65,
0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x68, 0x75, 0x6d,
0x61, 0x6e, 0x5f, 0x69, 0x64, 0x5f, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0c, 0x68, 0x75, 0x6d, 0x61, 0x6e, 0x49, 0x64, 0x4c, 0x6f, 0x77, 0x65, 0x72, 0x22,
0x87, 0x02, 0x0a, 0x10, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x53, 0x65, 0x74, 0x74,
0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18,
0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1f,
0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x61, 0x64, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20,
0x01, 0x28, 0x08, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x64, 0x75, 0x6c, 0x74, 0x12,
0x2e, 0x0a, 0x13, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f,
0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x67, 0x65,
0x6e, 0x65, 0x72, 0x61, 0x6c, 0x53, 0x61, 0x66, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12,
0x2e, 0x0a, 0x13, 0x79, 0x6f, 0x75, 0x74, 0x75, 0x62, 0x65, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f,
0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x79, 0x6f,
0x75, 0x74, 0x75, 0x62, 0x65, 0x53, 0x61, 0x66, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12,
0x29, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x08, 0x73, 0x63,
0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x53,
0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52,
0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x22, 0x54, 0x0a, 0x10, 0x53, 0x63, 0x68,
0x65, 0x64, 0x75, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x10, 0x0a,
0x03, 0x74, 0x6d, 0x7a, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x6d, 0x7a, 0x12,
0x2e, 0x0a, 0x0b, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e,
0x67, 0x65, 0x52, 0x0b, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x22,
0xd8, 0x01, 0x0a, 0x0b, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12,
0x1b, 0x0a, 0x03, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44,
0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x6d, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x03,
0x74, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52,
0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x74, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x03, 0x77, 0x65, 0x64,
0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67,
0x65, 0x52, 0x03, 0x77, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x03, 0x74, 0x68, 0x75, 0x18, 0x04, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03,
0x74, 0x68, 0x75, 0x12, 0x1b, 0x0a, 0x03, 0x66, 0x72, 0x69, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x66, 0x72, 0x69,
0x12, 0x1b, 0x0a, 0x03, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e,
0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x73, 0x61, 0x74, 0x12, 0x1b, 0x0a,
0x03, 0x73, 0x75, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79,
0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x73, 0x75, 0x6e, 0x22, 0x68, 0x0a, 0x08, 0x44, 0x61,
0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x2f, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18,
0x01, 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, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2b, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02,
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, 0x65, 0x6e, 0x64, 0x22, 0x3f, 0x0a, 0x11, 0x52, 0x75, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74,
0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61,
0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62,
0x6c, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x3e, 0x0a, 0x14, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e,
0x67, 0x4d, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x49, 0x50, 0x12, 0x12, 0x0a,
0x04, 0x69, 0x70, 0x76, 0x34, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x70, 0x76,
0x34, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x04, 0x69, 0x70, 0x76, 0x36, 0x22, 0x16, 0x0a, 0x14, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e,
0x67, 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x58, 0x44, 0x4f, 0x4d, 0x41, 0x49, 0x4e, 0x22, 0x14, 0x0a,
0x12, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x75, 0x6c,
0x6c, 0x49, 0x50, 0x22, 0x15, 0x0a, 0x13, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d,
0x6f, 0x64, 0x65, 0x52, 0x45, 0x46, 0x55, 0x53, 0x45, 0x44, 0x22, 0xe3, 0x01, 0x0a, 0x11, 0x44,
0x65, 0x76, 0x69, 0x63, 0x65, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74,
0x12, 0x48, 0x0a, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74,
0x79, 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, 0x75, 0x66, 0x2e, 0x54,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x63,
0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65,
0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64,
0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6c, 0x69, 0x65, 0x6e,
0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x14,
0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28,
0x0d, 0x52, 0x03, 0x61, 0x73, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65,
0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73,
0x22, 0x90, 0x02, 0x0a, 0x0e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69,
0x6e, 0x67, 0x73, 0x12, 0x31, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74,
0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69,
0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69,
0x73, 0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x31, 0x0a, 0x0e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c,
0x69, 0x73, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a,
0x2e, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63,
0x6b, 0x6c, 0x69, 0x73, 0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c,
0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d,
0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x6e, 0x12, 0x23,
0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18,
0x04, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74,
0x41, 0x73, 0x6e, 0x12, 0x34, 0x0a, 0x16, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74,
0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20,
0x03, 0x28, 0x09, 0x52, 0x14, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x44, 0x6f,
0x6d, 0x61, 0x69, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61,
0x62, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62,
0x6c, 0x65, 0x64, 0x22, 0x3d, 0x0a, 0x09, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65,
0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72,
0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66,
0x69, 0x78, 0x22, 0x85, 0x01, 0x0a, 0x16, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x22, 0x0a,
0x0d, 0x64, 0x6f, 0x68, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01,
0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x64, 0x6f, 0x68, 0x41, 0x75, 0x74, 0x68, 0x4f, 0x6e, 0x6c,
0x79, 0x12, 0x32, 0x0a, 0x14, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61,
0x73, 0x68, 0x5f, 0x62, 0x63, 0x72, 0x79, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48,
0x00, 0x52, 0x12, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x48, 0x61, 0x73, 0x68, 0x42,
0x63, 0x72, 0x79, 0x70, 0x74, 0x42, 0x13, 0x0a, 0x11, 0x64, 0x6f, 0x68, 0x5f, 0x70, 0x61, 0x73,
0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x22, 0x75, 0x0a, 0x13, 0x43, 0x72,
0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x6e, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x05, 0x64, 0x6e, 0x73, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x68, 0x75, 0x6d, 0x61,
0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x68, 0x75, 0x6d, 0x61,
0x6e, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x74, 0x79,
0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0b, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63,
0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70,
0x65, 0x22, 0x3f, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63,
0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x06, 0x64, 0x65, 0x76,
0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x44, 0x65, 0x76, 0x69,
0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x06, 0x64, 0x65, 0x76, 0x69,
0x63, 0x65, 0x22, 0x68, 0x0a, 0x10, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 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, 0x67, 0x65,
0x12, 0x3a, 0x0a, 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18,
0x02, 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, 0x0a, 0x72, 0x65, 0x74, 0x72, 0x79, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x22, 0x34, 0x0a, 0x18,
0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x45, 0x78, 0x63, 0x65, 0x65,
0x64, 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,
0x67, 0x65, 0x22, 0x6c, 0x0a, 0x11, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x53,
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c,
0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65,
0x64, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x70, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03,
0x72, 0x70, 0x73, 0x12, 0x2b, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x69,
0x64, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69, 0x64, 0x72, 0x52,
0x61, 0x6e, 0x67, 0x65, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x69, 0x64, 0x72,
0x22, 0x26, 0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x47, 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, 0x22, 0x64, 0x0a, 0x13, 0x52, 0x65, 0x6d, 0x6f,
0x74, 0x65, 0x4b, 0x56, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x14, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52,
0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2e, 0x0a, 0x05, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x48, 0x00, 0x52, 0x05,
0x65, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x67,
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,
0x67, 0x65, 0x22, 0x2b, 0x0a, 0x0f, 0x42, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
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, 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, 0x67, 0x65, 0x22, 0x6c, 0x0a, 0x11, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69,
0x6d, 0x69, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65,
0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e,
0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x70, 0x73, 0x18, 0x02, 0x20, 0x01,
0x28, 0x0d, 0x52, 0x03, 0x72, 0x70, 0x73, 0x12, 0x2b, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e,
0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43,
0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
0x43, 0x69, 0x64, 0x72, 0x22, 0x26, 0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56,
0x47, 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, 0x22, 0x64, 0x0a, 0x13,
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,
0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0c, 0x48, 0x00, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2e, 0x0a, 0x05, 0x65, 0x6d, 0x70,
0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
0x48, 0x00, 0x52, 0x05, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x22, 0x67, 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, 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 (

View File

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

View File

@ -9,30 +9,15 @@ import (
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.
// That includes gRPC deadlines, which do not match [context.DeadlineExceeded]
// correctly.
//
// It also updates the backend gRPC metrics depending on the type, see
// [Metrics.IncrementGRPCErrorCount].
func fixGRPCError(ctx context.Context, mtrc Metrics, err error) (res error) {
// [GRPCMetrics.IncrementErrorCount].
func fixGRPCError(ctx context.Context, mtrc GRPCMetrics, err error) (res error) {
metricsType := GRPCErrOther
defer func() { mtrc.IncrementGRPCErrorCount(ctx, metricsType) }()
defer func() { mtrc.IncrementErrorCount(ctx, metricsType) }()
s, ok := status.FromError(err)
if !ok {

View File

@ -5,13 +5,47 @@ import (
"time"
)
// Metrics is an interface that is used for the collection of the protobuf
// errors statistics.
type Metrics interface {
// IncrementGRPCErrorCount increments the gRPC error count of errType.
// errType must be one of GRPCError values.
IncrementGRPCErrorCount(ctx context.Context, errType GRPCError)
// GRPCError is a type alias for string that contains the gRPC error type.
//
// See [GRPCMetrics.IncrementErrorCount].
//
// TODO(s.chzhen): Rewrite as soon as the import cycle is resolved.
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(ctx context.Context)
@ -19,18 +53,61 @@ type Metrics interface {
UpdateStats(ctx context.Context, avgRecv, avgDec time.Duration)
}
// EmptyMetrics is the implementation of the [Metrics] interface that does
// nothing.
type EmptyMetrics struct{}
// EmptyProfileDBMetrics is the implementation of the [ProfileDBMetrics]
// interface that does nothing.
type EmptyProfileDBMetrics struct{}
// type check
var _ Metrics = EmptyMetrics{}
var _ ProfileDBMetrics = EmptyProfileDBMetrics{}
// IncrementGRPCErrorCount implements the [Metrics] interface for EmptyMetrics.
func (EmptyMetrics) IncrementGRPCErrorCount(_ context.Context, errType GRPCError) {}
// IncrementInvalidDevicesCount implements the [ProfileDBMetrics] interface for
// EmptyProfileDBMetrics.
func (EmptyProfileDBMetrics) IncrementInvalidDevicesCount(_ context.Context) {}
// IncrementInvalidDevicesCount implements the [Metrics] interface for EmptyMetrics.
func (EmptyMetrics) IncrementInvalidDevicesCount(_ context.Context) {}
// UpdateStats implements the [ProfileDBMetrics] interface for
// EmptyProfileDBMetrics.
func (EmptyProfileDBMetrics) UpdateStats(_ context.Context, _, _ time.Duration) {}
// UpdateStats implements the [Metrics] interface for EmptyMetrics.
func (EmptyMetrics) UpdateStats(_ context.Context, _, _ time.Duration) {}
// RemoteKVOp is the type alias for string that contains remote key-value
// 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 (
"context"
"fmt"
"log/slog"
"net/netip"
"time"
@ -11,6 +12,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
@ -20,20 +22,21 @@ import (
// toInternal converts the protobuf-encoded data into a profile structure and
// its device structures.
//
// TODO(a.garipov): Refactor into a method of [*ProfileStorage]?
// TODO(a.garipov): Refactor into methods of [*ProfileStorage].
func (x *DNSProfile) toInternal(
ctx context.Context,
updTime time.Time,
bindSet netutil.SubnetSet,
errColl errcoll.Interface,
mtrc Metrics,
logger *slog.Logger,
mtrc ProfileDBMetrics,
respSzEst datasize.ByteSize,
) (profile *agd.Profile, devices []*agd.Device, err error) {
if x == 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 {
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)
}
devices, deviceIds := devicesToInternal(ctx, x.Devices, bindSet, errColl, mtrc)
listsEnabled, listIDs := x.RuleLists.toInternal(ctx, errColl)
devices, deviceIds := devicesToInternal(ctx, x.Devices, bindSet, errColl, logger, mtrc)
profID, err := agd.NewProfileID(x.DnsId)
if err != nil {
@ -56,52 +58,63 @@ func (x *DNSProfile) toInternal(
fltRespTTL = respTTL.AsDuration()
}
return &agd.Profile{
Parental: parental,
BlockingMode: m,
ID: profID,
customRules := rulesToInternal(ctx, x.CustomRules, errColl, logger)
custom := &filter.ConfigCustom{
ID: string(x.DnsId),
UpdateTime: updTime,
DeviceIDs: deviceIds,
RuleListIDs: listIDs,
CustomRules: rulesToInternal(ctx, x.CustomRules, errColl),
FilteredResponseTTL: fltRespTTL,
FilteringEnabled: x.FilteringEnabled,
Ratelimiter: x.RateLimit.toInternal(ctx, errColl, respSzEst),
Rules: customRules,
// TODO(a.garipov): Consider adding an explicit flag to the protocol.
Enabled: len(customRules) > 0,
}
return &agd.Profile{
FilterConfig: &filter.ConfigClient{
Custom: custom,
Parental: parental,
RuleList: x.RuleLists.toInternal(ctx, errColl, logger),
SafeBrowsing: x.SafeBrowsing.toInternal(),
Access: x.Access.toInternal(ctx, errColl),
RuleListsEnabled: listsEnabled,
QueryLogEnabled: x.QueryLogEnabled,
Deleted: x.Deleted,
BlockPrivateRelay: x.BlockPrivateRelay,
BlockFirefoxCanary: x.BlockFirefoxCanary,
IPLogEnabled: x.IpLogEnabled,
},
Access: x.Access.toInternal(ctx, errColl, logger),
BlockingMode: m,
Ratelimiter: x.RateLimit.toInternal(ctx, errColl, logger, respSzEst),
ID: profID,
DeviceIDs: deviceIds,
FilteredResponseTTL: fltRespTTL,
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
}
// toInternal converts a protobuf parental-settings structure to an internal
// one. If x is nil, toInternal returns nil.
// toInternal converts a protobuf parental-protection settings structure to an
// internal one. If x is nil, toInternal returns a disabled configuration.
func (x *ParentalSettings) toInternal(
ctx context.Context,
errColl errcoll.Interface,
) (s *agd.ParentalProtectionSettings, err error) {
logger *slog.Logger,
) (c *filter.ConfigParental, err error) {
c = &filter.ConfigParental{}
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 {
return nil, fmt.Errorf("schedule: %w", err)
return nil, fmt.Errorf("pause schedule: %w", err)
}
return &agd.ParentalProtectionSettings{
Schedule: schedule,
BlockedServices: blockedSvcsToInternal(ctx, errColl, x.BlockedServices),
Enabled: x.Enabled,
BlockAdult: x.BlockAdult,
GeneralSafeSearch: x.GeneralSafeSearch,
YoutubeSafeSearch: x.YoutubeSafeSearch,
}, nil
return c, nil
}
// toInternal converts protobuf rate-limiting settings to an internal structure.
@ -109,6 +122,7 @@ func (x *ParentalSettings) toInternal(
func (x *RateLimitSettings) toInternal(
ctx context.Context,
errColl errcoll.Interface,
logger *slog.Logger,
respSzEst datasize.ByteSize,
) (r agd.Ratelimiter) {
if x == nil || !x.Enabled {
@ -116,24 +130,26 @@ func (x *RateLimitSettings) toInternal(
}
return agd.NewDefaultRatelimiter(&agd.RatelimitConfig{
ClientSubnets: cidrRangeToInternal(ctx, errColl, x.ClientCidr),
ClientSubnets: cidrRangeToInternal(ctx, errColl, logger, x.ClientCidr),
RPS: x.Rps,
Enabled: x.Enabled,
}, respSzEst)
}
// toInternal converts protobuf safe-browsing settings to an internal structure.
// If x is nil, toInternal returns nil.
func (x *SafeBrowsingSettings) toInternal() (sb *agd.SafeBrowsingSettings) {
// toInternal converts protobuf safe-browsing settings to an internal
// safe-browsing configuration. If x is nil, toInternal returns a disabled
// configuration.
func (x *SafeBrowsingSettings) toInternal() (c *filter.ConfigSafeBrowsing) {
c = &filter.ConfigSafeBrowsing{}
if x == nil {
return nil
return c
}
return &agd.SafeBrowsingSettings{
Enabled: x.Enabled,
BlockDangerousDomains: x.BlockDangerousDomains,
BlockNewlyRegisteredDomains: x.BlockNrd,
}
c.Enabled = x.Enabled
c.DangerousDomainsEnabled = x.BlockDangerousDomains
c.NewlyRegisteredDomainsEnabled = x.BlockNrd
return c
}
// 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(
ctx context.Context,
errColl errcoll.Interface,
logger *slog.Logger,
) (a access.Profile) {
if x == nil || !x.Enabled {
return access.EmptyProfile{}
}
return access.NewDefaultProfile(&access.ProfileConfig{
AllowedNets: cidrRangeToInternal(ctx, errColl, x.AllowlistCidr),
BlockedNets: cidrRangeToInternal(ctx, errColl, x.BlocklistCidr),
AllowedNets: cidrRangeToInternal(ctx, errColl, logger, x.AllowlistCidr),
BlockedNets: cidrRangeToInternal(ctx, errColl, logger, x.BlocklistCidr),
AllowedASN: asnToInternal(x.AllowlistAsn),
BlockedASN: asnToInternal(x.BlocklistAsn),
BlocklistDomainRules: x.BlocklistDomainRules,
@ -160,12 +177,14 @@ func (x *AccessSettings) toInternal(
func cidrRangeToInternal(
ctx context.Context,
errColl errcoll.Interface,
logger *slog.Logger,
cidrs []*CidrRange,
) (out []netip.Prefix) {
for i, c := range cidrs {
addr, ok := netip.AddrFromSlice(c.Address)
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
}
@ -187,71 +206,71 @@ func asnToInternal(asns []uint32) (out []geoip.ASN) {
}
// 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(
ctx context.Context,
errColl errcoll.Interface,
logger *slog.Logger,
respSvcs []string,
) (svcs []agd.BlockedServiceID) {
) (ids []filter.BlockedServiceID) {
l := len(respSvcs)
if l == 0 {
return nil
}
svcs = make([]agd.BlockedServiceID, 0, l)
for i, s := range respSvcs {
id, err := agd.NewBlockedServiceID(s)
ids = make([]filter.BlockedServiceID, 0, l)
for i, idStr := range respSvcs {
id, err := filter.NewBlockedServiceID(idStr)
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
}
svcs = append(svcs, id)
ids = append(ids, id)
}
return svcs
return ids
}
// toInternal converts a protobuf protection-schedule structure to an internal
// 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 {
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 {
return nil, fmt.Errorf("loading timezone: %w", err)
}
sch.Week = &agd.WeeklySchedule{}
w := x.WeeklyRange
days := []*DayRange{w.Sun, w.Mon, w.Tue, w.Wed, w.Thu, w.Fri, w.Sat}
for i, d := range days {
if d == nil {
sch.Week[i] = agd.ZeroLengthDayRange()
continue
}
sch.Week[i] = agd.DayRange{
ivl := &filter.DayInterval{
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 = r.Validate()
err = ivl.Validate()
if err != nil {
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.
@ -312,17 +331,19 @@ func rulesToInternal(
ctx context.Context,
respRules []string,
errColl errcoll.Interface,
) (rules []agd.FilterRuleText) {
logger *slog.Logger,
) (rules []filter.RuleText) {
l := len(respRules)
if l == 0 {
return nil
}
rules = make([]agd.FilterRuleText, 0, l)
rules = make([]filter.RuleText, 0, l)
for i, r := range respRules {
text, err := agd.NewFilterRuleText(r)
text, err := filter.NewRuleText(r)
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
}
@ -334,32 +355,32 @@ func rulesToInternal(
}
// 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
// false and nil.
// response to AdGuard DNS rule-list configuration. If x is nil, toInternal
// returns a disabled configuration.
func (x *RuleListsSettings) toInternal(
ctx context.Context,
errColl errcoll.Interface,
) (enabled bool, filterLists []agd.FilterListID) {
logger *slog.Logger,
) (c *filter.ConfigRuleList) {
c = &filter.ConfigRuleList{}
if x == nil {
return false, nil
return c
}
l := len(x.Ids)
if l == 0 {
return x.Enabled, nil
}
c.Enabled = x.Enabled
c.IDs = make([]filter.ID, 0, len(x.Ids))
filterLists = make([]agd.FilterListID, 0, l)
for i, f := range x.Ids {
id, err := agd.NewFilterListID(f)
for i, idStr := range x.Ids {
id, err := filter.NewID(idStr)
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
}
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/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/golibs/testutil"
"github.com/c2h5oh/datasize"
@ -35,7 +36,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
EmptyMetrics{},
TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
require.NoError(t, err)
@ -58,14 +60,15 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
savingErrColl,
EmptyMetrics{},
TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
require.NoError(t, err)
testutil.AssertErrorMsg(
t,
`backendpb: bad device settings for device with id "inv-d-ip":`+
" dedicated ips: ip at index 0: unexpected slice size",
`converting device: bad settings for device with id "inv-d-ip":`+
` dedicated ips: ip at index 0: unexpected slice size`,
errCollErr,
)
@ -89,13 +92,14 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
bindSet,
savingErrColl,
EmptyMetrics{},
TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
require.NoError(t, err)
testutil.AssertErrorMsg(
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",
errCollErr,
)
@ -114,7 +118,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
EmptyMetrics{},
TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
testutil.AssertErrorMsg(t, "profile is nil", err)
@ -133,7 +138,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
EmptyMetrics{},
TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
require.NoError(t, err)
@ -155,12 +161,13 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
EmptyMetrics{},
TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
testutil.AssertErrorMsg(
t,
"parental: schedule: loading timezone: unknown time zone invalid",
"parental: pause schedule: loading timezone: unknown time zone invalid",
err,
)
})
@ -179,12 +186,13 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
EmptyMetrics{},
TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
testutil.AssertErrorMsg(
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,
)
})
@ -201,7 +209,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
EmptyMetrics{},
TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
testutil.AssertErrorMsg(t, "blocking mode: bad custom ipv4: unexpected slice size", err)
@ -219,7 +228,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
EmptyMetrics{},
TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
testutil.AssertErrorMsg(t, "blocking mode: bad custom ipv6: unexpected slice size", err)
@ -238,7 +248,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
EmptyMetrics{},
TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
testutil.AssertErrorMsg(t, "blocking mode: no valid custom ips found", err)
@ -255,7 +266,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
EmptyMetrics{},
TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
require.NoError(t, err)
@ -279,7 +291,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
EmptyMetrics{},
TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
require.NoError(t, err)
@ -302,7 +315,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
EmptyMetrics{},
TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
require.NoError(t, err)
@ -464,6 +478,7 @@ func NewTestDNSProfile(tb testing.TB) (dp *DNSProfile) {
Prefix: 24,
}},
},
BlockChromePrefetch: true,
}
}
@ -484,35 +499,37 @@ func newProfile(tb testing.TB) (p *agd.Profile) {
wantLoc, err := agdtime.LoadLocation("GMT")
require.NoError(tb, err)
dayRange := agd.DayRange{
dayIvl := &filter.DayInterval{
Start: 0,
End: 59,
End: 60,
}
wantParental := &agd.ParentalProtectionSettings{
Schedule: &agd.ParentalProtectionSchedule{
Week: &agd.WeeklySchedule{
agd.ZeroLengthDayRange(),
dayRange,
dayRange,
dayRange,
dayRange,
dayRange,
agd.ZeroLengthDayRange(),
wantParental := &filter.ConfigParental{
PauseSchedule: &filter.ConfigSchedule{
Week: &filter.WeeklySchedule{
nil,
dayIvl,
dayIvl,
dayIvl,
dayIvl,
dayIvl,
nil,
},
TimeZone: wantLoc,
},
BlockedServices: []agd.BlockedServiceID{"youtube"},
BlockedServices: []filter.BlockedServiceID{
"youtube",
},
Enabled: false,
BlockAdult: false,
GeneralSafeSearch: false,
YoutubeSafeSearch: false,
AdultBlockingEnabled: false,
SafeSearchGeneralEnabled: false,
SafeSearchYouTubeEnabled: false,
}
wantSafeBrowsing := &agd.SafeBrowsingSettings{
wantSafeBrowsing := &filter.ConfigSafeBrowsing{
Enabled: true,
BlockDangerousDomains: true,
BlockNewlyRegisteredDomains: false,
DangerousDomainsEnabled: true,
NewlyRegisteredDomainsEnabled: false,
}
wantBlockingMode := &dnsmsg.BlockingModeCustomIP{
@ -535,30 +552,39 @@ func newProfile(tb testing.TB) (p *agd.Profile) {
}, 1*datasize.KB)
return &agd.Profile{
Parental: wantParental,
BlockingMode: wantBlockingMode,
ID: TestProfileID,
FilterConfig: &filter.ConfigClient{
Custom: &filter.ConfigCustom{
ID: TestProfileIDStr,
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{
TestDeviceID,
"2222bbbb",
"3333cccc",
"4444dddd",
},
RuleListIDs: []agd.FilterListID{"1"},
CustomRules: []agd.FilterRuleText{"||example.org^"},
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,
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,
TestBind,
errColl,
EmptyMetrics{},
TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
}
@ -639,10 +666,10 @@ func BenchmarkDNSProfile_ToInternal(b *testing.B) {
require.NotNil(b, profSink)
require.NoError(b, errSink)
// Most recent result, on a ThinkPad X13:
// Most recent results:
// goos: linux
// goarch: amd64
// pkg: github.com/AdguardTeam/AdGuardDNS/internal/backendpb
// 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.
ErrColl errcoll.Interface
// Logger is used as the base logger for the profile storage. It must not
// be nil.
// Logger is used for logging the operation of the profile storage. It must
// not be nil.
Logger *slog.Logger
// Metrics is used for the collection of the protobuf errors.
Metrics Metrics
// GRPCMetrics is used for the collection of the protobuf communication
// 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
// "grpcs". It must not be nil.
@ -63,7 +67,8 @@ type ProfileStorage struct {
errColl errcoll.Interface
client DNSServiceClient
logger *slog.Logger
metrics Metrics
grpcMetrics GRPCMetrics
metrics ProfileDBMetrics
apiKey string
respSzEst datasize.ByteSize
maxProfSize datasize.ByteSize
@ -83,6 +88,7 @@ func NewProfileStorage(c *ProfileStorageConfig) (s *ProfileStorage, err error) {
errColl: c.ErrColl,
client: NewDNSServiceClient(client),
logger: c.Logger,
grpcMetrics: c.GRPCMetrics,
metrics: c.Metrics,
apiKey: c.APIKey,
respSzEst: c.ResponseSizeEstimate,
@ -115,7 +121,7 @@ func (s *ProfileStorage) CreateAutoDevice(
DeviceType: DeviceType(req.DeviceType),
})
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)
@ -140,7 +146,7 @@ func (s *ProfileStorage) Profiles(
respSzOpt := grpc.MaxCallRecvMsgSize(int(s.maxProfSize.Bytes()))
stream, err := s.client.GetDNSProfiles(ctx, toProtobuf(req), respSzOpt)
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()) }()
@ -165,7 +171,7 @@ func (s *ProfileStorage) Profiles(
return nil, fmt.Errorf(
"receiving profile #%d: %w",
n,
fixGRPCError(ctx, s.metrics, profErr),
fixGRPCError(ctx, s.grpcMetrics, profErr),
)
}
stats.endRecv()
@ -176,11 +182,12 @@ func (s *ProfileStorage) Profiles(
time.Now(),
s.bindSet,
s.errColl,
s.logger,
s.metrics,
s.respSzEst,
)
if profErr != nil {
reportf(ctx, s.errColl, "loading profile: %w", profErr)
errcoll.Collect(ctx, s.errColl, s.logger, "loading profile", profErr)
continue
}

View File

@ -13,7 +13,6 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/AdGuardDNS/internal/backendpb"
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/c2h5oh/datasize"
"github.com/stretchr/testify/assert"
@ -69,8 +68,9 @@ func TestProfileStorage_CreateAutoDevice(t *testing.T) {
s, err := backendpb.NewProfileStorage(&backendpb.ProfileStorageConfig{
BindSet: backendpb.TestBind,
ErrColl: agdtest.NewErrorCollector(),
Logger: slogutil.NewDiscardLogger(),
Metrics: backendpb.EmptyMetrics{},
Logger: backendpb.TestLogger,
GRPCMetrics: backendpb.EmptyGRPCMetrics{},
Metrics: backendpb.EmptyProfileDBMetrics{},
Endpoint: &url.URL{
Scheme: "grpc",
Host: l.Addr().String(),
@ -159,8 +159,9 @@ func BenchmarkProfileStorage_Profiles(b *testing.B) {
s, err := backendpb.NewProfileStorage(&backendpb.ProfileStorageConfig{
BindSet: netip.MustParsePrefix("0.0.0.0/0"),
ErrColl: agdtest.NewErrorCollector(),
Logger: slogutil.NewDiscardLogger(),
Metrics: backendpb.EmptyMetrics{},
Logger: backendpb.TestLogger,
GRPCMetrics: backendpb.EmptyGRPCMetrics{},
Metrics: backendpb.EmptyProfileDBMetrics{},
Endpoint: &url.URL{
Scheme: "grpc",
Host: l.Addr().String(),
@ -195,8 +196,10 @@ func BenchmarkProfileStorage_Profiles(b *testing.B) {
require.NoError(b, errSink)
require.NotNil(b, respSink)
// goos: darwin
// goarch: arm64
// Most recent results:
// goos: linux
// goarch: amd64
// 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.
Logger *slog.Logger
// GRPCMetrics is used for the collection of the protobuf errors.
GRPCMetrics Metrics
// GRPCMetrics is used for the collection of the protobuf communication
// statistics.
GRPCMetrics GRPCMetrics
// Metrics is used to collect allowlist statistics.
Metrics consul.Metrics
@ -44,7 +45,7 @@ type RateLimiterConfig struct {
// that retrieves the rate limit settings from the business logic backend.
type RateLimiter struct {
logger *slog.Logger
grpcMetrics Metrics
grpcMetrics GRPCMetrics
metrics consul.Metrics
allowlist *ratelimit.DynamicAllowlist
errColl errcoll.Interface
@ -92,7 +93,7 @@ func (l *RateLimiter) Refresh(ctx context.Context) (err error) {
}
allowedSubnets := backendResp.AllowedSubnets
prefixes := cidrRangeToInternal(ctx, l.errColl, allowedSubnets)
prefixes := cidrRangeToInternal(ctx, l.errColl, l.logger, allowedSubnets)
l.allowlist.Update(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/consul"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -83,9 +82,9 @@ func TestRateLimiter_Refresh(t *testing.T) {
allowlist := ratelimit.NewDynamicAllowlist(nil, nil)
l, err := backendpb.NewRateLimiter(&backendpb.RateLimiterConfig{
Logger: slogutil.NewDiscardLogger(),
Logger: backendpb.TestLogger,
Metrics: consul.EmptyMetrics{},
GRPCMetrics: backendpb.EmptyMetrics{},
GRPCMetrics: backendpb.EmptyGRPCMetrics{},
Allowlist: allowlist,
Endpoint: &url.URL{
Scheme: "grpc",

View File

@ -7,18 +7,20 @@ import (
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/remotekv"
"github.com/AdguardTeam/golibs/errors"
"google.golang.org/protobuf/types/known/durationpb"
)
// RemoteKVConfig is the configuration for the business logic backend key-value
// storage.
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.
//
// 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
GRPCMetrics GRPCMetrics
// Endpoint is the backend API URL. The scheme should be either "grpc" or
// "grpcs".
@ -35,7 +37,8 @@ type RemoteKVConfig struct {
// uses the business logic backend as the key-value storage. It is safe for
// concurrent use.
type RemoteKV struct {
metrics Metrics
grpcMetrics GRPCMetrics
metrics RemoteKVMetrics
client RemoteKVServiceClient
apiKey string
ttl time.Duration
@ -51,6 +54,7 @@ func NewRemoteKV(c *RemoteKVConfig) (kv *RemoteKV, err error) {
}
return &RemoteKV{
grpcMetrics: c.GRPCMetrics,
metrics: c.Metrics,
client: NewRemoteKVServiceClient(client),
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)
start := time.Now()
resp, err := kv.client.Get(ctx, req)
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.
@ -87,10 +111,14 @@ func (kv *RemoteKV) Set(ctx context.Context, key string, val []byte) (err error)
}
ctx = ctxWithAuthentication(ctx, kv.apiKey)
start := time.Now()
_, err = kv.client.Set(ctx, req)
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
}

View File

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

View File

@ -54,7 +54,7 @@ func (s *profilesCallStats) endDec() {
}
// 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
if s.isFullSync {
lvl = slog.LevelInfo

View File

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

View File

@ -70,7 +70,12 @@ func listenControlWithSO(
opts,
newIntSetSockOptFunc("IP_RECVORIGDSTADDR", unix.IPPROTO_IP, unix.IP_RECVORIGDSTADDR, 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),
)
default:

View File

@ -6,14 +6,9 @@ import (
"log/slog"
"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/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/AdguardTeam/golibs/timeutil"
)
@ -91,24 +86,3 @@ func initProfDB(
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/agdcache"
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/backendpb"
"github.com/AdguardTeam/AdGuardDNS/internal/billstat"
"github.com/AdguardTeam/AdGuardDNS/internal/bindtodevice"
@ -32,6 +33,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnssvc"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"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/geoip"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
@ -57,6 +59,7 @@ const (
debugIDProfileDB = "profiledb"
debugIDRuleStat = "rulestat"
debugIDTicketRotator = "ticket_rotator"
debugIDTLSConfig = "tlsconfig"
debugIDWebSvc = "websvc"
)
@ -76,6 +79,7 @@ type builder struct {
cacheManager *agdcache.DefaultManager
cloner *dnsmsg.Cloner
conf *configuration
debugRefrs debugsvc.Refreshers
env *environment
errColl errcoll.Interface
geoIPError chan error
@ -91,7 +95,7 @@ type builder struct {
access *access.Global
adultBlocking *hashprefix.Filter
adultBlockingHashes *hashprefix.Storage
backendGRPCMtrc *metrics.BackendPB
backendGRPCMtrc *metrics.BackendGRPC
billStat billstat.Recorder
bindSet netutil.SubnetSet
btdManager *bindtodevice.Manager
@ -100,7 +104,8 @@ type builder struct {
dnsCheck dnscheck.Interface
dnsDB dnsdb.Interface
dnsSvc *dnssvc.Service
filterStorage *filter.DefaultStorage
filterMtrc filter.Metrics
filterStorage *filterstorage.Default
filteringGroups map[agd.FilteringGroupID]*agd.FilteringGroup
fwdHandler *forward.Handler
geoIP *geoip.File
@ -110,12 +115,11 @@ type builder struct {
newRegDomainsHashes *hashprefix.Storage
profileDB profiledb.Interface
rateLimit *ratelimit.Backoff
debugRefrs debugsvc.Refreshers
ruleStat rulestat.Interface
safeBrowsing *hashprefix.Filter
safeBrowsingHashes *hashprefix.Storage
sdeConf *dnsmsg.StructuredDNSErrorsConfig
tlsMtrc tlsconfig.Metrics
tlsManager *tlsconfig.DefaultManager
webSvc *websvc.Service
// 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,
ASNPath: asn,
CountryPath: ctry,
HostCacheSize: c.HostCacheSize,
IPCacheSize: c.IPCacheSize,
HostCacheCount: c.HostCacheSize,
IPCacheCount: c.IPCacheSize,
AllTopASNs: geoip.DefaultTopASNs,
CountryTopASNs: geoip.DefaultCountryTopASNs,
})
@ -225,6 +229,11 @@ func (b *builder) initHashPrefixFilters(ctx context.Context) (err error) {
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.
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
// also adds the refresher with ID
// [hashprefix.IDPrefix]/[agd.FilterListIDAdultBlocking] to the debug
// refreshers.
// [hashprefix.IDPrefix]/[filter.IDAdultBlocking] to the debug refreshers.
//
// It must be called from [builder.initHashPrefixFilters].
func (b *builder) initAdultBlocking(
@ -272,7 +280,7 @@ func (b *builder) initAdultBlocking(
}
c := b.conf.AdultBlocking
id := agd.FilterListIDAdultBlocking
id := filter.IDAdultBlocking
prefix := path.Join(hashprefix.IDPrefix, string(id))
b.adultBlocking, err = hashprefix.NewFilter(&hashprefix.FilterConfig{
Logger: b.baseLogger.With(slogutil.KeyPrefix, prefix),
@ -281,6 +289,7 @@ func (b *builder) initAdultBlocking(
Hashes: b.adultBlockingHashes,
URL: &b.env.AdultBlockingURL.URL,
ErrColl: b.errColl,
Metrics: b.filterMtrc,
ID: id,
CachePath: filepath.Join(cacheDir, string(id)),
ReplacementHost: c.BlockHost,
@ -289,7 +298,7 @@ func (b *builder) initAdultBlocking(
CacheTTL: c.CacheTTL.Duration,
// TODO(a.garipov): Make all sizes [datasize.ByteSize] and rename cache
// entity counts to fooCount.
CacheSize: c.CacheSize,
CacheCount: c.CacheSize,
MaxSize: maxSize,
})
if err != nil {
@ -327,8 +336,7 @@ func (b *builder) initAdultBlocking(
// initNewRegDomains initializes the newly-registered domain filter and hash
// storage. It also adds the refresher with ID
// [hashprefix.IDPrefix]/[agd.FilterListIDNewRegDomains] to the debug
// refreshers.
// [hashprefix.IDPrefix]/[filter.IDNewRegDomains] to the debug refreshers.
//
// It must be called from [builder.initHashPrefixFilters].
func (b *builder) initNewRegDomains(
@ -349,7 +357,7 @@ func (b *builder) initNewRegDomains(
// Reuse the general safe-browsing filter configuration with a new URL and
// ID.
c := b.conf.SafeBrowsing
id := agd.FilterListIDNewRegDomains
id := filter.IDNewRegDomains
prefix := path.Join(hashprefix.IDPrefix, string(id))
b.newRegDomains, err = hashprefix.NewFilter(&hashprefix.FilterConfig{
Logger: b.baseLogger.With(slogutil.KeyPrefix, prefix),
@ -358,13 +366,14 @@ func (b *builder) initNewRegDomains(
Hashes: b.newRegDomainsHashes,
URL: &b.env.NewRegDomainsURL.URL,
ErrColl: b.errColl,
Metrics: b.filterMtrc,
ID: id,
CachePath: filepath.Join(cacheDir, string(id)),
ReplacementHost: c.BlockHost,
Staleness: c.RefreshIvl.Duration,
RefreshTimeout: c.RefreshTimeout.Duration,
CacheTTL: c.CacheTTL.Duration,
CacheSize: c.CacheSize,
CacheCount: c.CacheSize,
MaxSize: maxSize,
})
if err != nil {
@ -399,8 +408,8 @@ func (b *builder) initNewRegDomains(
}
// initSafeBrowsing initializes the safe-browsing filter and hash storage. It
// also adds the refresher with ID
// [hashprefix.IDPrefix]/[agd.FilterListIDSafeBrowsing] to the debug refreshers.
// also adds the refresher with ID [hashprefix.IDPrefix]/[filter.IDSafeBrowsing]
// to the debug refreshers.
//
// It must be called from [builder.initHashPrefixFilters].
func (b *builder) initSafeBrowsing(
@ -420,7 +429,7 @@ func (b *builder) initSafeBrowsing(
}
c := b.conf.SafeBrowsing
id := agd.FilterListIDSafeBrowsing
id := filter.IDSafeBrowsing
prefix := path.Join(hashprefix.IDPrefix, string(id))
b.safeBrowsing, err = hashprefix.NewFilter(&hashprefix.FilterConfig{
Logger: b.baseLogger.With(slogutil.KeyPrefix, prefix),
@ -429,13 +438,14 @@ func (b *builder) initSafeBrowsing(
Hashes: b.safeBrowsingHashes,
URL: &b.env.SafeBrowsingURL.URL,
ErrColl: b.errColl,
Metrics: b.filterMtrc,
ID: id,
CachePath: filepath.Join(cacheDir, string(id)),
ReplacementHost: c.BlockHost,
Staleness: c.RefreshIvl.Duration,
RefreshTimeout: c.RefreshTimeout.Duration,
CacheTTL: c.CacheTTL.Duration,
CacheSize: c.CacheSize,
CacheCount: c.CacheSize,
MaxSize: maxSize,
})
if err != nil {
@ -477,26 +487,78 @@ func (b *builder) initSafeBrowsing(
// [builder.initHashPrefixFilters] must be called before this method.
func (b *builder) initFilterStorage(ctx context.Context) (err error) {
c := b.conf.Filters
b.filterStorage = filter.NewDefaultStorage(c.toInternal(
b.baseLogger,
b.errColl,
b.cacheManager,
b.env,
b.safeBrowsing,
b.adultBlocking,
b.newRegDomains,
))
refrIvl := c.RefreshIvl.Duration
refrTimeout := c.RefreshTimeout.Duration
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 {
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{
Context: newCtxWithTimeoutCons(c.RefreshIvl.Duration),
Context: newCtxWithTimeoutCons(refrTimeout),
Refresher: b.filterStorage,
Logger: b.baseLogger.With(slogutil.KeyPrefix, "filters/storage_refresh"),
Interval: c.RefreshIvl.Duration,
Interval: refrIvl,
RefreshOnShutdown: false,
RandomizeStart: false,
})
@ -514,6 +576,35 @@ func (b *builder) initFilterStorage(ctx context.Context) (err error) {
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.
//
// [builder.initFilterStorage] must be called before this method.
@ -606,26 +697,54 @@ func (b *builder) initMsgConstructor(ctx context.Context) (err error) {
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.
//
// The following methods must be called before this one:
// - [builder.initBindToDevice]
// - [builder.initFilteringGroups]
// - [builder.initMsgConstructor]
// - [builder.initTLSManager]
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
b.serverGroups, err = c.ServerGroups.toInternal(
ctx,
mtrc,
b.messages,
b.btdManager,
b.tlsManager,
b.filteringGroups,
c.RateLimit,
c.DNS,
@ -699,22 +818,12 @@ func (b *builder) startBindToDevice(ctx context.Context) (err error) {
return nil
}
// initTLS initializes the optional TLS key logging and session-ticket rotation.
// It also adds the refresher with ID [debugIDTicketRotator] to the debug
// refreshers.
// initTicketRotator initializes the TLS session ticket rotator. It also adds
// the refresher with ID [debugIDTicketRotator] to the debug refreshers.
//
// [builder.initServerGroups] must be called before this method.
func (b *builder) initTLS(ctx context.Context) (err error) {
if f := b.env.SSLKeyLogFile; f != "" {
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)
// [builder.initTLSManager] must be called before this method.
func (b *builder) initTicketRotator(ctx context.Context) (err error) {
tickRot := agdservice.RefresherFunc(b.tlsManager.RotateTickets)
err = tickRot.Refresh(ctx)
if err != nil {
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.
// [builder.initServerGroups] must be called before this method.
func (b *builder) initGRPCMetrics(ctx context.Context) (err error) {
switch {
case
@ -756,18 +866,19 @@ func (b *builder) initGRPCMetrics(ctx context.Context) (err error) {
return nil
}
b.backendGRPCMtrc, err = metrics.NewBackendPB(b.mtrcNamespace, b.promRegisterer)
b.backendGRPCMtrc, err = metrics.NewBackendGRPC(b.mtrcNamespace, b.promRegisterer)
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
}
// initBillStat initializes the billing-statistics recorder if necessary. It
// 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) {
if !b.profilesEnabled {
b.billStat = billstat.EmptyRecorder{}
@ -775,7 +886,7 @@ func (b *builder) initBillStat(ctx context.Context) (err error) {
return nil
}
upl, err := newBillStatUploader(b.env, b.errColl, b.backendGRPCMtrc)
upl, err := b.newBillStatUploader()
if err != nil {
return fmt.Errorf("creating billstat uploader: %w", err)
}
@ -819,11 +930,28 @@ func (b *builder) initBillStat(ctx context.Context) (err error) {
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.
//
// [builder.initGRPCMetrics] and [builder.initServerGroups] must be called
// before this method. It also adds the refresher with ID [debugIDProfileDB] to
// the debug refreshers.
// [builder.initGRPCMetrics] must be called before this method. It also adds
// the refresher with ID [debugIDProfileDB] to the debug refreshers.
func (b *builder) initProfileDB(ctx context.Context) (err error) {
if !b.profilesEnabled {
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)
}
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
strg, err := backendpb.NewProfileStorage(&backendpb.ProfileStorageConfig{
BindSet: b.bindSet,
ErrColl: b.errColl,
Logger: b.baseLogger.With(slogutil.KeyPrefix, "backendpb"),
Metrics: b.backendGRPCMtrc,
Logger: b.baseLogger.With(slogutil.KeyPrefix, "profilestorage"),
GRPCMetrics: b.backendGRPCMtrc,
Metrics: backendProfileDBMtrc,
Endpoint: apiURL,
APIKey: b.env.ProfilesAPIKey,
ResponseSizeEstimate: respSzEst,
@ -917,6 +1051,7 @@ func (b *builder) initDNSCheck(ctx context.Context) (err error) {
c := b.conf.Check
checkConf, err := c.toInternal(
b.baseLogger,
b.env,
b.messages,
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
// signal handler.
//
// [builder.initServerGroups] must be called before this method.
// signal handler. [builder.initDNSCheck] must be call before this method.
func (b *builder) initWeb(ctx context.Context) (err error) {
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 {
return fmt.Errorf("converting web configuration: %w", err)
}
@ -1142,7 +1275,6 @@ func (b *builder) waitGeoIP(ctx context.Context) (err error) {
// - [builder.initAccess]
// - [builder.initBillStat]
// - [builder.initBindToDevice]
// - [builder.initDNSCheck]
// - [builder.initFilterStorage]
// - [builder.initFilteringGroups]
// - [builder.initMsgConstructor]
@ -1153,7 +1285,7 @@ func (b *builder) waitGeoIP(ctx context.Context) (err error) {
// - [builder.waitGeoIP]
func (b *builder) initDNS(ctx context.Context) (err error) {
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{
BaseLogger: b.baseLogger,

View File

@ -2,10 +2,12 @@ package cmd
import (
"fmt"
"log/slog"
"net/netip"
"strings"
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
"github.com/AdguardTeam/AdGuardDNS/internal/backendpb"
"github.com/AdguardTeam/AdGuardDNS/internal/dnscheck"
@ -17,6 +19,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/remotekv/rediskv"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/timeutil"
"github.com/c2h5oh/datasize"
@ -50,14 +53,15 @@ type checkConfig struct {
// toInternal converts c to the DNS server check configuration for the DNS
// server. c must be valid.
func (c *checkConfig) toInternal(
baseLogger *slog.Logger,
envs *environment,
messages *dnsmsg.Constructor,
errColl errcoll.Interface,
namespace string,
reg prometheus.Registerer,
backendMtrc backendpb.Metrics,
grpcMtrc backendpb.GRPCMetrics,
) (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 {
// Don't wrap the error, because it's informative enough as is.
return nil, err
@ -69,6 +73,7 @@ func (c *checkConfig) toInternal(
}
return &dnscheck.RemoteKVConfig{
Logger: baseLogger.With(slogutil.KeyPrefix, "dnscheck"),
Messages: messages,
RemoteKV: kv,
ErrColl: errColl,
@ -87,33 +92,42 @@ const maxRespSize = 1 * datasize.MB
// [remotekv.KeyNamespace].
const keyNamespaceCheck = "check"
// newRemoteKV returns a new properly initialized remote key-value storage.
func newRemoteKV(
c *remoteKVConfig,
// newRemoteKV returns a new properly initialized remote key-value storage. c
// must be valid. grpcMtrc should be registered before calling this method.
func (c *remoteKVConfig) newRemoteKV(
envs *environment,
namespace string,
reg prometheus.Registerer,
backendMtrc backendpb.Metrics,
grpcMtrc backendpb.GRPCMetrics,
) (kv remotekv.Interface, err error) {
switch c.Type {
case kvModeBackend:
kv, err = backendpb.NewRemoteKV(&backendpb.RemoteKVConfig{
Metrics: backendMtrc,
Endpoint: &envs.DNSCheckRemoteKVURL.URL,
APIKey: envs.DNSCheckRemoteKVAPIKey,
TTL: c.TTL.Duration,
})
var backendKVMtrc *metrics.BackendRemoteKV
backendKVMtrc, err = metrics.NewBackendRemoteKV(namespace, reg)
if err != nil {
return nil, fmt.Errorf("registering backend kv metrics: %w", err)
}
kv, err = c.newBackendRemoteKV(envs, backendKVMtrc, grpcMtrc)
if err != nil {
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:
var redisKVMtrc rediskv.Metrics
var redisKVMtrc *metrics.RedisKV
redisKVMtrc, err = metrics.NewRedisKV(namespace, reg)
if err != nil {
return nil, fmt.Errorf("registering redis kv metrics: %w", err)
}
redisKV := rediskv.NewRedisKV(&rediskv.RedisKVConfig{
kv = rediskv.NewRedisKV(&rediskv.RedisKVConfig{
Metrics: redisKVMtrc,
Addr: &netutil.HostPort{
Host: envs.RedisAddr,
@ -124,12 +138,59 @@ func newRemoteKV(
IdleTimeout: envs.RedisIdleTimeout.Duration,
TTL: c.TTL.Duration,
})
kv = remotekv.NewKeyNamespace(&remotekv.KeyNamespaceConfig{
KV: redisKV,
Prefix: fmt.Sprintf("%s:%s:", envs.RedisKeyPrefix, keyNamespaceCheck),
})
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
consulSessionURL := envs.ConsulDNSCheckSessionURL
if consulKVURL == nil || consulSessionURL == nil {
@ -151,9 +212,6 @@ func newRemoteKV(
if err != nil {
return nil, fmt.Errorf("initializing consul dnscheck kv: %w", err)
}
default:
return remotekv.Empty{}, nil
}
return kv, nil
}
@ -238,6 +296,7 @@ func validateNonNilIPs(ips []netip.Addr, fam netutil.AddrFamily) (err error) {
// DNSCheck key-value database modes.
const (
kvModeBackend = "backend"
kvModeCache = "cache"
kvModeConsul = "consul"
kvModeRedis = "redis"
)
@ -246,7 +305,7 @@ const (
// checking.
type remoteKVConfig struct {
// 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"`
// 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 {
return newNotPositiveError("ttl", ttl)
}
case kvModeCache:
// Go on.
case kvModeConsul:
if ttl.Duration < consulkv.MinTTL || ttl.Duration > consulkv.MaxTTL {
return fmt.Errorf(
@ -288,8 +349,6 @@ func (c *remoteKVConfig) validate() (err error) {
ttl,
)
}
case "":
return fmt.Errorf("type: %w", errors.ErrEmptyValue)
default:
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.initTLSManager(ctx))
errors.Check(b.initServerGroups(ctx))
errors.Check(b.startBindToDevice(ctx))
errors.Check(b.initTLS(ctx))
errors.Check(b.initTicketRotator(ctx))
errors.Check(b.initGRPCMetrics(ctx))

View File

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

View File

@ -1,9 +1,12 @@
package cmd
import (
"log/slog"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsdb"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
)
// 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
// 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 {
return dnsdb.Empty{}
}
db := dnsdb.New(&dnsdb.DefaultConfig{
Logger: baseLogger.With(slogutil.KeyPrefix, "dnsdb"),
ErrColl: errColl,
MaxSize: c.MaxSize,
})

View File

@ -68,6 +68,8 @@ type environment struct {
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"`
RedisMaxIdle int `env:"REDIS_MAX_IDLE" envDefault:"3"`
@ -233,10 +235,12 @@ func (envs *environment) validateFromValidConfig(conf *configuration) (err error
var errs []error
switch typ := conf.Check.RemoteKV.Type; typ {
case kvModeRedis:
errs = envs.validateRedis(errs)
case kvModeBackend:
errs = envs.validateBackendKV(errs)
case kvModeCache:
errs = envs.validateCache(errs)
case kvModeRedis:
errs = envs.validateRedis(errs)
default:
// Probably consul.
}
@ -259,38 +263,51 @@ func (envs *environment) validateFromValidConfig(conf *configuration) (err error
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
// variables for Redis contain errors.
func (envs *environment) validateRedis(errs []error) (withRedis []error) {
withRedis = errs
func (envs *environment) validateRedis(errs []error) (res []error) {
res = errs
if envs.RedisAddr == "" {
err := fmt.Errorf("REDIS_ADDR: %q", errors.ErrEmptyValue)
withRedis = append(withRedis, err)
err := fmt.Errorf("REDIS_ADDR: %w", errors.ErrEmptyValue)
res = append(res, err)
}
if envs.RedisIdleTimeout.Duration <= 0 {
err := newNotPositiveError("REDIS_IDLE_TIMEOUT", envs.RedisIdleTimeout)
withRedis = append(withRedis, err)
res = append(res, err)
}
if envs.RedisMaxActive < 0 {
err := newNegativeError("REDIS_MAX_ACTIVE", envs.RedisMaxActive)
withRedis = append(withRedis, err)
res = append(res, err)
}
if envs.RedisMaxIdle < 0 {
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
// variables for a backend key-value store contain errors.
func (envs *environment) validateBackendKV(errs []error) (withKV []error) {
withKV = errs
func (envs *environment) validateBackendKV(errs []error) (res []error) {
res = errs
var u *url.URL
if envs.DNSCheckRemoteKVURL != nil {
@ -299,16 +316,16 @@ func (envs *environment) validateBackendKV(errs []error) (withKV []error) {
err := urlutil.ValidateGRPCURL(u)
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
// URLs in environment variables are invalid.
func (envs *environment) validateProfilesURLs(errs []error) (withURLs []error) {
withURLs = errs
func (envs *environment) validateProfilesURLs(errs []error) (res []error) {
res = errs
grpcOnlyURLs := []*urlEnvData{{
url: envs.BillStatURL,
@ -332,11 +349,11 @@ func (envs *environment) validateProfilesURLs(errs []error) (withURLs []error) {
err := urlutil.ValidateGRPCURL(u)
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

View File

@ -2,16 +2,8 @@ package cmd
import (
"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/netutil"
"github.com/AdguardTeam/golibs/timeutil"
"github.com/c2h5oh/datasize"
)
@ -27,9 +19,13 @@ type filtersConfig struct {
// CustomFilterCacheSize is the size of the LRU cache of compiled filtering
// engines for profiles with custom filtering rules.
//
// TODO(a.garipov): Rename to "custom_filter_cache_count"?
CustomFilterCacheSize int `yaml:"custom_filter_cache_size"`
// 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"`
// 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"`
}
// 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
var _ validator = (*filtersConfig)(nil)
@ -153,6 +97,8 @@ func (c *filtersConfig) validate() (err error) {
// fltRuleListCache contains filtering rule-list cache configuration.
type fltRuleListCache struct {
// Size defines the size of the LRU cache of rule-list filtering results.
//
// TODO(a.garipov): Rename to "count"?
Size int `yaml:"size"`
// 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 string `yaml:"id"`
// BlockPrivateRelay shows if Apple Private Relay queries are blocked for
// requests using this filtering group.
BlockPrivateRelay bool `yaml:"block_private_relay"`
// BlockChromePrefetch shows if the Chrome prefetch proxy feature should be
// disabled for requests using this filtering group.
BlockChromePrefetch bool `yaml:"block_chrome_prefetch"`
// BlockFirefoxCanary shows if Firefox canary domain is blocked for
// requests using this filtering group.
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
@ -44,6 +48,15 @@ type fltGrpRuleLists struct {
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
// group.
type fltGrpParental struct {
@ -64,6 +77,19 @@ type fltGrpParental struct {
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
// a filtering group.
type fltGrpSafeBrowsing struct {
@ -79,6 +105,16 @@ type fltGrpSafeBrowsing struct {
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
var _ validator = (*filteringGroup)(nil)
@ -87,21 +123,23 @@ func (g *filteringGroup) validate() (err error) {
switch {
case g == nil:
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:
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]()
for i, fltID := range g.RuleLists.IDs {
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 {
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 = make(map[agd.FilteringGroupID]*agd.FilteringGroup, len(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 {
// Assume that these have already been validated in
// [filteringGroup.validate].
id := agd.FilterListID(fltID)
id := filter.ID(fltID)
if !s.HasListID(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)
fltGrps[id] = &agd.FilteringGroup{
FilterConfig: &filter.ConfigGroup{
Parental: g.Parental.toInternal(),
RuleList: g.RuleLists.toInternal(filterIDs),
SafeBrowsing: g.SafeBrowsing.toInternal(),
},
ID: id,
RuleListsEnabled: g.RuleLists.Enabled,
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,
BlockChromePrefetch: g.BlockChromePrefetch,
BlockFirefoxCanary: g.BlockFirefoxCanary,
BlockPrivateRelay: g.BlockPrivateRelay,
}
}
@ -172,7 +207,7 @@ func (groups filteringGroups) validate() (err error) {
}
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)

View File

@ -8,9 +8,13 @@ import (
// geoIPConfig is the GeoIP database configuration.
type geoIPConfig struct {
// 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"`
// 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"`
// RefreshIvl defines how often AdGuard DNS reopens the GeoIP database

View File

@ -1,11 +1,14 @@
package cmd
import (
"crypto/tls"
"fmt"
"net/netip"
"slices"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/bindtodevice"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
@ -14,11 +17,11 @@ import (
// toInternal returns the configuration of DNS servers for a single server
// group. srvs and other parts of the configuration must be valid.
func (srvs servers) toInternal(
mtrc tlsconfig.Metrics,
tlsConfig *agd.TLS,
btdMgr *bindtodevice.Manager,
tlsMgr tlsconfig.Manager,
ratelimitConf *rateLimitConfig,
dnsConf *dnsConfig,
deviceDomains []string,
) (dnsSrvs []*agd.Server, err error) {
dnsSrvs = make([]*agd.Server, 0, len(srvs))
for _, srv := range srvs {
@ -66,18 +69,7 @@ func (srvs servers) toInternal(
QUICLimitsEnabled: ratelimitConf.QUIC.Enabled,
}
tlsConf := tlsConfig.Conf.Clone()
// 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.TLS = newTLSConfig(dnsSrv, tlsMgr, deviceDomains, srv)
}
dnsSrv.SetBindData(bindData)
@ -88,6 +80,35 @@ func (srvs servers) toInternal(
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
// nil items.
type servers []*server
@ -110,7 +131,7 @@ func (srvs servers) validate() (needsTLS bool, err error) {
}
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)
@ -337,11 +358,11 @@ func (c *serverBindInterface) validate() (err error) {
set := container.NewMapSet[netip.Prefix]()
for i, subnet := range c.Subnets {
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) {
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)

View File

@ -20,9 +20,9 @@ type serverGroups []*serverGroup
// service. srvGrps and other parts of the configuration must be valid.
func (srvGrps serverGroups) toInternal(
ctx context.Context,
mtrc tlsconfig.Metrics,
messages *dnsmsg.Constructor,
btdMgr *bindtodevice.Manager,
tlsMgr tlsconfig.Manager,
fltGrps map[agd.FilteringGroupID]*agd.FilteringGroup,
ratelimitConf *rateLimitConfig,
dnsConf *dnsConfig,
@ -35,26 +35,26 @@ func (srvGrps serverGroups) toInternal(
return nil, fmt.Errorf("server group %q: unknown filtering group %q", g.Name, fltGrpID)
}
var tlsConf *agd.TLS
tlsConf, err = g.TLS.toInternal(ctx, mtrc)
var deviceDomains []string
deviceDomains, err = g.TLS.toInternal(ctx, tlsMgr)
if err != nil {
return nil, fmt.Errorf("tls: %w", err)
return nil, fmt.Errorf("tls %q: %w", g.Name, err)
}
svcSrvGrps[i] = &agd.ServerGroup{
DDR: g.DDR.toInternal(messages),
TLS: tlsConf,
DeviceDomains: deviceDomains,
Name: agd.ServerGroupName(g.Name),
FilteringGroup: fltGrpID,
ProfilesEnabled: g.ProfilesEnabled,
}
svcSrvGrps[i].Servers, err = g.Servers.toInternal(
mtrc,
tlsConf,
btdMgr,
tlsMgr,
ratelimitConf,
dnsConf,
deviceDomains,
)
if err != nil {
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) {
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)
@ -148,3 +148,16 @@ func (g *serverGroup) validate() (err error) {
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 (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
@ -39,27 +35,22 @@ type tlsConfig struct {
// valid.
func (c *tlsConfig) toInternal(
ctx context.Context,
mtrc tlsconfig.Metrics,
) (conf *agd.TLS, err error) {
tlsMgr tlsconfig.Manager,
) (deviceDomains []string, err error) {
if c == nil {
return nil, nil
}
tlsConf, err := c.Certificates.toInternal(ctx, mtrc)
err = c.Certificates.store(ctx, tlsMgr)
if err != nil {
return nil, fmt.Errorf("certificates: %w", err)
}
var deviceDomains []string
for _, w := range c.DeviceIDWildcards {
deviceDomains = append(deviceDomains, strings.TrimPrefix(w, "*."))
}
return &agd.TLS{
Conf: tlsConf,
DeviceDomains: deviceDomains,
SessionKeys: c.SessionKeys,
}, nil
return deviceDomains, nil
}
// 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 {
// TODO(e.burkov): Consider removing this requirement.
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) {
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)
@ -125,42 +116,40 @@ type tlsConfigCert struct {
// no nil items.
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(
ctx context.Context,
mtrc tlsconfig.Metrics,
tlsMgr tlsconfig.Manager,
) (conf *tls.Config, err error) {
if len(certs) == 0 {
return nil, nil
}
tlsCerts := make([]tls.Certificate, len(certs))
for i, c := range certs {
var cert tls.Certificate
cert, err = tls.LoadX509KeyPair(c.Certificate, c.Key)
err = certs.store(ctx, tlsMgr)
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
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
return tlsMgr.Clone(), nil
}
// type check
@ -181,24 +170,3 @@ func (certs tlsConfigCerts) validate() (err error) {
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)
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
if c.Healthcheck.Enabled {

View File

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

View File

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

View File

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

View File

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

View File

@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"log/slog"
"net/http"
"net/netip"
"slices"
@ -19,7 +20,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/remotekv/consulkv"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/httphdr"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/miekg/dns"
cache "github.com/patrickmn/go-cache"
@ -28,8 +29,10 @@ import (
// RemoteKV is the RemoteKV KV based DNS checker.
type RemoteKV struct {
// mu protects cache. Don't use an RWMutex here, since the ratio of read
// and write access is expected to be approximately equal.
logger *slog.Logger
// 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
cache *cache.Cache
@ -49,6 +52,9 @@ type RemoteKV struct {
// RemoteKVConfig is the configuration structure for remote KV based DNS
// checker. All fields must be non-empty.
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
// IPv4 and IPv6 IPs.
Messages *dnsmsg.Constructor
@ -85,8 +91,9 @@ const (
)
// 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{
logger: c.Logger,
mu: &sync.Mutex{},
cache: cache.New(defaultCacheExp, defaultCacheGC),
kv: c.RemoteKV,
@ -104,7 +111,7 @@ func NewRemoteKV(c *RemoteKVConfig) (cc *RemoteKV) {
var _ Interface = (*RemoteKV)(nil)
// Check implements the Interface interface for *RemoteKV.
func (cc *RemoteKV) Check(
func (dc *RemoteKV) Check(
ctx context.Context,
req *dns.Msg,
ri *agd.RequestInfo,
@ -124,7 +131,7 @@ func (cc *RemoteKV) Check(
}()
var randomID string
randomID, matched, err = randomIDFromDomain(ri.Host, cc.domains)
randomID, matched, err = randomIDFromDomain(ri.Host, dc.domains)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return nil, err
@ -132,32 +139,32 @@ func (cc *RemoteKV) Check(
// Not a dnscheck domain, just ignore the request.
return nil, nil
} 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)
if err != nil {
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 {
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
// concurrent use.
func (cc *RemoteKV) addToCache(randomID string, inf []byte) {
cc.mu.Lock()
defer cc.mu.Unlock()
func (dc *RemoteKV) addToCache(randomID string, inf []byte) {
dc.mu.Lock()
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
@ -172,7 +179,7 @@ const (
// newInfo returns an information record with all available data about the
// 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
srvType := serverTypePublic
@ -186,8 +193,8 @@ func (cc *RemoteKV) newInfo(ri *agd.RequestInfo) (inf *info) {
ServerType: srvType,
Protocol: ri.Proto.String(),
NodeLocation: cc.nodeLocation,
NodeName: cc.nodeName,
NodeLocation: dc.nodeLocation,
NodeName: dc.nodeName,
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
// 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
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 {
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
var _ http.Handler = (*RemoteKV)(nil)
// 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
log.Debug("dnscheck: http req %s %s from %s", m, p, raddr)
defer log.Debug("dnscheck: finished http req %s %s from %s", m, p, raddr)
//
// TODO(a.garipov): Consider using the websvc logger once it switches to
// log/slog.
func (dc *RemoteKV) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// TODO(a.garipov): Put this into constant here and in package dnssvc.
if r.URL.Path == "/dnscheck/test" {
cc.serveCheckTest(r.Context(), w, r)
dc.serveCheckTest(r.Context(), w, r)
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
// errors and centralize the error handling.
func (cc *RemoteKV) serveCheckTest(ctx context.Context, w http.ResponseWriter, r *http.Request) {
raddr := r.RemoteAddr
func (dc *RemoteKV) serveCheckTest(ctx context.Context, w http.ResponseWriter, r *http.Request) {
l := dc.logger.With("raddr", r.RemoteAddr)
name, err := netutil.SplitHost(r.Host)
host, err := netutil.SplitHost(r.Host)
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)
return
}
randomID, matched, err := randomIDFromDomain(name, cc.domains)
randomID, matched, err := randomIDFromDomain(host, dc.domains)
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)
return
} else if !matched || randomID == "" {
// 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)
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.
if errors.Is(err, consulkv.ErrRateLimited) {
http.Error(w, err.Error(), http.StatusTooManyRequests)
return
} 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)
return
} 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)
@ -295,12 +301,12 @@ func (cc *RemoteKV) serveCheckTest(ctx context.Context, w http.ResponseWriter, r
_, err = w.Write(inf)
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.
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() {
metrics.DNSCheckRequestTotal.With(prometheus.Labels{
"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") }()
cc.mu.Lock()
defer cc.mu.Unlock()
dc.mu.Lock()
defer dc.mu.Unlock()
infoVal, ok := cc.cache.Get(randomID)
infoVal, ok := dc.cache.Get(randomID)
if ok {
return infoVal.([]byte), true, nil
}
inf, ok, err = cc.kv.Get(ctx, randomID)
inf, ok, err = dc.kv.Get(ctx, randomID)
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.
return nil, false, err

View File

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

View File

@ -6,6 +6,7 @@ package dnsdb
import (
"context"
"log/slog"
"sync"
"sync/atomic"
"time"
@ -35,6 +36,7 @@ func (Empty) Record(_ context.Context, _ *dns.Msg, _ *agd.RequestInfo) {}
// Default is the default DNSDB implementation.
type Default struct {
logger *slog.Logger
buffer *atomic.Pointer[buffer]
errColl errcoll.Interface
maxSize int
@ -42,6 +44,9 @@ type Default struct {
// DefaultConfig is the default DNS database configuration structure.
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 errcoll.Interface
@ -52,6 +57,7 @@ type DefaultConfig struct {
// New creates a new default DNS database. c must not be nil.
func New(c *DefaultConfig) (db *Default) {
db = &Default{
logger: c.Logger,
buffer: &atomic.Pointer[buffer]{},
errColl: c.ErrColl,
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() {
if err != nil {
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/dnsserver/dnsservertest"
"github.com/AdguardTeam/golibs/httphdr"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
@ -120,6 +121,7 @@ func TestDefault_ServeHTTP(t *testing.T) {
for _, tc := range testCases {
db := dnsdb.New(&dnsdb.DefaultConfig{
Logger: slogutil.NewDiscardLogger(),
ErrColl: agdtest.NewErrorCollector(),
MaxSize: 100,
})

View File

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

View File

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

View File

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

View File

@ -38,20 +38,30 @@ func CreateServerTLSConfig(tlsServerName string) (tlsConfig *tls.Config) {
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature |
x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
IsCA: true,
}
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 {
panic(fmt.Sprintf("failed to create certificate: %v", err))
}
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)
if err != nil {

View File

@ -1,9 +1,9 @@
module github.com/AdguardTeam/AdGuardDNS/internal/dnsserver
go 1.23.2
go 1.23.4
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/dnsstamps v1.0.3
github.com/bluele/gcache v0.0.2
@ -12,11 +12,11 @@ require (
github.com/panjf2000/ants/v2 v2.10.0
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible
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
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
golang.org/x/net v0.30.0
golang.org/x/sys v0.26.0
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
golang.org/x/net v0.32.0
golang.org/x/sys v0.28.0
)
require (
@ -26,23 +26,23 @@ require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // 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/kr/text v0.2.0 // 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/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/quic-go/qpack v0.5.1 // indirect
go.uber.org/mock v0.5.0 // indirect
golang.org/x/crypto v0.28.0 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/time v0.7.0 // indirect
golang.org/x/tools v0.26.0 // indirect
golang.org/x/crypto v0.30.0 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.8.0 // indirect
golang.org/x/tools v0.28.0 // indirect
google.golang.org/protobuf v1.35.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.1/go.mod h1:FkwcNQEJoGsgDGXcalrVa/4gWbE68KsmE2guXWtBQUE=
github.com/AdguardTeam/golibs v0.30.4 h1:zfFX1v4hkOCz6BifkneiBW2PCwSK523kYNr+VwaFrIw=
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/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/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/pprof v0.0.0-20241023014458-598669927662 h1:SKMkD83p7FwUqKmBsPdLHF5dNyxq3jOWwu9w9UyH5vA=
github.com/google/pprof v0.0.0-20241023014458-598669927662/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/pprof v0.0.0-20241203143554-1e3fdc7de467 h1:keEZFtbLJugfE0qHn+Ge1JCE71spzkchQobDf3mzS/4=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
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/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/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
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/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_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
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.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
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/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/quic-go v0.48.1 h1:y/8xmfWI9qmGTc+lBr4jKRUWLGSlSigv847ULJ4hYXA=
github.com/quic-go/quic-go v0.48.1/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
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/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=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
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.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
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/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY=
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
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.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
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=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
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=
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) {
cacheMiddleware := cache.NewMiddleware(&cache.MiddlewareConfig{
MetricsListener: prometheus.NewCacheMetricsListener(testNamespace),
Size: 100,
Count: 100,
})
handlerWithMiddleware := dnsserver.WithMiddlewares(

View File

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

View File

@ -43,15 +43,15 @@ const (
httpIdleTimeout = 120 * time.Second
)
// nextProtoDoH is a list of ALPN that we would add by default to the server's
// *tls.Config if no NextProto is specified there. Note, that with this order,
// we prioritize HTTP/2 over HTTP/1.1.
var nextProtoDoH = []string{http2.NextProtoTLS, "http/1.1"}
// 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,
// HTTP/2 is prioritized over HTTP/1.1.
var NextProtoDoH = []string{http2.NextProtoTLS, "http/1.1"}
// nextProtoDoH3 is a list of ALPN that we should add by default to the server's
// 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
// used.
var nextProtoDoH3 = []string{http3.NextProtoH3, http2.NextProtoTLS, "http/1.1"}
// used. Note that with this order, HTTP/2 is prioritized over 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
// 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
// server will only use HTTP/2 and NetworkUDP will mean HTTP/3 only.
type ConfigHTTPS struct {
// TLSConfig is the TLS configuration for HTTPS. If not set and
// [ConfigBase.Network] is set to NetworkTCP the server will listen to
// plain HTTP.
TLSConfig *tls.Config
// TLSConfDefault is the TLS configuration for HTTPS. If not set and
// [ConfigBase.Network] is set to NetworkTCP the server will listen to plain
// HTTP. If it is not nil, it must be set to [NextProtoDoH].
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.
// 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)
scheme := urlutil.SchemeHTTPS
if s.conf.TLSConfig == nil {
if s.conf.TLSConfDefault == nil {
scheme = urlutil.SchemeHTTP
}
@ -516,12 +520,9 @@ func (s *ServerHTTPS) listenTLS(ctx context.Context) (err error) {
}
// Prepare the TLS configuration of the server.
tlsConf := s.conf.TLSConfig
tlsConf := s.conf.TLSConfDefault
if tlsConf == nil {
return nil
} else if len(tlsConf.NextProtos) == 0 {
tlsConf = tlsConf.Clone()
tlsConf.NextProtos = nextProtoDoH
}
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.
func (s *ServerHTTPS) listenQUIC(ctx context.Context) (err error) {
// Prepare the TLS configuration of the server.
tlsConf := s.conf.TLSConfig
if tlsConf != nil && len(tlsConf.NextProtos) == 0 {
tlsConf = tlsConf.Clone()
tlsConf.NextProtos = nextProtoDoH3
}
tlsConf := s.conf.TLSConfH3
conn, err := s.listenConfig.ListenPacket(ctx, "udp", s.addr)
if err != nil {

View File

@ -307,7 +307,8 @@ func TestDNSMsgToJSONMsg(t *testing.T) {
Type: dns.TypeHTTPS,
Class: dns.ClassINET,
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)
require.Equal(t, []dnsserver.JSONAnswer{{
Name: "example.org",

View File

@ -62,10 +62,16 @@ const (
// compatProtoDQ are ALPNs for backwards compatibility.
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
// initialize a new ServerQUIC instance.
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
ConfigBase
@ -111,12 +117,6 @@ const quicBytePoolSize = dns.MaxMsgSize
// NewServerQUIC creates a new ServerQUIC instance.
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 {
// Do not enable OOB here as quic-go will do that on its own.
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) {
tlsConfig := dnsservertest.CreateServerTLSConfig("example.org")
tlsConfig.NextProtos = dnsserver.NextProtoDoQ
srv, addr, err := dnsservertest.RunLocalQUICServer(
dnsservertest.NewDefaultHandler(),
tlsConfig,

View File

@ -347,14 +347,15 @@ func NewListener(
case agd.ProtoDoH:
l = dnsserver.NewServerHTTPS(dnsserver.ConfigHTTPS{
ConfigBase: baseConf,
TLSConfig: s.TLS,
TLSConfDefault: s.TLS.Default,
TLSConfH3: s.TLS.H3,
NonDNSHandler: nonDNS,
MaxStreamsPerPeer: quicConf.MaxStreamsPerPeer,
QUICLimitsEnabled: quicConf.QUICLimitsEnabled,
})
case agd.ProtoDoQ:
l = dnsserver.NewServerQUIC(dnsserver.ConfigQUIC{
TLSConfig: s.TLS,
TLSConfig: s.TLS.Default,
ConfigBase: baseConf,
MaxStreamsPerPeer: quicConf.MaxStreamsPerPeer,
QUICLimitsEnabled: quicConf.QUICLimitsEnabled,
@ -369,7 +370,7 @@ func NewListener(
MaxPipelineCount: tcpConf.MaxPipelineCount,
TCPIdleTimeout: tcpConf.IdleTimeout,
},
TLSConfig: s.TLS,
TLSConfig: s.TLS.Default,
})
default:
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{
// TODO(a.garipov): Do not use promauto and refactor.
MetricsListener: dnssrvprom.NewCacheMetricsListener(metrics.Namespace()),
Size: conf.NoECSCount,
Count: conf.NoECSCount,
MinTTL: conf.MinTTL,
OverrideTTL: conf.OverrideCacheTTL,
})
@ -146,7 +146,10 @@ func newMainMiddlewareMetrics(c *HandlersConfig) (mainMwMtrc MainMiddlewareMetri
// newHandlersForServers returns a handler map for each server group and each
// server.
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 {
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,
HumanIDParser: c.HumanIDParser,
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{
dnssvctest.FilteringGroupID: {
ID: dnssvctest.FilteringGroupID,
RuleListIDs: []agd.FilterListID{dnssvctest.FilterListID1},
RuleListsEnabled: 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,
}
fltGrps := map[agd.FilteringGroupID]*agd.FilteringGroup{
dnssvctest.FilteringGroupID: fltGrp,
}
fltStrg := &agdtest.FilterStorage{
OnFilterFromContext: func(_ context.Context, _ *agd.RequestInfo) (f filter.Interface) {
OnForConfig: func(_ context.Context, _ filter.Config) (f filter.Interface) {
panic("not implemented")
},
OnHasListID: func(_ agd.FilterListID) (ok bool) { panic("not implemented") },
OnHasListID: func(_ filter.ID) (ok bool) { panic("not implemented") },
}
hashMatcher := &agdtest.HashMatcher{
@ -91,7 +99,7 @@ func TestNewHandlers(t *testing.T) {
}
ruleStat := &agdtest.RuleStat{
OnCollect: func(_ context.Context, _ agd.FilterListID, _ agd.FilterRuleText) {
OnCollect: func(_ context.Context, _ filter.ID, _ filter.RuleText) {
panic("not implemented")
},
}
@ -104,7 +112,6 @@ func TestNewHandlers(t *testing.T) {
DDR: &agd.DDR{
Enabled: true,
},
TLS: &agd.TLS{},
Name: dnssvctest.ServerGroupName,
FilteringGroup: dnssvctest.FilteringGroupID,
Servers: []*agd.Server{srv},

View File

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

View File

@ -408,7 +408,12 @@ func BenchmarkDefault(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
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)

View File

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

View File

@ -29,6 +29,12 @@ const (
// Resolvers for querying the resolver with unknown or absent name.
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
// use its own DNS-over-HTTPS settings.
//
@ -251,6 +257,10 @@ func (mw *Middleware) specialDomainHandler(
if shouldBlockPrivateRelay(ri, prof) {
return mw.handlePrivateRelay, "apple_private_relay"
}
case ChromePrefetchHost:
if shouldBlockChromePrefetch(ri, prof) {
return mw.handleChromePrefetch, "chrome_prefetch"
}
case FirefoxCanaryHost:
if shouldBlockFirefoxCanary(ri, prof) {
return mw.handleFirefoxCanary, "firefox"
@ -262,36 +272,34 @@ func (mw *Middleware) specialDomainHandler(
return nil, ""
}
// shouldBlockPrivateRelay returns true if the query is for an Apple Private
// Relay check domain and the request information or profile indicates that
// Apple Private Relay should be blocked.
func shouldBlockPrivateRelay(ri *agd.RequestInfo, prof *agd.Profile) (ok bool) {
// shouldBlockChromePrefetch returns true request information or profile
// indicate that the Chrome prefetch domain should be blocked.
func shouldBlockChromePrefetch(ri *agd.RequestInfo, prof *agd.Profile) (ok bool) {
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
// response.
func (mw *Middleware) handlePrivateRelay(
// handleChromePrefetch responds to Chrome prefetch domain queries with an
// NXDOMAIN response.
func (mw *Middleware) handleChromePrefetch(
ctx context.Context,
rw dnsserver.ResponseWriter,
req *dns.Msg,
ri *agd.RequestInfo,
) (err error) {
metrics.DNSSvcApplePrivateRelayRequestsTotal.Inc()
metrics.DNSSvcChromePrefetchRequestsTotal.Inc()
resp := ri.Messages.NewRespRCode(req, dns.RcodeNameError)
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
// domain and the request information or profile indicates that Firefox canary
// domain should be blocked.
// shouldBlockFirefoxCanary returns true request information or profile indicate
// that the Firefox canary domain should be blocked.
func shouldBlockFirefoxCanary(ri *agd.RequestInfo, prof *agd.Profile) (ok bool) {
if prof != nil {
return prof.BlockFirefoxCanary
@ -315,3 +323,29 @@ func (mw *Middleware) handleFirefoxCanary(
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 (
profAllowed = &agd.Profile{
Access: access.EmptyProfile{},
BlockPrivateRelay: false,
BlockChromePrefetch: false,
BlockFirefoxCanary: false,
BlockPrivateRelay: false,
}
profBlocked = &agd.Profile{
Access: access.EmptyProfile{},
BlockPrivateRelay: true,
BlockChromePrefetch: true,
BlockFirefoxCanary: true,
BlockPrivateRelay: true,
}
)
var (
fltGrpAllowed = &agd.FilteringGroup{
BlockPrivateRelay: false,
BlockChromePrefetch: false,
BlockFirefoxCanary: false,
BlockPrivateRelay: false,
}
fltGrpBlocked = &agd.FilteringGroup{
BlockPrivateRelay: true,
BlockChromePrefetch: true,
BlockFirefoxCanary: true,
BlockPrivateRelay: true,
}
)
const (
appleHost = initial.ApplePrivateRelayMaskHost
chromeHost = initial.ChromePrefetchHost
firefoxHost = initial.FirefoxCanaryHost
)
@ -100,29 +105,27 @@ func TestMiddleware_Wrap_specialDomain(t *testing.T) {
reqInfo: newSpecDomReqInfo(t, nil, fltGrpBlocked, firefoxHost, dns.TypeA),
name: "firefox_canary_blocked_by_fltgrp",
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 {
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{
Logger: slogutil.NewDiscardLogger(),
})
h := mw.Wrap(handler)
h := mw.Wrap(newSpecDomHandler(tc.wantRCode == dns.RcodeSuccess))
ctx := testutil.ContextWithTimeout(t, dnssvctest.Timeout)
ctx = agd.ContextWithRequestInfo(ctx, tc.reqInfo)
@ -185,3 +188,21 @@ func newSpecDomReqInfo(
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 {
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)

View File

@ -7,6 +7,7 @@ import (
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/miekg/dns"
)
@ -57,9 +58,12 @@ func (mw *Middleware) filterRequest(
) {
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 {
mw.reportf(ctx, "filtering request: %w", err)
errcoll.Collect(ctx, mw.errColl, mw.logger, "filtering request", err)
}
if mod, ok := reqRes.(*filter.ResultModifiedRequest); ok {
@ -70,6 +74,38 @@ func (mw *Middleware) filterRequest(
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.
// 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
@ -95,9 +131,12 @@ func (mw *Middleware) filterResponse(
var rr dns.RR = ri.Messages.NewAnswerCNAME(origReq, modReq.Question[0].Name)
origResp.Answer = slices.Insert(origResp.Answer, 0, rr)
} 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 {
mw.reportf(ctx, "filtering response: %w", err)
errcoll.Collect(ctx, mw.errColl, mw.logger, "filtering response", err)
}
fctx.responseResult = respRes
@ -106,11 +145,41 @@ func (mw *Middleware) filterResponse(
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
// from the filtering context.
func filteringData(
fctx *filteringContext,
) (id agd.FilterListID, text agd.FilterRuleText, blocked bool) {
) (id filter.ID, text filter.RuleText, blocked bool) {
if fctx.requestResult != nil {
return resultData(fctx.requestResult, "reqRes")
}
@ -123,9 +192,9 @@ func filteringData(
func resultData(
res filter.Result,
argName string,
) (id agd.FilterListID, text agd.FilterRuleText, blocked bool) {
) (id filter.ID, text filter.RuleText, blocked bool) {
if res == nil {
return agd.FilterListIDNone, "", false
return filter.IDNone, "", false
}
id, text = res.MatchedRule()
@ -164,7 +233,13 @@ func (mw *Middleware) setFilteredResponse(
var err error
fctx.filteredResponse, err = ri.Messages.NewBlockedResp(fctx.originalRequest)
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
}
case *filter.ResultAllowed, *filter.ResultModifiedRequest:
@ -201,7 +276,13 @@ func (mw *Middleware) setFilteredResponseNoReq(
var err error
fctx.filteredResponse, err = ri.Messages.NewBlockedResp(fctx.originalRequest)
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
}
default:

View File

@ -27,6 +27,8 @@ import (
type Middleware struct {
cloner *dnsmsg.Cloner
fltCtxPool *syncutil.Pool[filteringContext]
fltReqPool *syncutil.Pool[filter.Request]
fltRespPool *syncutil.Pool[filter.Response]
logger *slog.Logger
messages *dnsmsg.Constructor
billStat billstat.Recorder
@ -84,6 +86,12 @@ func New(c *Config) (mw *Middleware) {
fltCtxPool: syncutil.NewPool(func() (v *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,
messages: c.Messages,
billStat: c.BillStat,
@ -125,7 +133,7 @@ func (mw *Middleware) Wrap(next dnsserver.Handler) (wrapped dnsserver.Handler) {
"remote_ip", ri.RemoteIP,
)
flt := mw.fltStrg.FilterFromContext(ctx, ri)
flt := mw.filter(ctx, ri)
mw.filterRequest(ctx, fctx, flt, ri)
// 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)
}
// 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
// with taking the filtering context into account.
func (mw *Middleware) nextParams(
@ -239,8 +261,3 @@ func (mw *Middleware) reportMetrics(
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"
testRewriteAddrStr = "7.8.9.0"
testRuleAllow agd.FilterRuleText = "@@||" + dnssvctest.DomainAllowed + "^"
testRuleBlockReq agd.FilterRuleText = "||" + dnssvctest.DomainBlocked + "^"
testRuleBlockResp agd.FilterRuleText = "||" + testRespAddr4Str + "^"
testRuleRewrite agd.FilterRuleText = "||" + dnssvctest.DomainRewritten +
testRuleAllow filter.RuleText = "@@||" + dnssvctest.DomainAllowed + "^"
testRuleBlockReq filter.RuleText = "||" + dnssvctest.DomainBlocked + "^"
testRuleBlockResp filter.RuleText = "||" + testRespAddr4Str + "^"
testRuleRewrite filter.RuleText = "||" + dnssvctest.DomainRewritten +
"^$dnsrewrite=NOERROR;A;" + testRewriteAddrStr
testRuleRewriteCNAME agd.FilterRuleText = "||" + dnssvctest.DomainRewritten +
testRuleRewriteCNAME filter.RuleText = "||" + dnssvctest.DomainRewritten +
"^$dnsrewrite=NOERROR;CNAME;" + dnssvctest.DomainRewrittenCNAME
)
@ -96,25 +96,23 @@ func TestMiddleware_Wrap(t *testing.T) {
flt := &agdtest.Filter{
OnFilterRequest: func(
_ context.Context,
_ *dns.Msg,
_ *agd.RequestInfo,
_ *filter.Request,
) (r filter.Result, err error) {
return nil, nil
},
OnFilterResponse: func(
_ context.Context,
_ *dns.Msg,
_ *agd.RequestInfo,
_ *filter.Response,
) (r filter.Result, err error) {
return nil, nil
},
}
fltStrg := &agdtest.FilterStorage{
OnFilterFromContext: func(_ context.Context, _ *agd.RequestInfo) (f filter.Interface) {
OnForConfig: func(_ context.Context, _ filter.Config) (f filter.Interface) {
return flt
},
OnHasListID: func(_ agd.FilterListID) (ok bool) { panic("not implemented") },
OnHasListID: func(_ filter.ID) (ok bool) { panic("not implemented") },
}
geoIP := agdtest.NewGeoIP()
@ -131,10 +129,10 @@ func TestMiddleware_Wrap(t *testing.T) {
}
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{}
require.Equal(pt, agd.FilterListID(""), id)
require.Equal(pt, agd.FilterRuleText(""), text)
require.Equal(pt, filter.ID(""), id)
require.Equal(pt, filter.RuleText(""), text)
},
}
@ -238,7 +236,10 @@ func TestMiddleware_Wrap(t *testing.T) {
h := mw.Wrap(newSimpleHandler(t, tc.req, wantResp))
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)
testutil.AssertErrorMsg(t, tc.wantErrMsg, serveErr)
@ -332,6 +333,19 @@ func newContext(
ctx = dnsserver.ContextWithRequestInfo(ctx, &dnsserver.RequestInfo{
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{
DeviceResult: &agd.DeviceResultOK{
Device: d,
@ -342,11 +356,7 @@ func newContext(
ASN: testASN,
},
FilteringGroup: &agd.FilteringGroup{
RuleListIDs: []agd.FilterListID{
dnssvctest.FilterListID1,
dnssvctest.FilterListID2,
},
RuleListsEnabled: true,
FilterConfig: fltConf,
},
Messages: agdtest.NewConstructor(tb),
RemoteIP: dnssvctest.ClientAddr,
@ -540,7 +550,7 @@ func TestMiddleware_Wrap_filtering(t *testing.T) {
respRes filter.Result
name string
wantErrMsg string
wantRule agd.FilterRuleText
wantRule filter.RuleText
}{{
req: reqAllow,
device: nil,
@ -641,28 +651,23 @@ func TestMiddleware_Wrap_filtering(t *testing.T) {
flt := &agdtest.Filter{
OnFilterRequest: func(
_ context.Context,
_ *dns.Msg,
_ *agd.RequestInfo,
_ *filter.Request,
) (r filter.Result, err error) {
return tc.reqRes, nil
},
OnFilterResponse: func(
_ context.Context,
_ *dns.Msg,
_ *agd.RequestInfo,
_ *filter.Response,
) (r filter.Result, err error) {
return tc.respRes, nil
},
}
fltStrg := &agdtest.FilterStorage{
OnFilterFromContext: func(
_ context.Context,
_ *agd.RequestInfo,
) (f filter.Interface) {
OnForConfig: func(_ context.Context, _ filter.Config) (f filter.Interface) {
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]
@ -680,7 +685,7 @@ func TestMiddleware_Wrap_filtering(t *testing.T) {
}
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{}
require.Equal(pt, dnssvctest.FilterListID1, id)
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))
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)
testutil.AssertErrorMsg(t, tc.wantErrMsg, serveErr)

View File

@ -11,6 +11,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/AdGuardDNS/internal/optslog"
"github.com/AdguardTeam/AdGuardDNS/internal/querylog"
@ -87,7 +88,7 @@ func (mw *Middleware) recordQueryInfo(
err := mw.queryLog.Write(ctx, e)
if err != nil {
// 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)
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
@ -228,7 +229,7 @@ func (mw *Middleware) country(ctx context.Context, host string, ip netip.Addr) (
l, err := mw.geoIP.Data(host, ip)
if err != nil {
// 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 {

View File

@ -42,7 +42,7 @@ func TestMiddleware_Wrap_access(t *testing.T) {
blockedClient2Prefix = netip.MustParsePrefix("2001:db8::/120")
)
accessMgr, errAccess := access.NewGlobal(
accessMgr, accessErr := access.NewGlobal(
[]string{
domainBlockedNormal,
domainBlockedUppercase,
@ -53,7 +53,7 @@ func TestMiddleware_Wrap_access(t *testing.T) {
blockedClient2Prefix,
},
)
require.NoError(t, errAccess)
require.NoError(t, accessErr)
geoIP := agdtest.NewGeoIP()
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,
}}
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))
})
},
)
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {

View File

@ -15,10 +15,10 @@ var msgSink *dns.Msg
func BenchmarkMiddleware_Get(b *testing.B) {
mw := &Middleware{
cache: agdcache.NewLRU[uint64, *cacheItem](&agdcache.LRUConfig{
Size: 10,
Count: 10,
}),
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.
func NewMiddleware(c *MiddlewareConfig) (m *Middleware) {
cache := agdcache.NewLRU[uint64, *cacheItem](&agdcache.LRUConfig{
Size: c.NoECSCount,
Count: c.NoECSCount,
})
ecsCache := agdcache.NewLRU[uint64, *cacheItem](&agdcache.LRUConfig{
Size: c.ECSCount,
Count: c.ECSCount,
})
c.CacheManager.Add(cacheIDNoECS, cache)

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