diff --git a/CHANGELOG.md b/CHANGELOG.md
index b56c719..bd6ffe2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,14 +2,22 @@
All notable environment, configuration file, and other changes to this project will be documented in this file.
-The format is **not** based on [Keep a Changelog][kec], since the project **doesn't** currently adhere to [Semantic Versioning][sem].
+The format is **not** based on [Keep a Changelog][kec], since the project **doesn’t** currently adhere to [Semantic Versioning][sem].
[kec]: https://keepachangelog.com/en/1.0.0/
[sem]: https://semver.org/spec/v2.0.0.html
+## AGDNS-2360 / Build 969
+
+- The environment variable `LOG_FORMAT` has been added.
+
+## AGDNS-1519 / Build 944
+
+- Profiles’ file cache version was incremented.
+
## 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:
+- Profiles’ 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"
@@ -17,7 +25,7 @@ The format is **not** based on [Keep a Changelog][kec], since the project **does
## AGDNS-2327 / Build 916
-- Profile's file cache version was incremented. The new field `BlockChromePrefetch` has been added to profile's object.
+- Profiles’ 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:
@@ -164,7 +172,7 @@ The format is **not** based on [Keep a Changelog][kec], since the project **does
## AGDNS-2331 / Build 818
-- Profile's file cache version was incremented. The new field `RateLimit` has been added to profile's object.
+- Profiles’ file cache version was incremented. The new field `RateLimit` has been added to profile’s object.
## AGDNS-2008 / Build 809
@@ -238,7 +246,7 @@ The format is **not** based on [Keep a Changelog][kec], since the project **does
- The objects within the `server_groups` array had a change in their `block_page_redirect` configuration, it now supports arrays of IP addresses in `ipv4` and `ipv6` fields.
-- Profile's file cache version was incremented. In case of `BlockingModeCustomIP` the `profile.blocking_mode` IPv4/IPv6 fields are now arrays of IP addresses.
+- Profiles’ file cache version was incremented. In case of `BlockingModeCustomIP` the `profile.blocking_mode` IPv4/IPv6 fields are now arrays of IP addresses.
## AGDNS-2012 / Build 732
@@ -246,7 +254,7 @@ The format is **not** based on [Keep a Changelog][kec], since the project **does
## AGDNS-1879 / Build 729
-- Profile's file cache version was incremented. The new field `authentication` has been added to profile's device object.
+- Profiles’ file cache version was incremented. The new field `authentication` has been added to profile’s device object.
## AGDNS-1934 / Build 728
@@ -333,7 +341,7 @@ The format is **not** based on [Keep a Changelog][kec], since the project **does
## AGDNS-1761 / Build 702
-- The property `upstream` has been modified. Its property `timeout` has been replaced with the new property `servers.timeout` for each server in the `servers` list. Concomitantly the `fallback.timeout` has been replaced with `fallback.servers.timeout` for each fallback server. The `fallback.servers` now supports not only the addresses of the servers, but URLs in the `[scheme://]ip:port` format like it's done with the main servers. So replace this:
+- The property `upstream` has been modified. Its property `timeout` has been replaced with the new property `servers.timeout` for each server in the `servers` list. Concomitantly the `fallback.timeout` has been replaced with `fallback.servers.timeout` for each fallback server. The `fallback.servers` now supports not only the addresses of the servers, but URLs in the `[scheme://]ip:port` format like it’s done with the main servers. So replace this:
```yaml
upstream:
@@ -494,7 +502,7 @@ The format is **not** based on [Keep a Changelog][kec], since the project **does
## AGDNS-1684 / Build 661
-- Profile's file cache version was incremented. The new field `access` has been added.
+- Profiles’ file cache version was incremented. The new field `access` has been added.
## AGDNS-1664 / Build 636
@@ -665,7 +673,7 @@ The format is **not** based on [Keep a Changelog][kec], since the project **does
## AGDNS-1556 / Build 547
-- The object `cache` has a new property `ttl_override`. It describes the TTL override settings, such as the minimum TTL for cache items and the `enabled` switch. It overwrites the TTL from DNS response in case it's less than this minimum value. So replace this:
+- The object `cache` has a new property `ttl_override`. It describes the TTL override settings, such as the minimum TTL for cache items and the `enabled` switch. It overwrites the TTL from DNS response in case it’s less than this minimum value. So replace this:
```yaml
cache:
@@ -1137,7 +1145,7 @@ The format is **not** based on [Keep a Changelog][kec], since the project **does
# …
```
- Adjust the values, if necessary. Make sure to synchronize and keep in sync the addresses and ports with the values of the server groups' servers.
+ Adjust the values, if necessary. Make sure to synchronize and keep in sync the addresses and ports with the values of the server groups’ servers.
## AGDNS-624 / Build 317
@@ -1334,7 +1342,7 @@ The format is **not** based on [Keep a Changelog][kec], since the project **does
## AGDNS-344 / Build 226
-- The environment variables `CONSUL_DNSCHECK_KV_URL` and `CONSUL_DNSCHECK_SESSION_URL` are now unset by default. Which means that by default HTTP key-value database isn't used.
+- The environment variables `CONSUL_DNSCHECK_KV_URL` and `CONSUL_DNSCHECK_SESSION_URL` are now unset by default. Which means that by default HTTP key-value database isn’t used.
## AGDNS-431 / Build 211
@@ -1455,7 +1463,7 @@ The format is **not** based on [Keep a Changelog][kec], since the project **does
## AGDNS-305 / Build 114
-- The new required environment variable `BLOCKED_SERVICE_INDEX_URL` has been added. It has no default value, so it's necessary to set it.
+- The new required environment variable `BLOCKED_SERVICE_INDEX_URL` has been added. It has no default value, so it’s necessary to set it.
## AGDNS-319 / Build 113
@@ -1479,7 +1487,7 @@ The format is **not** based on [Keep a Changelog][kec], since the project **does
- The environment variable `CONSUL_URL` has been renamed to `CONSUL_ALLOWLIST_URL`.
-- The new required environment variables `CONSUL_DNSCHECK_KV_URL` and `CONSUL_DNSCHECK_SESSION_URL` are added. They have no default value, so it's necessary to set them.
+- The new required environment variables `CONSUL_DNSCHECK_KV_URL` and `CONSUL_DNSCHECK_SESSION_URL` are added. They have no default value, so it’s necessary to set them.
- The object `check` has a new property, `ttl`. Set it to a human-readable duration, for example `1m`.
@@ -1499,7 +1507,7 @@ The format is **not** based on [Keep a Changelog][kec], since the project **does
The default value is `1`, adjust the value, if necessary.
-- The environment variable `VERBOSE` doesn't support a set but empty value. Unset the value or replace it with a `0`.
+- The environment variable `VERBOSE` doesn’t support a set but empty value. Unset the value or replace it with a `0`.
## AGDNS-295 / Build 105
@@ -1702,7 +1710,7 @@ The format is **not** based on [Keep a Changelog][kec], since the project **does
## AGDNS-139 / Build 73
-- The new required environment variable `CONSUL_URL` has been added. It has no default value, so it's necessary to set it.
+- The new required environment variable `CONSUL_URL` has been added. It has no default value, so it’s necessary to set it.
- The ratelimit configuration for a server has changed from this:
@@ -1741,7 +1749,7 @@ The format is **not** based on [Keep a Changelog][kec], since the project **does
## AGDNS-230 / Build 67
-- The new required environment variable `FILTER_INDEX_URL` has been added. It has no default value, so it's necessary to set it.
+- The new required environment variable `FILTER_INDEX_URL` has been added. It has no default value, so it’s necessary to set it.
- The environment variable `BACKEND_ENDPOINT` is now required and has no default value.
@@ -1951,7 +1959,7 @@ The format is **not** based on [Keep a Changelog][kec], since the project **does
key: /test/cert.key
```
- The domains to be used in device ID detection are now expected to be contained in the certificate's DNS Names section of SAN.
+ The domains to be used in device ID detection are now expected to be contained in the certificate’s DNS Names section of SAN.
## AGDNS-167 / Build 39
diff --git a/Makefile b/Makefile
index 2ac1ff9..94fadc8 100644
--- a/Makefile
+++ b/Makefile
@@ -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.4
+GOTOOLCHAIN = go1.23.6
RACE = 0
REVISION = $${REVISION:-$$(git rev-parse --short HEAD)}
VERSION = 0
diff --git a/doc/debughttp.md b/doc/debughttp.md
index 7a6de92..59ca4e7 100644
--- a/doc/debughttp.md
+++ b/doc/debughttp.md
@@ -55,7 +55,6 @@ Supported IDs:
- `dns/ecscache_no_ecs`
- `dns/ecscache_with_ecs`
- `filters/blocked_service/*`
-- `filters/custom`
- `filters/hashprefix/adult_blocking`
- `filters/hashprefix/newly_registered_domains`
- `filters/hashprefix/safe_browsing`
diff --git a/doc/environment.md b/doc/environment.md
index b8053e2..f1e6bf5 100644
--- a/doc/environment.md
+++ b/doc/environment.md
@@ -27,6 +27,7 @@ AdGuard DNS uses [environment variables][wiki-env] to store some of the more sen
- [`LINKED_IP_TARGET_URL`](#LINKED_IP_TARGET_URL)
- [`LISTEN_ADDR`](#LISTEN_ADDR)
- [`LISTEN_PORT`](#LISTEN_PORT)
+- [`LOG_FORMAT`](#LOG_FORMAT)
- [`LOG_TIMESTAMP`](#LOG_TIMESTAMP)
- [`METRICS_NAMESPACE`](#METRICS_NAMESPACE)
- [`NEW_REG_DOMAINS_ENABLED`](#NEW_REG_DOMAINS_ENABLED)
@@ -224,6 +225,18 @@ The port on which to bind the [debug HTTP API][debughttp], which includes the he
**Default:** `8181`.
+## `LOG_FORMAT`
+
+The format for the server logs:
+
+- `text`: Structured text format, it is the default value.
+
+- `default`: Simple and human-readable plain-text format.
+
+- `json`: JSON format.
+
+- `jsonhybrid`: JSON with a schema consisting of `level`, `msg`, and `time` properties.
+
## `LOG_TIMESTAMP`
If `1`, show timestamps in the plain text logs. If `0`, don't show the timestamps.
diff --git a/go.mod b/go.mod
index 1dac177..c5384f2 100644
--- a/go.mod
+++ b/go.mod
@@ -1,34 +1,34 @@
module github.com/AdguardTeam/AdGuardDNS
-go 1.23.4
+go 1.23.6
require (
github.com/AdguardTeam/AdGuardDNS/internal/dnsserver v0.0.0-20240607112746-5690301129fe
- github.com/AdguardTeam/golibs v0.30.4
+ github.com/AdguardTeam/golibs v0.32.1
github.com/AdguardTeam/urlfilter v0.20.0
github.com/ameshkov/dnscrypt/v2 v2.3.0
- github.com/axiomhq/hyperloglog v0.2.0
+ github.com/axiomhq/hyperloglog v0.2.3
github.com/bluele/gcache v0.0.2
github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500
github.com/caarlos0/env/v7 v7.1.0
- github.com/getsentry/sentry-go v0.29.1
+ github.com/getsentry/sentry-go v0.31.1
github.com/gomodule/redigo v1.9.2
github.com/google/renameio/v2 v2.0.0
- github.com/miekg/dns v1.1.62
+ github.com/miekg/dns v1.1.63
github.com/oschwald/maxminddb-golang v1.13.1
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.1
- github.com/quic-go/quic-go v0.48.2
- github.com/stretchr/testify v1.9.0
- 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
+ github.com/prometheus/common v0.62.0
+ github.com/quic-go/quic-go v0.49.0
+ github.com/stretchr/testify v1.10.0
+ golang.org/x/crypto v0.32.0
+ golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3
+ golang.org/x/net v0.34.0
+ golang.org/x/sys v0.30.0
+ golang.org/x/time v0.10.0
+ google.golang.org/grpc v1.70.0
+ google.golang.org/protobuf v1.36.5
gopkg.in/yaml.v2 v2.4.0
)
@@ -39,22 +39,25 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
- github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
+ github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
- github.com/google/pprof v0.0.0-20241203143554-1e3fdc7de467 // indirect
+ github.com/google/pprof v0.0.0-20250202011525-fc3143867406 // indirect
+ github.com/kamstrup/intmap v0.5.1 // 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.22.0 // indirect
- github.com/panjf2000/ants/v2 v2.10.0 // indirect
+ github.com/onsi/ginkgo/v2 v2.22.2 // indirect
+ github.com/panjf2000/ants/v2 v2.11.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
+ github.com/robfig/cron/v3 v3.0.1 // indirect
go.uber.org/mock v0.5.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/tools v0.28.0 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect
+ golang.org/x/mod v0.23.0 // indirect
+ golang.org/x/sync v0.11.0 // indirect
+ golang.org/x/text v0.22.0 // indirect
+ golang.org/x/tools v0.29.0 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index 4905022..63c4b72 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,5 @@
-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/golibs v0.32.1 h1:Ajf6Q0k+A9zjFbj8HOzNAbHImrV4JtbT0vwy02D6VeI=
+github.com/AdguardTeam/golibs v0.32.1/go.mod h1:dXRLSsnJQDxOfQVl0ochy1bfk4NgnJQGQdR1YPJdwcw=
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=
@@ -10,8 +10,8 @@ github.com/ameshkov/dnscrypt/v2 v2.3.0 h1:pDXDF7eFa6Lw+04C0hoMh8kCAQM8NwUdFEllSP
github.com/ameshkov/dnscrypt/v2 v2.3.0/go.mod h1:N5hDwgx2cNb4Ay7AhvOSKst+eUiOZ/vbKRO9qMpQttE=
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
github.com/ameshkov/dnsstamps v1.0.3/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
-github.com/axiomhq/hyperloglog v0.2.0 h1:u1XT3yyY1rjzlWuP6NQIrV4bRYHOaqZaovqjcBEvZJo=
-github.com/axiomhq/hyperloglog v0.2.0/go.mod h1:GcgMjz9gaDKZ3G0UMS6Fq/VkZ4l7uGgcJyxA7M+omIM=
+github.com/axiomhq/hyperloglog v0.2.3 h1:2ZGwz3FGcx77e9/aNjqJijsGhH6RZOlglzxnDpVBCQY=
+github.com/axiomhq/hyperloglog v0.2.3/go.mod h1:DLUK9yIzpU5B6YFLjxTIcbHu1g4Y1WQb1m5RH3radaM=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
@@ -22,17 +22,19 @@ github.com/caarlos0/env/v7 v7.1.0 h1:9lzTF5amyQeWHZzuZeKlCb5FWSUxpG1js43mhbY8ozg
github.com/caarlos0/env/v7 v7.1.0/go.mod h1:LPPWniDUq4JaO6Q41vtlyikhMknqymCLBw0eX4dcH1E=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0=
-github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
-github.com/getsentry/sentry-go v0.29.1 h1:DyZuChN8Hz3ARxGVV8ePaNXh1dQ7d76AiB117xcREwA=
-github.com/getsentry/sentry-go v0.29.1/go.mod h1:x3AtIzN01d6SiWkderzaH28Tm0lgkafpJ5Bm3li39O0=
+github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33 h1:ucRHb6/lvW/+mTEIGbvhcYU3S8+uSNkuMjx/qZFfhtM=
+github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
+github.com/getsentry/sentry-go v0.31.1 h1:ELVc0h7gwyhnXHDouXkhqTFSO5oslsRDk0++eyE0KJ4=
+github.com/getsentry/sentry-go v0.31.1/go.mod h1:CYNcMMz73YigoHljQRG+qPF+eMq8gG72XcGN/p71BAY=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-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=
@@ -43,10 +45,14 @@ 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-20241203143554-1e3fdc7de467 h1:keEZFtbLJugfE0qHn+Ge1JCE71spzkchQobDf3mzS/4=
-github.com/google/pprof v0.0.0-20241203143554-1e3fdc7de467/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
+github.com/google/pprof v0.0.0-20250202011525-fc3143867406 h1:wlQI2cYY0BsWmmPPAnxfQ8SDW0S3Jasn+4B8kXFxprg=
+github.com/google/pprof v0.0.0-20250202011525-fc3143867406/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/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/kamstrup/intmap v0.5.1 h1:ENGAowczZA+PJPYYlreoqJvWgQVtAmX1l899WfYFVK0=
+github.com/kamstrup/intmap v0.5.1/go.mod h1:gWUVWHKzWj8xpJVFf5GC0O26bWmv3GqdnIX/LMT6Aq4=
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=
@@ -57,18 +63,18 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
-github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
-github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
+github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
+github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
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.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/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
+github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
+github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
+github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
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=
-github.com/panjf2000/ants/v2 v2.10.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I=
+github.com/panjf2000/ants/v2 v2.11.0 h1:sHrqEwTBQTQ2w6PMvbMfvBtVUuhsaYPzUmAYDLYmJPg=
+github.com/panjf2000/ants/v2 v2.11.0/go.mod h1:V9HhTupTWxcaRmIglJvGwvzqXUTnIZW9uO6q4hAfApw=
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible h1:IWzUvJ72xMjmrjR9q3H1PF+jwdN0uNQiR2t1BLNalyo=
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
@@ -83,66 +89,74 @@ 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.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
-github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
+github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
+github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
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.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
-github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
+github.com/quic-go/quic-go v0.49.0 h1:w5iJHXwHxs1QxyBv1EHKuC50GX5to8mJAxvtnttJp94=
+github.com/quic-go/quic-go v0.49.0/go.mod h1:s2wDnmCdooUQBmQfpUSTCYBl1/D4FcqbULMMkASvR6s=
+github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
+github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
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=
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
-github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
-github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
+go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
+go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
+go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
+go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
+go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
+go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
+go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
+go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
+go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
+go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
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.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.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=
+golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
+golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
+golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 h1:qNgPs5exUA+G0C96DrPwNrvLSj7GT/9D+3WMWUcUg34=
+golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
+golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
+golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
+golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
+golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
+golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
+golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
+golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
+golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
+golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
+golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
+golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489 h1:5bKytslY8ViY0Cj/ewmRtrWHW64bNF03cAatUUFCdFI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk=
+google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
+google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
+google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
+google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/go.work b/go.work
index f91c55e..6fa92c9 100644
--- a/go.work
+++ b/go.work
@@ -1,4 +1,4 @@
-go 1.23.4
+go 1.23.6
use (
.
diff --git a/go.work.sum b/go.work.sum
index 9d83983..e857cfb 100644
--- a/go.work.sum
+++ b/go.work.sum
@@ -2,6 +2,8 @@ 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=
+cel.dev/expr v0.19.0 h1:lXuo+nDhpyJSpWxpPVi5cPUwzKb+dsdOiw6IreM5yt0=
+cel.dev/expr v0.19.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
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=
@@ -48,6 +50,8 @@ cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2Aawl
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
+cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo=
+cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k=
cloud.google.com/go/contactcenterinsights v1.13.0/go.mod h1:ieq5d5EtHsu8vhe2y3amtZ+BE+AQwX5qAy7cpo0POsI=
cloud.google.com/go/container v1.31.0/go.mod h1:7yABn5s3Iv3lmw7oMmyGbeV6tQj86njcTijkkGuvdZA=
cloud.google.com/go/containeranalysis v0.11.4/go.mod h1:cVZT7rXYBS9NG1rhQbWL9pWbXCKHWJPYraE8/FTSYPE=
@@ -163,6 +167,9 @@ github.com/AdguardTeam/golibs v0.30.0/go.mod h1:vjw1OVZG6BYyoqGRY88U4LCJLOMfhBFh
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/golibs v0.30.6-0.20241204165356-565456b436b4/go.mod h1:wIkZ9o2UnppeW6/YD7yJB71dYbMhiuC1Fh/I2ElW7GQ=
+github.com/AdguardTeam/golibs v0.31.2 h1:UMeyMlJoLVCtPpOJcTuxk6RXANXbRJyaWKsF0YaJpQk=
+github.com/AdguardTeam/golibs v0.31.2/go.mod h1:DzCfc0HFaaKv7sV17gZnSMUiHRtUJ1ypX82VlXUWI6M=
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=
@@ -179,6 +186,8 @@ github.com/CloudyKit/jet/v6 v6.1.0 h1:hvO96X345XagdH1fAoBjpBYG4a1ghhL/QzalkduPuX
github.com/CloudyKit/jet/v6 v6.1.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4=
github.com/CloudyKit/jet/v6 v6.2.0 h1:EpcZ6SR9n28BUGtNJSvlBqf90IpjeFr36Tizxhn/oME=
github.com/CloudyKit/jet/v6 v6.2.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4=
+github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw=
+github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM=
github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=
github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
@@ -210,6 +219,8 @@ github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625 h1:ckJgFhFWywOx+
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 h1:D21IyuvjDCshj1/qq+pCNd3VZOAEI9jy6Bi131YlXgI=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
+github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
+github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=
@@ -253,6 +264,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 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI=
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=
@@ -272,12 +284,15 @@ github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQ
github.com/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI=
github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0=
github.com/envoyproxy/go-control-plane v0.13.0/go.mod h1:GRaKG3dwvFoTg4nj7aXdZnvMg4d7nvT/wl9WgVXn3Q8=
+github.com/envoyproxy/go-control-plane v0.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE=
+github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw=
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA=
github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE=
github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
+github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM=
github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
@@ -314,7 +329,6 @@ github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
-github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk=
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
@@ -350,6 +364,8 @@ github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwm
github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4=
github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
+github.com/golang/glog v1.2.3 h1:oDTdz9f5VGVVNGu/Q7UXKWYsD0873HXLHdJUNBsSEKM=
+github.com/golang/glog v1.2.3/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -410,8 +426,6 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
-github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
@@ -429,6 +443,8 @@ github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:Fecb
github.com/grpc-ecosystem/grpc-gateway v1.5.0 h1:WcmKMm43DR7RdtlkEXQJyo5ws8iTp98CyhCCbOHMvNI=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 h1:VD1gqscl4nYs1YxVuSdemTrSgTKrwOWDK0FVFMqm+Cg=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0/go.mod h1:4EgsQoS4TOhJizV+JTFg40qx1Ofh3XmXEQNBpgvNT40=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
@@ -591,6 +607,7 @@ github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwb
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
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=
@@ -740,13 +757,25 @@ go.etcd.io/gofail v0.1.0/go.mod h1:VZBCXYGZhHAinaBiiqYvuDynvahNsAyLFwB3kEHKz1M=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
+go.opentelemetry.io/contrib/detectors/gcp v1.32.0 h1:P78qWqkLSShicHmAzfECaTgvslqHxblNE9j62Ws1NK8=
+go.opentelemetry.io/contrib/detectors/gcp v1.32.0/go.mod h1:TVqo0Sda4Cv8gCIixd7LuLwW4EylumVWfhjZJjDD4DU=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 h1:BEj3SPM81McUZHYjRS5pEgNgnmzGJ5tRpU5krWnV8Bs=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0/go.mod h1:9cKLGBDzI/F3NoHLQGm4ZrYdIHsvGt6ej6hUowxY0J4=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.34.0 h1:jBpDk4HAUsrnVO1FsfCfCOTEc/MkInJmvfCHYLFiT80=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.34.0/go.mod h1:H9LUIM1daaeZaz91vZcfeM0fejXPmgCYE8ZhzqfJuiU=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
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.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
+go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
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=
@@ -769,6 +798,7 @@ golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOM
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/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
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=
@@ -837,6 +867,7 @@ 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/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
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=
@@ -857,6 +888,8 @@ golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
+golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852 h1:xYq6+9AtI+xP3M4r0N1hCkHrInHDBohhquRgx9Kk6gI=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -949,6 +982,8 @@ 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/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
+golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
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=
@@ -1046,6 +1081,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:
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/api v0.0.0-20250122153221-138b5a5a4fd4 h1://y4MHaM7tNLqTeWKyfBIeoAMxwKwRm/nODb5IKA3BE=
+google.golang.org/genproto/googleapis/api v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:AfA77qWLcidQWywD0YgqfpJzf50w2VjzBml3TybHeJU=
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=
diff --git a/internal/access/engine.go b/internal/access/engine.go
index 26c9548..d81e6bb 100644
--- a/internal/access/engine.go
+++ b/internal/access/engine.go
@@ -15,6 +15,8 @@ import (
)
// blockedHostEngine is a lazy blocklist rules engine.
+//
+// TODO(a.garipov): Replace/merge with [custom.Filter].
type blockedHostEngine struct {
lazyEngine *urlfilter.DNSEngine
initOnce *sync.Once
diff --git a/internal/agd/agd.go b/internal/agd/agd.go
index 2d1349b..d5ec5fd 100644
--- a/internal/agd/agd.go
+++ b/internal/agd/agd.go
@@ -1,41 +1,2 @@
// Package agd contains common entities and interfaces of AdGuard DNS.
package agd
-
-import (
- "fmt"
-)
-
-// firstNonIDRune returns the first non-printable or non-ASCII rune and its
-// index. If slashes is true, it also looks for slashes. If there are no such
-// runes, i is -1.
-func firstNonIDRune(s string, slashes bool) (i int, r rune) {
- for i, r = range s {
- if r < '!' || r > '~' || (slashes && r == '/') {
- return i, r
- }
- }
-
- return -1, 0
-}
-
-// Unit name constants.
-const (
- UnitByte = "bytes"
- UnitRune = "runes"
-)
-
-// ValidateInclusion returns an error if n is greater than max or less than min.
-// unitName is used for error messages, see UnitFoo constants.
-//
-// TODO(a.garipov): Consider switching min and max; the current order seems
-// confusing.
-func ValidateInclusion(n, max, min int, unitName string) (err error) {
- switch {
- case n > max:
- return fmt.Errorf("too long: got %d %s, max %d", n, unitName, max)
- case n < min:
- return fmt.Errorf("too short: got %d %s, min %d", n, unitName, min)
- default:
- return nil
- }
-}
diff --git a/internal/agd/context.go b/internal/agd/context.go
index 6132aae..0d540f7 100644
--- a/internal/agd/context.go
+++ b/internal/agd/context.go
@@ -80,15 +80,13 @@ type RequestInfo struct {
// to this request.
Messages *dnsmsg.Constructor
- // ServerGroup is the server group which handles this request.
- ServerGroup *ServerGroup
+ // ServerInfo contains the information about the server processing the query
+ // and its server group. It must not be nil.
+ ServerInfo *RequestServerInfo
// RemoteIP is the remote IP address of the client.
RemoteIP netip.Addr
- // Server is the name of the server which handles this request.
- Server ServerName
-
// Host is the lowercased, non-FQDN version of the hostname from the
// question of the request.
Host string
@@ -102,11 +100,32 @@ type RequestInfo struct {
// QClass is the class of question for this request.
QClass dnsmsg.Class
-
- // Proto is the protocol by which this request is made.
- Proto Protocol
}
+// RequestServerInfo contains the information about the server and its group
+// relevant to the request.
+type RequestServerInfo struct {
+ // GroupName is the unique name of the server group. It must not be empty.
+ GroupName ServerGroupName
+
+ // Name is the unique name of the server. It must not be empty.
+ Name ServerName
+
+ // DeviceDomains is the list of domain names that the server group uses to
+ // detect device IDs from clients' server names.
+ DeviceDomains []string
+
+ // Protocol is the protocol by which this request is made.
+ Protocol Protocol
+
+ // ProfilesEnabled, if true, enables recognition of user devices and
+ // profiles for the server group.
+ ProfilesEnabled bool
+}
+
+// ServerGroupName is the name of a server group.
+type ServerGroupName string
+
// DeviceData returns the profile and device data if any. Either both p and d
// are nil or neither is nil.
func (ri *RequestInfo) DeviceData() (p *Profile, d *Device) {
diff --git a/internal/agd/device.go b/internal/agd/device.go
index 62832aa..1ec3bf7 100644
--- a/internal/agd/device.go
+++ b/internal/agd/device.go
@@ -6,6 +6,7 @@ import (
"unicode/utf8"
"github.com/AdguardTeam/AdGuardDNS/internal/agdpasswd"
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdvalidate"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
)
@@ -66,7 +67,12 @@ func NewDeviceID(s string) (id DeviceID, err error) {
}
}()
- err = ValidateInclusion(len(s), MaxDeviceIDLen, MinDeviceIDLen, UnitByte)
+ err = agdvalidate.Inclusion(
+ len(s),
+ MinDeviceIDLen,
+ MaxDeviceIDLen,
+ agdvalidate.UnitByte,
+ )
if err != nil {
// The error will be wrapped by the deferred helper.
return "", err
@@ -102,7 +108,12 @@ func NewDeviceName(s string) (n DeviceName, err error) {
}
}()
- err = ValidateInclusion(utf8.RuneCountInString(s), MaxDeviceNameRuneLen, 0, UnitRune)
+ err = agdvalidate.Inclusion(
+ utf8.RuneCountInString(s),
+ 0,
+ MaxDeviceNameRuneLen,
+ agdvalidate.UnitRune,
+ )
if err != nil {
// The error will be wrapped by the deferred helper.
return "", err
diff --git a/internal/agd/devicetype.go b/internal/agd/devicetype.go
index 1ad15c9..4ac3572 100644
--- a/internal/agd/devicetype.go
+++ b/internal/agd/devicetype.go
@@ -4,6 +4,7 @@ import (
"fmt"
"strings"
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdvalidate"
"github.com/AdguardTeam/golibs/errors"
)
@@ -54,7 +55,7 @@ func DeviceTypeFromDNS(s string) (dt DeviceType, err error) {
}
}()
- err = ValidateInclusion(len(s), 3, 3, UnitByte)
+ err = agdvalidate.Inclusion(len(s), 3, 3, agdvalidate.UnitByte)
if err != nil {
// The error will be wrapped by the deferred helper.
return DeviceTypeNone, err
diff --git a/internal/agd/filteringgroup.go b/internal/agd/filteringgroup.go
index 0725150..a0efba9 100644
--- a/internal/agd/filteringgroup.go
+++ b/internal/agd/filteringgroup.go
@@ -3,6 +3,9 @@ package agd
import "github.com/AdguardTeam/AdGuardDNS/internal/filter"
// FilteringGroup represents a set of filtering settings.
+//
+// TODO(a.garipov): Extract the pre-filtering booleans and logic into a new
+// package.
type FilteringGroup struct {
// FilterConfig is the configuration of the filters used for this filtering
// group. It must not be nil.
diff --git a/internal/agd/humanid.go b/internal/agd/humanid.go
index 7427a98..b8b5a29 100644
--- a/internal/agd/humanid.go
+++ b/internal/agd/humanid.go
@@ -6,6 +6,7 @@ import (
"strings"
"unicode/utf8"
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdvalidate"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/syncutil"
@@ -42,7 +43,7 @@ func NewHumanID(s string) (id HumanID, err error) {
// valid. It does not wrap the error to be used in places where that could
// create additional allocations.
func newHumanID(s string) (id HumanID, err error) {
- err = ValidateInclusion(len(s), MaxHumanIDLen, MinHumanIDLen, UnitByte)
+ err = agdvalidate.Inclusion(len(s), MinHumanIDLen, MaxHumanIDLen, agdvalidate.UnitByte)
if err != nil {
// Don't wrap the error, because the caller should do that.
return "", err
@@ -130,7 +131,12 @@ func (p *HumanIDParser) ParseNormalized(s string) (id HumanID, err error) {
}()
// Immediately validate it against the upper DNS hostname-length limit.
- err = ValidateInclusion(len(s), netutil.MaxDomainNameLen, MinHumanIDLen, UnitByte)
+ err = agdvalidate.Inclusion(
+ len(s),
+ MinHumanIDLen,
+ netutil.MaxDomainNameLen,
+ agdvalidate.UnitByte,
+ )
if err != nil {
// Don't wrap the error, because there is already a deferred wrap, and
// the error is informative enough as is.
diff --git a/internal/agd/profile.go b/internal/agd/profile.go
index 68b2331..8e52091 100644
--- a/internal/agd/profile.go
+++ b/internal/agd/profile.go
@@ -5,6 +5,7 @@ import (
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/access"
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdvalidate"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
)
@@ -15,6 +16,9 @@ import (
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
+//
+// TODO(a.garipov): Extract the pre-filtering booleans and logic into a new
+// package.
type Profile struct {
// 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
@@ -85,14 +89,14 @@ const MaxProfileIDLen = 8
// NewProfileID converts a simple string into a ProfileID and makes sure that
// it's valid. This should be preferred to a simple type conversion.
func NewProfileID(s string) (id ProfileID, err error) {
- if err = ValidateInclusion(len(s), MaxProfileIDLen, 0, UnitByte); err != nil {
+ if err = agdvalidate.Inclusion(len(s), 0, MaxProfileIDLen, agdvalidate.UnitByte); err != nil {
return "", fmt.Errorf("bad profile id %q: %w", s, err)
}
// For now, allow only the printable, non-whitespace ASCII characters.
// Technically we only need to exclude carriage return and line feed
// characters, but let's be more strict just in case.
- if i, r := firstNonIDRune(s, false); i != -1 {
+ if i, r := agdvalidate.FirstNonIDRune(s, false); i != -1 {
return "", fmt.Errorf("bad profile id: bad char %q at index %d", r, i)
}
diff --git a/internal/agd/requestid.go b/internal/agd/requestid.go
index 75c49eb..df0918c 100644
--- a/internal/agd/requestid.go
+++ b/internal/agd/requestid.go
@@ -3,9 +3,8 @@ package agd
import (
"encoding/base64"
"fmt"
- "time"
- "golang.org/x/exp/rand"
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdrand"
)
// RequestIDLen is the length of a [RequestID] in bytes. A RequestID is
@@ -20,13 +19,7 @@ type RequestID [RequestIDLen]byte
// requestIDRand is used to create [RequestID]s.
//
// TODO(a.garipov): Consider making a struct instead of using one global source.
-var requestIDRand = rand.New(&rand.LockedSource{})
-
-// InitRequestID initializes the [RequestID] generator.
-func InitRequestID() {
- // #nosec G115 -- The Unix epoch time is highly unlikely to be negative.
- requestIDRand.Seed(uint64(time.Now().UnixNano()))
-}
+var requestIDRand = agdrand.NewReader(agdrand.MustNewSeed())
// NewRequestID returns a new pseudorandom RequestID. Prefer this to manual
// conversion from other string types.
diff --git a/internal/agd/requestid_test.go b/internal/agd/requestid_test.go
index 5ef9e90..ccd2ecc 100644
--- a/internal/agd/requestid_test.go
+++ b/internal/agd/requestid_test.go
@@ -10,8 +10,6 @@ import (
var reqIDSink agd.RequestID
func BenchmarkNewRequestID(b *testing.B) {
- agd.InitRequestID()
-
b.ReportAllocs()
b.ResetTimer()
for range b.N {
@@ -20,10 +18,10 @@ func BenchmarkNewRequestID(b *testing.B) {
require.NotEmpty(b, reqIDSink)
- // Most recent result, on a ThinkPad X13 with a Ryzen Pro 7 CPU:
- // goos: linux
- // goarch: amd64
+ // Most recent results:
+ // goos: darwin
+ // goarch: arm64
// pkg: github.com/AdguardTeam/AdGuardDNS/internal/agd
- // cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
- // BenchmarkNewRequestID-16 50985721 24.91 ns/op 0 B/op 0 allocs/op
+ // cpu: Apple M1 Pro
+ // BenchmarkNewRequestID-8 56177144 21.33 ns/op 0 B/op 0 allocs/op
}
diff --git a/internal/agd/servergroup.go b/internal/agd/servergroup.go
deleted file mode 100644
index e0c7024..0000000
--- a/internal/agd/servergroup.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package agd
-
-import (
- "github.com/AdguardTeam/golibs/container"
- "github.com/miekg/dns"
-)
-
-// ServerGroup is a group of DNS servers all of which use the same filtering
-// settings.
-type ServerGroup struct {
- // DDR is the configuration for the server group's Discovery Of Designated
- // Resolvers (DDR) handlers. DDR must not be nil.
- DDR *DDR
-
- // 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
-
- // FilteringGroup is the ID of the filtering group for this server group.
- FilteringGroup FilteringGroupID
-
- // Servers are the settings for servers. Each element must be non-nil.
- Servers []*Server
-
- // ProfilesEnabled, if true, enables recognition of user devices and
- // profiles for this server group.
- ProfilesEnabled bool
-}
-
-// ServerGroupName is the name of a server group.
-type ServerGroupName string
-
-// DDR is the configuration for the server group's Discovery Of Designated
-// Resolvers (DDR) handlers.
-type DDR struct {
- // DeviceTargets is the set of all domain names, subdomains of which should
- // be checked for DDR queries with device IDs.
- DeviceTargets *container.MapSet[string]
-
- // PublicTargets is the set of all public domain names, DDR queries for
- // which should be processed.
- PublicTargets *container.MapSet[string]
-
- // DeviceRecordTemplates are used to respond to DDR queries from recognized
- // devices.
- DeviceRecordTemplates []*dns.SVCB
-
- // PubilcRecordTemplates are used to respond to DDR queries from
- // unrecognized devices.
- PublicRecordTemplates []*dns.SVCB
-
- // Enabled shows if DDR queries are processed. If it is false, DDR domain
- // name queries receive an NXDOMAIN response.
- Enabled bool
-}
diff --git a/internal/agdrand/agdrand.go b/internal/agdrand/agdrand.go
new file mode 100644
index 0000000..ee90727
--- /dev/null
+++ b/internal/agdrand/agdrand.go
@@ -0,0 +1,76 @@
+// Package agdrand contains utilities for random numbers.
+//
+// TODO(a.garipov): Move to golibs.
+package agdrand
+
+import (
+ cryptorand "crypto/rand"
+ "math/rand/v2"
+ "sync"
+)
+
+// Reader is a ChaCha8-based cryptographically strong random number reader.
+// It's safe for concurrent use.
+type Reader struct {
+ // mu protects reader.
+ mu *sync.Mutex
+
+ reader *rand.ChaCha8
+}
+
+// NewReader returns a new properly initialized *Reader seeded with the given
+// seed.
+func NewReader(seed [32]byte) (r *Reader) {
+ return &Reader{
+ mu: &sync.Mutex{},
+ reader: rand.NewChaCha8(seed),
+ }
+}
+
+// Read generates len(p) random bytes and writes them into p. It always returns
+// len(p) and a nil error. It's safe for concurrent use.
+func (r *Reader) Read(p []byte) (n int, err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ return r.reader.Read(p)
+}
+
+// LockedSource is an implementation of [rand.Source] that is concurrency-safe.
+type LockedSource struct {
+ // mu protects src.
+ mu *sync.Mutex
+
+ src rand.Source
+}
+
+// NewLockedSource returns new properly initialized *LockedSource.
+func NewLockedSource(src rand.Source) (s *LockedSource) {
+ return &LockedSource{
+ mu: &sync.Mutex{},
+ src: src,
+ }
+}
+
+// type check
+var _ rand.Source = (*LockedSource)(nil)
+
+// Uint64 implements the [rand.Source] interface for *LockedSource.
+func (s *LockedSource) Uint64() (r uint64) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ return s.src.Uint64()
+}
+
+// MustNewSeed returns new 32 byte seed for pseudorandom generators. Panics on
+// errors.
+func MustNewSeed() (seed [32]byte) {
+ _, err := cryptorand.Read(seed[:])
+ if err != nil {
+ // Don't wrap the error, because it's informative enough as is.
+ panic(err)
+ }
+
+ return seed
+}
diff --git a/internal/agdrand/agdrand_test.go b/internal/agdrand/agdrand_test.go
new file mode 100644
index 0000000..25eb717
--- /dev/null
+++ b/internal/agdrand/agdrand_test.go
@@ -0,0 +1,118 @@
+package agdrand_test
+
+import (
+ "math/rand/v2"
+ "sync"
+ "testing"
+
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdrand"
+ "github.com/stretchr/testify/require"
+)
+
+// routinesLimit is the number of goroutines for tests.
+const routinesLimit = 512
+
+func TestReader_race(t *testing.T) {
+ t.Parallel()
+
+ const length = 128
+
+ reader := agdrand.NewReader(agdrand.MustNewSeed())
+
+ wg := &sync.WaitGroup{}
+ wg.Add(routinesLimit)
+
+ startCh := make(chan struct{})
+ for range routinesLimit {
+ go func() {
+ defer wg.Done()
+
+ <-startCh
+ for range 1_000 {
+ buf := make([]byte, length)
+ _, _ = reader.Read(buf)
+ }
+ }()
+ }
+
+ close(startCh)
+
+ wg.Wait()
+}
+
+func TestLockedSource_race(t *testing.T) {
+ t.Parallel()
+
+ src := agdrand.NewLockedSource(rand.NewPCG(0, 0))
+
+ wg := &sync.WaitGroup{}
+ wg.Add(routinesLimit)
+
+ startCh := make(chan struct{})
+ for range routinesLimit {
+ go func() {
+ defer wg.Done()
+
+ <-startCh
+ for range 1_000 {
+ _ = src.Uint64()
+ }
+ }()
+ }
+
+ close(startCh)
+
+ wg.Wait()
+}
+
+// testSeed is a seed for tests.
+var testSeed = [32]byte{}
+
+// Sinks for benchmarks.
+var (
+ errSink error
+ intSink int
+ uint64Sink uint64
+)
+
+func BenchmarkReader_Read(b *testing.B) {
+ const length = 16
+
+ reader := agdrand.NewReader(testSeed)
+
+ b.ReportAllocs()
+ b.ResetTimer()
+
+ buf := make([]byte, length)
+ for range b.N {
+ intSink, errSink = reader.Read(buf)
+ }
+
+ require.Equal(b, length, intSink)
+ require.NoError(b, errSink)
+
+ // Most recent results:
+ // goos: darwin
+ // goarch: arm64
+ // pkg: github.com/AdguardTeam/AdGuardDNS/internal/agdrand
+ // cpu: Apple M1 Pro
+ // BenchmarkReader_Read-8 57008931 20.60 ns/op 0 B/op 0 allocs/op
+}
+
+func BenchmarkLockedSource_Uint64(b *testing.B) {
+ src := agdrand.NewLockedSource(rand.NewChaCha8(testSeed))
+
+ b.ReportAllocs()
+ b.ResetTimer()
+
+ for range b.N {
+ uint64Sink = src.Uint64()
+ }
+
+ // Most recent results:
+ // goos: darwin
+ // goarch: arm64
+ // pkg: github.com/AdguardTeam/AdGuardDNS/internal/agdrand
+ // cpu: Apple M1 Pro
+ // BenchmarkLockedSource_Uint64-8 77621248 15.35 ns/op 0 B/op 0 allocs/op
+}
diff --git a/internal/agdservice/agdservice.go b/internal/agdservice/agdservice.go
deleted file mode 100644
index 3fa27bc..0000000
--- a/internal/agdservice/agdservice.go
+++ /dev/null
@@ -1,7 +0,0 @@
-// Package agdservice defines types and interfaces for long-running services.
-//
-// TODO(a.garipov): Move more to golibs.
-package agdservice
-
-// unit is a convenient alias for struct{}.
-type unit = struct{}
diff --git a/internal/agdservice/agdservice_test.go b/internal/agdservice/agdservice_test.go
deleted file mode 100644
index a708d88..0000000
--- a/internal/agdservice/agdservice_test.go
+++ /dev/null
@@ -1,6 +0,0 @@
-package agdservice_test
-
-import "time"
-
-// testTimeout is the timeout for common test operations.
-const testTimeout = 1 * time.Second
diff --git a/internal/agdservice/refresh.go b/internal/agdservice/refresh.go
deleted file mode 100644
index fbde0ab..0000000
--- a/internal/agdservice/refresh.go
+++ /dev/null
@@ -1,248 +0,0 @@
-package agdservice
-
-import (
- "context"
- "fmt"
- "log/slog"
- "time"
-
- "github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
- "github.com/AdguardTeam/golibs/logutil/slogutil"
- "github.com/AdguardTeam/golibs/service"
- "github.com/AdguardTeam/golibs/timeutil"
- "golang.org/x/exp/rand"
-)
-
-// Refresher is the interface for entities that can update themselves.
-type Refresher interface {
- // Refresh is called by a [RefreshWorker]. The error returned by Refresh is
- // only returned from [RefreshWorker.Shutdown] and only when
- // [RefreshWorkerConfig.RefreshOnShutdown] is true. In all other cases, the
- // error is ignored, and refreshers must handle error reporting themselves.
- 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 {
- logger *slog.Logger
- done chan unit
- context func() (ctx context.Context, cancel context.CancelFunc)
- tick *time.Ticker
- rand *rand.Rand
- refr Refresher
- maxStartSleep time.Duration
-
- refrOnShutdown bool
-}
-
-// RefreshWorkerConfig is the configuration structure for a *RefreshWorker.
-type RefreshWorkerConfig struct {
- // Context is used to provide a context for the Refresh method of Refresher.
- //
- // NOTE: It is not used for the shutdown refresh.
- //
- // TODO(a.garipov): Consider ways of fixing that.
- Context func() (ctx context.Context, cancel context.CancelFunc)
-
- // Refresher is the entity being refreshed.
- Refresher Refresher
-
- // Logger is used for logging the operation of the worker.
- Logger *slog.Logger
-
- // Interval is the refresh interval. Must be greater than zero.
- //
- // TODO(a.garipov): Consider switching to an interface à la
- // github.com/robfig/cron/v3.Schedule.
- Interval time.Duration
-
- // RefreshOnShutdown, if true, instructs the worker to call the Refresher's
- // Refresh method before shutting down the worker. This is useful for items
- // that should persist to disk or remote storage before shutting down.
- RefreshOnShutdown bool
-
- // RandomizeStart, if true, instructs the worker to sleep before starting a
- // refresh. The duration of the sleep is a random duration of up to 10 % of
- // Interval.
- //
- // TODO(a.garipov): Switch to something like a cron schedule and see if this
- // is still necessary
- RandomizeStart bool
-}
-
-// NewRefreshWorker returns a new valid *RefreshWorker with the provided
-// parameters. c must not be nil.
-func NewRefreshWorker(c *RefreshWorkerConfig) (w *RefreshWorker) {
- var maxStartSleep time.Duration
- var rng *rand.Rand
- if c.RandomizeStart {
- maxStartSleep = c.Interval / 10
- // #nosec G115 -- The Unix epoch time is highly unlikely to be negative.
- rng = rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
- }
-
- return &RefreshWorker{
- logger: c.Logger,
- done: make(chan unit),
- context: c.Context,
- tick: time.NewTicker(c.Interval),
- rand: rng,
- refr: c.Refresher,
- maxStartSleep: maxStartSleep,
- refrOnShutdown: c.RefreshOnShutdown,
- }
-}
-
-// type check
-var _ service.Interface = (*RefreshWorker)(nil)
-
-// Start implements the [service.Interface] interface for *RefreshWorker. err
-// is always nil.
-func (w *RefreshWorker) Start(_ context.Context) (err error) {
- go w.refreshInALoop()
-
- return nil
-}
-
-// Shutdown implements the [service.Interface] interface for *RefreshWorker.
-//
-// NOTE: The context provided by [RefreshWorkerConfig.Context] is not used for
-// the shutdown refresh.
-func (w *RefreshWorker) Shutdown(ctx context.Context) (err error) {
- if w.refrOnShutdown {
- err = w.refr.Refresh(slogutil.ContextWithLogger(ctx, w.logger))
- }
-
- close(w.done)
-
- w.tick.Stop()
-
- if err != nil {
- err = fmt.Errorf("refresh on shutdown: %w", err)
- } else {
- w.logger.InfoContext(ctx, "shut down successfully")
- }
-
- return err
-}
-
-// refreshInALoop refreshes the entity every tick of w.tick until Shutdown is
-// called.
-func (w *RefreshWorker) refreshInALoop() {
- ctx := context.Background()
- defer slogutil.RecoverAndLog(ctx, w.logger)
-
- w.logger.InfoContext(ctx, "starting refresh loop")
-
- for {
- select {
- case <-w.done:
- w.logger.InfoContext(ctx, "finished refresh loop")
-
- return
- case <-w.tick.C:
- if w.sleepRandom(ctx) {
- w.refresh()
- }
- }
- }
-}
-
-// sleepRandom sleeps for up to maxStartSleep unless it's zero. shouldRefresh
-// shows if a refresh should be performed once the sleep is finished.
-func (w *RefreshWorker) sleepRandom(ctx context.Context) (shouldRefresh bool) {
- if w.maxStartSleep == 0 {
- return true
- }
-
- sleepDur := time.Duration(w.rand.Int63n(int64(w.maxStartSleep)))
- // TODO(a.garipov): Augment our JSON handler to use time.Duration.String
- // automatically?
- w.logger.DebugContext(ctx, "sleeping before refresh", "dur", timeutil.Duration{
- Duration: sleepDur,
- })
-
- timer := time.NewTimer(sleepDur)
- defer func() {
- if !timer.Stop() {
- // We don't know if the timer's value has been consumed yet or not,
- // so use a select with default to make sure that this doesn't
- // block.
- select {
- case <-timer.C:
- default:
- }
- }
- }()
-
- select {
- case <-w.done:
- return false
- case <-timer.C:
- return true
- }
-}
-
-// refresh refreshes the entity and logs the status of the refresh.
-func (w *RefreshWorker) refresh() {
- // TODO(a.garipov): Consider adding a helper for enriching errors with
- // context deadline data without duplication. See an example in method
- // filter.refreshableFilter.refresh.
- ctx, cancel := w.context()
- defer cancel()
-
- ctx = slogutil.ContextWithLogger(ctx, w.logger)
-
- _ = w.refr.Refresh(ctx)
-}
-
-// RefresherWithErrColl reports all refresh errors to errColl and logs them
-// using a provided logging function.
-type RefresherWithErrColl struct {
- logger *slog.Logger
- refr Refresher
- errColl errcoll.Interface
- prefix string
-}
-
-// NewRefresherWithErrColl wraps refr into a refresher that collects errors and
-// logs them.
-func NewRefresherWithErrColl(
- refr Refresher,
- logger *slog.Logger,
- errColl errcoll.Interface,
- prefix string,
-) (wrapped *RefresherWithErrColl) {
- return &RefresherWithErrColl{
- refr: refr,
- logger: logger,
- errColl: errColl,
- prefix: prefix,
- }
-}
-
-// type check
-var _ Refresher = (*RefresherWithErrColl)(nil)
-
-// Refresh implements the [Refresher] interface for *RefresherWithErrColl.
-func (r *RefresherWithErrColl) Refresh(ctx context.Context) (err error) {
- err = r.refr.Refresh(ctx)
- if err != nil {
- errcoll.Collect(ctx, r.errColl, r.logger, "refreshing", err)
- }
-
- return err
-}
diff --git a/internal/agdservice/refresh_test.go b/internal/agdservice/refresh_test.go
deleted file mode 100644
index 85541a8..0000000
--- a/internal/agdservice/refresh_test.go
+++ /dev/null
@@ -1,125 +0,0 @@
-package agdservice_test
-
-import (
- "context"
- "testing"
- "time"
-
- "github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
- "github.com/AdguardTeam/golibs/errors"
- "github.com/AdguardTeam/golibs/logutil/slogutil"
- "github.com/AdguardTeam/golibs/testutil"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-// sig is a convenient alias for struct{} when it's used as a signal for
-// synchronization.
-type sig = struct{}
-
-const (
- testIvl = 5 * time.Millisecond
- testIvlLong = 1 * time.Hour
- name = "test refresher"
- testError errors.Error = "test error"
-)
-
-// newTestRefresher is a helper that returns refr and linked syncCh channel.
-func newTestRefresher(t *testing.T, respErr error) (refr *agdtest.Refresher, syncCh chan sig) {
- t.Helper()
-
- pt := testutil.PanicT{}
-
- syncCh = make(chan sig, 1)
- refr = &agdtest.Refresher{
- OnRefresh: func(_ context.Context) (err error) {
- testutil.RequireSend(pt, syncCh, sig{}, testTimeout)
-
- return respErr
- },
- }
-
- return refr, syncCh
-}
-
-// newRefrConfig returns worker configuration.
-func newRefrConfig(
- t *testing.T,
- refr agdservice.Refresher,
- ivl time.Duration,
- refrOnShutDown bool,
-) (conf *agdservice.RefreshWorkerConfig) {
- t.Helper()
-
- return &agdservice.RefreshWorkerConfig{
- Context: func() (ctx context.Context, cancel context.CancelFunc) {
- return context.WithTimeout(context.Background(), testTimeout)
- },
- Logger: slogutil.NewDiscardLogger(),
- Refresher: refr,
- Interval: ivl,
- RefreshOnShutdown: refrOnShutDown,
- RandomizeStart: false,
- }
-}
-
-func TestRefreshWorker(t *testing.T) {
- t.Run("success", func(t *testing.T) {
- refr, syncCh := newTestRefresher(t, nil)
-
- w := agdservice.NewRefreshWorker(newRefrConfig(t, refr, testIvl, false))
-
- err := w.Start(testutil.ContextWithTimeout(t, testTimeout))
- require.NoError(t, err)
-
- testutil.RequireReceive(t, syncCh, testTimeout)
-
- err = w.Shutdown(testutil.ContextWithTimeout(t, testTimeout))
- require.NoError(t, err)
- })
-
- t.Run("success_on_shutdown", func(t *testing.T) {
- refr, syncCh := newTestRefresher(t, nil)
- errCh := make(chan sig, 1)
-
- w := agdservice.NewRefreshWorker(newRefrConfig(t, refr, testIvlLong, true))
-
- err := w.Start(testutil.ContextWithTimeout(t, testTimeout))
- require.NoError(t, err)
-
- err = w.Shutdown(testutil.ContextWithTimeout(t, testTimeout))
- require.NoError(t, err)
-
- testutil.RequireReceive(t, syncCh, testTimeout)
- require.Empty(t, errCh)
- })
-
- t.Run("error", func(t *testing.T) {
- refrWithError, syncCh := newTestRefresher(t, testError)
-
- w := agdservice.NewRefreshWorker(newRefrConfig(t, refrWithError, testIvl, false))
-
- err := w.Start(testutil.ContextWithTimeout(t, testTimeout))
- require.NoError(t, err)
-
- testutil.RequireReceive(t, syncCh, testTimeout)
-
- err = w.Shutdown(testutil.ContextWithTimeout(t, testTimeout))
- require.NoError(t, err)
- })
-
- t.Run("error_on_shutdown", func(t *testing.T) {
- refrWithError, syncCh := newTestRefresher(t, testError)
-
- w := agdservice.NewRefreshWorker(newRefrConfig(t, refrWithError, testIvlLong, true))
-
- err := w.Start(testutil.ContextWithTimeout(t, testTimeout))
- require.NoError(t, err)
-
- err = w.Shutdown(testutil.ContextWithTimeout(t, testTimeout))
- assert.ErrorIs(t, err, testError)
-
- testutil.RequireReceive(t, syncCh, testTimeout)
- })
-}
diff --git a/internal/agdtest/interface.go b/internal/agdtest/interface.go
index 47e7ba8..262f036 100644
--- a/internal/agdtest/interface.go
+++ b/internal/agdtest/interface.go
@@ -10,8 +10,6 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/access"
"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"
@@ -97,36 +95,6 @@ func (a *Authenticator) Authenticate(ctx context.Context, passwd []byte) (ok boo
return a.OnAuthenticate(ctx, passwd)
}
-// Package agdservice
-
-// type check
-var _ agdservice.Refresher = (*Refresher)(nil)
-
-// Refresher is an [agdservice.Refresher] for tests.
-type Refresher struct {
- OnRefresh func(ctx context.Context) (err error)
-}
-
-// Refresh implements the [agdservice.Refresher] interface for *Refresher.
-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
diff --git a/internal/agdtime/agdtime.go b/internal/agdtime/agdtime.go
index e0efc2c..c0da4c8 100644
--- a/internal/agdtime/agdtime.go
+++ b/internal/agdtime/agdtime.go
@@ -8,24 +8,6 @@ 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.
//
diff --git a/internal/agdvalidate/agdvalidate.go b/internal/agdvalidate/agdvalidate.go
new file mode 100644
index 0000000..6953df5
--- /dev/null
+++ b/internal/agdvalidate/agdvalidate.go
@@ -0,0 +1,37 @@
+// Package agdvalidate contains validation utilities.
+package agdvalidate
+
+import "fmt"
+
+// FirstNonIDRune returns the first non-printable or non-ASCII rune and its
+// index. If includeSlashes is true, it also looks for slashes. If there are
+// no such runes, i is -1.
+func FirstNonIDRune(s string, excludeSlashes bool) (i int, r rune) {
+ for i, r = range s {
+ if r < '!' || r > '~' || (excludeSlashes && r == '/') {
+ return i, r
+ }
+ }
+
+ return -1, 0
+}
+
+// Unit name constants.
+const (
+ UnitByte = "bytes"
+ UnitRune = "runes"
+)
+
+// Inclusion returns an error if n is greater than maxVal or less than minVal.
+// unitName is used for error messages, see [UnitByte] and the related
+// constants.
+func Inclusion(n, minVal, maxVal int, unitName string) (err error) {
+ switch {
+ case n > maxVal:
+ return fmt.Errorf("too long: got %d %s, max %d", n, unitName, maxVal)
+ case n < minVal:
+ return fmt.Errorf("too short: got %d %s, min %d", n, unitName, minVal)
+ default:
+ return nil
+ }
+}
diff --git a/internal/backendpb/backendpb.go b/internal/backendpb/backendpb.go
index 45dc3b1..2a160e3 100644
--- a/internal/backendpb/backendpb.go
+++ b/internal/backendpb/backendpb.go
@@ -28,7 +28,11 @@ func newClient(apiURL *url.URL) (client *grpc.ClientConn, err error) {
return nil, fmt.Errorf("bad grpc url scheme %q", s)
}
- conn, err := grpc.NewClient(apiURL.Host, grpc.WithTransportCredentials(creds))
+ conn, err := grpc.NewClient(
+ apiURL.Host,
+ grpc.WithDisableServiceConfig(),
+ grpc.WithTransportCredentials(creds),
+ )
if err != nil {
return nil, fmt.Errorf("dialing: %w", err)
}
diff --git a/internal/backendpb/backendpb_internal_test.go b/internal/backendpb/backendpb_internal_test.go
index 7c012ed..3554d07 100644
--- a/internal/backendpb/backendpb_internal_test.go
+++ b/internal/backendpb/backendpb_internal_test.go
@@ -24,8 +24,8 @@ const (
TestProfileID agd.ProfileID = TestProfileIDStr
)
-// TestUpdTime is the common update time for tests.
-var TestUpdTime = time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
+// TestSyncTime is the common update time for tests.
+var TestSyncTime = time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
// TestBind includes any IPv4 address.
//
diff --git a/internal/backendpb/dns.pb.go b/internal/backendpb/dns.pb.go
index 0c07bdb..fe8dfb0 100644
--- a/internal/backendpb/dns.pb.go
+++ b/internal/backendpb/dns.pb.go
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
-// protoc-gen-go v1.35.2
-// protoc v5.28.3
+// protoc-gen-go v1.36.5
+// protoc v5.29.1
// source: dns.proto
package backendpb
@@ -14,6 +14,7 @@ import (
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
+ unsafe "unsafe"
)
const (
@@ -94,9 +95,9 @@ func (DeviceType) EnumDescriptor() ([]byte, []int) {
}
type RateLimitSettingsRequest struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *RateLimitSettingsRequest) Reset() {
@@ -130,11 +131,10 @@ func (*RateLimitSettingsRequest) Descriptor() ([]byte, []int) {
}
type RateLimitSettingsResponse struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- AllowedSubnets []*CidrRange `protobuf:"bytes,1,rep,name=allowed_subnets,json=allowedSubnets,proto3" json:"allowed_subnets,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ AllowedSubnets []*CidrRange `protobuf:"bytes,1,rep,name=allowed_subnets,json=allowedSubnets,proto3" json:"allowed_subnets,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *RateLimitSettingsResponse) Reset() {
@@ -175,11 +175,10 @@ func (x *RateLimitSettingsResponse) GetAllowedSubnets() []*CidrRange {
}
type DNSProfilesRequest struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ SyncTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=sync_time,json=syncTime,proto3" json:"sync_time,omitempty"`
unknownFields protoimpl.UnknownFields
-
- SyncTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=sync_time,json=syncTime,proto3" json:"sync_time,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *DNSProfilesRequest) Reset() {
@@ -220,23 +219,20 @@ func (x *DNSProfilesRequest) GetSyncTime() *timestamppb.Timestamp {
}
type DNSProfile struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- DnsId string `protobuf:"bytes,1,opt,name=dns_id,json=dnsId,proto3" json:"dns_id,omitempty"`
- FilteringEnabled bool `protobuf:"varint,2,opt,name=filtering_enabled,json=filteringEnabled,proto3" json:"filtering_enabled,omitempty"`
- QueryLogEnabled bool `protobuf:"varint,3,opt,name=query_log_enabled,json=queryLogEnabled,proto3" json:"query_log_enabled,omitempty"`
- Deleted bool `protobuf:"varint,4,opt,name=deleted,proto3" json:"deleted,omitempty"`
- SafeBrowsing *SafeBrowsingSettings `protobuf:"bytes,5,opt,name=safe_browsing,json=safeBrowsing,proto3" json:"safe_browsing,omitempty"`
- Parental *ParentalSettings `protobuf:"bytes,6,opt,name=parental,proto3" json:"parental,omitempty"`
- RuleLists *RuleListsSettings `protobuf:"bytes,7,opt,name=rule_lists,json=ruleLists,proto3" json:"rule_lists,omitempty"`
- Devices []*DeviceSettings `protobuf:"bytes,8,rep,name=devices,proto3" json:"devices,omitempty"`
- CustomRules []string `protobuf:"bytes,9,rep,name=custom_rules,json=customRules,proto3" json:"custom_rules,omitempty"`
- FilteredResponseTtl *durationpb.Duration `protobuf:"bytes,10,opt,name=filtered_response_ttl,json=filteredResponseTtl,proto3" json:"filtered_response_ttl,omitempty"`
- BlockPrivateRelay bool `protobuf:"varint,11,opt,name=block_private_relay,json=blockPrivateRelay,proto3" json:"block_private_relay,omitempty"`
- BlockFirefoxCanary bool `protobuf:"varint,12,opt,name=block_firefox_canary,json=blockFirefoxCanary,proto3" json:"block_firefox_canary,omitempty"`
- // Types that are assignable to BlockingMode:
+ state protoimpl.MessageState `protogen:"open.v1"`
+ DnsId string `protobuf:"bytes,1,opt,name=dns_id,json=dnsId,proto3" json:"dns_id,omitempty"`
+ FilteringEnabled bool `protobuf:"varint,2,opt,name=filtering_enabled,json=filteringEnabled,proto3" json:"filtering_enabled,omitempty"`
+ QueryLogEnabled bool `protobuf:"varint,3,opt,name=query_log_enabled,json=queryLogEnabled,proto3" json:"query_log_enabled,omitempty"`
+ Deleted bool `protobuf:"varint,4,opt,name=deleted,proto3" json:"deleted,omitempty"`
+ SafeBrowsing *SafeBrowsingSettings `protobuf:"bytes,5,opt,name=safe_browsing,json=safeBrowsing,proto3" json:"safe_browsing,omitempty"`
+ Parental *ParentalSettings `protobuf:"bytes,6,opt,name=parental,proto3" json:"parental,omitempty"`
+ RuleLists *RuleListsSettings `protobuf:"bytes,7,opt,name=rule_lists,json=ruleLists,proto3" json:"rule_lists,omitempty"`
+ Devices []*DeviceSettings `protobuf:"bytes,8,rep,name=devices,proto3" json:"devices,omitempty"`
+ CustomRules []string `protobuf:"bytes,9,rep,name=custom_rules,json=customRules,proto3" json:"custom_rules,omitempty"`
+ FilteredResponseTtl *durationpb.Duration `protobuf:"bytes,10,opt,name=filtered_response_ttl,json=filteredResponseTtl,proto3" json:"filtered_response_ttl,omitempty"`
+ BlockPrivateRelay bool `protobuf:"varint,11,opt,name=block_private_relay,json=blockPrivateRelay,proto3" json:"block_private_relay,omitempty"`
+ BlockFirefoxCanary bool `protobuf:"varint,12,opt,name=block_firefox_canary,json=blockFirefoxCanary,proto3" json:"block_firefox_canary,omitempty"`
+ // Types that are valid to be assigned to BlockingMode:
//
// *DNSProfile_BlockingModeCustomIp
// *DNSProfile_BlockingModeNxdomain
@@ -248,6 +244,8 @@ type DNSProfile struct {
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"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *DNSProfile) Reset() {
@@ -364,37 +362,45 @@ func (x *DNSProfile) GetBlockFirefoxCanary() bool {
return false
}
-func (m *DNSProfile) GetBlockingMode() isDNSProfile_BlockingMode {
- if m != nil {
- return m.BlockingMode
+func (x *DNSProfile) GetBlockingMode() isDNSProfile_BlockingMode {
+ if x != nil {
+ return x.BlockingMode
}
return nil
}
func (x *DNSProfile) GetBlockingModeCustomIp() *BlockingModeCustomIP {
- if x, ok := x.GetBlockingMode().(*DNSProfile_BlockingModeCustomIp); ok {
- return x.BlockingModeCustomIp
+ if x != nil {
+ if x, ok := x.BlockingMode.(*DNSProfile_BlockingModeCustomIp); ok {
+ return x.BlockingModeCustomIp
+ }
}
return nil
}
func (x *DNSProfile) GetBlockingModeNxdomain() *BlockingModeNXDOMAIN {
- if x, ok := x.GetBlockingMode().(*DNSProfile_BlockingModeNxdomain); ok {
- return x.BlockingModeNxdomain
+ if x != nil {
+ if x, ok := x.BlockingMode.(*DNSProfile_BlockingModeNxdomain); ok {
+ return x.BlockingModeNxdomain
+ }
}
return nil
}
func (x *DNSProfile) GetBlockingModeNullIp() *BlockingModeNullIP {
- if x, ok := x.GetBlockingMode().(*DNSProfile_BlockingModeNullIp); ok {
- return x.BlockingModeNullIp
+ if x != nil {
+ if x, ok := x.BlockingMode.(*DNSProfile_BlockingModeNullIp); ok {
+ return x.BlockingModeNullIp
+ }
}
return nil
}
func (x *DNSProfile) GetBlockingModeRefused() *BlockingModeREFUSED {
- if x, ok := x.GetBlockingMode().(*DNSProfile_BlockingModeRefused); ok {
- return x.BlockingModeRefused
+ if x != nil {
+ if x, ok := x.BlockingMode.(*DNSProfile_BlockingModeRefused); ok {
+ return x.BlockingModeRefused
+ }
}
return nil
}
@@ -463,13 +469,12 @@ func (*DNSProfile_BlockingModeNullIp) isDNSProfile_BlockingMode() {}
func (*DNSProfile_BlockingModeRefused) isDNSProfile_BlockingMode() {}
type SafeBrowsingSettings struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
- BlockDangerousDomains bool `protobuf:"varint,2,opt,name=block_dangerous_domains,json=blockDangerousDomains,proto3" json:"block_dangerous_domains,omitempty"`
- BlockNrd bool `protobuf:"varint,3,opt,name=block_nrd,json=blockNrd,proto3" json:"block_nrd,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
+ BlockDangerousDomains bool `protobuf:"varint,2,opt,name=block_dangerous_domains,json=blockDangerousDomains,proto3" json:"block_dangerous_domains,omitempty"`
+ BlockNrd bool `protobuf:"varint,3,opt,name=block_nrd,json=blockNrd,proto3" json:"block_nrd,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *SafeBrowsingSettings) Reset() {
@@ -524,10 +529,7 @@ func (x *SafeBrowsingSettings) GetBlockNrd() bool {
}
type DeviceSettings struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
+ state protoimpl.MessageState `protogen:"open.v1"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
FilteringEnabled bool `protobuf:"varint,3,opt,name=filtering_enabled,json=filteringEnabled,proto3" json:"filtering_enabled,omitempty"`
@@ -535,7 +537,9 @@ type DeviceSettings struct {
DedicatedIps [][]byte `protobuf:"bytes,5,rep,name=dedicated_ips,json=dedicatedIps,proto3" json:"dedicated_ips,omitempty"`
Authentication *AuthenticationSettings `protobuf:"bytes,6,opt,name=authentication,proto3" json:"authentication,omitempty"`
// Value in lower case. Will be empty for "ordinary" devices and non-empty for "automatically" created devices.
- HumanIdLower string `protobuf:"bytes,7,opt,name=human_id_lower,json=humanIdLower,proto3" json:"human_id_lower,omitempty"`
+ HumanIdLower string `protobuf:"bytes,7,opt,name=human_id_lower,json=humanIdLower,proto3" json:"human_id_lower,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *DeviceSettings) Reset() {
@@ -618,16 +622,15 @@ func (x *DeviceSettings) GetHumanIdLower() string {
}
type ParentalSettings struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
- BlockAdult bool `protobuf:"varint,2,opt,name=block_adult,json=blockAdult,proto3" json:"block_adult,omitempty"`
- GeneralSafeSearch bool `protobuf:"varint,3,opt,name=general_safe_search,json=generalSafeSearch,proto3" json:"general_safe_search,omitempty"`
- YoutubeSafeSearch bool `protobuf:"varint,4,opt,name=youtube_safe_search,json=youtubeSafeSearch,proto3" json:"youtube_safe_search,omitempty"`
- BlockedServices []string `protobuf:"bytes,5,rep,name=blocked_services,json=blockedServices,proto3" json:"blocked_services,omitempty"`
- Schedule *ScheduleSettings `protobuf:"bytes,6,opt,name=schedule,proto3" json:"schedule,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
+ BlockAdult bool `protobuf:"varint,2,opt,name=block_adult,json=blockAdult,proto3" json:"block_adult,omitempty"`
+ GeneralSafeSearch bool `protobuf:"varint,3,opt,name=general_safe_search,json=generalSafeSearch,proto3" json:"general_safe_search,omitempty"`
+ YoutubeSafeSearch bool `protobuf:"varint,4,opt,name=youtube_safe_search,json=youtubeSafeSearch,proto3" json:"youtube_safe_search,omitempty"`
+ BlockedServices []string `protobuf:"bytes,5,rep,name=blocked_services,json=blockedServices,proto3" json:"blocked_services,omitempty"`
+ Schedule *ScheduleSettings `protobuf:"bytes,6,opt,name=schedule,proto3" json:"schedule,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *ParentalSettings) Reset() {
@@ -703,12 +706,11 @@ func (x *ParentalSettings) GetSchedule() *ScheduleSettings {
}
type ScheduleSettings struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Tmz string `protobuf:"bytes,1,opt,name=tmz,proto3" json:"tmz,omitempty"`
+ WeeklyRange *WeeklyRange `protobuf:"bytes,2,opt,name=weeklyRange,proto3" json:"weeklyRange,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Tmz string `protobuf:"bytes,1,opt,name=tmz,proto3" json:"tmz,omitempty"`
- WeeklyRange *WeeklyRange `protobuf:"bytes,2,opt,name=weeklyRange,proto3" json:"weeklyRange,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *ScheduleSettings) Reset() {
@@ -756,17 +758,16 @@ func (x *ScheduleSettings) GetWeeklyRange() *WeeklyRange {
}
type WeeklyRange struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Mon *DayRange `protobuf:"bytes,1,opt,name=mon,proto3" json:"mon,omitempty"`
+ Tue *DayRange `protobuf:"bytes,2,opt,name=tue,proto3" json:"tue,omitempty"`
+ Wed *DayRange `protobuf:"bytes,3,opt,name=wed,proto3" json:"wed,omitempty"`
+ Thu *DayRange `protobuf:"bytes,4,opt,name=thu,proto3" json:"thu,omitempty"`
+ Fri *DayRange `protobuf:"bytes,5,opt,name=fri,proto3" json:"fri,omitempty"`
+ Sat *DayRange `protobuf:"bytes,6,opt,name=sat,proto3" json:"sat,omitempty"`
+ Sun *DayRange `protobuf:"bytes,7,opt,name=sun,proto3" json:"sun,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Mon *DayRange `protobuf:"bytes,1,opt,name=mon,proto3" json:"mon,omitempty"`
- Tue *DayRange `protobuf:"bytes,2,opt,name=tue,proto3" json:"tue,omitempty"`
- Wed *DayRange `protobuf:"bytes,3,opt,name=wed,proto3" json:"wed,omitempty"`
- Thu *DayRange `protobuf:"bytes,4,opt,name=thu,proto3" json:"thu,omitempty"`
- Fri *DayRange `protobuf:"bytes,5,opt,name=fri,proto3" json:"fri,omitempty"`
- Sat *DayRange `protobuf:"bytes,6,opt,name=sat,proto3" json:"sat,omitempty"`
- Sun *DayRange `protobuf:"bytes,7,opt,name=sun,proto3" json:"sun,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *WeeklyRange) Reset() {
@@ -849,12 +850,11 @@ func (x *WeeklyRange) GetSun() *DayRange {
}
type DayRange struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Start *durationpb.Duration `protobuf:"bytes,1,opt,name=start,proto3" json:"start,omitempty"`
+ End *durationpb.Duration `protobuf:"bytes,2,opt,name=end,proto3" json:"end,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Start *durationpb.Duration `protobuf:"bytes,1,opt,name=start,proto3" json:"start,omitempty"`
- End *durationpb.Duration `protobuf:"bytes,2,opt,name=end,proto3" json:"end,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *DayRange) Reset() {
@@ -902,12 +902,11 @@ func (x *DayRange) GetEnd() *durationpb.Duration {
}
type RuleListsSettings struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
+ Ids []string `protobuf:"bytes,2,rep,name=ids,proto3" json:"ids,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
- Ids []string `protobuf:"bytes,2,rep,name=ids,proto3" json:"ids,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *RuleListsSettings) Reset() {
@@ -955,12 +954,11 @@ func (x *RuleListsSettings) GetIds() []string {
}
type BlockingModeCustomIP struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Ipv4 []byte `protobuf:"bytes,1,opt,name=ipv4,proto3" json:"ipv4,omitempty"`
+ Ipv6 []byte `protobuf:"bytes,2,opt,name=ipv6,proto3" json:"ipv6,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Ipv4 []byte `protobuf:"bytes,1,opt,name=ipv4,proto3" json:"ipv4,omitempty"`
- Ipv6 []byte `protobuf:"bytes,2,opt,name=ipv6,proto3" json:"ipv6,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *BlockingModeCustomIP) Reset() {
@@ -1008,9 +1006,9 @@ func (x *BlockingModeCustomIP) GetIpv6() []byte {
}
type BlockingModeNXDOMAIN struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *BlockingModeNXDOMAIN) Reset() {
@@ -1044,9 +1042,9 @@ func (*BlockingModeNXDOMAIN) Descriptor() ([]byte, []int) {
}
type BlockingModeNullIP struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *BlockingModeNullIP) Reset() {
@@ -1080,9 +1078,9 @@ func (*BlockingModeNullIP) Descriptor() ([]byte, []int) {
}
type BlockingModeREFUSED struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *BlockingModeREFUSED) Reset() {
@@ -1116,17 +1114,16 @@ func (*BlockingModeREFUSED) Descriptor() ([]byte, []int) {
}
type DeviceBillingStat struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
+ state protoimpl.MessageState `protogen:"open.v1"`
LastActivityTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=last_activity_time,json=lastActivityTime,proto3" json:"last_activity_time,omitempty"`
DeviceId string `protobuf:"bytes,2,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"`
ClientCountry string `protobuf:"bytes,3,opt,name=client_country,json=clientCountry,proto3" json:"client_country,omitempty"`
// Protocol type. Possible values see here: https://bit.adguard.com/projects/DNS/repos/dns-server/browse#ql-properties
- Proto uint32 `protobuf:"varint,4,opt,name=proto,proto3" json:"proto,omitempty"`
- Asn uint32 `protobuf:"varint,5,opt,name=asn,proto3" json:"asn,omitempty"`
- Queries uint32 `protobuf:"varint,6,opt,name=queries,proto3" json:"queries,omitempty"`
+ Proto uint32 `protobuf:"varint,4,opt,name=proto,proto3" json:"proto,omitempty"`
+ Asn uint32 `protobuf:"varint,5,opt,name=asn,proto3" json:"asn,omitempty"`
+ Queries uint32 `protobuf:"varint,6,opt,name=queries,proto3" json:"queries,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *DeviceBillingStat) Reset() {
@@ -1202,16 +1199,15 @@ func (x *DeviceBillingStat) GetQueries() uint32 {
}
type AccessSettings struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- AllowlistCidr []*CidrRange `protobuf:"bytes,1,rep,name=allowlist_cidr,json=allowlistCidr,proto3" json:"allowlist_cidr,omitempty"`
- BlocklistCidr []*CidrRange `protobuf:"bytes,2,rep,name=blocklist_cidr,json=blocklistCidr,proto3" json:"blocklist_cidr,omitempty"`
- AllowlistAsn []uint32 `protobuf:"varint,3,rep,packed,name=allowlist_asn,json=allowlistAsn,proto3" json:"allowlist_asn,omitempty"`
- BlocklistAsn []uint32 `protobuf:"varint,4,rep,packed,name=blocklist_asn,json=blocklistAsn,proto3" json:"blocklist_asn,omitempty"`
- BlocklistDomainRules []string `protobuf:"bytes,5,rep,name=blocklist_domain_rules,json=blocklistDomainRules,proto3" json:"blocklist_domain_rules,omitempty"`
- Enabled bool `protobuf:"varint,6,opt,name=enabled,proto3" json:"enabled,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ AllowlistCidr []*CidrRange `protobuf:"bytes,1,rep,name=allowlist_cidr,json=allowlistCidr,proto3" json:"allowlist_cidr,omitempty"`
+ BlocklistCidr []*CidrRange `protobuf:"bytes,2,rep,name=blocklist_cidr,json=blocklistCidr,proto3" json:"blocklist_cidr,omitempty"`
+ AllowlistAsn []uint32 `protobuf:"varint,3,rep,packed,name=allowlist_asn,json=allowlistAsn,proto3" json:"allowlist_asn,omitempty"`
+ BlocklistAsn []uint32 `protobuf:"varint,4,rep,packed,name=blocklist_asn,json=blocklistAsn,proto3" json:"blocklist_asn,omitempty"`
+ BlocklistDomainRules []string `protobuf:"bytes,5,rep,name=blocklist_domain_rules,json=blocklistDomainRules,proto3" json:"blocklist_domain_rules,omitempty"`
+ Enabled bool `protobuf:"varint,6,opt,name=enabled,proto3" json:"enabled,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *AccessSettings) Reset() {
@@ -1287,12 +1283,11 @@ func (x *AccessSettings) GetEnabled() bool {
}
type CidrRange struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
+ Prefix uint32 `protobuf:"varint,2,opt,name=prefix,proto3" json:"prefix,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
- Prefix uint32 `protobuf:"varint,2,opt,name=prefix,proto3" json:"prefix,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *CidrRange) Reset() {
@@ -1340,15 +1335,14 @@ func (x *CidrRange) GetPrefix() uint32 {
}
type AuthenticationSettings struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- DohAuthOnly bool `protobuf:"varint,1,opt,name=doh_auth_only,json=dohAuthOnly,proto3" json:"doh_auth_only,omitempty"`
- // Types that are assignable to DohPasswordHash:
+ state protoimpl.MessageState `protogen:"open.v1"`
+ DohAuthOnly bool `protobuf:"varint,1,opt,name=doh_auth_only,json=dohAuthOnly,proto3" json:"doh_auth_only,omitempty"`
+ // Types that are valid to be assigned to DohPasswordHash:
//
// *AuthenticationSettings_PasswordHashBcrypt
DohPasswordHash isAuthenticationSettings_DohPasswordHash `protobuf_oneof:"doh_password_hash"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *AuthenticationSettings) Reset() {
@@ -1388,16 +1382,18 @@ func (x *AuthenticationSettings) GetDohAuthOnly() bool {
return false
}
-func (m *AuthenticationSettings) GetDohPasswordHash() isAuthenticationSettings_DohPasswordHash {
- if m != nil {
- return m.DohPasswordHash
+func (x *AuthenticationSettings) GetDohPasswordHash() isAuthenticationSettings_DohPasswordHash {
+ if x != nil {
+ return x.DohPasswordHash
}
return nil
}
func (x *AuthenticationSettings) GetPasswordHashBcrypt() []byte {
- if x, ok := x.GetDohPasswordHash().(*AuthenticationSettings_PasswordHashBcrypt); ok {
- return x.PasswordHashBcrypt
+ if x != nil {
+ if x, ok := x.DohPasswordHash.(*AuthenticationSettings_PasswordHashBcrypt); ok {
+ return x.PasswordHashBcrypt
+ }
}
return nil
}
@@ -1413,13 +1409,12 @@ type AuthenticationSettings_PasswordHashBcrypt struct {
func (*AuthenticationSettings_PasswordHashBcrypt) isAuthenticationSettings_DohPasswordHash() {}
type CreateDeviceRequest struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ DnsId string `protobuf:"bytes,1,opt,name=dns_id,json=dnsId,proto3" json:"dns_id,omitempty"`
+ HumanId string `protobuf:"bytes,2,opt,name=human_id,json=humanId,proto3" json:"human_id,omitempty"`
+ DeviceType DeviceType `protobuf:"varint,3,opt,name=device_type,json=deviceType,proto3,enum=DeviceType" json:"device_type,omitempty"`
unknownFields protoimpl.UnknownFields
-
- DnsId string `protobuf:"bytes,1,opt,name=dns_id,json=dnsId,proto3" json:"dns_id,omitempty"`
- HumanId string `protobuf:"bytes,2,opt,name=human_id,json=humanId,proto3" json:"human_id,omitempty"`
- DeviceType DeviceType `protobuf:"varint,3,opt,name=device_type,json=deviceType,proto3,enum=DeviceType" json:"device_type,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *CreateDeviceRequest) Reset() {
@@ -1474,11 +1469,10 @@ func (x *CreateDeviceRequest) GetDeviceType() DeviceType {
}
type CreateDeviceResponse struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Device *DeviceSettings `protobuf:"bytes,1,opt,name=device,proto3" json:"device,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Device *DeviceSettings `protobuf:"bytes,1,opt,name=device,proto3" json:"device,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *CreateDeviceResponse) Reset() {
@@ -1519,12 +1513,11 @@ func (x *CreateDeviceResponse) GetDevice() *DeviceSettings {
}
type RateLimitedError struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
+ RetryDelay *durationpb.Duration `protobuf:"bytes,2,opt,name=retry_delay,json=retryDelay,proto3" json:"retry_delay,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
- RetryDelay *durationpb.Duration `protobuf:"bytes,2,opt,name=retry_delay,json=retryDelay,proto3" json:"retry_delay,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *RateLimitedError) Reset() {
@@ -1572,11 +1565,10 @@ func (x *RateLimitedError) GetRetryDelay() *durationpb.Duration {
}
type DeviceQuotaExceededError struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *DeviceQuotaExceededError) Reset() {
@@ -1617,11 +1609,10 @@ func (x *DeviceQuotaExceededError) GetMessage() string {
}
type BadRequestError struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *BadRequestError) Reset() {
@@ -1662,11 +1653,10 @@ func (x *BadRequestError) GetMessage() string {
}
type AuthenticationFailedError struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *AuthenticationFailedError) Reset() {
@@ -1707,13 +1697,12 @@ func (x *AuthenticationFailedError) GetMessage() string {
}
type RateLimitSettings struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
+ Rps uint32 `protobuf:"varint,2,opt,name=rps,proto3" json:"rps,omitempty"`
+ ClientCidr []*CidrRange `protobuf:"bytes,3,rep,name=client_cidr,json=clientCidr,proto3" json:"client_cidr,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
- Rps uint32 `protobuf:"varint,2,opt,name=rps,proto3" json:"rps,omitempty"`
- ClientCidr []*CidrRange `protobuf:"bytes,3,rep,name=client_cidr,json=clientCidr,proto3" json:"client_cidr,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *RateLimitSettings) Reset() {
@@ -1768,11 +1757,10 @@ func (x *RateLimitSettings) GetClientCidr() []*CidrRange {
}
type RemoteKVGetRequest struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *RemoteKVGetRequest) Reset() {
@@ -1813,15 +1801,14 @@ func (x *RemoteKVGetRequest) GetKey() string {
}
type RemoteKVGetResponse struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- // Types that are assignable to Value:
+ state protoimpl.MessageState `protogen:"open.v1"`
+ // Types that are valid to be assigned to Value:
//
// *RemoteKVGetResponse_Data
// *RemoteKVGetResponse_Empty
- Value isRemoteKVGetResponse_Value `protobuf_oneof:"value"`
+ Value isRemoteKVGetResponse_Value `protobuf_oneof:"value"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *RemoteKVGetResponse) Reset() {
@@ -1854,23 +1841,27 @@ func (*RemoteKVGetResponse) Descriptor() ([]byte, []int) {
return file_dns_proto_rawDescGZIP(), []int{27}
}
-func (m *RemoteKVGetResponse) GetValue() isRemoteKVGetResponse_Value {
- if m != nil {
- return m.Value
+func (x *RemoteKVGetResponse) GetValue() isRemoteKVGetResponse_Value {
+ if x != nil {
+ return x.Value
}
return nil
}
func (x *RemoteKVGetResponse) GetData() []byte {
- if x, ok := x.GetValue().(*RemoteKVGetResponse_Data); ok {
- return x.Data
+ if x != nil {
+ if x, ok := x.Value.(*RemoteKVGetResponse_Data); ok {
+ return x.Data
+ }
}
return nil
}
func (x *RemoteKVGetResponse) GetEmpty() *emptypb.Empty {
- if x, ok := x.GetValue().(*RemoteKVGetResponse_Empty); ok {
- return x.Empty
+ if x != nil {
+ if x, ok := x.Value.(*RemoteKVGetResponse_Empty); ok {
+ return x.Empty
+ }
}
return nil
}
@@ -1892,13 +1883,12 @@ func (*RemoteKVGetResponse_Data) isRemoteKVGetResponse_Value() {}
func (*RemoteKVGetResponse_Empty) isRemoteKVGetResponse_Value() {}
type RemoteKVSetRequest struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
+ Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
+ Ttl *durationpb.Duration `protobuf:"bytes,3,opt,name=ttl,proto3" json:"ttl,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
- Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
- Ttl *durationpb.Duration `protobuf:"bytes,3,opt,name=ttl,proto3" json:"ttl,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *RemoteKVSetRequest) Reset() {
@@ -1953,9 +1943,9 @@ func (x *RemoteKVSetRequest) GetTtl() *durationpb.Duration {
}
type RemoteKVSetResponse struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *RemoteKVSetResponse) Reset() {
@@ -1990,7 +1980,7 @@ func (*RemoteKVSetResponse) Descriptor() ([]byte, []int) {
var File_dns_proto protoreflect.FileDescriptor
-var file_dns_proto_rawDesc = []byte{
+var file_dns_proto_rawDesc = string([]byte{
0x0a, 0x09, 0x64, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f,
@@ -2295,16 +2285,16 @@ var file_dns_proto_rawDesc = []byte{
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 (
file_dns_proto_rawDescOnce sync.Once
- file_dns_proto_rawDescData = file_dns_proto_rawDesc
+ file_dns_proto_rawDescData []byte
)
func file_dns_proto_rawDescGZIP() []byte {
file_dns_proto_rawDescOnce.Do(func() {
- file_dns_proto_rawDescData = protoimpl.X.CompressGZIP(file_dns_proto_rawDescData)
+ file_dns_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_dns_proto_rawDesc), len(file_dns_proto_rawDesc)))
})
return file_dns_proto_rawDescData
}
@@ -2423,7 +2413,7 @@ func file_dns_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
- RawDescriptor: file_dns_proto_rawDesc,
+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_dns_proto_rawDesc), len(file_dns_proto_rawDesc)),
NumEnums: 1,
NumMessages: 30,
NumExtensions: 0,
@@ -2435,7 +2425,6 @@ func file_dns_proto_init() {
MessageInfos: file_dns_proto_msgTypes,
}.Build()
File_dns_proto = out.File
- file_dns_proto_rawDesc = nil
file_dns_proto_goTypes = nil
file_dns_proto_depIdxs = nil
}
diff --git a/internal/backendpb/dns_grpc.pb.go b/internal/backendpb/dns_grpc.pb.go
index 945451c..38bdf21 100644
--- a/internal/backendpb/dns_grpc.pb.go
+++ b/internal/backendpb/dns_grpc.pb.go
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
-// - protoc v5.28.3
+// - protoc v5.29.1
// source: dns.proto
package backendpb
diff --git a/internal/backendpb/profile.go b/internal/backendpb/profile.go
index ec6e8d3..20bdf8c 100644
--- a/internal/backendpb/profile.go
+++ b/internal/backendpb/profile.go
@@ -13,6 +13,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter/custom"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
@@ -25,10 +26,10 @@ import (
// TODO(a.garipov): Refactor into methods of [*ProfileStorage].
func (x *DNSProfile) toInternal(
ctx context.Context,
- updTime time.Time,
bindSet netutil.SubnetSet,
errColl errcoll.Interface,
logger *slog.Logger,
+ baseCustomLogger *slog.Logger,
mtrc ProfileDBMetrics,
respSzEst datasize.ByteSize,
) (profile *agd.Profile, devices []*agd.Device, err error) {
@@ -59,12 +60,20 @@ func (x *DNSProfile) toInternal(
}
customRules := rulesToInternal(ctx, x.CustomRules, errColl, logger)
+ customEnabled := len(customRules) > 0
+
+ var customFilter filter.Custom
+ if customEnabled {
+ customFilter = custom.New(&custom.Config{
+ Logger: baseCustomLogger.With("client_id", string(profID)),
+ Rules: customRules,
+ })
+ }
+
custom := &filter.ConfigCustom{
- ID: string(x.DnsId),
- UpdateTime: updTime,
- Rules: customRules,
+ Filter: customFilter,
// TODO(a.garipov): Consider adding an explicit flag to the protocol.
- Enabled: len(customRules) > 0,
+ Enabled: customEnabled,
}
return &agd.Profile{
diff --git a/internal/backendpb/profile_internal_test.go b/internal/backendpb/profile_internal_test.go
index 7ef9dc4..eeece71 100644
--- a/internal/backendpb/profile_internal_test.go
+++ b/internal/backendpb/profile_internal_test.go
@@ -13,7 +13,9 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter/custom"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/c2h5oh/datasize"
"github.com/stretchr/testify/assert"
@@ -33,10 +35,10 @@ func TestDNSProfile_ToInternal(t *testing.T) {
got, gotDevices, err := NewTestDNSProfile(t).toInternal(
ctx,
- TestUpdTime,
TestBind,
errColl,
TestLogger,
+ TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
@@ -57,10 +59,10 @@ func TestDNSProfile_ToInternal(t *testing.T) {
}
got, gotDevices, err := newDNSProfileWithBadData(t).toInternal(
ctx,
- TestUpdTime,
TestBind,
savingErrColl,
TestLogger,
+ TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
@@ -89,10 +91,10 @@ func TestDNSProfile_ToInternal(t *testing.T) {
bindSet := netip.MustParsePrefix("2.2.2.2/32")
got, gotDevices, err := NewTestDNSProfile(t).toInternal(
ctx,
- TestUpdTime,
bindSet,
savingErrColl,
TestLogger,
+ TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
@@ -115,10 +117,10 @@ func TestDNSProfile_ToInternal(t *testing.T) {
var emptyDNSProfile *DNSProfile
_, _, err := emptyDNSProfile.toInternal(
ctx,
- TestUpdTime,
TestBind,
errColl,
TestLogger,
+ TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
@@ -135,10 +137,10 @@ func TestDNSProfile_ToInternal(t *testing.T) {
got, gotDevices, err := dp.toInternal(
ctx,
- TestUpdTime,
TestBind,
errColl,
TestLogger,
+ TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
@@ -158,10 +160,10 @@ func TestDNSProfile_ToInternal(t *testing.T) {
_, _, err := dp.toInternal(
ctx,
- TestUpdTime,
TestBind,
errColl,
TestLogger,
+ TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
@@ -183,10 +185,10 @@ func TestDNSProfile_ToInternal(t *testing.T) {
_, _, err := dp.toInternal(
ctx,
- TestUpdTime,
TestBind,
errColl,
TestLogger,
+ TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
@@ -206,10 +208,10 @@ func TestDNSProfile_ToInternal(t *testing.T) {
_, _, err := dp.toInternal(
ctx,
- TestUpdTime,
TestBind,
errColl,
TestLogger,
+ TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
@@ -225,10 +227,10 @@ func TestDNSProfile_ToInternal(t *testing.T) {
_, _, err := dp.toInternal(
ctx,
- TestUpdTime,
TestBind,
errColl,
TestLogger,
+ TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
@@ -245,10 +247,10 @@ func TestDNSProfile_ToInternal(t *testing.T) {
_, _, err := dp.toInternal(
ctx,
- TestUpdTime,
TestBind,
errColl,
TestLogger,
+ TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
@@ -263,10 +265,10 @@ func TestDNSProfile_ToInternal(t *testing.T) {
got, gotDevices, err := dp.toInternal(
ctx,
- TestUpdTime,
TestBind,
errColl,
TestLogger,
+ TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
@@ -288,10 +290,10 @@ func TestDNSProfile_ToInternal(t *testing.T) {
got, _, err := dp.toInternal(
ctx,
- TestUpdTime,
TestBind,
errColl,
TestLogger,
+ TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
@@ -312,10 +314,10 @@ func TestDNSProfile_ToInternal(t *testing.T) {
got, _, err := dp.toInternal(
ctx,
- TestUpdTime,
TestBind,
errColl,
TestLogger,
+ TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
@@ -504,6 +506,14 @@ func newProfile(tb testing.TB) (p *agd.Profile) {
End: 60,
}
+ wantCustom := &filter.ConfigCustom{
+ Filter: custom.New(&custom.Config{
+ Logger: slogutil.NewDiscardLogger(),
+ Rules: []filter.RuleText{"||example.org^"},
+ }),
+ Enabled: true,
+ }
+
wantParental := &filter.ConfigParental{
PauseSchedule: &filter.ConfigSchedule{
Week: &filter.WeeklySchedule{
@@ -526,6 +536,11 @@ func newProfile(tb testing.TB) (p *agd.Profile) {
SafeSearchYouTubeEnabled: false,
}
+ wantRuleList := &filter.ConfigRuleList{
+ IDs: []filter.ID{"1"},
+ Enabled: true,
+ }
+
wantSafeBrowsing := &filter.ConfigSafeBrowsing{
Enabled: true,
DangerousDomainsEnabled: true,
@@ -553,17 +568,9 @@ func newProfile(tb testing.TB) (p *agd.Profile) {
return &agd.Profile{
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,
- },
+ Custom: wantCustom,
+ Parental: wantParental,
+ RuleList: wantRuleList,
SafeBrowsing: wantSafeBrowsing,
},
Access: wantAccess,
@@ -654,10 +661,10 @@ func BenchmarkDNSProfile_ToInternal(b *testing.B) {
for range b.N {
profSink, _, errSink = dp.toInternal(
ctx,
- TestUpdTime,
TestBind,
errColl,
TestLogger,
+ TestLogger,
EmptyProfileDBMetrics{},
TestRespSzEst,
)
diff --git a/internal/backendpb/profiledb.go b/internal/backendpb/profiledb.go
index 3477150..ddcd8e6 100644
--- a/internal/backendpb/profiledb.go
+++ b/internal/backendpb/profiledb.go
@@ -23,6 +23,13 @@ import (
// ProfileStorageConfig is the configuration for the business logic backend
// profile storage.
type ProfileStorageConfig struct {
+ // Logger is used for logging the operation of the profile storage. It must
+ // not be nil.
+ Logger *slog.Logger
+
+ // BaseCustomLogger is the base logger used for the custom filters.
+ BaseCustomLogger *slog.Logger
+
// BindSet is the subnet set created from DNS servers listening addresses.
// It must not be nil.
BindSet netutil.SubnetSet
@@ -31,10 +38,6 @@ type ProfileStorageConfig struct {
// non-critical errors. It must not be nil.
ErrColl errcoll.Interface
- // Logger is used for logging the operation of the profile storage. It must
- // not be nil.
- Logger *slog.Logger
-
// GRPCMetrics is used for the collection of the protobuf communication
// statistics.
GRPCMetrics GRPCMetrics
@@ -63,15 +66,16 @@ type ProfileStorageConfig struct {
// that retrieves the profile and device information from the business logic
// backend. It is safe for concurrent use.
type ProfileStorage struct {
- bindSet netutil.SubnetSet
- errColl errcoll.Interface
- client DNSServiceClient
- logger *slog.Logger
- grpcMetrics GRPCMetrics
- metrics ProfileDBMetrics
- apiKey string
- respSzEst datasize.ByteSize
- maxProfSize datasize.ByteSize
+ logger *slog.Logger
+ baseCustomLogger *slog.Logger
+ bindSet netutil.SubnetSet
+ errColl errcoll.Interface
+ client DNSServiceClient
+ grpcMetrics GRPCMetrics
+ metrics ProfileDBMetrics
+ apiKey string
+ respSzEst datasize.ByteSize
+ maxProfSize datasize.ByteSize
}
// NewProfileStorage returns a new [ProfileStorage] that retrieves information
@@ -84,15 +88,16 @@ func NewProfileStorage(c *ProfileStorageConfig) (s *ProfileStorage, err error) {
}
return &ProfileStorage{
- bindSet: c.BindSet,
- errColl: c.ErrColl,
- client: NewDNSServiceClient(client),
- logger: c.Logger,
- grpcMetrics: c.GRPCMetrics,
- metrics: c.Metrics,
- apiKey: c.APIKey,
- respSzEst: c.ResponseSizeEstimate,
- maxProfSize: c.MaxProfilesSize,
+ logger: c.Logger,
+ baseCustomLogger: c.BaseCustomLogger,
+ bindSet: c.BindSet,
+ errColl: c.ErrColl,
+ client: NewDNSServiceClient(client),
+ grpcMetrics: c.GRPCMetrics,
+ metrics: c.Metrics,
+ apiKey: c.APIKey,
+ respSzEst: c.ResponseSizeEstimate,
+ maxProfSize: c.MaxProfilesSize,
}, nil
}
@@ -179,10 +184,10 @@ func (s *ProfileStorage) Profiles(
stats.startDec()
prof, devices, profErr := profile.toInternal(
ctx,
- time.Now(),
s.bindSet,
s.errColl,
s.logger,
+ s.baseCustomLogger,
s.metrics,
s.respSzEst,
)
diff --git a/internal/backendpb/profiledb_internal_test.go b/internal/backendpb/profiledb_internal_test.go
index a81b603..333a97c 100644
--- a/internal/backendpb/profiledb_internal_test.go
+++ b/internal/backendpb/profiledb_internal_test.go
@@ -13,7 +13,7 @@ import (
func TestSyncTimeFromTrailer(t *testing.T) {
t.Parallel()
- milliseconds := strconv.FormatInt(TestUpdTime.UnixMilli(), 10)
+ milliseconds := strconv.FormatInt(TestSyncTime.UnixMilli(), 10)
testCases := []struct {
in metadata.MD
@@ -38,7 +38,7 @@ func TestSyncTimeFromTrailer(t *testing.T) {
}, {
in: metadata.MD{"sync_time": []string{milliseconds}},
wantError: "",
- want: TestUpdTime,
+ want: TestSyncTime,
name: "success",
}}
diff --git a/internal/backendpb/profiledb_test.go b/internal/backendpb/profiledb_test.go
index f872646..d195910 100644
--- a/internal/backendpb/profiledb_test.go
+++ b/internal/backendpb/profiledb_test.go
@@ -66,11 +66,12 @@ func TestProfileStorage_CreateAutoDevice(t *testing.T) {
require.NoError(t, err)
s, err := backendpb.NewProfileStorage(&backendpb.ProfileStorageConfig{
- BindSet: backendpb.TestBind,
- ErrColl: agdtest.NewErrorCollector(),
- Logger: backendpb.TestLogger,
- GRPCMetrics: backendpb.EmptyGRPCMetrics{},
- Metrics: backendpb.EmptyProfileDBMetrics{},
+ BindSet: backendpb.TestBind,
+ ErrColl: agdtest.NewErrorCollector(),
+ Logger: backendpb.TestLogger,
+ BaseCustomLogger: backendpb.TestLogger,
+ GRPCMetrics: backendpb.EmptyGRPCMetrics{},
+ Metrics: backendpb.EmptyProfileDBMetrics{},
Endpoint: &url.URL{
Scheme: "grpc",
Host: l.Addr().String(),
@@ -122,7 +123,7 @@ var (
)
func BenchmarkProfileStorage_Profiles(b *testing.B) {
- syncTime := strconv.FormatInt(backendpb.TestUpdTime.UnixMilli(), 10)
+ syncTime := strconv.FormatInt(backendpb.TestSyncTime.UnixMilli(), 10)
srvProf := backendpb.NewTestDNSProfile(b)
trailerMD := metadata.MD{
"sync_time": []string{syncTime},
@@ -157,11 +158,12 @@ func BenchmarkProfileStorage_Profiles(b *testing.B) {
require.NoError(b, err)
s, err := backendpb.NewProfileStorage(&backendpb.ProfileStorageConfig{
- BindSet: netip.MustParsePrefix("0.0.0.0/0"),
- ErrColl: agdtest.NewErrorCollector(),
- Logger: backendpb.TestLogger,
- GRPCMetrics: backendpb.EmptyGRPCMetrics{},
- Metrics: backendpb.EmptyProfileDBMetrics{},
+ BindSet: netip.MustParsePrefix("0.0.0.0/0"),
+ ErrColl: agdtest.NewErrorCollector(),
+ Logger: backendpb.TestLogger,
+ BaseCustomLogger: backendpb.TestLogger,
+ GRPCMetrics: backendpb.EmptyGRPCMetrics{},
+ Metrics: backendpb.EmptyProfileDBMetrics{},
Endpoint: &url.URL{
Scheme: "grpc",
Host: l.Addr().String(),
diff --git a/internal/backendpb/ratelimiter.go b/internal/backendpb/ratelimiter.go
index 5572752..8cc51ee 100644
--- a/internal/backendpb/ratelimiter.go
+++ b/internal/backendpb/ratelimiter.go
@@ -6,10 +6,10 @@ import (
"log/slog"
"net/url"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/consul"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
+ "github.com/AdguardTeam/golibs/service"
)
// RateLimiterConfig is the configuration structure for the business logic
@@ -41,8 +41,8 @@ type RateLimiterConfig struct {
APIKey string
}
-// RateLimiter is the implementation of the [agdservice.Refresher] interface
-// that retrieves the rate limit settings from the business logic backend.
+// RateLimiter is the implementation of the [service.Refresher] interface that
+// retrieves the rate limit settings from the business logic backend.
type RateLimiter struct {
logger *slog.Logger
grpcMetrics GRPCMetrics
@@ -74,9 +74,9 @@ func NewRateLimiter(c *RateLimiterConfig) (l *RateLimiter, err error) {
}
// type check
-var _ agdservice.Refresher = (*RateLimiter)(nil)
+var _ service.Refresher = (*RateLimiter)(nil)
-// Refresh implements the [agdservice.Refresher] interface for *RateLimiter.
+// Refresh implements the [service.Refresher] interface for *RateLimiter.
func (l *RateLimiter) Refresh(ctx context.Context) (err error) {
l.logger.InfoContext(ctx, "refresh started")
defer l.logger.InfoContext(ctx, "refresh finished")
diff --git a/internal/billstat/metrics.go b/internal/billstat/metrics.go
index 25907da..2218de8 100644
--- a/internal/billstat/metrics.go
+++ b/internal/billstat/metrics.go
@@ -5,11 +5,11 @@ import "context"
// Metrics is an interface that is used for the collection of the billing
// statistics.
type Metrics interface {
- // BufferSizeSet sets the number of stored records to n.
- BufferSizeSet(ctx context.Context, n float64)
+ // SetRecordCount sets the total number of records stored.
+ SetRecordCount(ctx context.Context, count int)
// HandleUploadDuration handles the upload duration of billing statistics.
- HandleUploadDuration(ctx context.Context, dur float64, isSuccess bool)
+ HandleUploadDuration(ctx context.Context, dur float64, err error)
}
// EmptyMetrics is the implementation of the [Metrics] interface that does
@@ -19,8 +19,8 @@ type EmptyMetrics struct{}
// type check
var _ Metrics = EmptyMetrics{}
-// BufferSizeSet implements the [Metrics] interface for EmptyMetrics.
-func (EmptyMetrics) BufferSizeSet(_ context.Context, _ float64) {}
+// SetRecordCount implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) SetRecordCount(_ context.Context, _ int) {}
// HandleUploadDuration implements the [Metrics] interface for EmptyMetrics.
-func (EmptyMetrics) HandleUploadDuration(_ context.Context, _ float64, _ bool) {}
+func (EmptyMetrics) HandleUploadDuration(_ context.Context, _ float64, _ error) {}
diff --git a/internal/billstat/runtime.go b/internal/billstat/runtime.go
index b4bbd9c..638f3e1 100644
--- a/internal/billstat/runtime.go
+++ b/internal/billstat/runtime.go
@@ -7,9 +7,9 @@ import (
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
+ "github.com/AdguardTeam/golibs/service"
)
// RuntimeRecorderConfig is the configuration structure for a runtime billing
@@ -88,7 +88,7 @@ func (r *RuntimeRecorder) Record(
Proto: proto,
}
- r.metrics.BufferSizeSet(ctx, float64(len(r.records)))
+ r.metrics.SetRecordCount(ctx, len(r.records))
} else {
rec.Time = start
rec.Country = ctry
@@ -99,9 +99,9 @@ func (r *RuntimeRecorder) Record(
}
// type check
-var _ agdservice.Refresher = (*RuntimeRecorder)(nil)
+var _ service.Refresher = (*RuntimeRecorder)(nil)
-// Refresh implements the [agdserivce.Refresher] interface for *RuntimeRecorder.
+// Refresh implements the [service.Refresher] interface for *RuntimeRecorder.
// It uploads the currently available data and resets it.
func (r *RuntimeRecorder) Refresh(ctx context.Context) (err error) {
r.logger.DebugContext(ctx, "refresh started")
@@ -112,14 +112,12 @@ func (r *RuntimeRecorder) Refresh(ctx context.Context) (err error) {
startTime := time.Now()
defer func() {
dur := time.Since(startTime).Seconds()
+ r.metrics.HandleUploadDuration(ctx, dur, err)
- isSuccess := err == nil
- if !isSuccess {
+ if err != nil {
r.remergeRecords(ctx, records)
r.logger.WarnContext(ctx, "refresh failed, records remerged")
}
-
- r.metrics.HandleUploadDuration(ctx, dur, isSuccess)
}()
err = r.uploader.Upload(ctx, records)
@@ -138,7 +136,7 @@ func (r *RuntimeRecorder) resetRecords(ctx context.Context) (records Records) {
records, r.records = r.records, Records{}
- r.metrics.BufferSizeSet(ctx, 0)
+ r.metrics.SetRecordCount(ctx, 0)
return records
}
@@ -157,5 +155,5 @@ func (r *RuntimeRecorder) remergeRecords(ctx context.Context, records Records) {
}
}
- r.metrics.BufferSizeSet(ctx, float64(len(r.records)))
+ r.metrics.SetRecordCount(ctx, len(r.records))
}
diff --git a/internal/cmd/access.go b/internal/cmd/access.go
index bd4f5d0..486efad 100644
--- a/internal/cmd/access.go
+++ b/internal/cmd/access.go
@@ -3,6 +3,7 @@ package cmd
import (
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
+ "github.com/AdguardTeam/golibs/validate"
)
// accessConfig is the configuration that controls IP and hosts blocking.
@@ -15,10 +16,10 @@ type accessConfig struct {
}
// type check
-var _ validator = (*accessConfig)(nil)
+var _ validate.Interface = (*accessConfig)(nil)
-// validate implements the [validator] interface for *accessConfig.
-func (c *accessConfig) validate() (err error) {
+// Validate implements the [validate.Interface] interface for *accessConfig.
+func (c *accessConfig) Validate() (err error) {
if c == nil {
return errors.ErrNoValue
}
diff --git a/internal/cmd/additional.go b/internal/cmd/additional.go
index 19103f3..ea71537 100644
--- a/internal/cmd/additional.go
+++ b/internal/cmd/additional.go
@@ -5,6 +5,8 @@ import (
"maps"
"slices"
+ "github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/validate"
"github.com/prometheus/common/model"
)
@@ -12,15 +14,20 @@ import (
type additionalInfo map[string]string
// type check
-var _ validator = additionalInfo(nil)
+var _ validate.Interface = additionalInfo(nil)
-// validate implements the [validator] interface for additionalInfo.
-func (c additionalInfo) validate() (err error) {
+// Validate implements the [validate.Interface] interface for additionalInfo.
+func (c additionalInfo) Validate() (err error) {
+ var errs []error
for _, k := range slices.Sorted(maps.Keys(c)) {
if !model.LabelName(k).IsValid() {
- return fmt.Errorf("prometheus labels must match %s, got %q", model.LabelNameRE, k)
+ errs = append(errs, fmt.Errorf(
+ "prometheus labels must match %s, got %q",
+ model.LabelNameRE,
+ k,
+ ))
}
}
- return nil
+ return errors.Join(errs...)
}
diff --git a/internal/cmd/backend.go b/internal/cmd/backend.go
index d8824c5..534b4aa 100644
--- a/internal/cmd/backend.go
+++ b/internal/cmd/backend.go
@@ -10,6 +10,7 @@ import (
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/timeutil"
+ "github.com/AdguardTeam/golibs/validate"
)
// backendConfig is the backend module configuration.
@@ -39,26 +40,21 @@ type backendConfig struct {
}
// type check
-var _ validator = (*backendConfig)(nil)
+var _ validate.Interface = (*backendConfig)(nil)
-// validate implements the [validator] interface for *backendConfig.
-func (c *backendConfig) validate() (err error) {
- switch {
- case c == nil:
+// Validate implements the [validate.Interface] interface for *backendConfig.
+func (c *backendConfig) Validate() (err error) {
+ if c == nil {
return errors.ErrNoValue
- case c.Timeout.Duration < 0:
- return newNegativeError("timeout", c.Timeout)
- case c.RefreshIvl.Duration <= 0:
- return newNotPositiveError("refresh_interval", c.RefreshIvl)
- case c.FullRefreshIvl.Duration <= 0:
- return newNotPositiveError("full_refresh_interval", c.FullRefreshIvl)
- case c.FullRefreshRetryIvl.Duration <= 0:
- return newNotPositiveError("full_refresh_retry_interval", c.FullRefreshRetryIvl)
- case c.BillStatIvl.Duration <= 0:
- return newNotPositiveError("bill_stat_interval", c.BillStatIvl)
- default:
- return nil
}
+
+ return errors.Join(
+ validate.NotNegative("timeout", c.Timeout),
+ validate.Positive("refresh_interval", c.RefreshIvl),
+ validate.Positive("full_refresh_interval", c.FullRefreshIvl),
+ validate.Positive("full_refresh_retry_interval", c.FullRefreshRetryIvl),
+ validate.Positive("bill_stat_interval", c.BillStatIvl),
+ )
}
// initProfDB refreshes the profile database initially. It logs an error if
diff --git a/internal/cmd/builder.go b/internal/cmd/builder.go
index c286311..ee6f172 100644
--- a/internal/cmd/builder.go
+++ b/internal/cmd/builder.go
@@ -5,6 +5,7 @@ import (
"fmt"
"log/slog"
"maps"
+ "math/rand/v2"
"net/netip"
"net/url"
"path"
@@ -15,8 +16,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/access"
"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/agdrand"
"github.com/AdguardTeam/AdGuardDNS/internal/backendpb"
"github.com/AdguardTeam/AdGuardDNS/internal/billstat"
"github.com/AdguardTeam/AdGuardDNS/internal/bindtodevice"
@@ -29,6 +29,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/forward"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext"
+ dnssvcprom "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/prometheus"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit"
"github.com/AdguardTeam/AdGuardDNS/internal/dnssvc"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
@@ -42,11 +43,13 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/rulestat"
"github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig"
"github.com/AdguardTeam/AdGuardDNS/internal/websvc"
+ "github.com/AdguardTeam/golibs/contextutil"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/AdguardTeam/golibs/osutil"
"github.com/AdguardTeam/golibs/service"
+ "github.com/AdguardTeam/golibs/timeutil"
"github.com/c2h5oh/datasize"
"github.com/prometheus/client_golang/prometheus"
)
@@ -87,6 +90,7 @@ type builder struct {
mtrcNamespace string
plugins *plugin.Registry
promRegisterer prometheus.Registerer
+ rand *rand.Rand
sigHdlr *service.SignalHandler
// The fields below are initialized later by calling the builder's methods.
@@ -114,6 +118,7 @@ type builder struct {
newRegDomains *hashprefix.Filter
newRegDomainsHashes *hashprefix.Storage
profileDB profiledb.Interface
+ queryLog querylog.Interface
rateLimit *ratelimit.Backoff
ruleStat rulestat.Interface
safeBrowsing *hashprefix.Filter
@@ -125,7 +130,7 @@ type builder struct {
// The fields below are initialized later, just like with the fields above,
// but are placed in this order for alignment optimization.
- serverGroups []*agd.ServerGroup
+ serverGroups []*dnssvc.ServerGroupConfig
profilesEnabled bool
}
@@ -170,6 +175,10 @@ func newBuilder(c *builderConfig) (b *builder) {
plugins: c.plugins,
promRegisterer: prometheus.DefaultRegisterer,
debugRefrs: debugsvc.Refreshers{},
+ // #nosec G115 G404 -- The Unix epoch time is highly unlikely to be
+ // negative and we don't need a real random for simple refresh time
+ // randomization.
+ rand: rand.New(rand.NewPCG(uint64(time.Now().UnixNano()), 0)),
sigHdlr: service.NewSignalHandler(&service.SignalHandlerConfig{
Logger: c.baseLogger.With(slogutil.KeyPrefix, service.SignalHandlerPrefix),
ShutdownTimeout: shutdownTimeout,
@@ -199,9 +208,17 @@ func (b *builder) initGeoIP(ctx context.Context) {
asn, ctry := b.env.GeoIPASNPath, b.env.GeoIPCountryPath
b.logger.DebugContext(ctx, "using geoip files", "asn", asn, "ctry", ctry)
+ mtrc, err := metrics.NewGeoIP(b.mtrcNamespace, b.promRegisterer, asn, ctry)
+ if err != nil {
+ err = fmt.Errorf("registering geoip metrics: %w", err)
+
+ return
+ }
+
c := b.conf.GeoIP
b.geoIP = geoip.NewFile(&geoip.FileConfig{
Logger: b.baseLogger.With(slogutil.KeyPrefix, "geoip"),
+ Metrics: mtrc,
CacheManager: b.cacheManager,
ASNPath: asn,
CountryPath: ctry,
@@ -280,8 +297,22 @@ func (b *builder) initAdultBlocking(
}
c := b.conf.AdultBlocking
- id := filter.IDAdultBlocking
+ refrIvl := time.Duration(c.RefreshIvl)
+ refrTimeout := time.Duration(c.RefreshTimeout)
+
+ const id = filter.IDAdultBlocking
+
+ hashPrefMtcs, err := metrics.NewHashPrefixFilter(
+ b.mtrcNamespace,
+ string(id),
+ b.promRegisterer,
+ )
+ if err != nil {
+ return fmt.Errorf("registering hashprefix filter metrics: %w", err)
+ }
+
prefix := path.Join(hashprefix.IDPrefix, string(id))
+
b.adultBlocking, err = hashprefix.NewFilter(&hashprefix.FilterConfig{
Logger: b.baseLogger.With(slogutil.KeyPrefix, prefix),
Cloner: b.cloner,
@@ -289,13 +320,14 @@ func (b *builder) initAdultBlocking(
Hashes: b.adultBlockingHashes,
URL: &b.env.AdultBlockingURL.URL,
ErrColl: b.errColl,
+ HashPrefixMtcs: hashPrefMtcs,
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,
+ Staleness: refrIvl,
+ RefreshTimeout: refrTimeout,
+ CacheTTL: time.Duration(c.CacheTTL),
// TODO(a.garipov): Make all sizes [datasize.ByteSize] and rename cache
// entity counts to fooCount.
CacheCount: c.CacheSize,
@@ -310,15 +342,14 @@ func (b *builder) initAdultBlocking(
return fmt.Errorf("initial refresh: %w", err)
}
- refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
+ refr := service.NewRefreshWorker(&service.RefreshWorkerConfig{
// Note that we also set the same timeout for the http.Client in
// [hashprefix.NewFilter].
- Context: newCtxWithTimeoutCons(c.RefreshTimeout.Duration),
- Refresher: b.adultBlocking,
- Logger: b.baseLogger.With(slogutil.KeyPrefix, string(id)+"_refresh"),
- Interval: c.RefreshIvl.Duration,
- RefreshOnShutdown: false,
- RandomizeStart: false,
+ ContextConstructor: contextutil.NewTimeoutConstructor(refrTimeout),
+ ErrorHandler: newSlogErrorHandler(b.baseLogger, string(id)+"_refresh"),
+ Refresher: b.adultBlocking,
+ Schedule: timeutil.NewConstSchedule(refrIvl),
+ RefreshOnShutdown: false,
})
err = refr.Start(ctx)
if err != nil {
@@ -334,6 +365,16 @@ func (b *builder) initAdultBlocking(
return nil
}
+// newSlogErrorHandler is a convenient wrapper around
+// [service.NewSlogErrorHandler].
+func newSlogErrorHandler(baseLogger *slog.Logger, prefix string) (h *service.SlogErrorHandler) {
+ return service.NewSlogErrorHandler(
+ baseLogger.With(slogutil.KeyPrefix, prefix),
+ slog.LevelError,
+ "refreshing",
+ )
+}
+
// initNewRegDomains initializes the newly-registered domain filter and hash
// storage. It also adds the refresher with ID
// [hashprefix.IDPrefix]/[filter.IDNewRegDomains] to the debug refreshers.
@@ -357,8 +398,22 @@ func (b *builder) initNewRegDomains(
// Reuse the general safe-browsing filter configuration with a new URL and
// ID.
c := b.conf.SafeBrowsing
- id := filter.IDNewRegDomains
+ refrIvl := time.Duration(c.RefreshIvl)
+ refrTimeout := time.Duration(c.RefreshTimeout)
+
+ const id = filter.IDNewRegDomains
+
+ hashPrefMtcs, err := metrics.NewHashPrefixFilter(
+ b.mtrcNamespace,
+ string(id),
+ b.promRegisterer,
+ )
+ if err != nil {
+ return fmt.Errorf("registering hashprefix filter metrics: %w", err)
+ }
+
prefix := path.Join(hashprefix.IDPrefix, string(id))
+
b.newRegDomains, err = hashprefix.NewFilter(&hashprefix.FilterConfig{
Logger: b.baseLogger.With(slogutil.KeyPrefix, prefix),
Cloner: b.cloner,
@@ -366,13 +421,14 @@ func (b *builder) initNewRegDomains(
Hashes: b.newRegDomainsHashes,
URL: &b.env.NewRegDomainsURL.URL,
ErrColl: b.errColl,
+ HashPrefixMtcs: hashPrefMtcs,
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,
+ Staleness: refrIvl,
+ RefreshTimeout: refrTimeout,
+ CacheTTL: time.Duration(c.CacheTTL),
CacheCount: c.CacheSize,
MaxSize: maxSize,
})
@@ -385,15 +441,14 @@ func (b *builder) initNewRegDomains(
return fmt.Errorf("initial refresh: %w", err)
}
- refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
+ refr := service.NewRefreshWorker(&service.RefreshWorkerConfig{
// Note that we also set the same timeout for the http.Client in
// [hashprefix.NewFilter].
- Context: newCtxWithTimeoutCons(c.RefreshTimeout.Duration),
- Refresher: b.newRegDomains,
- Logger: b.baseLogger.With(slogutil.KeyPrefix, string(id)+"_refresh"),
- Interval: c.RefreshIvl.Duration,
- RefreshOnShutdown: false,
- RandomizeStart: false,
+ ContextConstructor: contextutil.NewTimeoutConstructor(refrTimeout),
+ ErrorHandler: newSlogErrorHandler(b.baseLogger, string(id)+"_refresh"),
+ Refresher: b.newRegDomains,
+ Schedule: timeutil.NewConstSchedule(refrIvl),
+ RefreshOnShutdown: false,
})
err = refr.Start(ctx)
if err != nil {
@@ -429,8 +484,22 @@ func (b *builder) initSafeBrowsing(
}
c := b.conf.SafeBrowsing
- id := filter.IDSafeBrowsing
+ refrIvl := time.Duration(c.RefreshIvl)
+ refrTimeout := time.Duration(c.RefreshTimeout)
+
+ const id = filter.IDSafeBrowsing
+
+ hashPrefMtcs, err := metrics.NewHashPrefixFilter(
+ b.mtrcNamespace,
+ string(id),
+ b.promRegisterer,
+ )
+ if err != nil {
+ return fmt.Errorf("registering hashprefix filter metrics: %w", err)
+ }
+
prefix := path.Join(hashprefix.IDPrefix, string(id))
+
b.safeBrowsing, err = hashprefix.NewFilter(&hashprefix.FilterConfig{
Logger: b.baseLogger.With(slogutil.KeyPrefix, prefix),
Cloner: b.cloner,
@@ -438,13 +507,14 @@ func (b *builder) initSafeBrowsing(
Hashes: b.safeBrowsingHashes,
URL: &b.env.SafeBrowsingURL.URL,
ErrColl: b.errColl,
+ HashPrefixMtcs: hashPrefMtcs,
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,
+ Staleness: refrIvl,
+ RefreshTimeout: refrTimeout,
+ CacheTTL: time.Duration(c.CacheTTL),
CacheCount: c.CacheSize,
MaxSize: maxSize,
})
@@ -457,15 +527,14 @@ func (b *builder) initSafeBrowsing(
return fmt.Errorf("initial refresh: %w", err)
}
- refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
+ refr := service.NewRefreshWorker(&service.RefreshWorkerConfig{
// Note that we also set the same timeout for the http.Client in
// [hashprefix.NewFilter].
- Context: newCtxWithTimeoutCons(c.RefreshTimeout.Duration),
- Refresher: b.safeBrowsing,
- Logger: b.baseLogger.With(slogutil.KeyPrefix, string(id)+"_refresh"),
- Interval: c.RefreshIvl.Duration,
- RefreshOnShutdown: false,
- RandomizeStart: false,
+ ContextConstructor: contextutil.NewTimeoutConstructor(refrTimeout),
+ ErrorHandler: newSlogErrorHandler(b.baseLogger, string(id)+"_refresh"),
+ Refresher: b.safeBrowsing,
+ Schedule: timeutil.NewConstSchedule(refrIvl),
+ RefreshOnShutdown: false,
})
err = refr.Start(ctx)
if err != nil {
@@ -487,14 +556,19 @@ 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
- refrIvl := c.RefreshIvl.Duration
- refrTimeout := c.RefreshTimeout.Duration
+ refrIvl := time.Duration(c.RefreshIvl)
+ refrTimeout := time.Duration(c.RefreshTimeout)
+
+ var blockedSvcIdxURL *url.URL
+ if b.env.BlockedServiceEnabled {
+ blockedSvcIdxURL = &b.env.BlockedServiceIndexURL.URL
+ }
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,
+ IndexURL: blockedSvcIdxURL,
// TODO(a.garipov): Consider adding a separate parameter here.
IndexMaxSize: c.MaxSize,
// TODO(a.garipov): Consider making configurable.
@@ -520,7 +594,7 @@ func (b *builder) initFilterStorage(ctx context.Context) (err error) {
// TODO(a.garipov): Consider adding a separate parameter here.
IndexMaxSize: c.MaxSize,
MaxSize: c.MaxSize,
- IndexRefreshTimeout: c.IndexRefreshTimeout.Duration,
+ IndexRefreshTimeout: time.Duration(c.IndexRefreshTimeout),
// TODO(a.garipov): Consider adding a separate parameter here.
IndexStaleness: refrIvl,
RefreshTimeout: refrTimeout,
@@ -540,7 +614,7 @@ func (b *builder) initFilterStorage(ctx context.Context) (err error) {
bool(b.env.YoutubeSafeSearchEnabled),
),
CacheManager: b.cacheManager,
- Clock: agdtime.SystemClock{},
+ Clock: timeutil.SystemClock{},
ErrColl: b.errColl,
Metrics: b.filterMtrc,
CacheDir: b.env.FilterCachePath,
@@ -554,13 +628,12 @@ func (b *builder) initFilterStorage(ctx context.Context) (err error) {
return fmt.Errorf("refreshing default filter storage: %w", err)
}
- refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
- Context: newCtxWithTimeoutCons(refrTimeout),
- Refresher: b.filterStorage,
- Logger: b.baseLogger.With(slogutil.KeyPrefix, "filters/storage_refresh"),
- Interval: refrIvl,
- RefreshOnShutdown: false,
- RandomizeStart: false,
+ refr := service.NewRefreshWorker(&service.RefreshWorkerConfig{
+ ContextConstructor: contextutil.NewTimeoutConstructor(refrTimeout),
+ ErrorHandler: newSlogErrorHandler(b.baseLogger, "filters/storage_refresh"),
+ Refresher: b.filterStorage,
+ Schedule: timeutil.NewConstSchedule(refrIvl),
+ RefreshOnShutdown: false,
})
err = refr.Start(ctx)
if err != nil {
@@ -597,9 +670,9 @@ func (b *builder) newSafeSearchConfig(
// TODO(a.garipov): Consider making this configurable.
ResultCacheTTL: 1 * time.Hour,
// TODO(a.garipov): Consider adding a separate parameter here.
- RefreshTimeout: fltConf.RefreshTimeout.Duration,
+ RefreshTimeout: time.Duration(fltConf.RefreshTimeout),
// TODO(a.garipov): Consider adding a separate parameter here.
- Staleness: fltConf.RefreshIvl.Duration,
+ Staleness: time.Duration(fltConf.RefreshIvl),
ResultCacheCount: fltConf.SafeSearchCacheSize,
Enabled: true,
}
@@ -651,6 +724,59 @@ func (b *builder) initBindToDevice(ctx context.Context) (err error) {
return nil
}
+// initDNSDB initializes the DNS database.
+func (b *builder) initDNSDB(ctx context.Context) (err error) {
+ if !b.conf.DNSDB.Enabled {
+ b.dnsDB = dnsdb.Empty{}
+
+ return nil
+ }
+
+ mtrc, err := metrics.NewDNSDB(b.mtrcNamespace, b.promRegisterer)
+ if err != nil {
+ return fmt.Errorf("registering dnsdb metrics: %w", err)
+ }
+
+ b.dnsDB = dnsdb.New(&dnsdb.DefaultConfig{
+ Logger: b.baseLogger.With(slogutil.KeyPrefix, "dnsdb"),
+ ErrColl: b.errColl,
+ Metrics: mtrc,
+ MaxSize: b.conf.DNSDB.MaxSize,
+ })
+
+ b.logger.DebugContext(ctx, "initialized dns database")
+
+ return nil
+}
+
+// initQueryLog initializes the appropriate query log implementation from the
+// configuration and environment data.
+func (b *builder) initQueryLog(ctx context.Context) (err error) {
+ if !b.conf.QueryLog.File.Enabled {
+ b.queryLog = querylog.Empty{}
+
+ b.logger.DebugContext(ctx, "initialized empty query log")
+
+ return nil
+ }
+
+ mtrc, err := metrics.NewQueryLog(b.mtrcNamespace, b.promRegisterer)
+ if err != nil {
+ return fmt.Errorf("registering querylog metrics: %w", err)
+ }
+
+ b.queryLog = querylog.NewFileSystem(&querylog.FileSystemConfig{
+ Logger: b.baseLogger.With(slogutil.KeyPrefix, "querylog"),
+ Path: b.env.QueryLogPath,
+ Metrics: mtrc,
+ RandSeed: agdrand.MustNewSeed(),
+ })
+
+ b.logger.DebugContext(ctx, "initialized file-based query log")
+
+ return nil
+}
+
// Constants for the experimental Structured DNS Errors feature.
//
// TODO(a.garipov): Make configurable.
@@ -685,7 +811,7 @@ func (b *builder) initMsgConstructor(ctx context.Context) (err error) {
Cloner: b.cloner,
BlockingMode: &dnsmsg.BlockingModeNullIP{},
StructuredErrors: b.sdeConf,
- FilteredResponseTTL: fltConf.ResponseTTL.Duration,
+ FilteredResponseTTL: time.Duration(fltConf.ResponseTTL),
EDEEnabled: fltConf.EDEEnabled,
})
if err != nil {
@@ -823,20 +949,19 @@ func (b *builder) startBindToDevice(ctx context.Context) (err error) {
//
// [builder.initTLSManager] must be called before this method.
func (b *builder) initTicketRotator(ctx context.Context) (err error) {
- tickRot := agdservice.RefresherFunc(b.tlsManager.RotateTickets)
+ tickRot := service.RefresherFunc(b.tlsManager.RotateTickets)
err = tickRot.Refresh(ctx)
if err != nil {
return fmt.Errorf("initial session ticket refresh: %w", err)
}
- refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
- Context: ctxWithDefaultTimeout,
- Refresher: tickRot,
- Logger: b.baseLogger.With(slogutil.KeyPrefix, "tickrot_refresh"),
+ refr := service.NewRefreshWorker(&service.RefreshWorkerConfig{
+ ContextConstructor: contextutil.NewTimeoutConstructor(defaultTimeout),
+ ErrorHandler: newSlogErrorHandler(b.baseLogger, "tickrot_refresh"),
+ Refresher: tickRot,
// TODO(a.garipov): Make configurable.
- Interval: 1 * time.Minute,
+ Schedule: timeutil.NewConstSchedule(1 * time.Minute),
RefreshOnShutdown: false,
- RandomizeStart: false,
})
err = refr.Start(ctx)
if err != nil {
@@ -852,13 +977,17 @@ func (b *builder) initTicketRotator(ctx context.Context) (err error) {
return nil
}
+// defaultTimeout is the timeout used for some operations where another timeout
+// hasn't been defined yet.
+const defaultTimeout = 30 * time.Second
+
// 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
b.profilesEnabled,
- b.conf.Check.RemoteKV.Type == kvModeBackend,
+ b.conf.Check.KV.Type == kvModeBackend,
b.conf.RateLimit.Allowlist.Type == rlAllowlistTypeBackend:
// Go on.
default:
@@ -904,17 +1033,16 @@ func (b *builder) initBillStat(ctx context.Context) (err error) {
})
c := b.conf.Backend
- refrIvl := c.BillStatIvl.Duration
- timeout := c.Timeout.Duration
+ refrIvl := time.Duration(c.BillStatIvl)
+ timeout := time.Duration(c.Timeout)
b.billStat = billStat
- refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
- Context: newCtxWithTimeoutCons(timeout),
- Refresher: billStat,
- Logger: b.baseLogger.With(slogutil.KeyPrefix, "billstat_refresh"),
- Interval: refrIvl,
- RefreshOnShutdown: true,
- RandomizeStart: false,
+ refr := service.NewRefreshWorker(&service.RefreshWorkerConfig{
+ ContextConstructor: contextutil.NewTimeoutConstructor(timeout),
+ ErrorHandler: newSlogErrorHandler(b.baseLogger, "billstat_refresh"),
+ Refresher: billStat,
+ Schedule: timeutil.NewConstSchedule(refrIvl),
+ RefreshOnShutdown: true,
})
err = refr.Start(ctx)
if err != nil {
@@ -971,10 +1099,12 @@ func (b *builder) initProfileDB(ctx context.Context) (err error) {
}
respSzEst := b.conf.RateLimit.ResponseSizeEstimate
+ customLogger := b.baseLogger.With(slogutil.KeyPrefix, "filters/"+string(filter.IDCustom))
strg, err := backendpb.NewProfileStorage(&backendpb.ProfileStorageConfig{
BindSet: b.bindSet,
ErrColl: b.errColl,
Logger: b.baseLogger.With(slogutil.KeyPrefix, "profilestorage"),
+ BaseCustomLogger: customLogger,
GRPCMetrics: b.backendGRPCMtrc,
Metrics: backendProfileDBMtrc,
Endpoint: apiURL,
@@ -992,15 +1122,16 @@ func (b *builder) initProfileDB(ctx context.Context) (err error) {
}
c := b.conf.Backend
- timeout := c.Timeout.Duration
+ timeout := time.Duration(c.Timeout)
profDB, err := profiledb.New(&profiledb.Config{
Logger: b.baseLogger.With(slogutil.KeyPrefix, "profiledb"),
+ BaseCustomLogger: customLogger,
Storage: strg,
ErrColl: b.errColl,
Metrics: profDBMtrc,
CacheFilePath: b.env.ProfilesCachePath,
- FullSyncIvl: c.FullRefreshIvl.Duration,
- FullSyncRetryIvl: c.FullRefreshRetryIvl.Duration,
+ FullSyncIvl: time.Duration(c.FullRefreshIvl),
+ FullSyncRetryIvl: time.Duration(c.FullRefreshRetryIvl),
ResponseSizeEstimate: respSzEst,
})
if err != nil {
@@ -1014,13 +1145,22 @@ func (b *builder) initProfileDB(ctx context.Context) (err error) {
// TODO(a.garipov): Add a separate refresher ID for full refreshes.
b.profileDB = profDB
- refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
- Context: newCtxWithTimeoutCons(timeout),
- Logger: b.baseLogger.With(slogutil.KeyPrefix, "profiledb_refresh"),
- Refresher: profDB,
- Interval: c.RefreshIvl.Duration,
- RefreshOnShutdown: false,
- RandomizeStart: true,
+
+ // Randomize the start of the profile DB refresh by up to 10 % to not
+ // overload the profile storage.
+ refrIvl := time.Duration(c.RefreshIvl)
+ sched := timeutil.NewRandomizedSchedule(
+ timeutil.NewConstSchedule(refrIvl),
+ b.rand,
+ 0,
+ refrIvl/10,
+ )
+ refr := service.NewRefreshWorker(&service.RefreshWorkerConfig{
+ ContextConstructor: contextutil.NewTimeoutConstructor(timeout),
+ ErrorHandler: newSlogErrorHandler(b.baseLogger, "profiledb_refresh"),
+ Refresher: profDB,
+ Schedule: sched,
+ RefreshOnShutdown: false,
})
err = refr.Start(ctx)
if err != nil {
@@ -1082,21 +1222,26 @@ func (b *builder) initRuleStat(ctx context.Context) (err error) {
return nil
}
+ mtrc, err := metrics.NewRuleStat(b.mtrcNamespace, b.promRegisterer)
+ if err != nil {
+ return fmt.Errorf("rulestat metrics: %w", err)
+ }
+
ruleStat := rulestat.NewHTTP(&rulestat.HTTPConfig{
Logger: b.baseLogger.With(slogutil.KeyPrefix, "rulestat"),
ErrColl: b.errColl,
+ Metrics: mtrc,
URL: &u.URL,
})
b.ruleStat = ruleStat
- refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
- Context: ctxWithDefaultTimeout,
- Refresher: ruleStat,
- Logger: b.baseLogger.With(slogutil.KeyPrefix, "rulestat_refresh"),
+ refr := service.NewRefreshWorker(&service.RefreshWorkerConfig{
+ ContextConstructor: contextutil.NewTimeoutConstructor(defaultTimeout),
+ ErrorHandler: newSlogErrorHandler(b.baseLogger, "rulestat_refresh"),
+ Refresher: ruleStat,
// TODO(a.garipov): Make configurable.
- Interval: 10 * time.Minute,
+ Schedule: timeutil.NewConstSchedule(10 * time.Minute),
RefreshOnShutdown: true,
- RandomizeStart: false,
})
err = refr.Start(ctx)
if err != nil {
@@ -1129,7 +1274,7 @@ func (b *builder) initRateLimiter(ctx context.Context) (err error) {
return fmt.Errorf("ratelimit metrics: %w", err)
}
- var updater agdservice.Refresher
+ var updater service.Refresher
if typ == rlAllowlistTypeBackend {
updater, err = backendpb.NewRateLimiter(&backendpb.RateLimiterConfig{
Logger: b.baseLogger.With(slogutil.KeyPrefix, "backend_ratelimiter"),
@@ -1160,13 +1305,12 @@ func (b *builder) initRateLimiter(ctx context.Context) (err error) {
return fmt.Errorf("allowlist: initial refresh: %w", err)
}
- refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
- Context: ctxWithDefaultTimeout,
- Refresher: updater,
- Logger: b.baseLogger.With(slogutil.KeyPrefix, "ratelimit_allowlist_refresh"),
- Interval: c.Allowlist.RefreshIvl.Duration,
- RefreshOnShutdown: false,
- RandomizeStart: false,
+ refr := service.NewRefreshWorker(&service.RefreshWorkerConfig{
+ ContextConstructor: contextutil.NewTimeoutConstructor(defaultTimeout),
+ ErrorHandler: newSlogErrorHandler(b.baseLogger, "ratelimit_allowlist_refresh"),
+ Refresher: updater,
+ Schedule: timeutil.NewConstSchedule(time.Duration(c.Allowlist.RefreshIvl)),
+ RefreshOnShutdown: false,
})
err = refr.Start(ctx)
if err != nil {
@@ -1175,7 +1319,11 @@ func (b *builder) initRateLimiter(ctx context.Context) (err error) {
b.sigHdlr.Add(refr)
- b.connLimit = c.ConnectionLimit.toInternal(b.baseLogger)
+ err = b.initConnLimit(ctx, c.ConnectionLimit)
+ if err != nil {
+ return fmt.Errorf("connlimit: %w", err)
+ }
+
b.rateLimit = ratelimit.NewBackoff(c.toInternal(allowlist))
b.debugRefrs[debugIDAllowlist] = updater
@@ -1185,11 +1333,27 @@ func (b *builder) initRateLimiter(ctx context.Context) (err error) {
return nil
}
+// initConnLimit initializes the connection limiter from the given conf.
+func (b *builder) initConnLimit(ctx context.Context, conf *connLimitConfig) (err error) {
+ if !conf.Enabled {
+ return nil
+ }
+
+ mtrc, err := metrics.NewConnLimiter(b.mtrcNamespace, b.promRegisterer)
+ if err != nil {
+ return fmt.Errorf("metrics: %w", err)
+ }
+
+ b.connLimit = connlimiter.New(conf.toInternal(ctx, b.baseLogger, mtrc))
+
+ return nil
+}
+
// initWeb initializes the web service, starts it, and registers it in the
// 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.tlsManager)
+ webConf, err := c.toInternal(ctx, b.env, b.dnsCheck, b.errColl, b.baseLogger, b.tlsManager)
if err != nil {
return fmt.Errorf("converting web configuration: %w", err)
}
@@ -1201,14 +1365,13 @@ func (b *builder) initWeb(ctx context.Context) (err error) {
return fmt.Errorf("web: initial refresh: %w", err)
}
- refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
- Context: ctxWithDefaultTimeout,
- Refresher: b.webSvc,
- Logger: b.baseLogger.With(slogutil.KeyPrefix, "websvc_refresh"),
+ refr := service.NewRefreshWorker(&service.RefreshWorkerConfig{
+ ContextConstructor: contextutil.NewTimeoutConstructor(defaultTimeout),
+ ErrorHandler: newSlogErrorHandler(b.baseLogger, "websvc_refresh"),
+ Refresher: b.webSvc,
// TODO(a.garipov): Consider making configurable.
- Interval: 5 * time.Minute,
+ Schedule: timeutil.NewConstSchedule(5 * time.Minute),
RefreshOnShutdown: false,
- RandomizeStart: false,
})
err = refr.Start(ctx)
if err != nil {
@@ -1240,22 +1403,16 @@ func (b *builder) waitGeoIP(ctx context.Context) (err error) {
const prefix = "geoip_refresh"
refrLogger := b.baseLogger.With(slogutil.KeyPrefix, prefix)
- refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
- Context: ctxWithDefaultTimeout,
+ refr := service.NewRefreshWorker(&service.RefreshWorkerConfig{
+ ContextConstructor: contextutil.NewTimeoutConstructor(defaultTimeout),
// Do not add errColl to geoip's config, as that would create an import
// cycle.
//
// TODO(a.garipov): Resolve that.
- Refresher: agdservice.NewRefresherWithErrColl(
- b.geoIP,
- refrLogger,
- b.errColl,
- prefix,
- ),
- Logger: refrLogger,
- Interval: b.conf.GeoIP.RefreshIvl.Duration,
+ ErrorHandler: errcoll.NewRefreshErrorHandler(refrLogger, b.errColl),
+ Refresher: b.geoIP,
+ Schedule: timeutil.NewConstSchedule(time.Duration(b.conf.GeoIP.RefreshIvl)),
RefreshOnShutdown: false,
- RandomizeStart: false,
})
err = refr.Start(ctx)
if err != nil {
@@ -1275,17 +1432,27 @@ func (b *builder) waitGeoIP(ctx context.Context) (err error) {
// - [builder.initAccess]
// - [builder.initBillStat]
// - [builder.initBindToDevice]
+// - [builder.initDNSDB]
// - [builder.initFilterStorage]
// - [builder.initFilteringGroups]
// - [builder.initMsgConstructor]
+// - [builder.initQueryLog]
// - [builder.initProfileDB]
// - [builder.initRateLimiter]
// - [builder.initRuleStat]
// - [builder.initWeb]
// - [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.baseLogger, b.errColl)
+ mtrcListener, err := dnssvcprom.NewForwardMetricsListener(
+ b.mtrcNamespace,
+ b.promRegisterer,
+ len(b.conf.Upstream.Servers)+len(b.conf.Upstream.Fallback.Servers),
+ )
+ if err != nil {
+ return fmt.Errorf("forward metrics listener: %w", err)
+ }
+
+ b.fwdHandler = forward.NewHandler(b.conf.Upstream.toInternal(b.baseLogger, mtrcListener))
dnsHdlrsConf := &dnssvc.HandlersConfig{
BaseLogger: b.baseLogger,
@@ -1307,7 +1474,7 @@ func (b *builder) initDNS(ctx context.Context) (err error) {
HashMatcher: b.hashMatcher,
ProfileDB: b.profileDB,
PrometheusRegisterer: b.promRegisterer,
- QueryLog: b.queryLog(),
+ QueryLog: b.queryLog,
RateLimit: b.rateLimit,
RuleStat: b.ruleStat,
MetricsNamespace: b.mtrcNamespace,
@@ -1322,15 +1489,17 @@ func (b *builder) initDNS(ctx context.Context) (err error) {
}
dnsConf := &dnssvc.Config{
- Handlers: dnsHdlrs,
- Cloner: b.cloner,
- ControlConf: b.controlConf,
- ConnLimiter: b.connLimit,
- NonDNS: b.webSvc,
- ErrColl: b.errColl,
- MetricsNamespace: b.mtrcNamespace,
- ServerGroups: b.serverGroups,
- HandleTimeout: b.conf.DNS.HandleTimeout.Duration,
+ BaseLogger: b.baseLogger,
+ Handlers: dnsHdlrs,
+ Cloner: b.cloner,
+ ControlConf: b.controlConf,
+ ConnLimiter: b.connLimit,
+ NonDNS: b.webSvc,
+ ErrColl: b.errColl,
+ PrometheusRegisterer: b.promRegisterer,
+ MetricsNamespace: b.mtrcNamespace,
+ ServerGroups: b.serverGroups,
+ HandleTimeout: time.Duration(b.conf.DNS.HandleTimeout),
}
b.dnsSvc, err = dnssvc.New(dnsConf)
@@ -1343,22 +1512,6 @@ func (b *builder) initDNS(ctx context.Context) (err error) {
return nil
}
-// queryLog returns the appropriate query log implementation from the
-// configuration and environment data.
-func (b *builder) queryLog() (l querylog.Interface) {
- fileNeeded := b.conf.QueryLog.File.Enabled
- if !fileNeeded {
- return querylog.Empty{}
- }
-
- return querylog.NewFileSystem(&querylog.FileSystemConfig{
- Logger: b.baseLogger.With(slogutil.KeyPrefix, "querylog"),
- Path: b.env.QueryLogPath,
- // #nosec G115 -- The Unix epoch time is highly unlikely to be negative.
- RandSeed: uint64(time.Now().UnixNano()),
- })
-}
-
// performConnCheck performs the connectivity check in accordance to the
// configuration given so far.
//
diff --git a/internal/cmd/cache.go b/internal/cmd/cache.go
index ef222a7..6654233 100644
--- a/internal/cmd/cache.go
+++ b/internal/cmd/cache.go
@@ -2,10 +2,12 @@ package cmd
import (
"fmt"
+ "time"
"github.com/AdguardTeam/AdGuardDNS/internal/dnssvc"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/timeutil"
+ "github.com/AdguardTeam/golibs/validate"
)
// cacheConfig is the configuration of the DNS cacheConfig module
@@ -38,6 +40,18 @@ type ttlOverride struct {
Enabled bool `yaml:"enabled"`
}
+// type check
+var _ validate.Interface = (*ttlOverride)(nil)
+
+// Validate implements the [validate.Interface] interface for *ttlOverride.
+func (c *ttlOverride) Validate() (err error) {
+ if c == nil {
+ return errors.ErrNoValue
+ }
+
+ return validate.Positive("min", c.Min)
+}
+
// Cache types.
const (
cacheTypeECS = "ecs"
@@ -58,7 +72,7 @@ func (c *cacheConfig) toInternal() (cacheConf *dnssvc.CacheConfig) {
}
return &dnssvc.CacheConfig{
- MinTTL: c.TTLOverride.Min.Duration,
+ MinTTL: time.Duration(c.TTLOverride.Min),
ECSCount: c.ECSSize,
NoECSCount: c.Size,
Type: typ,
@@ -67,47 +81,36 @@ func (c *cacheConfig) toInternal() (cacheConf *dnssvc.CacheConfig) {
}
// type check
-var _ validator = (*cacheConfig)(nil)
+var _ validate.Interface = (*cacheConfig)(nil)
-// validate implements the [validator] interface for *cacheConfig.
-func (c *cacheConfig) validate() (err error) {
- switch {
- case c == nil:
+// Validate implements the [validate.Interface] interface for *cacheConfig.
+func (c *cacheConfig) Validate() (err error) {
+ if c == nil {
return errors.ErrNoValue
- case c.Type != cacheTypeSimple && c.Type != cacheTypeECS:
- return fmt.Errorf(
+ }
+
+ errs := []error{
+ validate.NotNegative("size", c.Size),
+ }
+
+ errs = validate.Append(errs, "ttl_override", c.TTLOverride)
+
+ switch c.Type {
+ case cacheTypeSimple:
+ // Go on.
+ case cacheTypeECS:
+ if err = validate.NotNegative("ecs_size", c.ECSSize); err != nil {
+ // Don't wrap the error, because it's informative enough as is.
+ errs = append(errs, err)
+ }
+ default:
+ errs = append(errs, fmt.Errorf(
"type: %w: %q, supported: %q",
errors.ErrBadEnumValue,
c.Type,
[]string{cacheTypeSimple, cacheTypeECS},
- )
- case c.Size < 0:
- return newNegativeError("size", c.Size)
- case c.Type == cacheTypeECS && c.ECSSize < 0:
- return newNegativeError("ecs_size", c.ECSSize)
- default:
- // Go on.
+ ))
}
- err = c.TTLOverride.validate()
- if err != nil {
- return fmt.Errorf("ttl_override: %w", err)
- }
-
- return nil
-}
-
-// type check
-var _ validator = (*ttlOverride)(nil)
-
-// validate implements the [validator] interface for *ttlOverride.
-func (c *ttlOverride) validate() (err error) {
- switch {
- case c == nil:
- return errors.ErrNoValue
- case c.Min.Duration <= 0:
- return newNotPositiveError("min", c.Min)
- default:
- return nil
- }
+ return errors.Join(errs...)
}
diff --git a/internal/cmd/check.go b/internal/cmd/check.go
index 72f572f..b3129e0 100644
--- a/internal/cmd/check.go
+++ b/internal/cmd/check.go
@@ -17,11 +17,11 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/remotekv"
"github.com/AdguardTeam/AdGuardDNS/internal/remotekv/consulkv"
"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/AdguardTeam/golibs/validate"
"github.com/c2h5oh/datasize"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/time/rate"
@@ -29,8 +29,8 @@ import (
// checkConfig is the DNS server checking configuration.
type checkConfig struct {
- // RemoteKV is remote key-value store configuration for DNS server checking.
- RemoteKV *remoteKVConfig `yaml:"kv"`
+ // KV is remote key-value store configuration for DNS server checking.
+ KV *remoteKVConfig `yaml:"kv"`
// Domains are the domain names used for DNS server checking.
Domains []string `yaml:"domains"`
@@ -61,7 +61,12 @@ func (c *checkConfig) toInternal(
reg prometheus.Registerer,
grpcMtrc backendpb.GRPCMetrics,
) (conf *dnscheck.RemoteKVConfig, err error) {
- kv, err := c.RemoteKV.newRemoteKV(envs, namespace, reg, grpcMtrc)
+ mtrc, err := metrics.NewDNSCheck(namespace, reg)
+ if err != nil {
+ return nil, fmt.Errorf("dnscheck metrics: %w", err)
+ }
+
+ kv, err := c.KV.newRemoteKV(envs, namespace, reg, grpcMtrc)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return nil, err
@@ -75,6 +80,7 @@ func (c *checkConfig) toInternal(
return &dnscheck.RemoteKVConfig{
Logger: baseLogger.With(slogutil.KeyPrefix, "dnscheck"),
Messages: messages,
+ Metrics: mtrc,
RemoteKV: kv,
ErrColl: errColl,
Domains: domains,
@@ -135,8 +141,8 @@ func (c *remoteKVConfig) newRemoteKV(
},
MaxActive: envs.RedisMaxActive,
MaxIdle: envs.RedisMaxIdle,
- IdleTimeout: envs.RedisIdleTimeout.Duration,
- TTL: c.TTL.Duration,
+ IdleTimeout: time.Duration(envs.RedisIdleTimeout),
+ TTL: time.Duration(c.TTL),
})
case kvModeConsul:
kv, err = c.newConsulRemoteKV(envs)
@@ -167,7 +173,7 @@ func (c *remoteKVConfig) newBackendRemoteKV(
Metrics: backendKVMtrc,
Endpoint: &envs.DNSCheckRemoteKVURL.URL,
APIKey: envs.DNSCheckRemoteKVAPIKey,
- TTL: c.TTL.Duration,
+ TTL: time.Duration(c.TTL),
})
if err != nil {
return nil, fmt.Errorf("initializing backend dnscheck kv: %w", err)
@@ -206,7 +212,7 @@ func (c *remoteKVConfig) newConsulRemoteKV(envs *environment) (kv remotekv.Inter
}),
// TODO(ameshkov): Consider making configurable.
Limiter: rate.NewLimiter(rate.Limit(200)/60, 1),
- TTL: c.TTL.Duration,
+ TTL: time.Duration(c.TTL),
MaxRespSize: maxRespSize,
})
if err != nil {
@@ -217,53 +223,38 @@ func (c *remoteKVConfig) newConsulRemoteKV(envs *environment) (kv remotekv.Inter
}
// type check
-var _ validator = (*checkConfig)(nil)
+var _ validate.Interface = (*checkConfig)(nil)
-// validate implements the [validator] interface for *checkConfig.
-func (c *checkConfig) validate() (err error) {
+// Validate implements the [validate.Interface] interface for *checkConfig.
+func (c *checkConfig) Validate() (err error) {
if c == nil {
return errors.ErrNoValue
}
- notEmptyParams := container.KeyValues[string, string]{{
- Key: "node_location",
- Value: c.NodeLocation,
- }, {
- Key: "node_name",
- Value: c.NodeName,
- }}
-
- for _, kv := range notEmptyParams {
- if kv.Value == "" {
- return fmt.Errorf("%s: %w", kv.Key, errors.ErrEmptyValue)
- }
- }
-
- if len(c.Domains) == 0 {
- return fmt.Errorf("domains: %w", errors.ErrEmptyValue)
+ errs := []error{
+ validate.NotEmpty("node_location", c.NodeLocation),
+ validate.NotEmpty("node_name", c.NodeName),
+ validate.NotEmptySlice("domains", c.Domains),
}
err = validateNonNilIPs(c.IPv4, netutil.AddrFamilyIPv4)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
- return err
+ errs = append(errs, err)
}
err = validateNonNilIPs(c.IPv6, netutil.AddrFamilyIPv6)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
- return err
+ errs = append(errs, err)
}
- err = c.RemoteKV.validate()
- if err != nil {
- return fmt.Errorf("kv: %w", err)
- }
+ errs = validate.Append(errs, "kv", c.KV)
- return nil
+ return errors.Join(errs...)
}
-// validateNonNilIPs returns an error if ips is empty or had IP addresses of
+// ValidateNonNilIPs returns an error if ips is empty or had IP addresses of
// incorrect protocol version.
//
// TODO(a.garipov): Merge with [validateAddrs].
@@ -313,45 +304,26 @@ type remoteKVConfig struct {
}
// type check
-var _ validator = (*remoteKVConfig)(nil)
+var _ validate.Interface = (*remoteKVConfig)(nil)
-// validate implements the [validator] interface for *remoteKVConfig.
-func (c *remoteKVConfig) validate() (err error) {
+// Validate implements the [validate.Interface] interface for *remoteKVConfig.
+func (c *remoteKVConfig) Validate() (err error) {
if c == nil {
return errors.ErrNoValue
}
- ttl := c.TTL
+ ttl := time.Duration(c.TTL)
switch c.Type {
case kvModeBackend:
- if ttl.Duration <= 0 {
- return newNotPositiveError("ttl", ttl)
- }
+ return validate.Positive("ttl", ttl)
case kvModeCache:
- // Go on.
+ return nil
case kvModeConsul:
- if ttl.Duration < consulkv.MinTTL || ttl.Duration > consulkv.MaxTTL {
- return fmt.Errorf(
- "ttl: %w: must be between %s and %s; got %s",
- errors.ErrOutOfRange,
- consulkv.MinTTL,
- consulkv.MaxTTL,
- ttl,
- )
- }
+ return validate.InRange("ttl", ttl, consulkv.MinTTL, consulkv.MaxTTL)
case kvModeRedis:
- if ttl.Duration < rediskv.MinTTL {
- return fmt.Errorf(
- "ttl: %w: must be greater than or equal to %s got %s",
- errors.ErrOutOfRange,
- rediskv.MinTTL,
- ttl,
- )
- }
+ return validate.NoLessThan("ttl", ttl, rediskv.MinTTL)
default:
return fmt.Errorf("type: %w: %q", errors.ErrBadEnumValue, c.Type)
}
-
- return nil
}
diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go
index 1feb5ab..9904882 100644
--- a/internal/cmd/cmd.go
+++ b/internal/cmd/cmd.go
@@ -9,32 +9,37 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/cmd/plugin"
+ "github.com/AdguardTeam/AdGuardDNS/internal/experiment"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/AdGuardDNS/internal/version"
"github.com/AdguardTeam/golibs/errors"
- "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
+ "github.com/AdguardTeam/golibs/sentryutil"
)
// Main is the entry point of application.
func Main(plugins *plugin.Registry) {
// TODO(a.garipov, e.burkov): Consider adding timeouts for initialization.
- agd.InitRequestID()
ctx := context.Background()
- // Log only to stdout and let users decide how to process it.
- log.SetOutput(os.Stdout)
-
envs := errors.Must(parseEnvironment())
+ errors.Check(envs.Validate())
- errors.Check(envs.validate())
+ lvl := errors.Must(slogutil.VerbosityToLevel(envs.Verbosity))
+ baseLogger := slogutil.New(&slogutil.Config{
+ // Don't use [slogutil.NewFormat] here, because the value is validated.
+ Format: slogutil.Format(envs.LogFormat),
+ AddTimestamp: bool(envs.LogTimestamp),
+ Level: lvl,
+ })
- // TODO(a.garipov): Use slog everywhere.
- logger := envs.configureLogs()
+ sentryutil.SetDefaultLogger(baseLogger, "")
+
+ experiment.Init(baseLogger)
// TODO(a.garipov): Consider ways of replacing a prefix and stop passing
// the main logger everywhere.
- mainLogger := logger.With(slogutil.KeyPrefix, "main")
+ mainLogger := baseLogger.With(slogutil.KeyPrefix, "main")
// Signal service startup now that we have the logs set up.
branch := version.Branch()
@@ -56,13 +61,13 @@ func Main(plugins *plugin.Registry) {
// TODO(a.garipov): Consider parsing SENTRY_DSN separately to set sentry up
// first and collect panics from the readEnvs call above as well.
- errColl := errors.Must(envs.buildErrColl())
+ errColl := errors.Must(envs.buildErrColl(baseLogger))
defer reportPanics(ctx, errColl, mainLogger)
c := errors.Must(parseConfig(envs.ConfPath))
- errors.Check(c.validate())
+ errors.Check(c.Validate())
errors.Check(envs.validateFromValidConfig(c))
@@ -73,7 +78,7 @@ func Main(plugins *plugin.Registry) {
b := newBuilder(&builderConfig{
envs: envs,
conf: c,
- baseLogger: logger,
+ baseLogger: baseLogger,
plugins: plugins,
errColl: errColl,
})
@@ -92,6 +97,10 @@ func Main(plugins *plugin.Registry) {
errors.Check(b.initBindToDevice(ctx))
+ errors.Check(b.initDNSDB(ctx))
+
+ errors.Check(b.initQueryLog(ctx))
+
errors.Check(b.initMsgConstructor(ctx))
errors.Check(b.initTLSManager(ctx))
diff --git a/internal/cmd/config.go b/internal/cmd/config.go
index e8d23c1..9c26f48 100644
--- a/internal/cmd/config.go
+++ b/internal/cmd/config.go
@@ -6,6 +6,7 @@ import (
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/validate"
"gopkg.in/yaml.v2"
)
@@ -83,16 +84,16 @@ type configuration struct {
}
// type check
-var _ validator = (*configuration)(nil)
+var _ validate.Interface = (*configuration)(nil)
-// validate implements the [validator] interface for *configuration.
-func (c *configuration) validate() (err error) {
+// Validate implements the [validate.Interface] interface for *configuration.
+func (c *configuration) Validate() (err error) {
if c == nil {
return errors.ErrNoValue
}
// Keep this in the same order as the fields in the config.
- validators := container.KeyValues[string, validator]{{
+ validators := container.KeyValues[string, validate.Interface]{{
Key: "ratelimit",
Value: c.RateLimit,
}, {
@@ -154,15 +155,12 @@ func (c *configuration) validate() (err error) {
Value: c.AdditionalMetricsInfo,
}}
- // TODO(a.garipov): Use errors.Join everywhere.
+ var errs []error
for _, kv := range validators {
- err = kv.Value.validate()
- if err != nil {
- return fmt.Errorf("%s: %w", kv.Key, err)
- }
+ errs = validate.Append(errs, kv.Key, kv.Value)
}
- return nil
+ return errors.Join(errs...)
}
// isProfilesEnabled returns true if there is at least one server group with
diff --git a/internal/cmd/conncheck.go b/internal/cmd/conncheck.go
index 0ab6a78..30476c6 100644
--- a/internal/cmd/conncheck.go
+++ b/internal/cmd/conncheck.go
@@ -5,8 +5,9 @@ import (
"net"
"net/netip"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
+ "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc"
"github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/validate"
)
// connCheckConfig is the connectivity check configuration.
@@ -19,18 +20,15 @@ type connCheckConfig struct {
}
// type check
-var _ validator = (*connCheckConfig)(nil)
+var _ validate.Interface = (*connCheckConfig)(nil)
-// validate implements the [validator] interface for *connCheckConfig.
-func (c *connCheckConfig) validate() (err error) {
- switch {
- case c == nil:
+// Validate implements the [validate.Interface] interface for *connCheckConfig.
+func (c *connCheckConfig) Validate() (err error) {
+ if c == nil {
return errors.ErrNoValue
- case c.ProbeIPv4 == netip.AddrPort{}:
- return fmt.Errorf("probe_ipv4: %w", errors.ErrEmptyValue)
}
- return nil
+ return validate.NotEmpty("probe_ipv4", c.ProbeIPv4)
}
// connectivityCheck performs connectivity checks for bind addresses with
@@ -38,7 +36,10 @@ func (c *connCheckConfig) validate() (err error) {
// server bind addresses looking up for IPv6 addresses. If an IPv6 address is
// found, then additionally to a general probe to IPv4 it will perform a check
// to IPv6 probe address.
-func connectivityCheck(srvGrps []*agd.ServerGroup, connCheck *connCheckConfig) (err error) {
+func connectivityCheck(
+ srvGrps []*dnssvc.ServerGroupConfig,
+ connCheck *connCheckConfig,
+) (err error) {
probeIPv4 := net.TCPAddrFromAddrPort(connCheck.ProbeIPv4)
// General check to IPv4 probe address.
@@ -78,7 +79,7 @@ func connectivityCheck(srvGrps []*agd.ServerGroup, connCheck *connCheckConfig) (
// requireIPv6ConnCheck returns true if provided serverGroups require IPv6
// connectivity check.
-func requireIPv6ConnCheck(serverGroups []*agd.ServerGroup) (ok bool) {
+func requireIPv6ConnCheck(serverGroups []*dnssvc.ServerGroupConfig) (ok bool) {
for _, srvGrp := range serverGroups {
for _, s := range srvGrp.Servers {
if s.HasIPv6() {
diff --git a/internal/cmd/context.go b/internal/cmd/context.go
deleted file mode 100644
index 5765ab8..0000000
--- a/internal/cmd/context.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package cmd
-
-import (
- "context"
- "time"
-)
-
-// defaultTimeout is the timeout used for some operations where another timeout
-// hasn't been defined yet.
-const defaultTimeout = 30 * time.Second
-
-// contextConstructor is a type alias for functions that can create a context.
-type contextConstructor = func() (ctx context.Context, cancel context.CancelFunc)
-
-// ctxWithDefaultTimeout is a helper function that returns a context with
-// timeout set to defaultTimeout.
-func ctxWithDefaultTimeout() (ctx context.Context, cancel context.CancelFunc) {
- return context.WithTimeout(context.Background(), defaultTimeout)
-}
-
-// newCtxWithTimeoutCons returns a context constructor that creates a simple
-// context with the given timeout.
-func newCtxWithTimeoutCons(timeout time.Duration) (c contextConstructor) {
- parent := context.Background()
-
- return func() (ctx context.Context, cancel context.CancelFunc) {
- return context.WithTimeout(parent, timeout)
- }
-}
diff --git a/internal/cmd/ddr.go b/internal/cmd/ddr.go
index 583c6b6..b5db58d 100644
--- a/internal/cmd/ddr.go
+++ b/internal/cmd/ddr.go
@@ -9,9 +9,11 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
+ "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
+ "github.com/AdguardTeam/golibs/validate"
"github.com/miekg/dns"
)
@@ -19,7 +21,7 @@ import (
type ddrConfig struct {
// DeviceRecords are used to respond to DDR queries from recognized devices.
// The keys of the map are device ID wildcards.
- DeviceRecords map[string]*ddrRecord `yaml:"device_records"`
+ DeviceRecords ddrDeviceRecords `yaml:"device_records"`
// PublicRecords are used to respond to DDR queries from unrecognized
// devices. The keys of the map are the public domain names.
@@ -30,10 +32,42 @@ type ddrConfig struct {
Enabled bool `yaml:"enabled"`
}
+// ddrDeviceRecords is a mapping of wildcard domains to the DDR records for
+// devices using that domain.
+type ddrDeviceRecords map[string]*ddrRecord
+
+// type check
+var _ validate.Interface = ddrDeviceRecords(nil)
+
+// Validate implements the [validate.Interface] interface for ddrDeviceRecords.
+func (recs ddrDeviceRecords) Validate() (err error) {
+ var errs []error
+ for wildcard, r := range recs {
+ if !strings.HasPrefix(wildcard, "*.") {
+ errs = append(errs, fmt.Errorf("wildcard %q: not a wildcard", wildcard))
+
+ continue
+ }
+
+ domainSuf := wildcard[2:]
+ err = netutil.ValidateHostname(domainSuf)
+ if err != nil {
+ errs = append(errs, fmt.Errorf("wildcard %q: %w", wildcard, err))
+ }
+
+ err = r.Validate()
+ if err != nil {
+ errs = append(errs, fmt.Errorf("wildcard %q: %w", wildcard, err))
+ }
+ }
+
+ return errors.Join(errs...)
+}
+
// toInternal returns the DDR configuration. messages must not be nil. c must
// be valid.
-func (c *ddrConfig) toInternal(msgs *dnsmsg.Constructor) (conf *agd.DDR) {
- conf = &agd.DDR{
+func (c *ddrConfig) toInternal(msgs *dnsmsg.Constructor) (conf *dnssvc.DDRConfig) {
+ conf = &dnssvc.DDRConfig{
Enabled: c.Enabled,
}
@@ -99,34 +133,31 @@ func appendDDRSVCBTmpls(
}
// type check
-var _ validator = (*ddrConfig)(nil)
+var _ validate.Interface = (*ddrConfig)(nil)
-// validate implements the [validator] interface for *ddrConfig.
-func (c *ddrConfig) validate() (err error) {
+// Validate implements the [validate.Interface] interface for *ddrConfig.
+func (c *ddrConfig) Validate() (err error) {
if c == nil {
return errors.ErrNoValue
}
- for wildcard, r := range c.DeviceRecords {
- if !strings.HasPrefix(wildcard, "*.") {
- return fmt.Errorf("device_records: record for wildcard %q: not a wildcard", wildcard)
- }
+ var errs []error
- domainSuf := wildcard[2:]
- err = errors.Join(netutil.ValidateHostname(domainSuf), r.validate())
- if err != nil {
- return fmt.Errorf("device_records: wildcard %q: %w", wildcard, err)
- }
- }
+ errs = validate.Append(errs, "device_records", c.DeviceRecords)
for domain, r := range c.PublicRecords {
- err = errors.Join(netutil.ValidateHostname(domain), r.validate())
+ err = netutil.ValidateHostname(domain)
if err != nil {
- return fmt.Errorf("public_records: domain %q: %w", domain, err)
+ errs = append(errs, fmt.Errorf("public_records: domain %q: %w", domain, err))
+ }
+
+ err = r.Validate()
+ if err != nil {
+ errs = append(errs, fmt.Errorf("public_records: domain %q: %w", domain, err))
}
}
- return nil
+ return errors.Join(errs...)
}
// ddrRecord is a DDR record template for responses to DDR queries from both
@@ -158,34 +189,42 @@ type ddrRecord struct {
}
// type check
-var _ validator = (*ddrRecord)(nil)
+var _ validate.Interface = (*ddrRecord)(nil)
-// validate implements the [validator] interface for *ddrRecord.
-func (r *ddrRecord) validate() (err error) {
+// Validate implements the [validate.Interface] interface for *ddrRecord.
+func (r *ddrRecord) Validate() (err error) {
if r == nil {
return errors.ErrNoValue
}
+ var errs []error
+
// TODO(a.garipov): Consider validating that r.DoHPath is a valid RFC 6570
// URI template.
if r.HTTPSPort != 0 && r.DoHPath == "" {
- return errors.Error("doh_path: cannot be empty if https_port is set")
+ errs = append(errs, errors.Error("doh_path: cannot be empty if https_port is set"))
}
// TODO(a.garipov): Merge with [validateAddrs] and [validateNonNilIPs].
for i, addr := range r.IPv4Hints {
if !addr.Is4() {
- return fmt.Errorf("ipv4_hints: at index %d: not an ipv4 addr", i)
+ errs = append(errs, fmt.Errorf("ipv4_hints: at index %d: not an ipv4 addr", i))
}
}
for i, addr := range r.IPv6Hints {
if !addr.Is6() {
- return fmt.Errorf("ipv6_hints: at index %d: not an ipv6 addr", i)
+ errs = append(errs, fmt.Errorf("ipv6_hints: at index %d: not an ipv6 addr", i))
}
}
- return r.validatePorts()
+ err = r.validatePorts()
+ if err != nil {
+ // Don't wrap the error, because it's informative enough as is.
+ errs = append(errs, err)
+ }
+
+ return errors.Join(errs...)
}
// validatePorts returns an error if the DDR record has invalid ports. r must
diff --git a/internal/cmd/dns.go b/internal/cmd/dns.go
index c097984..cf95c66 100644
--- a/internal/cmd/dns.go
+++ b/internal/cmd/dns.go
@@ -1,11 +1,12 @@
package cmd
import (
- "fmt"
+ "time"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/timeutil"
+ "github.com/AdguardTeam/golibs/validate"
"github.com/c2h5oh/datasize"
"github.com/miekg/dns"
)
@@ -34,37 +35,25 @@ type dnsConfig struct {
}
// type check
-var _ validator = (*dnsConfig)(nil)
+var _ validate.Interface = (*dnsConfig)(nil)
-// validate implements the [validator] interface for *dnsConfig.
-func (c *dnsConfig) validate() (err error) {
- switch {
- case c == nil:
+// Validate implements the [validate.Interface] interface for *dnsConfig.
+func (c *dnsConfig) Validate() (err error) {
+ if c == nil {
return errors.ErrNoValue
- case c.ReadTimeout.Duration <= 0:
- return newNotPositiveError("read_timeout", c.ReadTimeout)
- case c.TCPIdleTimeout.Duration <= 0:
- return newNotPositiveError("tcp_idle_timeout", c.TCPIdleTimeout)
- case c.TCPIdleTimeout.Duration > dnsserver.MaxTCPIdleTimeout:
- return fmt.Errorf(
- "tcp_idle_timeout: %w: must be less than or equal to %s got %s",
- errors.ErrOutOfRange,
- dnsserver.MaxTCPIdleTimeout,
- c.TCPIdleTimeout,
- )
- case c.WriteTimeout.Duration <= 0:
- return newNotPositiveError("write_timeout", c.WriteTimeout)
- case c.HandleTimeout.Duration <= 0:
- return newNotPositiveError("handle_timeout", c.HandleTimeout)
- case c.MaxUDPResponseSize.Bytes() == 0:
- return newNotPositiveError("max_udp_response_size", c.MaxUDPResponseSize)
- case c.MaxUDPResponseSize.Bytes() > dns.MaxMsgSize:
- return fmt.Errorf(
- "max_udp_response_size must be less than %s, got %s",
- datasize.ByteSize(dns.MaxMsgSize),
- c.MaxUDPResponseSize,
- )
- default:
- return nil
}
+
+ return errors.Join(
+ validate.Positive("read_timeout", c.ReadTimeout),
+ validate.Positive("tcp_idle_timeout", c.TCPIdleTimeout),
+ validate.NoGreaterThan(
+ "tcp_idle_timeout",
+ time.Duration(c.TCPIdleTimeout),
+ dnsserver.MaxTCPIdleTimeout,
+ ),
+ validate.Positive("write_timeout", c.WriteTimeout),
+ validate.Positive("handle_timeout", c.HandleTimeout),
+ validate.Positive("max_udp_response_size", c.MaxUDPResponseSize),
+ validate.NoGreaterThan("max_udp_response_size", c.MaxUDPResponseSize, dns.MaxMsgSize),
+ )
}
diff --git a/internal/cmd/dnscrypt.go b/internal/cmd/dnscrypt.go
index 2474c4a..0af522c 100644
--- a/internal/cmd/dnscrypt.go
+++ b/internal/cmd/dnscrypt.go
@@ -6,6 +6,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/validate"
"github.com/ameshkov/dnscrypt/v2"
"gopkg.in/yaml.v2"
)
@@ -63,9 +64,9 @@ func (c *dnsCryptConfig) toInternal() (conf *agd.DNSCryptConfig, err error) {
}, nil
}
-// validate returns an error if the DNSCrypt configuration is invalid for the
-// given protocol.
-func (c *dnsCryptConfig) validate(p serverProto) (err error) {
+// validateForProtocol returns an error if the DNSCrypt configuration is invalid
+// for the given protocol.
+func (c *dnsCryptConfig) validateForProtocol(p serverProto) (err error) {
needsDC := p == srvProtoDNSCrypt
switch {
case c == nil:
@@ -91,18 +92,18 @@ func (c *dnsCryptConfig) validate(p serverProto) (err error) {
return nil
}
-// validateDNSCrypt validates DNSCrypt resolver configuration.
+// validateDNSCrypt validates DNSCrypt resolver configuration. rc must not be
+// nil.
func validateDNSCrypt(rc *dnscrypt.ResolverConfig) (err error) {
- switch {
- case rc.ProviderName == "":
- return errors.Error("no provider_name")
- case rc.PublicKey == "":
- return errors.Error("no public_key")
- case rc.PrivateKey == "":
- return errors.Error("no private_key")
- case rc.EsVersion != dnscrypt.XChacha20Poly1305 && rc.EsVersion != dnscrypt.XSalsa20Poly1305:
- return fmt.Errorf("bad es_version: %d", rc.EsVersion)
- default:
- return nil
+ errs := []error{
+ validate.NotEmpty("provider_name", rc.ProviderName),
+ validate.NotEmpty("public_key", rc.PublicKey),
+ validate.NotEmpty("private_key", rc.PrivateKey),
}
+
+ if rc.EsVersion != dnscrypt.XChacha20Poly1305 && rc.EsVersion != dnscrypt.XSalsa20Poly1305 {
+ errs = append(errs, fmt.Errorf("es_version: %w: %d", errors.ErrBadEnumValue, rc.EsVersion))
+ }
+
+ return errors.Join(errs...)
}
diff --git a/internal/cmd/dnsdb.go b/internal/cmd/dnsdb.go
index 80bdc0a..15e8d95 100644
--- a/internal/cmd/dnsdb.go
+++ b/internal/cmd/dnsdb.go
@@ -1,12 +1,8 @@
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"
+ "github.com/AdguardTeam/golibs/validate"
)
// dnsDBConfig is the configuration of the DNSDB module.
@@ -19,37 +15,15 @@ type dnsDBConfig struct {
}
// type check
-var _ validator = (*dnsDBConfig)(nil)
+var _ validate.Interface = (*dnsDBConfig)(nil)
-// validate implements the [validator] interface for *dnsDBConfig.
-func (c *dnsDBConfig) validate() (err error) {
- switch {
- case c == nil:
+// Validate implements the [validate.Interface] interface for *dnsDBConfig.
+func (c *dnsDBConfig) Validate() (err error) {
+ if c == nil {
return errors.ErrNoValue
- case !c.Enabled:
- return nil
- case c.MaxSize <= 0:
- return newNotPositiveError("size", c.MaxSize)
- default:
+ } else if !c.Enabled {
return nil
}
-}
-
-// toInternal builds and returns an anonymous statistics collector. c must be
-// valid.
-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,
- })
-
- return db
+
+ return validate.Positive("max_size", c.MaxSize)
}
diff --git a/internal/cmd/env.go b/internal/cmd/env.go
index 87ff37d..5ceee25 100644
--- a/internal/cmd/env.go
+++ b/internal/cmd/env.go
@@ -15,11 +15,11 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/version"
"github.com/AdguardTeam/golibs/errors"
- "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/AdguardTeam/golibs/timeutil"
+ "github.com/AdguardTeam/golibs/validate"
"github.com/c2h5oh/datasize"
"github.com/caarlos0/env/v7"
"github.com/getsentry/sentry-go"
@@ -53,11 +53,12 @@ type environment struct {
FilterCachePath string `env:"FILTER_CACHE_PATH" envDefault:"./filters/"`
GeoIPASNPath string `env:"GEOIP_ASN_PATH" envDefault:"./asn.mmdb"`
GeoIPCountryPath string `env:"GEOIP_COUNTRY_PATH" envDefault:"./country.mmdb"`
+ LogFormat string `env:"LOG_FORMAT" envDefault:"text"`
ProfilesAPIKey string `env:"PROFILES_API_KEY"`
ProfilesCachePath string `env:"PROFILES_CACHE_PATH" envDefault:"./profilecache.pb"`
+ QueryLogPath string `env:"QUERYLOG_PATH" envDefault:"./querylog.jsonl"`
RedisAddr string `env:"REDIS_ADDR"`
RedisKeyPrefix string `env:"REDIS_KEY_PREFIX" envDefault:"agdns"`
- QueryLogPath string `env:"QUERYLOG_PATH" envDefault:"./querylog.jsonl"`
SSLKeyLogFile string `env:"SSL_KEY_LOG_FILE"`
SentryDSN string `env:"SENTRY_DSN" envDefault:"stderr"`
WebStaticDir string `env:"WEB_STATIC_DIR"`
@@ -100,11 +101,10 @@ func parseEnvironment() (envs *environment, err error) {
}
// type check
-var _ validator = (*environment)(nil)
+var _ validate.Interface = (*environment)(nil)
-// validate implements the [validator] interface for *environment.
-func (envs *environment) validate() (err error) {
- // TODO(a.garipov): Use a similar approach with errors.Join everywhere.
+// Validate implements the [validate.Interface] interface for *environment.
+func (envs *environment) Validate() (err error) {
var errs []error
errs = envs.validateHTTPURLs(errs)
@@ -112,19 +112,24 @@ func (envs *environment) validate() (err error) {
if s := envs.FilterIndexURL.Scheme; !strings.EqualFold(s, urlutil.SchemeFile) &&
!urlutil.IsValidHTTPURLScheme(s) {
errs = append(errs, fmt.Errorf(
- "env %s: not a valid http(s) url or file uri",
+ "%s: not a valid http(s) url or file uri",
"FILTER_INDEX_URL",
))
}
err = envs.validateWebStaticDir()
if err != nil {
- errs = append(errs, fmt.Errorf("env WEB_STATIC_DIR: %w", err))
+ errs = append(errs, fmt.Errorf("WEB_STATIC_DIR: %w", err))
+ }
+
+ _, err = slogutil.NewFormat(envs.LogFormat)
+ if err != nil {
+ errs = append(errs, fmt.Errorf("LOG_FORMAT: %w", err))
}
_, err = slogutil.VerbosityToLevel(envs.Verbosity)
if err != nil {
- errs = append(errs, fmt.Errorf("env VERBOSE: %w", err))
+ errs = append(errs, fmt.Errorf("VERBOSE: %w", err))
}
return errors.Join(errs...)
@@ -234,7 +239,7 @@ func (envs *environment) validateWebStaticDir() (err error) {
func (envs *environment) validateFromValidConfig(conf *configuration) (err error) {
var errs []error
- switch typ := conf.Check.RemoteKV.Type; typ {
+ switch typ := conf.Check.KV.Type; typ {
case kvModeBackend:
errs = envs.validateBackendKV(errs)
case kvModeCache:
@@ -248,13 +253,13 @@ func (envs *environment) validateFromValidConfig(conf *configuration) (err error
if conf.isProfilesEnabled() {
errs = envs.validateProfilesURLs(errs)
- if envs.ProfilesMaxRespSize > math.MaxInt {
- errs = append(errs, fmt.Errorf(
- "PROFILES_MAX_RESP_SIZE: %w: must be less than or equal to %s, got %s",
- errors.ErrOutOfRange,
- datasize.ByteSize(math.MaxInt),
- envs.ProfilesMaxRespSize,
- ))
+ err = validate.NoGreaterThan(
+ "PROFILES_MAX_RESP_SIZE",
+ envs.ProfilesMaxRespSize,
+ math.MaxInt,
+ )
+ if err != nil {
+ errs = append(errs, err)
}
}
@@ -268,8 +273,9 @@ func (envs *environment) validateFromValidConfig(conf *configuration) (err error
func (envs *environment) validateCache(errs []error) (res []error) {
res = errs
- if envs.DNSCheckCacheKVSize <= 0 {
- err := newNotPositiveError("DNSCHECK_CACHE_KV_SIZE", envs.DNSCheckCacheKVSize)
+ err := validate.Positive("env DNSCHECK_CACHE_KV_SIZE", envs.DNSCheckCacheKVSize)
+ if err != nil {
+ // Don't wrap the error, because it's informative enough as is.
res = append(res, err)
}
@@ -281,23 +287,23 @@ func (envs *environment) validateCache(errs []error) (res []error) {
func (envs *environment) validateRedis(errs []error) (res []error) {
res = errs
- if envs.RedisAddr == "" {
- err := fmt.Errorf("REDIS_ADDR: %w", errors.ErrEmptyValue)
+ if err := validate.NotEmpty("env REDIS_ADDR", envs.RedisAddr); err != nil {
+ // Don't wrap the error, because it's informative enough as is.
res = append(res, err)
}
- if envs.RedisIdleTimeout.Duration <= 0 {
- err := newNotPositiveError("REDIS_IDLE_TIMEOUT", envs.RedisIdleTimeout)
+ if err := validate.Positive("env REDIS_IDLE_TIMEOUT", envs.RedisIdleTimeout); err != nil {
+ // Don't wrap the error, because it's informative enough as is.
res = append(res, err)
}
- if envs.RedisMaxActive < 0 {
- err := newNegativeError("REDIS_MAX_ACTIVE", envs.RedisMaxActive)
+ if err := validate.NotNegative("env REDIS_MAX_ACTIVE", envs.RedisMaxActive); err != nil {
+ // Don't wrap the error, because it's informative enough as is.
res = append(res, err)
}
- if envs.RedisMaxIdle < 0 {
- err := newNegativeError("REDIS_MAX_IDLE", envs.RedisMaxIdle)
+ if err := validate.NotNegative("env REDIS_MAX_IDLE", envs.RedisMaxIdle); err != nil {
+ // Don't wrap the error, because it's informative enough as is.
res = append(res, err)
}
@@ -385,31 +391,11 @@ func (envs *environment) validateRateLimitURLs(
return errs
}
-// configureLogs sets the configuration for the plain text logs. It also
-// returns a [slog.Logger] for code that uses it.
-func (envs *environment) configureLogs() (slogLogger *slog.Logger) {
- var flags int
- if envs.LogTimestamp {
- flags = log.LstdFlags | log.Lmicroseconds
- }
-
- log.SetFlags(flags)
-
- lvl := errors.Must(slogutil.VerbosityToLevel(envs.Verbosity))
- if lvl < slog.LevelInfo {
- log.SetLevel(log.DEBUG)
- }
-
- return slogutil.New(&slogutil.Config{
- Output: os.Stdout,
- Format: slogutil.FormatAdGuardLegacy,
- Level: lvl,
- AddTimestamp: bool(envs.LogTimestamp),
- })
-}
-
// buildErrColl builds and returns an error collector from environment.
-func (envs *environment) buildErrColl() (errColl errcoll.Interface, err error) {
+// baseLogger must not be nil.
+func (envs *environment) buildErrColl(
+ baseLogger *slog.Logger,
+) (errColl errcoll.Interface, err error) {
dsn := envs.SentryDSN
if dsn == "stderr" {
return errcoll.NewWriterErrorCollector(os.Stderr), nil
@@ -424,7 +410,9 @@ func (envs *environment) buildErrColl() (errColl errcoll.Interface, err error) {
return nil, err
}
- return errcoll.NewSentryErrorCollector(cli), nil
+ l := baseLogger.With(slogutil.KeyPrefix, "sentry_errcoll")
+
+ return errcoll.NewSentryErrorCollector(cli, l), nil
}
// debugConf returns a debug HTTP service configuration from environment.
diff --git a/internal/cmd/error.go b/internal/cmd/error.go
index 7fc9f78..6d4aee6 100644
--- a/internal/cmd/error.go
+++ b/internal/cmd/error.go
@@ -2,25 +2,17 @@ package cmd
import (
"context"
- "fmt"
"log/slog"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
- "github.com/AdguardTeam/golibs/timeutil"
- "golang.org/x/exp/constraints"
)
-// validator is the interface for configuration entities that can validate
-// themselves.
-type validator interface {
- // validate returns an error if the entity isn't valid.
- validate() (err error)
-}
-
// reportPanics reports all panics in Main using the Sentry client, logs them,
// and repanics. It should be called in a defer.
+//
+// TODO(a.garipov): Consider switching to pure Sentry.
func reportPanics(ctx context.Context, errColl errcoll.Interface, l *slog.Logger) {
v := recover()
if v == nil {
@@ -39,31 +31,3 @@ func reportPanics(ctx context.Context, errColl errcoll.Interface, l *slog.Logger
panic(v)
}
-
-// numberOrDuration is the constraint for integer types along with
-// [timeutil.Duration].
-type numberOrDuration interface {
- constraints.Integer | timeutil.Duration
-}
-
-// newNotPositiveError returns an error about the value that must be positive
-// but isn't. prop is the name of the property to mention in the error message.
-// The returned error has underlying value of [errors.ErrNotPositive].
-func newNotPositiveError[T numberOrDuration](prop string, v T) (err error) {
- if s, ok := any(v).(fmt.Stringer); ok {
- return fmt.Errorf("%s: %w: got %s", prop, errors.ErrNotPositive, s)
- }
-
- return fmt.Errorf("%s: %w: got %d", prop, errors.ErrNotPositive, v)
-}
-
-// newNegativeError returns an error about the value that must be non-negative
-// but isn't. prop is the name of the property to mention in the error message.
-// The returned error has underlying value of [errors.ErrNegative].
-func newNegativeError[T numberOrDuration](prop string, v T) (err error) {
- if s, ok := any(v).(fmt.Stringer); ok {
- return fmt.Errorf("%s: %w: got %s", prop, errors.ErrNegative, s)
- }
-
- return fmt.Errorf("%s: %w: got %d", prop, errors.ErrNegative, v)
-}
diff --git a/internal/cmd/filter.go b/internal/cmd/filter.go
index 0596a32..9edd671 100644
--- a/internal/cmd/filter.go
+++ b/internal/cmd/filter.go
@@ -1,10 +1,9 @@
package cmd
import (
- "fmt"
-
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/timeutil"
+ "github.com/AdguardTeam/golibs/validate"
"github.com/c2h5oh/datasize"
)
@@ -63,33 +62,30 @@ type filtersConfig struct {
}
// type check
-var _ validator = (*filtersConfig)(nil)
+var _ validate.Interface = (*filtersConfig)(nil)
-// validate implements the [validator] interface for *filtersConfig.
-func (c *filtersConfig) validate() (err error) {
+// Validate implements the [validate.Interface] interface for *filtersConfig.
+func (c *filtersConfig) Validate() (err error) {
if c == nil {
return errors.ErrNoValue
}
errs := []error{
- validatePositive("custom_filter_cache_size", c.CustomFilterCacheSize),
- validatePositive("safe_search_cache_size", c.SafeSearchCacheSize),
- validatePositive("response_ttl", c.ResponseTTL),
- validatePositive("refresh_interval", c.RefreshIvl),
- validatePositive("refresh_timeout", c.RefreshTimeout),
- validatePositive("index_refresh_timeout", c.IndexRefreshTimeout),
- validatePositive("rule_list_refresh_timeout", c.RuleListRefreshTimeout),
- validatePositive("max_size", c.MaxSize),
+ validate.Positive("custom_filter_cache_size", c.CustomFilterCacheSize),
+ validate.Positive("safe_search_cache_size", c.SafeSearchCacheSize),
+ validate.Positive("response_ttl", c.ResponseTTL),
+ validate.Positive("refresh_interval", c.RefreshIvl),
+ validate.Positive("refresh_timeout", c.RefreshTimeout),
+ validate.Positive("index_refresh_timeout", c.IndexRefreshTimeout),
+ validate.Positive("rule_list_refresh_timeout", c.RuleListRefreshTimeout),
+ validate.Positive("max_size", c.MaxSize),
}
if !c.EDEEnabled && c.SDEEnabled {
errs = append(errs, errors.Error("ede must be enabled to enable sde"))
}
- err = c.RuleListCache.validate()
- if err != nil {
- errs = append(errs, fmt.Errorf("rule_list_cache: %w", err))
- }
+ errs = validate.Append(errs, "rule_list_cache", c.RuleListCache)
return errors.Join(errs...)
}
@@ -107,16 +103,13 @@ type fltRuleListCache struct {
}
// type check
-var _ validator = (*fltRuleListCache)(nil)
+var _ validate.Interface = (*fltRuleListCache)(nil)
-// validate implements the [validator] interface for *fltRuleListCache.
-func (c *fltRuleListCache) validate() (err error) {
- switch {
- case c == nil:
+// Validate implements the [validate.Interface] interface for *fltRuleListCache.
+func (c *fltRuleListCache) Validate() (err error) {
+ if c == nil {
return errors.ErrNoValue
- case c.Size <= 0:
- return newNotPositiveError("size", c.Size)
- default:
- return nil
}
+
+ return validate.Positive("size", c.Size)
}
diff --git a/internal/cmd/filteringgroup.go b/internal/cmd/filteringgroup.go
index 4ebc06b..aa88dd9 100644
--- a/internal/cmd/filteringgroup.go
+++ b/internal/cmd/filteringgroup.go
@@ -7,6 +7,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/validate"
)
// filteringGroup represents a set of filtering settings.
@@ -57,6 +58,38 @@ func (c *fltGrpRuleLists) toInternal(ids []filter.ID) (fltConf *filter.ConfigRul
}
}
+// type check
+var _ validate.Interface = (*fltGrpRuleLists)(nil)
+
+// Validate implements the [validate.Interface] interface for *fltGrpRuleLists.
+func (c *fltGrpRuleLists) Validate() (err error) {
+ if c == nil {
+ return errors.ErrNoValue
+ }
+
+ var errs []error
+ fltIDs := container.NewMapSet[string]()
+ for i, fltID := range c.IDs {
+ if fltIDs.Has(fltID) {
+ err = fmt.Errorf("at index %d: id: %w: %q", i, errors.ErrDuplicated, fltID)
+ errs = append(errs, err)
+
+ continue
+ }
+
+ _, err = filter.NewID(fltID)
+ if err != nil {
+ errs = append(errs, fmt.Errorf("rule_lists: at index %d: id: %w", i, err))
+
+ continue
+ }
+
+ fltIDs.Add(fltID)
+ }
+
+ return errors.Join(errs...)
+}
+
// fltGrpParental contains parental protection configuration for a filtering
// group.
type fltGrpParental struct {
@@ -116,42 +149,26 @@ func (c *fltGrpSafeBrowsing) toInternal() (fltConf *filter.ConfigSafeBrowsing) {
}
// type check
-var _ validator = (*filteringGroup)(nil)
+var _ validate.Interface = (*filteringGroup)(nil)
-// validate implements the [validator] interface for *filteringGroup.
-func (g *filteringGroup) validate() (err error) {
- switch {
- case g == nil:
+// Validate implements the [validate.Interface] interface for *filteringGroup.
+func (g *filteringGroup) Validate() (err error) {
+ if g == nil {
return errors.ErrNoValue
- 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: id: %w: %q", i, errors.ErrDuplicated, fltID)
- }
-
- _, err = filter.NewID(fltID)
- if err != nil {
- return fmt.Errorf("rule_lists: at index %d: %w", i, err)
- }
-
- fltIDs.Add(fltID)
+ errs := []error{
+ validate.NotEmpty("id", g.ID),
+ validate.NotNil("parental", g.Parental),
+ validate.NotNil("safe_browsing", g.SafeBrowsing),
}
- return nil
+ errs = validate.Append(errs, "rule_lists", g.RuleLists)
+
+ return errors.Join(errs...)
}
-// filteringGroups are the filtering settings. A valid instance of
-// filteringGroups has no nil items.
+// filteringGroups are the filtering settings. All items must not be nil.
type filteringGroups []*filteringGroup
// toInternal converts groups to the filtering groups for the DNS server.
@@ -191,27 +208,32 @@ func (groups filteringGroups) toInternal(
}
// type check
-var _ validator = filteringGroups(nil)
+var _ validate.Interface = filteringGroups(nil)
-// validate implements the [validator] interface for filteringGroups.
-func (groups filteringGroups) validate() (err error) {
+// Validate implements the [validate.Interface] interface for filteringGroups.
+func (groups filteringGroups) Validate() (err error) {
if len(groups) == 0 {
- return fmt.Errorf("filtering_groups: %w", errors.ErrEmptyValue)
+ return errors.ErrEmptyValue
}
+ var errs []error
ids := container.NewMapSet[string]()
for i, g := range groups {
- err = g.validate()
+ err = g.Validate()
if err != nil {
- return fmt.Errorf("at index %d: %w", i, err)
+ errs = append(errs, fmt.Errorf("at index %d: %w", i, err))
+
+ continue
}
if ids.Has(string(g.ID)) {
- return fmt.Errorf("at index %d: id: %w: %q", i, errors.ErrDuplicated, g.ID)
+ errs = append(errs, fmt.Errorf("at index %d: %w: %q", i, errors.ErrDuplicated, g.ID))
+
+ continue
}
ids.Add(g.ID)
}
- return nil
+ return errors.Join(errs...)
}
diff --git a/internal/cmd/geoip.go b/internal/cmd/geoip.go
index d691136..6941db6 100644
--- a/internal/cmd/geoip.go
+++ b/internal/cmd/geoip.go
@@ -3,6 +3,7 @@ package cmd
import (
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/timeutil"
+ "github.com/AdguardTeam/golibs/validate"
)
// geoIPConfig is the GeoIP database configuration.
@@ -23,22 +24,19 @@ type geoIPConfig struct {
}
// type check
-var _ validator = (*geoIPConfig)(nil)
+var _ validate.Interface = (*geoIPConfig)(nil)
-// validate implements the [validator] interface for *geoIPConfig.
-func (c *geoIPConfig) validate() (err error) {
- switch {
- case c == nil:
+// Validate implements the [validate.Interface] interface for *geoIPConfig.
+func (c *geoIPConfig) Validate() (err error) {
+ if c == nil {
return errors.ErrNoValue
- case c.HostCacheSize <= 0:
- // Note that while geoip.File can work with an empty host cache, that
- // feature is only used for tests.
- return newNotPositiveError("host_cache_size", c.HostCacheSize)
- case c.IPCacheSize <= 0:
- return newNotPositiveError("ip_cache_size", c.IPCacheSize)
- case c.RefreshIvl.Duration <= 0:
- return newNotPositiveError("refresh_interval", c.RefreshIvl)
- default:
- return nil
}
+
+ return errors.Join(
+ // NOTE: While a [geoip.File] can work with an empty host cache, that
+ // feature is only used for tests.
+ validate.Positive("host_cache_size", c.HostCacheSize),
+ validate.Positive("ip_cache_size", c.IPCacheSize),
+ validate.Positive("refresh_interval", c.RefreshIvl),
+ )
}
diff --git a/internal/cmd/ifacelistener.go b/internal/cmd/ifacelistener.go
index bb67238..46986dc 100644
--- a/internal/cmd/ifacelistener.go
+++ b/internal/cmd/ifacelistener.go
@@ -10,6 +10,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
+ "github.com/AdguardTeam/golibs/validate"
)
// interfaceListenersConfig contains the optional configuration for the network
@@ -53,33 +54,37 @@ func (c *interfaceListenersConfig) toInternal(
}
// type check
-var _ validator = (*interfaceListenersConfig)(nil)
+var _ validate.Interface = (*interfaceListenersConfig)(nil)
-// validate implements the [validator] interface for *interfaceListenersConfig.
-func (c *interfaceListenersConfig) validate() (err error) {
- switch {
- case c == nil:
+// Validate implements the [validate.Interface] interface for
+// *interfaceListenersConfig.
+func (c *interfaceListenersConfig) Validate() (err error) {
+ if c == nil {
// This configuration is optional.
//
// TODO(a.garipov): Consider making required or not relying on nil
// values.
return nil
- case c.ChannelBufferSize <= 0:
- return newNotPositiveError("channel_buffer_size", c.ChannelBufferSize)
- case len(c.List) == 0:
- return fmt.Errorf("list: %w", errors.ErrEmptyValue)
- default:
- // Go on.
}
+ errs := []error{
+ validate.Positive("channel_buffer_size", c.ChannelBufferSize),
+ }
+
+ // TODO(a.garipov): Consider adding validate.NotEmptyMap.
+ if len(c.List) == 0 {
+ errs = append(errs, fmt.Errorf("list: %w", errors.ErrEmptyValue))
+ }
+
+ // TODO(a.garipov): Consider adding validate.Map.
for _, id := range slices.Sorted(maps.Keys(c.List)) {
- err = c.List[id].validate()
+ err = c.List[id].Validate()
if err != nil {
- return fmt.Errorf("interface %q: %w", id, err)
+ errs = append(errs, fmt.Errorf("interface %q: %w", id, err))
}
}
- return err
+ return errors.Join(errs...)
}
// interfaceListener contains configuration for a single network interface
@@ -93,18 +98,17 @@ type interfaceListener struct {
}
// type check
-var _ validator = (*interfaceListener)(nil)
+var _ validate.Interface = (*interfaceListener)(nil)
-// validate implements the [validator] interface for *interfaceListener.
-func (l *interfaceListener) validate() (err error) {
- switch {
- case l == nil:
+// Validate implements the [validate.Interface] interface for
+// *interfaceListener.
+func (l *interfaceListener) Validate() (err error) {
+ if l == nil {
return errors.ErrNoValue
- case l.Port == 0:
- return fmt.Errorf("port: %w", errors.ErrEmptyValue)
- case l.Interface == "":
- return fmt.Errorf("interface: %w", errors.ErrEmptyValue)
- default:
- return nil
}
+
+ return errors.Join(
+ validate.Positive("port", l.Port),
+ validate.NotEmpty("interface", l.Interface),
+ )
}
diff --git a/internal/cmd/network.go b/internal/cmd/network.go
index 1472b4b..5a0e90a 100644
--- a/internal/cmd/network.go
+++ b/internal/cmd/network.go
@@ -1,12 +1,12 @@
package cmd
import (
- "fmt"
"math"
"github.com/AdguardTeam/AdGuardDNS/internal/bindtodevice"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext"
"github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/validate"
"github.com/c2h5oh/datasize"
)
@@ -22,29 +22,20 @@ type network struct {
}
// type check
-var _ validator = (*interfaceListener)(nil)
+var _ validate.Interface = (*network)(nil)
-// validate implements the [validator] interface for *network.
-func (n *network) validate() (err error) {
- const maxBufSize datasize.ByteSize = math.MaxInt32
- switch {
- case n == nil:
+// Validate implements the [validate.Interface] interface for *network.
+func (n *network) Validate() (err error) {
+ if n == nil {
return errors.ErrNoValue
- case n.SndBufSize > maxBufSize:
- return fmt.Errorf(
- "so_sndbuf: %s: must be less than or equal to %s",
- errors.ErrOutOfRange,
- maxBufSize,
- )
- case n.RcvBufSize > maxBufSize:
- return fmt.Errorf(
- "so_rcvbuf: %s: must be less than or equal to %s",
- errors.ErrOutOfRange,
- maxBufSize,
- )
- default:
- return nil
}
+
+ const maxBufSize datasize.ByteSize = math.MaxInt32
+
+ return errors.Join(
+ validate.NoGreaterThan("so_sndbuf", n.SndBufSize, maxBufSize),
+ validate.NoGreaterThan("so_rcvbuf", n.RcvBufSize, maxBufSize),
+ )
}
// toInternal converts n to the bindtodevice control configuration and network
diff --git a/internal/cmd/querylog.go b/internal/cmd/querylog.go
index 5ae9e6a..dd5e777 100644
--- a/internal/cmd/querylog.go
+++ b/internal/cmd/querylog.go
@@ -1,9 +1,8 @@
package cmd
import (
- "fmt"
-
"github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/validate"
)
// queryLogConfig is the query log configuration.
@@ -13,18 +12,15 @@ type queryLogConfig struct {
}
// type check
-var _ validator = (*queryLogConfig)(nil)
+var _ validate.Interface = (*queryLogConfig)(nil)
-// validate implements the [validator] interface for *queryLogConfig.
-func (c *queryLogConfig) validate() (err error) {
- switch {
- case c == nil:
+// Validate implements the [validate.Interface] interface for *queryLogConfig.
+func (c *queryLogConfig) Validate() (err error) {
+ if c == nil {
return errors.ErrNoValue
- case c.File == nil:
- return fmt.Errorf("file: %w", errors.ErrNoValue)
- default:
- return nil
}
+
+ return validate.NotNil("file", c.File)
}
// queryLogFileConfig is the JSONL file query log configuration.
diff --git a/internal/cmd/ratelimit.go b/internal/cmd/ratelimit.go
index b107f20..a3923f0 100644
--- a/internal/cmd/ratelimit.go
+++ b/internal/cmd/ratelimit.go
@@ -1,26 +1,21 @@
package cmd
import (
- "cmp"
+ "context"
"fmt"
"log/slog"
+ "time"
"github.com/AdguardTeam/AdGuardDNS/internal/connlimiter"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit"
- "github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/timeutil"
+ "github.com/AdguardTeam/golibs/validate"
"github.com/c2h5oh/datasize"
)
-// Constants for rate limit settings endpoints.
-const (
- rlAllowlistTypeBackend = "backend"
- rlAllowlistTypeConsul = "consul"
-)
-
// rateLimitConfig is the configuration of the instance's rate limiting.
type rateLimitConfig struct {
// AllowList is the allowlist of clients.
@@ -78,18 +73,18 @@ type rateLimitOptions struct {
}
// type check
-var _ validator = (*rateLimitOptions)(nil)
+var _ validate.Interface = (*rateLimitOptions)(nil)
-// validate implements the [validator] interface for *rateLimitOptions.
-func (o *rateLimitOptions) validate() (err error) {
+// Validate implements the [validate.Interface] interface for *rateLimitOptions.
+func (o *rateLimitOptions) Validate() (err error) {
if o == nil {
return errors.ErrNoValue
}
- return cmp.Or(
- validatePositive("count", o.Count),
- validatePositive("interval", o.Interval),
- validatePositive("subnet_key_len", o.SubnetKeyLen),
+ return errors.Join(
+ validate.Positive("count", o.Count),
+ validate.Positive("interval", o.Interval),
+ validate.Positive("subnet_key_len", o.SubnetKeyLen),
)
}
@@ -99,13 +94,13 @@ func (c *rateLimitConfig) toInternal(al ratelimit.Allowlist) (conf *ratelimit.Ba
return &ratelimit.BackoffConfig{
Allowlist: al,
ResponseSizeEstimate: c.ResponseSizeEstimate,
- Duration: c.BackoffDuration.Duration,
- Period: c.BackoffPeriod.Duration,
+ Duration: time.Duration(c.BackoffDuration),
+ Period: time.Duration(c.BackoffPeriod),
IPv4Count: c.IPv4.Count,
- IPv4Interval: c.IPv4.Interval.Duration,
+ IPv4Interval: time.Duration(c.IPv4.Interval),
IPv4SubnetKeyLen: c.IPv4.SubnetKeyLen,
IPv6Count: c.IPv6.Count,
- IPv6Interval: c.IPv6.Interval.Duration,
+ IPv6Interval: time.Duration(c.IPv6.Interval),
IPv6SubnetKeyLen: c.IPv6.SubnetKeyLen,
Count: c.BackoffCount,
RefuseANY: c.RefuseANY,
@@ -113,26 +108,29 @@ func (c *rateLimitConfig) toInternal(al ratelimit.Allowlist) (conf *ratelimit.Ba
}
// type check
-var _ validator = (*rateLimitConfig)(nil)
+var _ validate.Interface = (*rateLimitConfig)(nil)
-// validate implements the [validator] interface for *rateLimitConfig.
-func (c *rateLimitConfig) validate() (err error) {
+// Validate implements the [validate.Interface] interface for *rateLimitConfig.
+func (c *rateLimitConfig) Validate() (err error) {
if c == nil {
return errors.ErrNoValue
}
- return cmp.Or(
- validateProp("allowlist", c.Allowlist.validate),
- validateProp("connection_limit", c.ConnectionLimit.validate),
- validateProp("ipv4", c.IPv4.validate),
- validateProp("ipv6", c.IPv6.validate),
- validateProp("quic", c.QUIC.validate),
- validateProp("tcp", c.TCP.validate),
- validatePositive("backoff_count", c.BackoffCount),
- validatePositive("backoff_duration", c.BackoffDuration),
- validatePositive("backoff_period", c.BackoffPeriod),
- validatePositive("response_size_estimate", c.ResponseSizeEstimate),
- )
+ errs := []error{
+ validate.Positive("backoff_count", c.BackoffCount),
+ validate.Positive("backoff_duration", c.BackoffDuration),
+ validate.Positive("backoff_period", c.BackoffPeriod),
+ validate.Positive("response_size_estimate", c.ResponseSizeEstimate),
+ }
+
+ errs = validate.Append(errs, "allowlist", c.Allowlist)
+ errs = validate.Append(errs, "connection_limit", c.ConnectionLimit)
+ errs = validate.Append(errs, "ipv4", c.IPv4)
+ errs = validate.Append(errs, "ipv6", c.IPv6)
+ errs = validate.Append(errs, "quic", c.QUIC)
+ errs = validate.Append(errs, "tcp", c.TCP)
+
+ return errors.Join(errs...)
}
// allowListConfig is the consul allow list configuration.
@@ -148,23 +146,35 @@ type allowListConfig struct {
RefreshIvl timeutil.Duration `yaml:"refresh_interval"`
}
-// type check
-var _ validator = (*allowListConfig)(nil)
+// Constants for rate limit settings endpoints.
+const (
+ rlAllowlistTypeBackend = "backend"
+ rlAllowlistTypeConsul = "consul"
+)
-// validate implements the [validator] interface for *allowListConfig.
-func (c *allowListConfig) validate() (err error) {
+// type check
+var _ validate.Interface = (*allowListConfig)(nil)
+
+// Validate implements the [validate.Interface] interface for *allowListConfig.
+func (c *allowListConfig) Validate() (err error) {
if c == nil {
return errors.ErrNoValue
}
- switch c.Type {
- case rlAllowlistTypeBackend, rlAllowlistTypeConsul:
- // Go on.
- default:
- return fmt.Errorf("type: %w: %q", errors.ErrBadEnumValue, c.Type)
+ errs := []error{
+ validate.Positive("refresh_interval", c.RefreshIvl),
}
- return validatePositive("refresh_interval", c.RefreshIvl)
+ switch c.Type {
+ case
+ rlAllowlistTypeBackend,
+ rlAllowlistTypeConsul:
+ // Go on.
+ default:
+ errs = append(errs, fmt.Errorf("type: %w: %q", errors.ErrBadEnumValue, c.Type))
+ }
+
+ return errors.Join(errs...)
}
// connLimitConfig is the configuration structure for the stream-connection
@@ -187,44 +197,40 @@ type connLimitConfig struct {
Enabled bool `yaml:"enabled"`
}
-// toInternal converts c to the connection limiter to use. c must be valid.
-func (c *connLimitConfig) toInternal(logger *slog.Logger) (l *connlimiter.Limiter) {
- if !c.Enabled {
- return nil
+// toInternal converts c to a valid connection limiter config. c must be valid.
+// mtrc must not be nil.
+func (c *connLimitConfig) toInternal(
+ ctx context.Context,
+ logger *slog.Logger,
+ mtrc connlimiter.Metrics,
+) (l *connlimiter.Config) {
+ mtrc.SetStopLimit(ctx, c.Stop)
+ mtrc.SetResumeLimit(ctx, c.Resume)
+
+ return &connlimiter.Config{
+ Metrics: mtrc,
+ Logger: logger.With(slogutil.KeyPrefix, "connlimiter"),
+ Stop: c.Stop,
+ Resume: c.Resume,
}
-
- l, err := connlimiter.New(&connlimiter.Config{
- Logger: logger.With(slogutil.KeyPrefix, "connlimiter"),
- Stop: c.Stop,
- Resume: c.Resume,
- })
- if err != nil {
- panic(err)
- }
-
- metrics.ConnLimiterLimits.WithLabelValues("stop").Set(float64(c.Stop))
- metrics.ConnLimiterLimits.WithLabelValues("resume").Set(float64(c.Resume))
-
- return l
}
// type check
-var _ validator = (*connLimitConfig)(nil)
+var _ validate.Interface = (*connLimitConfig)(nil)
-// validate implements the [validator] interface for *connLimitConfig.
-func (c *connLimitConfig) validate() (err error) {
- switch {
- case c == nil:
+// Validate implements the [validate.Interface] interface for *connLimitConfig.
+func (c *connLimitConfig) Validate() (err error) {
+ if c == nil {
return errors.ErrNoValue
- case !c.Enabled:
- return nil
- case c.Stop == 0:
- return newNotPositiveError("stop", c.Stop)
- case c.Resume > c.Stop:
- return errors.Error("resume: must be less than or equal to stop")
- default:
+ } else if !c.Enabled {
return nil
}
+
+ return errors.Join(
+ validate.Positive("stop", c.Stop),
+ validate.Positive("resume", c.Resume),
+ validate.NoGreaterThan("resume", c.Resume, c.Stop),
+ )
}
// ratelimitTCPConfig is the configuration of TCP pipeline limiting.
@@ -238,15 +244,15 @@ type ratelimitTCPConfig struct {
}
// type check
-var _ validator = (*ratelimitTCPConfig)(nil)
+var _ validate.Interface = (*ratelimitTCPConfig)(nil)
-// validate implements the [validator] interface for *ratelimitTCPConfig.
-func (c *ratelimitTCPConfig) validate() (err error) {
+// Validate implements the [validate.Interface] interface for *ratelimitTCPConfig.
+func (c *ratelimitTCPConfig) Validate() (err error) {
if c == nil {
return errors.ErrNoValue
}
- return validatePositive("max_pipeline_count", c.MaxPipelineCount)
+ return validate.Positive("max_pipeline_count", c.MaxPipelineCount)
}
// ratelimitQUICConfig is the configuration of QUIC streams limiting.
@@ -260,13 +266,13 @@ type ratelimitQUICConfig struct {
}
// type check
-var _ validator = (*ratelimitQUICConfig)(nil)
+var _ validate.Interface = (*ratelimitQUICConfig)(nil)
-// validate implements the [validator] interface for *ratelimitQUICConfig.
-func (c *ratelimitQUICConfig) validate() (err error) {
+// Validate implements the [validate.Interface] interface for *ratelimitQUICConfig.
+func (c *ratelimitQUICConfig) Validate() (err error) {
if c == nil {
return errors.ErrNoValue
}
- return validatePositive("max_streams_per_peer", c.MaxStreamsPerPeer)
+ return validate.Positive("max_streams_per_peer", c.MaxStreamsPerPeer)
}
diff --git a/internal/cmd/safebrowsing.go b/internal/cmd/safebrowsing.go
index ca84772..06462b5 100644
--- a/internal/cmd/safebrowsing.go
+++ b/internal/cmd/safebrowsing.go
@@ -1,10 +1,9 @@
package cmd
import (
- "fmt"
-
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/timeutil"
+ "github.com/AdguardTeam/golibs/validate"
)
// safeBrowsingConfig is the configuration for one of the safe browsing filters.
@@ -30,24 +29,20 @@ type safeBrowsingConfig struct {
}
// type check
-var _ validator = (*safeBrowsingConfig)(nil)
+var _ validate.Interface = (*safeBrowsingConfig)(nil)
-// validate implements the [validator] interface for *safeBrowsingConfig.
-func (c *safeBrowsingConfig) validate() (err error) {
- switch {
- case c == nil:
+// Validate implements the [validate.Interface] interface for
+// *safeBrowsingConfig.
+func (c *safeBrowsingConfig) Validate() (err error) {
+ if c == nil {
return errors.ErrNoValue
- case c.BlockHost == "":
- return fmt.Errorf("block_host: %w", errors.ErrEmptyValue)
- case c.CacheSize <= 0:
- return newNotPositiveError("cache_size", c.CacheSize)
- case c.CacheTTL.Duration <= 0:
- return newNotPositiveError("cache_ttl", c.CacheTTL)
- case c.RefreshIvl.Duration <= 0:
- return newNotPositiveError("refresh_interval", c.RefreshIvl)
- case c.RefreshTimeout.Duration <= 0:
- return newNotPositiveError("refresh_timeout", c.RefreshTimeout)
- default:
- return nil
}
+
+ return errors.Join(
+ validate.NotEmpty("block_host", c.BlockHost),
+ validate.Positive("cache_size", c.CacheSize),
+ validate.Positive("cache_ttl", c.CacheTTL),
+ validate.Positive("refresh_interval", c.RefreshIvl),
+ validate.Positive("refresh_timeout", c.RefreshTimeout),
+ )
}
diff --git a/internal/cmd/server.go b/internal/cmd/server.go
index 4b512d1..6197e16 100644
--- a/internal/cmd/server.go
+++ b/internal/cmd/server.go
@@ -5,6 +5,7 @@ import (
"fmt"
"net/netip"
"slices"
+ "time"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/bindtodevice"
@@ -12,6 +13,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/validate"
)
// toInternal returns the configuration of DNS servers for a single server
@@ -34,14 +36,14 @@ func (srvs servers) toInternal(
name := agd.ServerName(srv.Name)
dnsSrv := &agd.Server{
Name: name,
- ReadTimeout: dnsConf.ReadTimeout.Duration,
- WriteTimeout: dnsConf.WriteTimeout.Duration,
+ ReadTimeout: time.Duration(dnsConf.ReadTimeout),
+ WriteTimeout: time.Duration(dnsConf.WriteTimeout),
LinkedIPEnabled: srv.LinkedIPEnabled,
Protocol: srv.Protocol.toInternal(),
}
tcpConf := &agd.TCPConfig{
- IdleTimeout: dnsConf.TCPIdleTimeout.Duration,
+ IdleTimeout: time.Duration(dnsConf.TCPIdleTimeout),
MaxPipelineCount: ratelimitConf.TCP.MaxPipelineCount,
MaxPipelineEnabled: ratelimitConf.TCP.Enabled,
}
@@ -113,25 +115,31 @@ func newTLSConfig(
// nil items.
type servers []*server
-// validate returns an error if the configuration is invalid.
-func (srvs servers) validate() (needsTLS bool, err error) {
+// validateWithTLS returns an error if the configuration is invalid.
+func (srvs servers) validateWithTLS() (needsTLS bool, err error) {
if len(srvs) == 0 {
- return false, errors.Error("no servers")
+ return false, errors.ErrEmptyValue
}
+ var errs []error
names := container.NewMapSet[string]()
for i, s := range srvs {
- if s == nil {
- return false, fmt.Errorf("at index %d: no server", i)
- }
-
- err = s.validate()
+ err = s.Validate()
if err != nil {
- return false, fmt.Errorf("at index %d: %w", i, err)
+ errs = append(errs, fmt.Errorf("at index %d: %w", i, err))
+
+ continue
}
if names.Has(s.Name) {
- return false, fmt.Errorf("at index %d: name: %w: %q", i, errors.ErrDuplicated, s.Name)
+ errs = append(errs, fmt.Errorf(
+ "at index %d: name: %w: %q",
+ i,
+ errors.ErrDuplicated,
+ s.Name,
+ ))
+
+ continue
}
names.Add(s.Name)
@@ -139,7 +147,7 @@ func (srvs servers) validate() (needsTLS bool, err error) {
needsTLS = needsTLS || s.Protocol.needsTLS()
}
- return needsTLS, nil
+ return needsTLS, errors.Join(errs...)
}
// serverProto is the type for the server protocols in the on-disk
@@ -181,10 +189,10 @@ func (p serverProto) toInternal() (sp agd.Protocol) {
}
// type check
-var _ validator = serverProto("")
+var _ validate.Interface = serverProto("")
-// validate implements the [validator] interface for serverProto.
-func (p serverProto) validate() (err error) {
+// Validate implements the [validate.Interface] interface for serverProto.
+func (p serverProto) Validate() (err error) {
switch p {
case srvProtoDNS,
srvProtoDNSCrypt,
@@ -265,34 +273,35 @@ func (s *server) bindData(
}
// type check
-var _ validator = (*server)(nil)
+var _ validate.Interface = (*server)(nil)
-// validate implements the [validator] interface for *server.
-func (s *server) validate() (err error) {
- switch {
- case s == nil:
+// Validate implements the [validate.Interface] interface for *server.
+func (s *server) Validate() (err error) {
+ if s == nil {
return errors.ErrNoValue
- case s.Name == "":
- return fmt.Errorf("name: %w", errors.ErrEmptyValue)
+ }
+
+ errs := []error{
+ validate.NotEmpty("name", s.Name),
}
err = s.validateBindData()
if err != nil {
// Don't wrap the error, because it's informative enough as is.
- return err
+ errs = append(errs, err)
}
- err = s.Protocol.validate()
+ err = s.Protocol.Validate()
if err != nil {
- return fmt.Errorf("protocol: %w", err)
+ errs = append(errs, fmt.Errorf("protocol: %w", err))
}
- err = s.DNSCrypt.validate(s.Protocol)
+ err = s.DNSCrypt.validateForProtocol(s.Protocol)
if err != nil {
- return fmt.Errorf("dnscrypt: %w", err)
+ errs = append(errs, fmt.Errorf("dnscrypt: %w", err))
}
- return nil
+ return errors.Join(errs...)
}
// validateBindData returns an error if the server's binding data aren't valid.
@@ -303,12 +312,8 @@ func (s *server) validateBindData() (err error) {
return errors.Error("bind_addresses and bind_interfaces cannot both be set")
}
- err = validateAddrs(s.BindAddresses)
- if err != nil {
- return fmt.Errorf("bind_addresses: %w", err)
- }
-
- return nil
+ // Don't wrap the error, because it's informative enough as is.
+ return validateBindAddrs(s.BindAddresses)
}
if !bindIfacesSet {
@@ -323,14 +328,21 @@ func (s *server) validateBindData() (err error) {
)
}
- for i, bindIface := range s.BindInterfaces {
- err = bindIface.validate()
- if err != nil {
- return fmt.Errorf("bind_interfaces: at index %d: %w", i, err)
+ return validate.Slice("bind_interfaces", s.BindInterfaces)
+}
+
+// validateBindAddrs returns an error if any of addrs isn't valid.
+//
+// TODO(a.garipov): Merge with [validateNonNilIPs].
+func validateBindAddrs(addrs []netip.AddrPort) (err error) {
+ var errs []error
+ for i, a := range addrs {
+ if !a.IsValid() {
+ errs = append(errs, fmt.Errorf("bind_addresses: at index %d: invalid addr", i))
}
}
- return nil
+ return errors.Join(errs...)
}
// serverBindInterface contains the data for a network interface binding.
@@ -340,33 +352,40 @@ type serverBindInterface struct {
}
// type check
-var _ validator = (*serverBindInterface)(nil)
+var _ validate.Interface = (*serverBindInterface)(nil)
-// validate implements the [validator] interface for *serverBindInterface.
-func (c *serverBindInterface) validate() (err error) {
- switch {
- case c == nil:
+// Validate implements the [validate.Interface] interface for *serverBindInterface.
+func (c *serverBindInterface) Validate() (err error) {
+ if c == nil {
return errors.ErrNoValue
- case c.ID == "":
- return fmt.Errorf("id: %w", errors.ErrEmptyValue)
- case len(c.Subnets) == 0:
- return fmt.Errorf("subnets: %w", errors.ErrEmptyValue)
- default:
- // Go on.
+ }
+
+ errs := []error{
+ validate.NotEmpty("id", c.ID),
+ validate.NotEmptySlice("subnets", c.Subnets),
}
set := container.NewMapSet[netip.Prefix]()
for i, subnet := range c.Subnets {
if !subnet.IsValid() {
- return fmt.Errorf("subnets: at index %d: bad subnet", i)
+ errs = append(errs, fmt.Errorf("subnets: at index %d: bad subnet", i))
+
+ continue
}
if set.Has(subnet) {
- return fmt.Errorf("subnets: at index %d: %w: %s", i, errors.ErrDuplicated, subnet)
+ errs = append(errs, fmt.Errorf(
+ "subnets: at index %d: %w: %s",
+ i,
+ errors.ErrDuplicated,
+ subnet,
+ ))
+
+ continue
}
set.Add(subnet)
}
- return nil
+ return errors.Join(errs...)
}
diff --git a/internal/cmd/servergroup.go b/internal/cmd/servergroup.go
index 648a282..95fb08a 100644
--- a/internal/cmd/servergroup.go
+++ b/internal/cmd/servergroup.go
@@ -7,9 +7,11 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/bindtodevice"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
+ "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc"
"github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/validate"
)
// serverGroups are the DNS server groups. A valid instance of serverGroups has
@@ -26,8 +28,8 @@ func (srvGrps serverGroups) toInternal(
fltGrps map[agd.FilteringGroupID]*agd.FilteringGroup,
ratelimitConf *rateLimitConfig,
dnsConf *dnsConfig,
-) (svcSrvGrps []*agd.ServerGroup, err error) {
- svcSrvGrps = make([]*agd.ServerGroup, len(srvGrps))
+) (svcSrvGrps []*dnssvc.ServerGroupConfig, err error) {
+ svcSrvGrps = make([]*dnssvc.ServerGroupConfig, len(srvGrps))
for i, g := range srvGrps {
fltGrpID := agd.FilteringGroupID(g.FilteringGroup)
_, ok := fltGrps[fltGrpID]
@@ -41,7 +43,7 @@ func (srvGrps serverGroups) toInternal(
return nil, fmt.Errorf("tls %q: %w", g.Name, err)
}
- svcSrvGrps[i] = &agd.ServerGroup{
+ svcSrvGrps[i] = &dnssvc.ServerGroupConfig{
DDR: g.DDR.toInternal(messages),
DeviceDomains: deviceDomains,
Name: agd.ServerGroupName(g.Name),
@@ -65,29 +67,34 @@ func (srvGrps serverGroups) toInternal(
}
// type check
-var _ validator = serverGroups(nil)
+var _ validate.Interface = serverGroups(nil)
-// validate implements the [validator] interface for serverGroups.
-func (srvGrps serverGroups) validate() (err error) {
+// Validate implements the [validate.Interface] interface for serverGroups.
+func (srvGrps serverGroups) Validate() (err error) {
if len(srvGrps) == 0 {
return errors.ErrEmptyValue
}
+ var errs []error
names := container.NewMapSet[string]()
for i, g := range srvGrps {
- err = g.validate()
+ err = g.Validate()
if err != nil {
- return fmt.Errorf("at index %d: %w", i, err)
+ errs = append(errs, fmt.Errorf("at index %d: %w", i, err))
+
+ continue
}
if names.Has(g.Name) {
- return fmt.Errorf("at index %d: name: %w: %q", i, errors.ErrDuplicated, g.Name)
+ errs = append(errs, fmt.Errorf("at index %d: %w: %q", i, errors.ErrDuplicated, g.Name))
+
+ continue
}
names.Add(g.Name)
}
- return nil
+ return errors.Join(errs...)
}
// serverGroup defines a group of DNS servers all of which use the same
@@ -118,35 +125,32 @@ type serverGroup struct {
}
// type check
-var _ validator = (*serverGroup)(nil)
+var _ validate.Interface = (*serverGroup)(nil)
-// validate implements the [validator] interface for *serverGroup.
-func (g *serverGroup) validate() (err error) {
- switch {
- case g == nil:
+// Validate implements the [validate.Interface] interface for *serverGroup.
+func (g *serverGroup) Validate() (err error) {
+ if g == nil {
return errors.ErrNoValue
- case g.Name == "":
- return fmt.Errorf("name: %w", errors.ErrEmptyValue)
- case g.FilteringGroup == "":
- return fmt.Errorf("filtering_group: %w", errors.ErrEmptyValue)
}
- err = g.DDR.validate()
+ errs := []error{
+ validate.NotEmpty("name", g.Name),
+ validate.NotEmpty("filtering_group", g.FilteringGroup),
+ }
+
+ errs = validate.Append(errs, "ddr", g.DDR)
+
+ needsTLS, err := g.Servers.validateWithTLS()
if err != nil {
- return fmt.Errorf("ddr: %w", err)
+ errs = append(errs, fmt.Errorf("servers: %w", err))
}
- needsTLS, err := g.Servers.validate()
+ err = g.TLS.validateIfNecessary(needsTLS)
if err != nil {
- return fmt.Errorf("servers: %w", err)
+ errs = append(errs, fmt.Errorf("tls: %w", err))
}
- err = g.TLS.validate(needsTLS)
- if err != nil {
- return fmt.Errorf("tls: %w", err)
- }
-
- return nil
+ return errors.Join(errs...)
}
// collectSessTicketPaths returns the list of unique session ticket file paths
@@ -154,7 +158,12 @@ func (g *serverGroup) validate() (err error) {
func (srvGrps serverGroups) collectSessTicketPaths() (paths []string) {
set := container.NewSortedSliceSet[string]()
for _, g := range srvGrps {
- for _, k := range g.TLS.SessionKeys {
+ grpTLS := g.TLS
+ if grpTLS == nil {
+ continue
+ }
+
+ for _, k := range grpTLS.SessionKeys {
set.Add(k)
}
}
diff --git a/internal/cmd/tls.go b/internal/cmd/tls.go
index 25ef9ea..afd1352 100644
--- a/internal/cmd/tls.go
+++ b/internal/cmd/tls.go
@@ -9,6 +9,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/validate"
)
// tlsConfig are the TLS settings of a DNS server, if any.
@@ -53,9 +54,9 @@ func (c *tlsConfig) toInternal(
return deviceDomains, nil
}
-// validate returns an error if the TLS configuration is invalid for the given
-// protocol.
-func (c *tlsConfig) validate(needsTLS bool) (err error) {
+// validateIfNecessary returns an error if the TLS configuration is invalid
+// depending on whether it is necessary or not.
+func (c *tlsConfig) validateIfNecessary(needsTLS bool) (err error) {
switch {
case c == nil:
if needsTLS {
@@ -68,21 +69,20 @@ func (c *tlsConfig) validate(needsTLS bool) (err error) {
return errors.Error("server group does not require tls")
}
- if len(c.Certificates) == 0 {
- return fmt.Errorf("certificates: %w", errors.ErrEmptyValue)
+ var errs []error
+ if err = validate.NotEmptySlice("certificates", c.Certificates); err != nil {
+ // Don't wrap the error, because it's informative enough as is.
+ errs = append(errs, err)
}
- err = c.Certificates.validate()
- if err != nil {
- return fmt.Errorf("certificates: %w", err)
- }
+ errs = validate.Append(errs, "certificates", c.Certificates)
err = validateDeviceIDWildcards(c.DeviceIDWildcards)
if err != nil {
- return fmt.Errorf("device_id_wildcards: %w", err)
+ errs = append(errs, fmt.Errorf("device_id_wildcards: %w", err))
}
- return nil
+ return errors.Join(errs...)
}
// validateDeviceIDWildcards returns an error if the device ID domain wildcards
@@ -126,11 +126,7 @@ func (certs tlsConfigCerts) store(ctx context.Context, tlsMgr tlsconfig.Manager)
}
}
- if len(errs) != 0 {
- return errors.Join(errs...)
- }
-
- return nil
+ return errors.Join(errs...)
}
// toInternal is like [tlsConfigCerts.store] but it also returns the TLS
@@ -153,20 +149,31 @@ func (certs tlsConfigCerts) toInternal(
}
// type check
-var _ validator = tlsConfigCerts(nil)
+var _ validate.Interface = tlsConfigCerts(nil)
-// validate implements the [validator] interface for tlsConfigCerts.
-func (certs tlsConfigCerts) validate() (err error) {
+// Validate implements the [validate.Interface] interface for tlsConfigCerts.
+// certs may be nil.
+func (certs tlsConfigCerts) Validate() (err error) {
+ if len(certs) == 0 {
+ return nil
+ }
+
+ var errs []error
for i, c := range certs {
- switch {
- case c == nil:
- return fmt.Errorf("at index %d: %w", i, errors.ErrNoValue)
- case c.Certificate == "":
- return fmt.Errorf("at index %d: certificate: %w", i, errors.ErrEmptyValue)
- case c.Key == "":
- return fmt.Errorf("at index %d: key: %w", i, errors.ErrEmptyValue)
+ if c == nil {
+ errs = append(errs, fmt.Errorf("at index %d: %w", i, errors.ErrNoValue))
+
+ continue
+ }
+
+ if err = validate.NotEmpty("certificate", c.Certificate); err != nil {
+ errs = append(errs, fmt.Errorf("at index %d: %w", i, err))
+ }
+
+ if err = validate.NotEmpty("key", c.Key); err != nil {
+ errs = append(errs, fmt.Errorf("at index %d: %w", i, err))
}
}
- return nil
+ return errors.Join(errs...)
}
diff --git a/internal/cmd/upstream.go b/internal/cmd/upstream.go
index 450b4a4..bfbb6c1 100644
--- a/internal/cmd/upstream.go
+++ b/internal/cmd/upstream.go
@@ -1,7 +1,6 @@
package cmd
import (
- "cmp"
"fmt"
"log/slog"
"net/netip"
@@ -9,15 +8,15 @@ import (
"strings"
"time"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/forward"
- "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/prometheus"
+ dnssvcprom "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/prometheus"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
- "github.com/AdguardTeam/AdGuardDNS/internal/metrics"
+ "github.com/AdguardTeam/golibs/contextutil"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/service"
"github.com/AdguardTeam/golibs/timeutil"
+ "github.com/AdguardTeam/golibs/validate"
)
// upstreamConfig is the upstream module configuration.
@@ -35,57 +34,45 @@ type upstreamConfig struct {
// toInternal converts c to the data storage configuration for the DNS server.
// c must be valid.
-func (c *upstreamConfig) toInternal(logger *slog.Logger) (fwdConf *forward.HandlerConfig) {
- upstreams := c.Servers
- fallbacks := c.Fallback.Servers
-
- upsConfs := toUpstreamConfigs(upstreams)
- fallbackConfs := toUpstreamConfigs(fallbacks)
- metricsListener := prometheus.NewForwardMetricsListener(
- metrics.Namespace(),
- len(upstreams)+len(fallbacks),
- )
-
+func (c *upstreamConfig) toInternal(
+ logger *slog.Logger,
+ mtrcListener *dnssvcprom.ForwardMetricsListener,
+) (fwdConf *forward.HandlerConfig) {
var hcInit time.Duration
if c.Healthcheck.Enabled {
- hcInit = c.Healthcheck.Timeout.Duration
+ hcInit = time.Duration(c.Healthcheck.Timeout)
}
- fwdConf = &forward.HandlerConfig{
+ return &forward.HandlerConfig{
Logger: logger.With(slogutil.KeyPrefix, "forward"),
- MetricsListener: metricsListener,
+ MetricsListener: mtrcListener,
HealthcheckDomainTmpl: c.Healthcheck.DomainTmpl,
- UpstreamsAddresses: upsConfs,
- FallbackAddresses: fallbackConfs,
- HealthcheckBackoffDuration: c.Healthcheck.BackoffDuration.Duration,
+ UpstreamsAddresses: toUpstreamConfigs(c.Servers),
+ FallbackAddresses: toUpstreamConfigs(c.Fallback.Servers),
+ HealthcheckBackoffDuration: time.Duration(c.Healthcheck.BackoffDuration),
HealthcheckInitDuration: hcInit,
}
-
- return fwdConf
}
// type check
-var _ validator = (*upstreamConfig)(nil)
+var _ validate.Interface = (*upstreamConfig)(nil)
-// validate implements the [validator] interface for *upstreamConfig.
-func (c *upstreamConfig) validate() (err error) {
- switch {
- case c == nil:
+// Validate implements the [validate.Interface] interface for *upstreamConfig.
+func (c *upstreamConfig) Validate() (err error) {
+ if c == nil {
return errors.ErrNoValue
- case len(c.Servers) == 0:
- return fmt.Errorf("servers: %w", errors.ErrEmptyValue)
}
- for i, s := range c.Servers {
- if err = s.validate(); err != nil {
- return fmt.Errorf("servers: at index %d: %w", i, err)
- }
+ errs := []error{
+ validate.NotEmptySlice("servers", c.Servers),
}
- return cmp.Or(
- validateProp("fallback", c.Fallback.validate),
- validateProp("healthcheck", c.Healthcheck.validate),
- )
+ errs = validate.AppendSlice(errs, "servers", c.Servers)
+
+ errs = validate.Append(errs, "fallback", c.Fallback)
+ errs = validate.Append(errs, "healthcheck", c.Healthcheck)
+
+ return errors.Join(errs...)
}
// splitUpstreamURL separates server url to net protocol and port address.
@@ -142,26 +129,23 @@ type upstreamHealthcheckConfig struct {
}
// type check
-var _ validator = (*upstreamHealthcheckConfig)(nil)
+var _ validate.Interface = (*upstreamHealthcheckConfig)(nil)
-// validate implements the [validator] interface for *upstreamHealthcheckConfig.
-func (c *upstreamHealthcheckConfig) validate() (err error) {
- switch {
- case c == nil:
+// Validate implements the [validate.Interface] interface for
+// *upstreamHealthcheckConfig.
+func (c *upstreamHealthcheckConfig) Validate() (err error) {
+ if c == nil {
return errors.ErrNoValue
- case !c.Enabled:
+ } else if !c.Enabled {
return nil
- case c.DomainTmpl == "":
- return fmt.Errorf("domain_template: %w", errors.ErrEmptyValue)
- case c.Interval.Duration <= 0:
- return newNotPositiveError("interval", c.Interval)
- case c.Timeout.Duration <= 0:
- return newNotPositiveError("timeout", c.Timeout)
- case c.BackoffDuration.Duration <= 0:
- return newNotPositiveError("backoff_duration", c.BackoffDuration)
}
- return nil
+ return errors.Join(
+ validate.NotEmpty("domain_template", c.DomainTmpl),
+ validate.Positive("backoff_duration", c.BackoffDuration),
+ validate.Positive("interval", c.Interval),
+ validate.Positive("timeout", c.Timeout),
+ )
}
// newUpstreamHealthcheck returns refresher worker service that performs
@@ -178,13 +162,13 @@ func newUpstreamHealthcheck(
const prefix = "upstream_healthcheck_refresh"
refrLogger := logger.With(slogutil.KeyPrefix, prefix)
- return agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
- Context: newCtxWithTimeoutCons(conf.Healthcheck.Timeout.Duration),
- Refresher: agdservice.NewRefresherWithErrColl(handler, refrLogger, errColl, prefix),
- Logger: refrLogger,
- Interval: conf.Healthcheck.Interval.Duration,
- RefreshOnShutdown: false,
- RandomizeStart: false,
+
+ return service.NewRefreshWorker(&service.RefreshWorkerConfig{
+ ContextConstructor: contextutil.NewTimeoutConstructor(time.Duration(conf.Healthcheck.Timeout)),
+ ErrorHandler: errcoll.NewRefreshErrorHandler(refrLogger, errColl),
+ Refresher: handler,
+ Schedule: timeutil.NewConstSchedule(time.Duration(conf.Healthcheck.Interval)),
+ RefreshOnShutdown: false,
})
}
@@ -197,24 +181,22 @@ type upstreamFallbackConfig struct {
}
// type check
-var _ validator = (*upstreamFallbackConfig)(nil)
+var _ validate.Interface = (*upstreamFallbackConfig)(nil)
-// validate implements the [validator] interface for *upstreamFallbackConfig.
-func (c *upstreamFallbackConfig) validate() (err error) {
- switch {
- case c == nil:
+// Validate implements the [validate.Interface] interface for
+// *upstreamFallbackConfig.
+func (c *upstreamFallbackConfig) Validate() (err error) {
+ if c == nil {
return errors.ErrNoValue
- case len(c.Servers) == 0:
- return fmt.Errorf("servers: %w", errors.ErrEmptyValue)
}
- for i, s := range c.Servers {
- if err = s.validate(); err != nil {
- return fmt.Errorf("servers: at index %d: %w", i, err)
- }
+ errs := []error{
+ validate.NotEmptySlice("servers", c.Servers),
}
- return nil
+ errs = validate.AppendSlice(errs, "servers", c.Servers)
+
+ return errors.Join(errs...)
}
// upstreamServerConfig is the configuration for the upstream server.
@@ -228,23 +210,25 @@ type upstreamServerConfig struct {
}
// type check
-var _ validator = (*upstreamServerConfig)(nil)
+var _ validate.Interface = (*upstreamServerConfig)(nil)
-// validate implements the [validator] interface for *upstreamServerConfig.
-func (c *upstreamServerConfig) validate() (err error) {
- switch {
- case c == nil:
+// Validate implements the [validate.Interface] interface for
+// *upstreamServerConfig.
+func (c *upstreamServerConfig) Validate() (err error) {
+ if c == nil {
return errors.ErrNoValue
- case c.Timeout.Duration <= 0:
- return newNotPositiveError("timeout", c.Timeout)
+ }
+
+ errs := []error{
+ validate.Positive("timeout", c.Timeout),
}
_, _, err = splitUpstreamURL(c.Address)
if err != nil {
- return fmt.Errorf("invalid addr: %s", c.Address)
+ errs = append(errs, fmt.Errorf("address: %w", err))
}
- return nil
+ return errors.Join(errs...)
}
// toUpstreamConfigs converts confs to the list of upstream configurations.
@@ -257,7 +241,7 @@ func toUpstreamConfigs(confs []*upstreamServerConfig) (upsConfs []*forward.Upstr
upsConfs = append(upsConfs, &forward.UpstreamPlainConfig{
Network: net,
Address: addrPort,
- Timeout: c.Timeout.Duration,
+ Timeout: time.Duration(c.Timeout),
})
}
diff --git a/internal/cmd/validation.go b/internal/cmd/validation.go
deleted file mode 100644
index e0afa1d..0000000
--- a/internal/cmd/validation.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package cmd
-
-import (
- "fmt"
- "net/netip"
-
- "github.com/AdguardTeam/golibs/timeutil"
-)
-
-// validatePositive returns an error if v is not a positive number. prop is the
-// name of the property being checked, used for error messages.
-func validatePositive[T numberOrDuration](prop string, v T) (err error) {
- if d, ok := any(v).(timeutil.Duration); ok && d.Duration <= 0 {
- return newNotPositiveError(prop, v)
- }
-
- return nil
-}
-
-// validateProp returns an error wrapped with prop name if the given validator
-// func returns an error.
-func validateProp(prop string, validator func() error) (err error) {
- err = validator()
- if err != nil {
- return fmt.Errorf("%s: %w", prop, err)
- }
-
- return nil
-}
-
-// netipAddr is the type constraint for the types from [netip], which we can
-// validate using [validateAddrs].
-type netipAddr interface {
- netip.Addr | netip.AddrPort
-
- IsValid() (ok bool)
-}
-
-// validateAddrs returns an error if any of the addrs isn't valid.
-//
-// TODO(a.garipov): Merge with [validateNonNilIPs].
-func validateAddrs[T netipAddr](addrs []T) (err error) {
- for i, a := range addrs {
- if !a.IsValid() {
- return fmt.Errorf("at index %d: invalid addr", i)
- }
- }
-
- return nil
-}
diff --git a/internal/cmd/websvc.go b/internal/cmd/websvc.go
index 76794f6..3d45248 100644
--- a/internal/cmd/websvc.go
+++ b/internal/cmd/websvc.go
@@ -4,6 +4,7 @@ import (
"context"
"encoding/base64"
"fmt"
+ "log/slog"
"maps"
"net/http"
"net/netip"
@@ -11,6 +12,7 @@ import (
"os"
"path"
"slices"
+ "time"
"github.com/AdguardTeam/AdGuardDNS/internal/dnscheck"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
@@ -18,9 +20,11 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/websvc"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/httphdr"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/AdguardTeam/golibs/timeutil"
+ "github.com/AdguardTeam/golibs/validate"
)
// webConfig contains configuration for the AdGuard DNS web service.
@@ -62,12 +66,13 @@ type webConfig struct {
}
// toInternal converts c to the AdGuardDNS web service configuration. c must be
-// valid.
+// valid. All arguments must not be nil.
func (c *webConfig) toInternal(
ctx context.Context,
envs *environment,
dnsCk dnscheck.Interface,
errColl errcoll.Interface,
+ baseLogger *slog.Logger,
tlsMgr tlsconfig.Manager,
) (conf *websvc.Config, err error) {
if c == nil {
@@ -75,8 +80,9 @@ func (c *webConfig) toInternal(
}
conf = &websvc.Config{
+ Logger: baseLogger.With(slogutil.KeyPrefix, "websvc"),
ErrColl: errColl,
- Timeout: c.Timeout.Duration,
+ Timeout: time.Duration(c.Timeout),
}
if dnsCkHdlr, ok := dnsCk.(http.Handler); ok {
@@ -174,50 +180,26 @@ func (c *webConfig) setStaticContent(envs *environment, conf *websvc.Config) (er
}
// type check
-var _ validator = (*webConfig)(nil)
+var _ validate.Interface = (*webConfig)(nil)
-// validate implements the [validator] interface for *webConfig.
-func (c *webConfig) validate() (err error) {
- switch {
- case c == nil:
+// Validate implements the [validate.Interface] interface for *webConfig.
+func (c *webConfig) Validate() (err error) {
+ if c == nil {
return nil
- case c.Timeout.Duration <= 0:
- return newNotPositiveError("timeout", c.Timeout)
- default:
- // Go on.
}
- err = c.LinkedIP.validate()
- if err != nil {
- return fmt.Errorf("linked_ip: %w", err)
+ errs := []error{
+ validate.Positive("timeout", c.Timeout),
}
- err = c.AdultBlocking.validate()
- if err != nil {
- return fmt.Errorf("adult_blocking: %w", err)
- }
+ errs = validate.Append(errs, "linked_ip", c.LinkedIP)
+ errs = validate.Append(errs, "adult_blocking", c.AdultBlocking)
+ errs = validate.Append(errs, "general_blocking", c.GeneralBlocking)
+ errs = validate.Append(errs, "safe_browsing", c.SafeBrowsing)
+ errs = validate.Append(errs, "static_content", c.StaticContent)
+ errs = validate.Append(errs, "non_doh_bind", c.NonDoHBind)
- err = c.GeneralBlocking.validate()
- if err != nil {
- return fmt.Errorf("general_blocking: %w", err)
- }
-
- err = c.SafeBrowsing.validate()
- if err != nil {
- return fmt.Errorf("safe_browsing: %w", err)
- }
-
- err = c.StaticContent.validate()
- if err != nil {
- return fmt.Errorf("static_content: %w", err)
- }
-
- err = c.NonDoHBind.validate()
- if err != nil {
- return fmt.Errorf("non_doh_bind: %w", err)
- }
-
- return nil
+ return errors.Join(errs...)
}
// linkedIPServer is the linked IP web server configuration.
@@ -253,25 +235,21 @@ func (s *linkedIPServer) toInternal(
}
// type check
-var _ validator = (*linkedIPServer)(nil)
+var _ validate.Interface = (*linkedIPServer)(nil)
-// validate implements the [validator] interface for *linkedIPServer.
-func (s *linkedIPServer) validate() (err error) {
- switch {
- case s == nil:
+// Validate implements the [validate.Interface] interface for *linkedIPServer.
+func (s *linkedIPServer) Validate() (err error) {
+ if s == nil {
return nil
- case len(s.Bind) == 0:
- return fmt.Errorf("bind: %w", errors.ErrEmptyValue)
- default:
- // Go on.
}
- err = s.Bind.validate()
- if err != nil {
- return fmt.Errorf("bind: %w", err)
+ errs := []error{
+ validate.NotEmptySlice("bind", s.Bind),
}
- return nil
+ errs = validate.Append(errs, "bind", s.Bind)
+
+ return errors.Join(errs...)
}
// blockPageServer is the safe browsing or adult blocking block page web servers
@@ -307,27 +285,22 @@ func (s *blockPageServer) toInternal(
}
// type check
-var _ validator = (*blockPageServer)(nil)
+var _ validate.Interface = (*blockPageServer)(nil)
-// validate implements the [validator] interface for *blockPageServer.
-func (s *blockPageServer) validate() (err error) {
- switch {
- case s == nil:
+// Validate implements the [validate.Interface] interface for *blockPageServer.
+func (s *blockPageServer) Validate() (err error) {
+ if s == nil {
return nil
- case s.BlockPage == "":
- return fmt.Errorf("block_page: %w", errors.ErrEmptyValue)
- case len(s.Bind) == 0:
- return fmt.Errorf("bind: %w", errors.ErrEmptyValue)
- default:
- // Go on.
}
- err = s.Bind.validate()
- if err != nil {
- return fmt.Errorf("bind: %w", err)
+ errs := []error{
+ validate.NotEmpty("block_page", s.BlockPage),
+ validate.NotEmptySlice("bind", s.Bind),
}
- return nil
+ errs = validate.Append(errs, "bind", s.Bind)
+
+ return errors.Join(errs...)
}
// bindData are the data for binding HTTP servers to addresses.
@@ -352,22 +325,23 @@ func (bd bindData) toInternal(
}
// type check
-var _ validator = bindData(nil)
+var _ validate.Interface = bindData(nil)
-// validate implements the [validator] interface for bindData.
-func (bd bindData) validate() (err error) {
+// Validate implements the [validate.Interface] interface for bindData.
+func (bd bindData) Validate() (err error) {
if len(bd) == 0 {
return nil
}
+ var errs []error
for i, d := range bd {
- err = d.validate()
+ err = d.Validate()
if err != nil {
- return fmt.Errorf("at index %d: %w", i, err)
+ errs = append(errs, fmt.Errorf("at index %d: %w", i, err))
}
}
- return nil
+ return errors.Join(errs...)
}
// bindItem is data for binding one HTTP server to an address.
@@ -397,25 +371,21 @@ func (i *bindItem) toInternal(
}
// type check
-var _ validator = (*bindItem)(nil)
+var _ validate.Interface = (*bindItem)(nil)
-// validate implements the [validator] interface for *bindItem.
-func (i *bindItem) validate() (err error) {
- switch {
- case i == nil:
+// Validate implements the [validate.Interface] interface for *bindItem.
+func (i *bindItem) Validate() (err error) {
+ if i == nil {
return errors.ErrNoValue
- case i.Address == netip.AddrPort{}:
- return fmt.Errorf("address: %w", errors.ErrEmptyValue)
- default:
- // Go on.
}
- err = i.Certificates.validate()
- if err != nil {
- return fmt.Errorf("certificates: %w", err)
+ errs := []error{
+ validate.NotEmpty("address", i.Address),
}
- return nil
+ errs = validate.Append(errs, "certificates", i.Certificates)
+
+ return errors.Join(errs...)
}
// staticContent is the static content mapping. Paths must be absolute and
@@ -441,26 +411,29 @@ func (sc staticContent) toInternal() (fs websvc.StaticContent, err error) {
}
// type check
-var _ validator = staticContent(nil)
+var _ validate.Interface = staticContent(nil)
-// validate implements the [validator] interface for staticContent.
-func (sc staticContent) validate() (err error) {
+// Validate implements the [validate.Interface] interface for staticContent.
+func (sc staticContent) Validate() (err error) {
if len(sc) == 0 {
return nil
}
+ var errs []error
for _, p := range slices.Sorted(maps.Keys(sc)) {
if !path.IsAbs(p) {
- return fmt.Errorf("path %q: not absolute", p)
+ errs = append(errs, fmt.Errorf("path %q: not absolute", p))
+
+ continue
}
- err = sc[p].validate()
+ err = sc[p].Validate()
if err != nil {
return fmt.Errorf("path %q: %w", p, err)
}
}
- return nil
+ return errors.Join(errs...)
}
// staticFile is a single file in a static content mapping.
@@ -499,10 +472,10 @@ func (f *staticFile) toInternal() (file *websvc.StaticFile, err error) {
}
// type check
-var _ validator = (*staticFile)(nil)
+var _ validate.Interface = (*staticFile)(nil)
-// validate implements the [validator] interface for *staticFile.
-func (f *staticFile) validate() (err error) {
+// Validate implements the [validate.Interface] interface for *staticFile.
+func (f *staticFile) Validate() (err error) {
if f == nil {
return errors.ErrNoValue
}
diff --git a/internal/connlimiter/conn.go b/internal/connlimiter/conn.go
index e7a2a23..82d3941 100644
--- a/internal/connlimiter/conn.go
+++ b/internal/connlimiter/conn.go
@@ -7,11 +7,8 @@ import (
"sync/atomic"
"time"
- "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
- "github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/AdGuardDNS/internal/optslog"
"github.com/AdguardTeam/golibs/errors"
- "github.com/AdguardTeam/golibs/timeutil"
)
// limitConn is a wrapper for a stream connection that decreases the counter
@@ -21,11 +18,12 @@ import (
type limitConn struct {
net.Conn
- logger *slog.Logger
- serverInfo *dnsserver.ServerInfo
- decrement func()
- start time.Time
- isClosed atomic.Bool
+ connInfo *ConnMetricsData
+ logger *slog.Logger
+ metrics Metrics
+ decrement func(ctx context.Context)
+ start time.Time
+ isClosed atomic.Bool
}
// Close closes the underlying connection and decrements the counter.
@@ -42,22 +40,11 @@ func (c *limitConn) Close() (err error) {
ctx := context.Background()
connLife := time.Since(c.start)
- optslog.Debug2(
- ctx,
- c.logger,
- "closed conn",
- "raddr", c.RemoteAddr(),
- "conn_life", timeutil.Duration{
- Duration: connLife,
- },
- )
- metrics.StreamConnLifeDuration.WithLabelValues(
- c.serverInfo.Name,
- c.serverInfo.Proto.String(),
- c.serverInfo.Addr,
- ).Observe(connLife.Seconds())
+ optslog.Debug2(ctx, c.logger, "closed conn", "raddr", c.RemoteAddr(), "conn_life", connLife)
- c.decrement()
+ c.metrics.ObserveLifeDuration(ctx, c.connInfo, connLife)
+
+ c.decrement(ctx)
return err
}
diff --git a/internal/connlimiter/limiter.go b/internal/connlimiter/limiter.go
index 1940a56..ca3b1cb 100644
--- a/internal/connlimiter/limiter.go
+++ b/internal/connlimiter/limiter.go
@@ -1,13 +1,11 @@
package connlimiter
import (
- "fmt"
"log/slog"
"net"
"sync"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
- "github.com/AdguardTeam/AdGuardDNS/internal/metrics"
)
// Config is the configuration structure for the stream-connection limiter.
@@ -15,6 +13,10 @@ type Config struct {
// Logger is used to log the operation of the limiter. It must not be nil.
Logger *slog.Logger
+ // Metrics is used for the collection of the stream connections statistics.
+ // It must not be nil.
+ Metrics Metrics
+
// Stop is the point at which the limiter stops accepting new connections.
// Once the number of active connections reaches this limit, new connections
// wait for the number to decrease to or below Resume.
@@ -31,7 +33,8 @@ type Config struct {
// Limiter is the stream-connection limiter.
type Limiter struct {
- logger *slog.Logger
+ logger *slog.Logger
+ metrics Metrics
// counterCond is the shared condition variable that protects counter.
counterCond *sync.Cond
@@ -40,14 +43,11 @@ type Limiter struct {
counter *counter
}
-// New returns a new *Limiter.
-func New(c *Config) (l *Limiter, err error) {
- if c == nil || c.Stop == 0 || c.Resume > c.Stop {
- return nil, fmt.Errorf("bad limiter config: %+v", c)
- }
-
+// New returns a new *Limiter. c must be valid.
+func New(c *Config) (l *Limiter) {
return &Limiter{
logger: c.Logger,
+ metrics: c.Metrics,
counterCond: sync.NewCond(&sync.Mutex{}),
counter: &counter{
current: 0,
@@ -55,27 +55,28 @@ func New(c *Config) (l *Limiter, err error) {
resume: c.Resume,
isAccepting: true,
},
- }, nil
+ }
}
// Limit wraps lsnr to control the number of active connections. srvInfo is
// used for logging and metrics.
func (l *Limiter) Limit(lsnr net.Listener, srvInfo *dnsserver.ServerInfo) (limited net.Listener) {
- name, addr := srvInfo.Name, srvInfo.Addr
- proto := srvInfo.Proto.String()
+ name := srvInfo.Name
return &limitListener{
Listener: lsnr,
- logger: l.logger.With("name", name),
+ logger: l.logger.With("name", name),
+ metrics: l.metrics,
counterCond: l.counterCond,
counter: l.counter,
- serverInfo: srvInfo,
-
- activeGauge: metrics.ConnLimiterActiveStreamConns.WithLabelValues(name, proto, addr),
- waitingHist: metrics.StreamConnWaitDuration.WithLabelValues(name, proto, addr),
+ connInfo: &ConnMetricsData{
+ Addr: srvInfo.Addr,
+ Name: name,
+ Proto: srvInfo.Proto.String(),
+ },
isClosed: false,
}
diff --git a/internal/connlimiter/limiter_test.go b/internal/connlimiter/limiter_test.go
index f747e39..8d6ba2f 100644
--- a/internal/connlimiter/limiter_test.go
+++ b/internal/connlimiter/limiter_test.go
@@ -27,12 +27,12 @@ var testServerInfo = &dnsserver.ServerInfo{
}
func TestLimiter(t *testing.T) {
- l, err := connlimiter.New(&connlimiter.Config{
- Logger: slogutil.NewDiscardLogger(),
- Stop: 1,
- Resume: 1,
+ l := connlimiter.New(&connlimiter.Config{
+ Metrics: connlimiter.EmptyMetrics{},
+ Logger: slogutil.NewDiscardLogger(),
+ Stop: 1,
+ Resume: 1,
})
- require.NoError(t, err)
conn := &fakenet.Conn{
OnClose: func() (err error) { return nil },
@@ -109,13 +109,3 @@ func TestLimiter(t *testing.T) {
// Check that double close causes an error.
assert.ErrorIs(t, limited.Close(), net.ErrClosed)
}
-
-func TestLimiter_badConf(t *testing.T) {
- l, err := connlimiter.New(&connlimiter.Config{
- Logger: slogutil.NewDiscardLogger(),
- Stop: 1,
- Resume: 2,
- })
- assert.Nil(t, l)
- assert.Error(t, err)
-}
diff --git a/internal/connlimiter/listenconfig_test.go b/internal/connlimiter/listenconfig_test.go
index ef86698..32f775b 100644
--- a/internal/connlimiter/listenconfig_test.go
+++ b/internal/connlimiter/listenconfig_test.go
@@ -49,12 +49,12 @@ func TestListenConfig(t *testing.T) {
},
}
- l, err := connlimiter.New(&connlimiter.Config{
- Logger: slogutil.NewDiscardLogger(),
- Stop: 1,
- Resume: 1,
+ l := connlimiter.New(&connlimiter.Config{
+ Metrics: connlimiter.EmptyMetrics{},
+ Logger: slogutil.NewDiscardLogger(),
+ Stop: 1,
+ Resume: 1,
})
- require.NoError(t, err)
limited := connlimiter.NewListenConfig(c, l)
diff --git a/internal/connlimiter/listener.go b/internal/connlimiter/listener.go
index 80bd01f..6ec41ff 100644
--- a/internal/connlimiter/listener.go
+++ b/internal/connlimiter/listener.go
@@ -9,9 +9,7 @@ import (
"sync"
"time"
- "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/golibs/errors"
- "github.com/prometheus/client_golang/prometheus"
)
// limitListener is a wrapper that uses a counter to limit the number of active
@@ -23,9 +21,13 @@ type limitListener struct {
logger *slog.Logger
- // serverInfo is used for logging and metrics in both the listener itself
- // and in its conns. It's never nil.
- serverInfo *dnsserver.ServerInfo
+ // metrics is used for the collection of the stream connections statistics.
+ // It must not be nil.
+ metrics Metrics
+
+ // connInfo is used for metrics in both the listener itself and in its
+ // conns. It's never nil.
+ connInfo *ConnMetricsData
// counterCond is the condition variable that protects counter and isClosed
// through its locker, as well as signals when connections can be accepted
@@ -35,13 +37,6 @@ type limitListener struct {
// counter is the shared counter for all listeners.
counter *counter
- // activeGauge is the metrics gauge of currently active stream-connections.
- activeGauge prometheus.Gauge
-
- // waitingHist is the metrics histogram of how much a connection spends
- // waiting for an accept.
- waitingHist prometheus.Observer
-
// isClosed shows whether this listener has been closed.
isClosed bool
}
@@ -60,12 +55,12 @@ func (l *limitListener) Accept() (conn net.Conn, err error) {
return nil, net.ErrClosed
}
- l.waitingHist.Observe(time.Since(waitStart).Seconds())
- l.activeGauge.Inc()
+ l.metrics.ObserveWaitingDuration(ctx, l.connInfo, time.Since(waitStart))
+ l.metrics.IncrementActive(ctx, l.connInfo)
conn, err = l.Listener.Accept()
if err != nil {
- l.decrement()
+ l.decrement(ctx)
return nil, err
}
@@ -73,10 +68,11 @@ func (l *limitListener) Accept() (conn net.Conn, err error) {
return &limitConn{
Conn: conn,
- logger: l.logger,
- decrement: l.decrement,
- start: time.Now(),
- serverInfo: l.serverInfo,
+ connInfo: l.connInfo,
+ metrics: l.metrics,
+ logger: l.logger,
+ decrement: l.decrement,
+ start: time.Now(),
}, nil
}
@@ -110,14 +106,13 @@ func (l *limitListener) increment(ctx context.Context) (isClosed bool) {
// decrement decreases the number of active connections in the counter and
// broadcasts the change.
-func (l *limitListener) decrement() {
+func (l *limitListener) decrement(ctx context.Context) {
+ defer l.metrics.DecrementActive(ctx, l.connInfo)
+
l.counterCond.L.Lock()
defer l.counterCond.L.Unlock()
- l.activeGauge.Dec()
-
l.counter.decrement()
-
l.counterCond.Signal()
}
diff --git a/internal/connlimiter/metrics.go b/internal/connlimiter/metrics.go
new file mode 100644
index 0000000..5ad5f46
--- /dev/null
+++ b/internal/connlimiter/metrics.go
@@ -0,0 +1,75 @@
+package connlimiter
+
+import (
+ "context"
+ "time"
+)
+
+// Metrics is an interface used for collection of the stream-connections
+// statistics.
+type Metrics interface {
+ // IncrementActive increments the number of active stream-connections. m
+ // must not be nil.
+ IncrementActive(ctx context.Context, m *ConnMetricsData)
+
+ // DecrementActive decrements the number of active stream-connections. m
+ // must not be nil.
+ DecrementActive(ctx context.Context, m *ConnMetricsData)
+
+ // ObserveLifeDuration updates the duration of life times for
+ // stream-connections. m must not be nil.
+ ObserveLifeDuration(ctx context.Context, m *ConnMetricsData, dur time.Duration)
+
+ // ObserveWaitingDuration updates the duration of waiting times for
+ // accepting stream-connections. m must not be nil.
+ ObserveWaitingDuration(ctx context.Context, m *ConnMetricsData, dur time.Duration)
+
+ // SetStopLimit sets the stopping limit number of active stream-connections.
+ SetStopLimit(ctx context.Context, n uint64)
+
+ // SetResumeLimit sets the resuming limit number of active
+ // stream-connections.
+ SetResumeLimit(ctx context.Context, n uint64)
+}
+
+// ConnMetricsData is an alias for a structure that contains the information
+// about a stream-connection. All fields must not be empty.
+//
+// NOTE: This is an alias to reduce the amount of dependencies required of
+// implementations. This is also the reason why only built-in or stdlib types
+// are used.
+type ConnMetricsData = struct {
+ // Addr is the address that the server is configured to listen on.
+ Addr string
+
+ // Name is the name of the server.
+ Name string
+
+ // Proto is the protocol of the server.
+ Proto string
+}
+
+// EmptyMetrics is the implementation of the [Metrics] interface that does
+// nothing.
+type EmptyMetrics struct{}
+
+// type check
+var _ Metrics = EmptyMetrics{}
+
+// IncrementActive implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) IncrementActive(_ context.Context, _ *ConnMetricsData) {}
+
+// DecrementActive implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) DecrementActive(_ context.Context, _ *ConnMetricsData) {}
+
+// ObserveLifeDuration implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) ObserveLifeDuration(_ context.Context, _ *ConnMetricsData, _ time.Duration) {}
+
+// ObserveWaitingDuration implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) ObserveWaitingDuration(_ context.Context, _ *ConnMetricsData, _ time.Duration) {}
+
+// SetStopLimit implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) SetStopLimit(_ context.Context, _ uint64) {}
+
+// SetResumeLimit implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) SetResumeLimit(_ context.Context, _ uint64) {}
diff --git a/internal/consul/allowlist.go b/internal/consul/allowlist.go
index a1d2023..2243c51 100644
--- a/internal/consul/allowlist.go
+++ b/internal/consul/allowlist.go
@@ -12,11 +12,11 @@ import (
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil/urlutil"
+ "github.com/AdguardTeam/golibs/service"
)
// AllowlistUpdater is a wrapper that updates the allowlist on refresh. It
@@ -68,10 +68,9 @@ func NewAllowlistUpdater(c *AllowlistUpdaterConfig) (upd *AllowlistUpdater) {
}
// type check
-var _ agdservice.Refresher = (*AllowlistUpdater)(nil)
+var _ service.Refresher = (*AllowlistUpdater)(nil)
-// Refresh implements the [agdservice.Refresher] interface for
-// *AllowlistUpdater.
+// Refresh implements the [service.Refresher] interface for *AllowlistUpdater.
func (upd *AllowlistUpdater) Refresh(ctx context.Context) (err error) {
upd.logger.InfoContext(ctx, "refresh started")
defer upd.logger.InfoContext(ctx, "refresh finished")
diff --git a/internal/debugsvc/debugsvc_test.go b/internal/debugsvc/debugsvc_test.go
index 3083ca9..fba977b 100644
--- a/internal/debugsvc/debugsvc_test.go
+++ b/internal/debugsvc/debugsvc_test.go
@@ -11,12 +11,12 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/AdGuardDNS/internal/debugsvc"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil/httputil"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/AdguardTeam/golibs/testutil"
+ "github.com/AdguardTeam/golibs/testutil/fakeservice"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -39,21 +39,21 @@ func TestService_Start(t *testing.T) {
var refreshed []string
refreshers := debugsvc.Refreshers{
- "test": &agdtest.Refresher{
+ "test": &fakeservice.Refresher{
OnRefresh: func(_ context.Context) (err error) {
refreshed = append(refreshed, "test")
return nil
},
},
- "parent/first": &agdtest.Refresher{
+ "parent/first": &fakeservice.Refresher{
OnRefresh: func(_ context.Context) (err error) {
refreshed = append(refreshed, "parent/first")
return nil
},
},
- "parent/second": &agdtest.Refresher{
+ "parent/second": &fakeservice.Refresher{
OnRefresh: func(_ context.Context) (err error) {
refreshed = append(refreshed, "parent/second")
diff --git a/internal/debugsvc/refresh.go b/internal/debugsvc/refresh.go
index 2acd48d..0eacd80 100644
--- a/internal/debugsvc/refresh.go
+++ b/internal/debugsvc/refresh.go
@@ -12,10 +12,10 @@ import (
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/httphdr"
"github.com/AdguardTeam/golibs/logutil/slogutil"
+ "github.com/AdguardTeam/golibs/service"
)
// RefresherID is a type alias for strings that represent IDs of refreshers.
@@ -25,7 +25,7 @@ type RefresherID = string
// Refreshers is a type alias for maps of refresher IDs to Refreshers
// themselves.
-type Refreshers map[RefresherID]agdservice.Refresher
+type Refreshers map[RefresherID]service.Refresher
// refreshHandler performs debug refreshes.
type refreshHandler struct {
diff --git a/internal/dnscheck/dnscheck.go b/internal/dnscheck/dnscheck.go
index ed9e63a..c34f162 100644
--- a/internal/dnscheck/dnscheck.go
+++ b/internal/dnscheck/dnscheck.go
@@ -8,6 +8,7 @@ import (
"strings"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdvalidate"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
"github.com/miekg/dns"
@@ -79,7 +80,7 @@ func validateRandomID(id string) (err error) {
min = 4
)
- if err = agd.ValidateInclusion(len(id), max, min, agd.UnitByte); err != nil {
+ if err = agdvalidate.Inclusion(len(id), min, max, agdvalidate.UnitByte); err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
diff --git a/internal/dnscheck/error.go b/internal/dnscheck/error.go
deleted file mode 100644
index b83a721..0000000
--- a/internal/dnscheck/error.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package dnscheck
-
-import (
- "context"
-
- "github.com/AdguardTeam/AdGuardDNS/internal/metrics"
- "github.com/AdguardTeam/AdGuardDNS/internal/remotekv/consulkv"
- "github.com/AdguardTeam/golibs/errors"
-)
-
-// incErrMetrics increments error gauge metrics for the given src and err.
-// "source" can be "dns" or "http".
-func incErrMetrics(src string, err error) {
- if err == nil {
- return
- }
-
- var errType string
- switch {
- case errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled):
- errType = "timeout"
- case errors.Is(err, consulkv.ErrRateLimited):
- errType = "ratelimit"
- default:
- errType = "other"
- }
-
- metrics.DNSCheckErrorTotal.WithLabelValues(src, errType).Inc()
-}
diff --git a/internal/dnscheck/metrics.go b/internal/dnscheck/metrics.go
new file mode 100644
index 0000000..527854c
--- /dev/null
+++ b/internal/dnscheck/metrics.go
@@ -0,0 +1,65 @@
+package dnscheck
+
+import (
+ "context"
+
+ "github.com/AdguardTeam/AdGuardDNS/internal/remotekv/consulkv"
+ "github.com/AdguardTeam/golibs/errors"
+)
+
+// Error types for [Metrics.HandleError].
+const (
+ errMtrcTypeTimeout = "timeout"
+ errMtrcTypeRatelimit = "ratelimit"
+ errMtrcTypeOther = "other"
+)
+
+// Request types for [Metrics].
+const (
+ reqMtrcTypeDNS = "dns"
+ reqMtrcTypeHTTP = "http"
+)
+
+// Metrics is an interface that is used for the collection of the DNSCheck
+// service statistics.
+type Metrics interface {
+ // HandleError handles the total number of errors by type. reqType must be
+ // [reqMtrcTypeDNS] or [reqMtrcTypeHTTP]. errType must be either
+ // [errMtrcTypeTimeout], [errMtrcTypeRatelimit], [errMtrcTypeOther] or an
+ // empty string.
+ HandleError(ctx context.Context, reqType, errType string)
+
+ // HandleRequest handles the total number of requests by type. reqType must
+ // be [reqMtrcTypeDNS] or [reqMtrcTypeHTTP].
+ HandleRequest(ctx context.Context, reqType string, isValid bool)
+}
+
+// EmptyMetrics is the implementation of the [Metrics] interface that does
+// nothing.
+type EmptyMetrics struct{}
+
+// type check
+var _ Metrics = EmptyMetrics{}
+
+// HandleError implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) HandleError(_ context.Context, _, _ string) {}
+
+// HandleRequest implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) HandleRequest(_ context.Context, _ string, _ bool) {}
+
+// errMetricsType returns the error type for [Metrics.HandleError]. It is an
+// empty string if there is no error.
+func errMetricsType(err error) (errType string) {
+ if err == nil {
+ return ""
+ }
+
+ switch {
+ case errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled):
+ return errMtrcTypeTimeout
+ case errors.Is(err, consulkv.ErrRateLimited):
+ return errMtrcTypeRatelimit
+ default:
+ return errMtrcTypeOther
+ }
+}
diff --git a/internal/dnscheck/remotekv.go b/internal/dnscheck/remotekv.go
index 3933471..489325d 100644
--- a/internal/dnscheck/remotekv.go
+++ b/internal/dnscheck/remotekv.go
@@ -15,7 +15,6 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
- "github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/AdGuardDNS/internal/remotekv"
"github.com/AdguardTeam/AdGuardDNS/internal/remotekv/consulkv"
"github.com/AdguardTeam/golibs/errors"
@@ -24,12 +23,12 @@ import (
"github.com/AdguardTeam/golibs/netutil"
"github.com/miekg/dns"
cache "github.com/patrickmn/go-cache"
- "github.com/prometheus/client_golang/prometheus"
)
// RemoteKV is the RemoteKV KV based DNS checker.
type RemoteKV struct {
- logger *slog.Logger
+ logger *slog.Logger
+ metrics Metrics
// mu protects cache. Don't use an RWMutex here, since it is expected that
// there are about as many reads as there are writes.
@@ -59,6 +58,9 @@ type RemoteKVConfig struct {
// IPv4 and IPv6 IPs.
Messages *dnsmsg.Constructor
+ // Metrics is used for the collection of the DNSCheck service statistics.
+ Metrics Metrics
+
// RemoteKV for DNS server checking.
RemoteKV remotekv.Interface
@@ -94,6 +96,7 @@ const (
func NewRemoteKV(c *RemoteKVConfig) (dc *RemoteKV) {
return &RemoteKV{
logger: c.Logger,
+ metrics: c.Metrics,
mu: &sync.Mutex{},
cache: cache.New(defaultCacheExp, defaultCacheGC),
kv: c.RemoteKV,
@@ -118,16 +121,13 @@ func (dc *RemoteKV) Check(
) (resp *dns.Msg, err error) {
var matched bool
defer func() {
- incErrMetrics("dns", err)
+ dc.metrics.HandleError(ctx, reqMtrcTypeDNS, errMetricsType(err))
if !matched {
return
}
- metrics.DNSCheckRequestTotal.With(prometheus.Labels{
- "type": "dns",
- "valid": metrics.BoolString(err == nil),
- }).Inc()
+ dc.metrics.HandleRequest(ctx, reqMtrcTypeDNS, err == nil)
}()
var randomID string
@@ -180,19 +180,19 @@ const (
// newInfo returns an information record with all available data about the
// server and the request. ri must not be nil.
func (dc *RemoteKV) newInfo(ri *agd.RequestInfo) (inf *info) {
- g := ri.ServerGroup
+ srvInfo := ri.ServerInfo
srvType := serverTypePublic
- if g.ProfilesEnabled {
+ if srvInfo.ProfilesEnabled {
srvType = serverTypePrivate
}
inf = &info{
- ServerGroupName: g.Name,
- ServerName: ri.Server,
+ ServerGroupName: srvInfo.GroupName,
+ ServerName: srvInfo.Name,
ServerType: srvType,
- Protocol: ri.Proto.String(),
+ Protocol: srvInfo.Protocol.String(),
NodeLocation: dc.nodeLocation,
NodeName: dc.nodeName,
@@ -230,10 +230,10 @@ var _ http.Handler = (*RemoteKV)(nil)
// ServeHTTP implements the http.Handler interface for *RemoteKV.
//
-// TODO(a.garipov): Consider using the websvc logger once it switches to
-// log/slog.
+// TODO(a.garipov): Find ways of merging the attributes of [RemoteKV.logger]
+// and the logger that websvc adds.
func (dc *RemoteKV) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- // TODO(a.garipov): Put this into constant here and in package dnssvc.
+ // TODO(a.garipov): Put this into constant here and in package websvc.
if r.URL.Path == "/dnscheck/test" {
dc.serveCheckTest(r.Context(), w, r)
@@ -308,12 +308,8 @@ func (dc *RemoteKV) serveCheckTest(ctx context.Context, w http.ResponseWriter, r
// info returns an information record by the random request ID.
func (dc *RemoteKV) info(ctx context.Context, randomID string) (inf []byte, ok bool, err error) {
defer func() {
- metrics.DNSCheckRequestTotal.With(prometheus.Labels{
- "type": "http",
- "valid": metrics.BoolString(err == nil),
- }).Inc()
-
- incErrMetrics("http", err)
+ dc.metrics.HandleError(ctx, reqMtrcTypeHTTP, errMetricsType(err))
+ dc.metrics.HandleRequest(ctx, reqMtrcTypeHTTP, err == nil)
}()
defer func() { err = errors.Annotate(err, "getting from remote kv: %w") }()
diff --git a/internal/dnscheck/remotekv_test.go b/internal/dnscheck/remotekv_test.go
index 9452a73..2006af5 100644
--- a/internal/dnscheck/remotekv_test.go
+++ b/internal/dnscheck/remotekv_test.go
@@ -49,6 +49,7 @@ func TestConsul_ServeHTTP(t *testing.T) {
conf := &dnscheck.RemoteKVConfig{
Logger: slogutil.NewDiscardLogger(),
Messages: &dnsmsg.Constructor{},
+ Metrics: dnscheck.EmptyMetrics{},
RemoteKV: remotekv.Empty{},
ErrColl: agdtest.NewErrorCollector(),
Domains: []string{checkDomain},
@@ -72,15 +73,16 @@ func TestConsul_ServeHTTP(t *testing.T) {
Device: &agd.Device{ID: agd.DeviceID(theOnlyVal["device_id"].(string))},
Profile: &agd.Profile{ID: agd.ProfileID(theOnlyVal["profile_id"].(string))},
},
- ServerGroup: &agd.ServerGroup{
- Name: agd.ServerGroupName(theOnlyVal["server_group_name"].(string)),
+ ServerInfo: &agd.RequestServerInfo{
+ GroupName: agd.ServerGroupName(theOnlyVal["server_group_name"].(string)),
+ Name: agd.ServerName(theOnlyVal["server_name"].(string)),
+ DeviceDomains: nil,
+ Protocol: agd.ProtoDNS,
ProfilesEnabled: theOnlyVal["server_type"] == "private",
},
- Server: agd.ServerName(theOnlyVal["server_name"].(string)),
Host: randomid + "-" + checkDomain,
RemoteIP: testRemoteIP,
QType: dns.TypeA,
- Proto: agd.ProtoDNS,
},
)
require.NoError(t, err)
@@ -175,6 +177,7 @@ func TestConsul_Check(t *testing.T) {
conf := &dnscheck.RemoteKVConfig{
Logger: slogutil.NewDiscardLogger(),
Messages: msgs,
+ Metrics: dnscheck.EmptyMetrics{},
RemoteKV: remotekv.Empty{},
Domains: []string{checkDomain},
IPv4: []netip.Addr{netip.MustParseAddr("1.2.3.4")},
@@ -194,12 +197,13 @@ func TestConsul_Check(t *testing.T) {
}},
}
ri := &agd.RequestInfo{
- Host: tc.host,
- ServerGroup: &agd.ServerGroup{},
- RemoteIP: testRemoteIP,
- QType: tc.qtype,
- Messages: msgs,
- Proto: agd.ProtoDNS,
+ Host: tc.host,
+ ServerInfo: &agd.RequestServerInfo{
+ Protocol: agd.ProtoDNS,
+ },
+ RemoteIP: testRemoteIP,
+ QType: tc.qtype,
+ Messages: msgs,
}
resp, cErr := dnsCk.Check(ctx, req, ri)
diff --git a/internal/dnsdb/buffer.go b/internal/dnsdb/buffer.go
index 4e23647..8b62679 100644
--- a/internal/dnsdb/buffer.go
+++ b/internal/dnsdb/buffer.go
@@ -4,7 +4,6 @@ import (
"sync"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
- "github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/golibs/container"
"github.com/miekg/dns"
)
@@ -23,15 +22,22 @@ type buffer struct {
maxSize int
}
-// add increments the records for all answers.
-func (b *buffer) add(target string, answers []dns.RR, qt dnsmsg.RRType, rc dnsmsg.RCode) {
+// add adds a new record to the buffer or updates the number of hits on the
+// stored record. count is the total number of records stored, ok is true if
+// the new record was added.
+func (b *buffer) add(
+ target string,
+ answers []dns.RR,
+ qt dnsmsg.RRType,
+ rc dnsmsg.RCode,
+) (count int, ok bool) {
b.mu.Lock()
defer b.mu.Unlock()
// Do nothing if the buffer is already full.
- l := len(b.entries)
- if l >= b.maxSize {
- return
+ count = len(b.entries)
+ if count >= b.maxSize {
+ return count, false
}
key := recordKey{
@@ -47,7 +53,7 @@ func (b *buffer) add(target string, answers []dns.RR, qt dnsmsg.RRType, rc dnsms
// If a more detailed response is needed, maps.Copy can be used to
// achieve that.
- return
+ return count, false
}
b.entries[key] = &recordValue{
@@ -55,7 +61,7 @@ func (b *buffer) add(target string, answers []dns.RR, qt dnsmsg.RRType, rc dnsms
hits: 1,
}
- metrics.DNSDBBufferSize.Set(float64(l + 1))
+ return count + 1, true
}
// all returns buffered records.
diff --git a/internal/dnsdb/dnsdb.go b/internal/dnsdb/dnsdb.go
index f798333..efb2bcf 100644
--- a/internal/dnsdb/dnsdb.go
+++ b/internal/dnsdb/dnsdb.go
@@ -15,7 +15,6 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
- "github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/miekg/dns"
)
@@ -39,18 +38,25 @@ type Default struct {
logger *slog.Logger
buffer *atomic.Pointer[buffer]
errColl errcoll.Interface
+ metrics Metrics
maxSize int
}
// DefaultConfig is the default DNS database configuration structure.
type DefaultConfig struct {
- // Logger is used to log the operation of the DNS database.
+ // Logger is used to log the operation of the DNS database. It must not be
+ // nil.
Logger *slog.Logger
- // ErrColl is used to collect HTTP errors.
+ // ErrColl is used to collect HTTP errors. It must not be nil.
ErrColl errcoll.Interface
- // MaxSize is the maximum amount of records in the memory buffer.
+ // Metrics is used for the collection of the DNS database statistics. It
+ // must not be nil.
+ Metrics Metrics
+
+ // MaxSize is the maximum amount of records in the memory buffer. It must
+ // be positive.
MaxSize int
}
@@ -60,6 +66,7 @@ func New(c *DefaultConfig) (db *Default) {
logger: c.Logger,
buffer: &atomic.Pointer[buffer]{},
errColl: c.ErrColl,
+ metrics: c.Metrics,
maxSize: c.MaxSize,
}
@@ -88,11 +95,14 @@ func (db *Default) Record(ctx context.Context, m *dns.Msg, ri *agd.RequestInfo)
}
// #nosec G115 -- RCODE is currently defined to be 16 bit or less.
- db.buffer.Load().add(ri.Host, m.Answer, q.Qtype, dnsmsg.RCode(m.Rcode))
+ count, ok := db.buffer.Load().add(ri.Host, m.Answer, q.Qtype, dnsmsg.RCode(m.Rcode))
+ if ok {
+ db.metrics.SetRecordCount(ctx, count)
+ }
}
// reset returns buffered records and resets the database.
-func (db *Default) reset() (records []*record) {
+func (db *Default) reset(ctx context.Context) (records []*record) {
start := time.Now()
prevBuf := db.buffer.Swap(&buffer{
@@ -103,9 +113,8 @@ func (db *Default) reset() (records []*record) {
records = prevBuf.all()
- metrics.DNSDBBufferSize.Set(0)
- metrics.DNSDBRotateTime.SetToCurrentTime()
- metrics.DNSDBSaveDuration.Observe(time.Since(start).Seconds())
+ db.metrics.SetRecordCount(ctx, 0)
+ db.metrics.ObserveRotation(ctx, time.Since(start))
return records
}
diff --git a/internal/dnsdb/http.go b/internal/dnsdb/http.go
index a54200c..7ba6890 100644
--- a/internal/dnsdb/http.go
+++ b/internal/dnsdb/http.go
@@ -17,12 +17,12 @@ import (
// type check
var _ http.Handler = (*Default)(nil)
-// ServeHTTP implements the http.Handler interface for *Default.
+// ServeHTTP implements the [http.Handler] interface for *Default.
func (db *Default) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var err error
ctx := r.Context()
- records := db.reset()
+ records := db.reset(ctx)
h := w.Header()
h.Add(httphdr.ContentType, agdhttp.HdrValTextCSV)
diff --git a/internal/dnsdb/http_test.go b/internal/dnsdb/http_test.go
index 9e124cb..c78fd66 100644
--- a/internal/dnsdb/http_test.go
+++ b/internal/dnsdb/http_test.go
@@ -123,6 +123,7 @@ func TestDefault_ServeHTTP(t *testing.T) {
db := dnsdb.New(&dnsdb.DefaultConfig{
Logger: slogutil.NewDiscardLogger(),
ErrColl: agdtest.NewErrorCollector(),
+ Metrics: dnsdb.EmptyMetrics{},
MaxSize: 100,
})
rw := httptest.NewRecorder()
diff --git a/internal/dnsdb/metrics.go b/internal/dnsdb/metrics.go
new file mode 100644
index 0000000..39be0f0
--- /dev/null
+++ b/internal/dnsdb/metrics.go
@@ -0,0 +1,31 @@
+package dnsdb
+
+import (
+ "context"
+ "time"
+)
+
+// Metrics is an interface that is used for the collection of the DNS database
+// statistics.
+type Metrics interface {
+ // SetRecordCount sets the number of records that have not yet been
+ // uploaded.
+ SetRecordCount(ctx context.Context, count int)
+
+ // ObserveRotation updates the time of the database rotation and stores the
+ // duration of the rotation.
+ ObserveRotation(ctx context.Context, dur time.Duration)
+}
+
+// EmptyMetrics is the implementation of the [Metrics] interface that does
+// nothing.
+type EmptyMetrics struct{}
+
+// type check
+var _ Metrics = EmptyMetrics{}
+
+// SetRecordCount implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) SetRecordCount(_ context.Context, _ int) {}
+
+// ObserveRotation implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) ObserveRotation(_ context.Context, dur time.Duration) {}
diff --git a/internal/dnsmsg/constructor.go b/internal/dnsmsg/constructor.go
index 3ed4b36..9ab2ea7 100644
--- a/internal/dnsmsg/constructor.go
+++ b/internal/dnsmsg/constructor.go
@@ -17,11 +17,11 @@ type ConstructorConfig struct {
// StructuredErrors is the configuration for the experimental Structured DNS
// Errors feature. It must not be nil. If enabled,
- // [ConstructorConfig.Enabled] should also be true.
+ // [ConstructorConfig.EDEEnabled] should also be true.
StructuredErrors *StructuredDNSErrorsConfig
- // BlockingMode is the blocking mode to use in
- // [Constructor.NewBlockedRespMsg]. It must not be nil.
+ // BlockingMode is the blocking mode to use in [Constructor.NewBlockedResp].
+ // It must not be nil.
BlockingMode BlockingMode
// FilteredResponseTTL is the time-to-live value used for responses created
@@ -118,7 +118,7 @@ func (c *Constructor) AppendDebugExtra(req, resp *dns.Msg, str string) (err erro
// positive numbers, but we need a ceiling operation here.
strNum := (strLen + MaxTXTStringLen - 1) / MaxTXTStringLen
- // TODO(a.garipov): Use slices.Chunk in Go 1.23.
+ // TODO(a.garipov): Consider adding strings.Chunks to golibs.
newStr := make([]string, strNum)
for i := range strNum {
start := i * MaxTXTStringLen
diff --git a/internal/dnsmsg/constructor_test.go b/internal/dnsmsg/constructor_test.go
index 74e3be4..9398dc8 100644
--- a/internal/dnsmsg/constructor_test.go
+++ b/internal/dnsmsg/constructor_test.go
@@ -183,7 +183,46 @@ func TestConstructor_AppendDebugExtra(t *testing.T) {
wantExtra[0].Header().Name = fqdn
}
- assert.Equal(t, resp.Extra, tc.wantExtra)
+ assert.Equal(t, tc.wantExtra, resp.Extra)
})
}
}
+
+// errSink is a sink for benchmark results.
+var errSink error
+
+func BenchmarkConstructor_AppendDebugExtra(b *testing.B) {
+ msgs := agdtest.NewConstructor(b)
+
+ longText := strings.Repeat("abc", 2*dnsmsg.MaxTXTStringLen)
+
+ req := &dns.Msg{
+ MsgHdr: dns.MsgHdr{
+ Id: dns.Id(),
+ },
+ Question: []dns.Question{{
+ Name: testFQDN,
+ Qtype: dns.TypeTXT,
+ Qclass: dns.ClassCHAOS,
+ }},
+ }
+
+ resp := &dns.Msg{}
+ resp = resp.SetReply(req)
+
+ b.ReportAllocs()
+ b.ResetTimer()
+ for range b.N {
+ errSink = msgs.AppendDebugExtra(req, resp, longText)
+ }
+
+ assert.NoError(b, errSink)
+
+ // Most recent results:
+ //
+ // goos: darwin
+ // goarch: arm64
+ // pkg: github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg
+ // cpu: Apple M1 Pro
+ // BenchmarkConstructor_AppendDebugExtra-8 8809124 137.3 ns/op 253 B/op 2 allocs/op
+}
diff --git a/internal/dnsmsg/dnsmsg_test.go b/internal/dnsmsg/dnsmsg_test.go
index 83dea9b..8e799aa 100644
--- a/internal/dnsmsg/dnsmsg_test.go
+++ b/internal/dnsmsg/dnsmsg_test.go
@@ -14,10 +14,6 @@ import (
"github.com/stretchr/testify/assert"
)
-func TestMain(m *testing.M) {
- testutil.DiscardLogOutput(m)
-}
-
// Common domain names for tests.
const (
testDomain = "test.example"
diff --git a/internal/dnsmsg/svcbmsg.go b/internal/dnsmsg/svcbmsg.go
index ed33288..4251c5f 100644
--- a/internal/dnsmsg/svcbmsg.go
+++ b/internal/dnsmsg/svcbmsg.go
@@ -8,7 +8,6 @@ import (
"strconv"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
- "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns"
)
@@ -64,8 +63,6 @@ var svcbKeyHandlers = map[string]svcbKeyHandler{
"ech": func(valStr string) (val dns.SVCBKeyValue) {
ech, err := base64.StdEncoding.DecodeString(valStr)
if err != nil {
- log.Debug("can't parse svcb/https ech: %s; ignoring", err)
-
return nil
}
@@ -77,8 +74,6 @@ var svcbKeyHandlers = map[string]svcbKeyHandler{
"ipv4hint": func(valStr string) (val dns.SVCBKeyValue) {
ip := net.ParseIP(valStr)
if ip4 := ip.To4(); ip == nil || ip4 == nil {
- log.Debug("can't parse svcb/https ipv4 hint %q; ignoring", valStr)
-
return nil
}
@@ -90,8 +85,6 @@ var svcbKeyHandlers = map[string]svcbKeyHandler{
"ipv6hint": func(valStr string) (val dns.SVCBKeyValue) {
ip := net.ParseIP(valStr)
if ip == nil {
- log.Debug("can't parse svcb/https ipv6 hint %q; ignoring", valStr)
-
return nil
}
@@ -103,8 +96,6 @@ var svcbKeyHandlers = map[string]svcbKeyHandler{
"mandatory": func(valStr string) (val dns.SVCBKeyValue) {
code, ok := strToSVCBKey[valStr]
if !ok {
- log.Debug("unknown svcb/https mandatory key %q, ignoring", valStr)
-
return nil
}
@@ -120,8 +111,6 @@ var svcbKeyHandlers = map[string]svcbKeyHandler{
"port": func(valStr string) (val dns.SVCBKeyValue) {
port64, err := strconv.ParseUint(valStr, 10, 16)
if err != nil {
- log.Debug("can't parse svcb/https port: %s; ignoring", err)
-
return nil
}
@@ -142,7 +131,9 @@ var svcbKeyHandlers = map[string]svcbKeyHandler{
// ipv4hint=127.0.0.1,127.0.0.2 // Unsupported.
// ipv4hint="127.0.0.1,127.0.0.2" // Unsupported.
//
-// TODO(a.garipov): Support all of these.
+// TODO(a.garipov): Support all of these.
+//
+// TODO(a.garipov): Consider re-adding debug logging for SVCB handlers.
func (c *Constructor) NewAnswerSVCB(req *dns.Msg, svcb *rules.DNSSVCB) (ans *dns.SVCB) {
ans = &dns.SVCB{
Hdr: c.newHdr(req, dns.TypeSVCB),
@@ -157,8 +148,6 @@ func (c *Constructor) NewAnswerSVCB(req *dns.Msg, svcb *rules.DNSSVCB) (ans *dns
for k, valStr := range svcb.Params {
handler, ok := svcbKeyHandlers[k]
if !ok {
- log.Debug("unknown svcb/https key %q, ignoring", k)
-
continue
}
diff --git a/internal/dnsserver/cache/cache.go b/internal/dnsserver/cache/cache.go
index 535c9bd..9738e17 100644
--- a/internal/dnsserver/cache/cache.go
+++ b/internal/dnsserver/cache/cache.go
@@ -4,16 +4,18 @@
package cache
import (
+ "cmp"
"context"
"encoding/binary"
"fmt"
+ "log/slog"
"math"
"strings"
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/golibs/errors"
- "github.com/AdguardTeam/golibs/log"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/bluele/gcache"
"github.com/miekg/dns"
)
@@ -22,21 +24,19 @@ import (
//
// TODO(a.garipov): Extract cache logic to golibs.
type Middleware struct {
- // metrics is a listener for the middleware events.
- metrics MetricsListener
-
- // cache is the underlying LRU cache.
- cache gcache.Cache
-
- // cacheMinTTL is the minimum supported TTL for cache items.
+ logger *slog.Logger
+ metrics MetricsListener
+ cache gcache.Cache
cacheMinTTL time.Duration
-
- // overrideTTL shows if the TTL overrides logic should be used.
overrideTTL bool
}
// MiddlewareConfig is the configuration structure for NewMiddleware.
type MiddlewareConfig struct {
+ // Logger is used to log the operation of the middleware. If Logger is nil,
+ // [slog.Default] is used.
+ Logger *slog.Logger
+
// MetricsListener is the optional listener for the middleware events. Set
// it if you want to keep track of what the middleware does and record
// performance metrics. If not set, EmptyMetricsListener is used.
@@ -55,15 +55,9 @@ type MiddlewareConfig struct {
// NewMiddleware initializes a new LRU caching middleware. c must not be nil.
func NewMiddleware(c *MiddlewareConfig) (m *Middleware) {
- var metrics MetricsListener
- if c.MetricsListener != nil {
- metrics = c.MetricsListener
- } else {
- metrics = EmptyMetricsListener{}
- }
-
return &Middleware{
- metrics: metrics,
+ logger: cmp.Or(c.Logger, slog.Default()),
+ metrics: cmp.Or[MetricsListener](c.MetricsListener, EmptyMetricsListener{}),
cache: gcache.New(c.Count).LRU().Build(),
cacheMinTTL: c.MinTTL,
overrideTTL: c.OverrideTTL,
@@ -78,7 +72,7 @@ func (m *Middleware) Wrap(handler dnsserver.Handler) (wrapped dnsserver.Handler)
f := func(ctx context.Context, rw dnsserver.ResponseWriter, req *dns.Msg) (err error) {
defer func() { err = errors.Annotate(err, "cache: %w") }()
- resp, ok := m.get(req)
+ resp, ok := m.get(ctx, req)
if ok {
m.metrics.OnCacheHit(ctx, req)
@@ -115,13 +109,13 @@ func (m *Middleware) Wrap(handler dnsserver.Handler) (wrapped dnsserver.Handler)
}
// get retrieves a DNS message for the specified request from the cache.
-func (m *Middleware) get(req *dns.Msg) (resp *dns.Msg, found bool) {
+func (m *Middleware) get(ctx context.Context, req *dns.Msg) (resp *dns.Msg, found bool) {
key := toCacheKey(req)
ciVal, err := m.cache.Get(key)
if err != nil {
if !errors.Is(err, gcache.KeyNotFoundError) {
// Shouldn't happen, since we don't set a serialization function.
- log.Error("cache: error while retrieving a message from the cache: %v", err)
+ m.logger.ErrorContext(ctx, "retrieving from cache", slogutil.KeyError, err)
}
return nil, false
@@ -129,7 +123,12 @@ func (m *Middleware) get(req *dns.Msg) (resp *dns.Msg, found bool) {
item, ok := ciVal.(cacheItem)
if !ok {
- log.Error("cache: bad type %T of cache item for name %q", ciVal, req.Question[0].Name)
+ m.logger.ErrorContext(
+ ctx,
+ "bad type in cache",
+ "type", fmt.Sprintf("%T", ciVal),
+ "target", req.Question[0].Name,
+ )
return nil, false
}
diff --git a/internal/dnsserver/cache/cache_test.go b/internal/dnsserver/cache/cache_test.go
index f7c9cd4..b2a6a9f 100644
--- a/internal/dnsserver/cache/cache_test.go
+++ b/internal/dnsserver/cache/cache_test.go
@@ -10,11 +10,15 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/cache"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
+// testLogger is the common logger for tests.
+var testLogger = slogutil.NewDiscardLogger()
+
func TestMiddleware_Wrap(t *testing.T) {
const (
servFailMaxCacheTTL = 30
@@ -186,6 +190,7 @@ func TestMiddleware_Wrap(t *testing.T) {
withCache := dnsserver.WithMiddlewares(
handler,
cache.NewMiddleware(&cache.MiddlewareConfig{
+ Logger: testLogger,
Count: 100,
MinTTL: minTTL,
OverrideTTL: tc.minTTL != nil,
diff --git a/internal/dnsserver/cache/metrics.go b/internal/dnsserver/cache/metrics.go
index 7dada82..d1571f6 100644
--- a/internal/dnsserver/cache/metrics.go
+++ b/internal/dnsserver/cache/metrics.go
@@ -13,9 +13,11 @@ import (
type MetricsListener interface {
// OnCacheItemAdded is called when an item has been added to the cache.
OnCacheItemAdded(ctx context.Context, resp *dns.Msg, cacheLen int)
+
// OnCacheHit is called when a response for the specified request has been
// found in the cache.
OnCacheHit(ctx context.Context, req *dns.Msg)
+
// OnCacheMiss is called when a response for the specified request has not
// been found in the cache.
OnCacheMiss(ctx context.Context, req *dns.Msg)
diff --git a/internal/dnsserver/context.go b/internal/dnsserver/context.go
index 0a23d15..69cf8b1 100644
--- a/internal/dnsserver/context.go
+++ b/internal/dnsserver/context.go
@@ -7,50 +7,6 @@ import (
"time"
)
-// ContextConstructor is an interface for constructing interfaces with
-// deadlines, e.g. for request contexts.
-type ContextConstructor interface {
- New() (ctx context.Context, cancel context.CancelFunc)
-}
-
-// DefaultContextConstructor is the default implementation of the
-// [ContextConstructor] interface.
-type DefaultContextConstructor struct{}
-
-// type check
-var _ ContextConstructor = DefaultContextConstructor{}
-
-// New implements the [ContextConstructor] interface for
-// DefaultContextConstructor. It returns [context.Background] and an empty
-// [context.CancelFunc].
-func (DefaultContextConstructor) New() (ctx context.Context, cancel context.CancelFunc) {
- return context.Background(), func() {}
-}
-
-// TimeoutContextConstructor is an implementation of the [ContextConstructor]
-// interface that returns a context with the given timeout.
-type TimeoutContextConstructor struct {
- timeout time.Duration
-}
-
-// NewTimeoutContextConstructor returns a new properly initialized
-// *TimeoutContextConstructor.
-func NewTimeoutContextConstructor(timeout time.Duration) (c *TimeoutContextConstructor) {
- return &TimeoutContextConstructor{
- timeout: timeout,
- }
-}
-
-// type check
-var _ ContextConstructor = (*TimeoutContextConstructor)(nil)
-
-// New implements the [ContextConstructor] interface for
-// *TimeoutContextConstructor. It returns a context with its timeout and the
-// corresponding cancelation function.
-func (c *TimeoutContextConstructor) New() (ctx context.Context, cancel context.CancelFunc) {
- return context.WithTimeout(context.Background(), c.timeout)
-}
-
// ctxKey is the type for context keys.
type ctxKey int
diff --git a/internal/dnsserver/dnsserver.go b/internal/dnsserver/dnsserver.go
index 614a12a..6b301ef 100644
--- a/internal/dnsserver/dnsserver.go
+++ b/internal/dnsserver/dnsserver.go
@@ -9,92 +9,47 @@ package dnsserver
import (
"context"
"net"
-
- "github.com/miekg/dns"
)
-// Handler is an interface that defines how the DNS server would process DNS
-// queries. Inspired by net/http.Server and it's Handler.
-type Handler interface {
- // ServeDNS should process the request and write a DNS response to the
- // specified ResponseWriter.
- //
- // It accepts context.Context argument which has some additional info
- // attached to it. This context always contains [ServerInfo] which can be
- // retrieved using [ServerInfoFromContext] or [MustServerInfoFromContext].
- // It also must contain [RequestInfo] that can be retrieved with
- // [RequestInfoFromContext] or [MustRequestInfoFromContext].
- ServeDNS(context.Context, ResponseWriter, *dns.Msg) (err error)
-}
-
-// The HandlerFunc type is an adapter to allow the use of ordinary functions
-// as DNS handlers. If f is a function with the appropriate signature,
-// HandlerFunc(f) is a Handler that calls f.
-type HandlerFunc func(context.Context, ResponseWriter, *dns.Msg) (err error)
-
-// ServeDNS implements the Handler interface for HandlerFunc.
-func (f HandlerFunc) ServeDNS(ctx context.Context, rw ResponseWriter, req *dns.Msg) (err error) {
- return f(ctx, rw, req)
-}
-
-// notImplementedHandlerFunc is used if no Handler is configured for a server.
-var notImplementedHandlerFunc HandlerFunc = func(
- ctx context.Context,
- w ResponseWriter,
- r *dns.Msg,
-) (err error) {
- res := new(dns.Msg)
- res.SetRcode(r, dns.RcodeNotImplemented)
-
- return w.WriteMsg(ctx, r, res)
-}
-
// Server represents a DNS server.
//
-// TODO(ameshkov): move validation to ctors (for all structs that inherit this).
-//
-// TODO(ameshkov): consider Proto()/Network()/Addr() -> single Info() func.
+// TODO(a.garipov): Minimize the number of methods; consider embedding
+// service.Service from golibs.
type Server interface {
// Name returns the server name.
+ //
+ // TODO(a.garipov): Consider removing.
Name() (name string)
+
// Proto returns the protocol of the server.
+ //
+ // TODO(a.garipov): Consider removing.
Proto() (proto Protocol)
+
// Network is a network (tcp, udp or empty) this server listens to. If it
// is empty, the server listens to all networks that are supposed to be
// used by its protocol.
+ //
+ // TODO(a.garipov): Consider removing.
Network() (network Network)
+
// Addr returns the address the server was configured to listen to.
Addr() (addr string)
+
// Start starts the server, exits immediately if it failed to start
// listening. Start returns once all servers are considered up.
Start(ctx context.Context) (err error)
+
// Shutdown stops the server and waits for all active connections to close.
Shutdown(ctx context.Context) (err error)
+
// LocalTCPAddr returns the TCP address the server listens to at the moment
// or nil if it does not listen to TCP. Depending on the server protocol
// it may correspond to DNS-over-TCP, DNS-over-TLS, HTTP2, DNSCrypt (TCP).
LocalTCPAddr() (addr net.Addr)
- // LocalUDPAddr returns the UDP address the server listens to at the moment or
- // nil if it does not listen to UDP. Depending on the server protocol
- // it may correspond to DNS-over-UDP, HTTP3, QUIC, DNSCrypt (UDP).
+
+ // LocalUDPAddr returns the UDP address the server listens to at the moment
+ // or nil if it does not listen to UDP. Depending on the server protocol it
+ // may correspond to DNS-over-UDP, HTTP3, QUIC, DNSCrypt (UDP).
LocalUDPAddr() (addr net.Addr)
}
-
-// A ResponseWriter interface is used by a DNS handler to construct a DNS
-// response.
-type ResponseWriter interface {
- // LocalAddr returns the net.Addr of the server.
- LocalAddr() net.Addr
-
- // RemoteAddr returns the net.Addr of the client that sent the current
- // request.
- RemoteAddr() net.Addr
-
- // WriteMsg writes a reply back to the client.
- //
- // Handlers must not modify req and resp after the call to WriteMsg, since
- // their ResponseWriter implementation may be a recorder.
- //
- // TODO(a.garipov): Store bytes written to the socket.
- WriteMsg(ctx context.Context, req, resp *dns.Msg) error
-}
diff --git a/internal/dnsserver/dnsserver_test.go b/internal/dnsserver/dnsserver_test.go
index 038c7d7..f9869c3 100644
--- a/internal/dnsserver/dnsserver_test.go
+++ b/internal/dnsserver/dnsserver_test.go
@@ -1,15 +1,6 @@
package dnsserver_test
-import (
- "testing"
-
- "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
- "github.com/AdguardTeam/golibs/testutil"
-)
-
-func TestMain(m *testing.M) {
- testutil.DiscardLogOutput(m)
-}
+import "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
// testTimeout is a common timeout for tests.
const testTimeout = dnsserver.DefaultReadTimeout
diff --git a/internal/dnsserver/dnsservertest/server.go b/internal/dnsserver/dnsservertest/server.go
index b89c592..fe200ef 100644
--- a/internal/dnsserver/dnsservertest/server.go
+++ b/internal/dnsserver/dnsservertest/server.go
@@ -12,6 +12,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/ameshkov/dnscrypt/v2"
"github.com/miekg/dns"
@@ -27,11 +28,12 @@ import (
func RunDNSServer(t testing.TB, h dnsserver.Handler) (s *dnsserver.ServerDNS, addr string) {
t.Helper()
- conf := dnsserver.ConfigDNS{
- ConfigBase: dnsserver.ConfigBase{
- Name: "test",
- Addr: "127.0.0.1:0",
- Handler: h,
+ conf := &dnsserver.ConfigDNS{
+ Base: &dnsserver.ConfigBase{
+ BaseLogger: slogutil.NewDiscardLogger(),
+ Name: "test",
+ Addr: "127.0.0.1:0",
+ Handler: h,
},
MaxUDPRespSize: dns.MaxMsgSize,
}
@@ -59,12 +61,13 @@ func RunDNSServer(t testing.TB, h dnsserver.Handler) (s *dnsserver.ServerDNS, ad
func RunTLSServer(t testing.TB, h dnsserver.Handler, tlsConfig *tls.Config) (addr *net.TCPAddr) {
t.Helper()
- conf := dnsserver.ConfigTLS{
- ConfigDNS: dnsserver.ConfigDNS{
- ConfigBase: dnsserver.ConfigBase{
- Name: "test",
- Addr: "127.0.0.1:0",
- Handler: h,
+ conf := &dnsserver.ConfigTLS{
+ DNS: &dnsserver.ConfigDNS{
+ Base: &dnsserver.ConfigBase{
+ BaseLogger: slogutil.NewDiscardLogger(),
+ Name: "test",
+ Addr: "127.0.0.1:0",
+ Handler: h,
},
},
TLSConfig: tlsConfig,
@@ -118,14 +121,15 @@ func RunDNSCryptServer(t testing.TB, h dnsserver.Handler) (s *TestDNSCryptServer
s.ResolverPk = testutil.RequireTypeAssert[ed25519.PublicKey](t, pk)
- conf := dnsserver.ConfigDNSCrypt{
- ConfigBase: dnsserver.ConfigBase{
- Name: "test",
- Addr: "127.0.0.1:0",
- Handler: h,
+ conf := &dnsserver.ConfigDNSCrypt{
+ Base: &dnsserver.ConfigBase{
+ BaseLogger: slogutil.NewDiscardLogger(),
+ Name: "test",
+ Addr: "127.0.0.1:0",
+ Handler: h,
},
- DNSCryptProviderName: s.ProviderName,
- DNSCryptResolverCert: cert,
+ ProviderName: s.ProviderName,
+ ResolverCert: cert,
}
// Create a new ServerDNSCrypt and run it.
@@ -166,12 +170,13 @@ func RunLocalHTTPSServer(
tlsConfigH3.NextProtos = dnsserver.NextProtoDoH3
}
- conf := dnsserver.ConfigHTTPS{
- ConfigBase: dnsserver.ConfigBase{
- Name: "test",
- Addr: "127.0.0.1:0",
- Handler: h,
- Network: network,
+ conf := &dnsserver.ConfigHTTPS{
+ Base: &dnsserver.ConfigBase{
+ BaseLogger: slogutil.NewDiscardLogger(),
+ Name: "test",
+ Addr: "127.0.0.1:0",
+ Handler: h,
+ Network: network,
},
TLSConfDefault: tlsConfig,
TLSConfH3: tlsConfigH3,
@@ -197,12 +202,13 @@ func RunLocalQUICServer(
h dnsserver.Handler,
tlsConfig *tls.Config,
) (s *dnsserver.ServerQUIC, addr *net.UDPAddr, err error) {
- conf := dnsserver.ConfigQUIC{
+ conf := &dnsserver.ConfigQUIC{
TLSConfig: tlsConfig,
- ConfigBase: dnsserver.ConfigBase{
- Name: "test",
- Addr: "127.0.0.1:0",
- Handler: h,
+ Base: &dnsserver.ConfigBase{
+ BaseLogger: slogutil.NewDiscardLogger(),
+ Name: "test",
+ Addr: "127.0.0.1:0",
+ Handler: h,
},
}
diff --git a/internal/dnsserver/doc.go b/internal/dnsserver/doc.go
index b23178a..9c39802 100644
--- a/internal/dnsserver/doc.go
+++ b/internal/dnsserver/doc.go
@@ -49,7 +49,7 @@ is specified in the configuration. Here's how to create a simple plain
DNS server:
conf := dnsserver.ConfigDNS{
- ConfigBase: dnsserver.ConfigBase{
+ Base: &dnsserver.ConfigBase{
// server name
Name: "test",
// listen address
@@ -70,8 +70,8 @@ In order to use a DoT server, you also need to supply a [*tls.Config] with the
certificate and its private key.
conf := dnsserver.ConfigTLS{
- ConfigDNS: dnsserver.ConfigDNS{
- ConfigBase: dnsserver.ConfigBase{
+ DNS: &dnsserver.ConfigDNS{
+ Base: &dnsserver.ConfigBase{
Name: "test",
Addr: "127.0.0.1:0",
Handler: h,
@@ -87,11 +87,11 @@ certificate and its private key.
DoH server uses an [*http.Server] and/or [*http3.Server] internally. There are
a couple of things to note:
- 1. tls.Config can be omitted, but you must set [ConfigBase.Network] to
- NetworkTCP. In this case the server will work simply as a plain HTTP
- server. This might be useful if you're running a reverse proxy like Nginx
- in front of your DoH server. If you do specify it, the server will listen
- to both DoH2 and DoH3 by default.
+ 1. tls.Config can be omitted, but you must set [Base.Network] to NetworkTCP.
+ In this case the server will work simply as a plain HTTP server. This might
+ be useful if you're running a reverse proxy like Nginx in front of your DoH
+ server. If you do specify it, the server will listen to both DoH2 and DoH3
+ by default.
2. In the constructor you can specify an optional [http.HandlerFunc] that
processes non-DNS requests, e.g. requests to paths different from
@@ -100,7 +100,7 @@ a couple of things to note:
Example:
conf := dnsserver.ConfigHTTPS{
- ConfigBase: dnsserver.ConfigBase{
+ Base: &dnsserver.ConfigBase{
Name: "test",
Addr: "127.0.0.1:0",
Handler: h,
@@ -117,7 +117,7 @@ DoQ server uses the [quic-go module]. Just like DoH and DoT, it requires a
[*tls.Config] to encrypt the data.
conf := dnsserver.ConfigQUIC{
- ConfigBase: dnsserver.ConfigBase{
+ Base: &dnsserver.ConfigBase{
Name: "test",
Addr: "127.0.0.1:0",
Handler: h,
@@ -134,7 +134,7 @@ server you need to supply DNSCrypt configuration. Read the [module
documentation] about how to initialize it.
conf := dnsserver.ConfigDNSCrypt{
- ConfigBase: dnsserver.ConfigBase{
+ Base: &dnsserver.ConfigBase{
Name: "test",
Addr: "127.0.0.1:0",
Handler: h,
diff --git a/internal/dnsserver/error.go b/internal/dnsserver/error.go
index a1341ee..ae51d79 100644
--- a/internal/dnsserver/error.go
+++ b/internal/dnsserver/error.go
@@ -1,11 +1,15 @@
package dnsserver
import (
+ "context"
"fmt"
+ "io"
+ "log/slog"
"net"
"os"
"github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
)
// Common Errors And Error Helpers
@@ -67,3 +71,15 @@ func isNonCriticalNetError(err error) (ok bool) {
return errors.As(err, &netErr) && netErr.Timeout()
}
+
+// closeWithLog closes c and logs a debug message if c.Close returns an error
+// that isn't [net.ErrClosed].
+//
+// TODO(a.garipov): Unify error handling with regards to [io.EOF],
+// net.ErrClosed, etc.
+func closeWithLog(ctx context.Context, l *slog.Logger, msg string, c io.Closer) {
+ err := c.Close()
+ if err != nil && !errors.Is(err, net.ErrClosed) {
+ l.DebugContext(ctx, msg, slogutil.KeyError, err)
+ }
+}
diff --git a/internal/dnsserver/example_test.go b/internal/dnsserver/example_test.go
index 4025830..9fab102 100644
--- a/internal/dnsserver/example_test.go
+++ b/internal/dnsserver/example_test.go
@@ -2,13 +2,14 @@ package dnsserver_test
import (
"context"
- "fmt"
+ "log/slog"
"net/netip"
"os"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/forward"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/querylog"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/miekg/dns"
)
@@ -27,13 +28,22 @@ func ExampleNewServerDNS() {
},
)
+ baseLogger := slogutil.New(&slogutil.Config{
+ Format: slogutil.FormatText,
+ Level: slog.LevelDebug,
+ }).With("server_name", "test")
+
// Init the server with this handler func
- conf := dnsserver.ConfigDNS{
- ConfigBase: dnsserver.ConfigBase{
+ conf := &dnsserver.ConfigDNS{
+ Base: &dnsserver.ConfigBase{
+ BaseLogger: baseLogger,
+
// server name
Name: "test",
+
// listen address
Addr: "127.0.0.1:0",
+
// handler that will process incoming DNS queries
Handler: handler,
},
@@ -51,7 +61,13 @@ func ExampleNewServerDNS() {
}
}()
- // Output:
+ // Unordered output:
+ // level=INFO msg="starting server" server_name=test
+ // level=INFO msg="server has been started" server_name=test
+ // level=INFO msg="shutting down server" server_name=test
+ // level=INFO msg="starting listening udp" server_name=test
+ // level=INFO msg="starting listening tcp" server_name=test
+ // level=INFO msg="server has been shut down" server_name=test
}
func ExampleWithMiddlewares() {
@@ -63,24 +79,32 @@ func ExampleWithMiddlewares() {
}},
})
- middleware := querylog.NewLogMiddleware(os.Stdout)
+ baseLogger := slogutil.New(&slogutil.Config{
+ Format: slogutil.FormatText,
+ Level: slog.LevelDebug,
+ })
+
+ middleware := querylog.NewLogMiddleware(os.Stdout, baseLogger)
handler := dnsserver.WithMiddlewares(forwarder, middleware)
- // Init the server with this handler func
- conf := dnsserver.ConfigDNS{
- ConfigBase: dnsserver.ConfigBase{
- Name: "test",
- Addr: "127.0.0.1:0",
- Handler: handler,
+ // Init the server with this handler func.
+ conf := &dnsserver.ConfigDNS{
+ Base: &dnsserver.ConfigBase{
+ BaseLogger: baseLogger.With("server_name", "test"),
+ Name: "test",
+ Addr: "127.0.0.1:0",
+ Handler: handler,
},
}
srv := dnsserver.NewServerDNS(conf)
- err := srv.Start(context.Background())
+
+ ctx := context.Background()
+ err := srv.Start(ctx)
if err != nil {
panic("failed to start the server")
}
- fmt.Println("started successfully")
+ baseLogger.InfoContext(ctx, "started successfully")
defer func() {
err = srv.Shutdown(context.Background())
@@ -88,10 +112,16 @@ func ExampleWithMiddlewares() {
panic("failed to shutdown the server")
}
- fmt.Println("stopped successfully")
+ baseLogger.InfoContext(ctx, "stopped successfully")
}()
- // Output:
- // started successfully
- // stopped successfully
+ // Unordered output:
+ // level=INFO msg="starting server" server_name=test
+ // level=INFO msg="server has been started" server_name=test
+ // level=INFO msg="started successfully"
+ // level=INFO msg="shutting down server" server_name=test
+ // level=INFO msg="starting listening udp" server_name=test
+ // level=INFO msg="starting listening tcp" server_name=test
+ // level=INFO msg="server has been shut down" server_name=test
+ // level=INFO msg="stopped successfully"
}
diff --git a/internal/dnsserver/forward/example_test.go b/internal/dnsserver/forward/example_test.go
index e0c5b8b..f7b912e 100644
--- a/internal/dnsserver/forward/example_test.go
+++ b/internal/dnsserver/forward/example_test.go
@@ -7,13 +7,15 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/forward"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
)
func ExampleNewHandler() {
- conf := dnsserver.ConfigDNS{
- ConfigBase: dnsserver.ConfigBase{
- Name: "srv",
- Addr: "127.0.0.1:0",
+ conf := &dnsserver.ConfigDNS{
+ Base: &dnsserver.ConfigBase{
+ BaseLogger: slogutil.NewDiscardLogger(),
+ Name: "srv",
+ Addr: "127.0.0.1:0",
Handler: forward.NewHandler(&forward.HandlerConfig{
UpstreamsAddresses: []*forward.UpstreamPlainConfig{{
Network: forward.NetworkAny,
diff --git a/internal/dnsserver/forward/forward.go b/internal/dnsserver/forward/forward.go
index 0fc261f..815fac0 100644
--- a/internal/dnsserver/forward/forward.go
+++ b/internal/dnsserver/forward/forward.go
@@ -30,14 +30,16 @@ import (
"fmt"
"io"
"log/slog"
+ "math/rand/v2"
"net"
"sync"
"time"
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdrand"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/service"
"github.com/miekg/dns"
- "golang.org/x/exp/rand"
)
// Handler is a struct that implements [dnsserver.Handler] and forwards DNS
@@ -104,6 +106,10 @@ type HandlerConfig struct {
// metrics. If not set, EmptyMetricsListener is used.
MetricsListener MetricsListener
+ // RandSource is used for randomized upstream selection and other
+ // non-sensitive tasks. If it is nil, [rand.ChaCha8] is used.
+ RandSource rand.Source
+
// HealthcheckDomainTmpl is the template for domains used to perform
// healthcheck queries. If the HealthcheckDomainTmpl contains the string
// "${RANDOM}", all occurrences of this string are replaced with a random
@@ -136,17 +142,21 @@ type HandlerConfig struct {
// check afterwards if c.HealthcheckInitDuration is not zero. Note, that this
// handler only support plain DNS upstreams. c must not be nil.
func NewHandler(c *HandlerConfig) (h *Handler) {
+ src := c.RandSource
+ if src == nil {
+ // Do not initialize through [cmp.Or], as the default value could panic.
+ src = rand.NewChaCha8(agdrand.MustNewSeed())
+ }
+
h = &Handler{
- logger: cmp.Or(c.Logger, slog.Default()),
- rand: rand.New(&rand.LockedSource{}),
+ logger: cmp.Or(c.Logger, slog.Default()),
+ // #nosec G404 -- We don't need a real random, pseudorandom is enough.
+ rand: rand.New(agdrand.NewLockedSource(src)),
activeUpstreamsMu: &sync.RWMutex{},
hcDomainTmpl: c.HealthcheckDomainTmpl,
hcBackoff: c.HealthcheckBackoffDuration,
}
- // #nosec G115 -- The Unix epoch time is highly unlikely to be negative.
- h.rand.Seed(uint64(time.Now().UnixNano()))
-
if l := c.MetricsListener; l != nil {
h.metrics = l
} else {
@@ -230,7 +240,7 @@ func (h *Handler) ServeDNS(
}
if useFallbacks && len(h.fallbacks) > 0 {
- i := h.rand.Intn(len(h.fallbacks))
+ i := h.rand.IntN(len(h.fallbacks))
fallbackUps = h.fallbacks[i]
resp, err = h.exchange(ctx, fallbackUps, req)
}
@@ -268,13 +278,15 @@ func (h *Handler) exchange(
return resp, err
}
-// Refresh implements the [agdservice.Refresher] interface for *Handler.
-//
-// It checks the accessibility of main upstreams and updates handler's list of
-// active upstreams. In case all main upstreams are down, it returns an error
-// and when all requests are redirected to the fallbacks. When any of the main
-// upstreams is detected to be up again, requests are redirected back to the
-// main upstreams.
+// type check
+var _ service.Refresher = (*Handler)(nil)
+
+// Refresh implements the [service.Refresher] interface for *Handler. It checks
+// the accessibility of main upstreams and updates handler's list of active
+// upstreams. In case all main upstreams are down, it returns an error and when
+// all requests are redirected to the fallbacks. When any of the main upstreams
+// is detected to be up again, requests are redirected back to the main
+// upstreams.
func (h *Handler) Refresh(ctx context.Context) (err error) {
h.logger.DebugContext(ctx, "healthcheck refresh started")
defer h.logger.DebugContext(ctx, "healthcheck refresh finished")
@@ -293,7 +305,7 @@ func (h *Handler) pickActiveUpstream() (u Upstream) {
return nil
}
- i := h.rand.Intn(len(h.activeUpstreams))
+ i := h.rand.IntN(len(h.activeUpstreams))
return h.activeUpstreams[i]
}
diff --git a/internal/dnsserver/forward/forward_test.go b/internal/dnsserver/forward/forward_test.go
index 5e9cb4e..3fc4d6f 100644
--- a/internal/dnsserver/forward/forward_test.go
+++ b/internal/dnsserver/forward/forward_test.go
@@ -14,11 +14,6 @@ import (
"github.com/stretchr/testify/require"
)
-func TestMain(m *testing.M) {
- // TODO(a.garipov): Remove after removing golibs/log from dnsserver.
- testutil.DiscardLogOutput(m)
-}
-
// testTimeout is the timeout for tests.
const testTimeout = 1 * time.Second
diff --git a/internal/dnsserver/forward/upstreamplain_test.go b/internal/dnsserver/forward/upstreamplain_test.go
index b3123d6..963839f 100644
--- a/internal/dnsserver/forward/upstreamplain_test.go
+++ b/internal/dnsserver/forward/upstreamplain_test.go
@@ -8,7 +8,6 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/forward"
- "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
@@ -93,7 +92,7 @@ func TestUpstreamPlain_Exchange_truncated(t *testing.T) {
Network: forward.NetworkUDP,
Address: addr,
})
- defer log.OnCloserError(uUDP, log.DEBUG)
+ defer testutil.CleanupAndRequireSuccess(t, uUDP.Close)
res, nw, err := uUDP.Exchange(testutil.ContextWithTimeout(t, testTimeout), req)
require.NoError(t, err)
@@ -106,7 +105,7 @@ func TestUpstreamPlain_Exchange_truncated(t *testing.T) {
Network: forward.NetworkTCP,
Address: addr,
})
- defer log.OnCloserError(uTCP, log.DEBUG)
+ defer testutil.CleanupAndRequireSuccess(t, uTCP.Close)
res, nw, err = uTCP.Exchange(testutil.ContextWithTimeout(t, testTimeout), req)
require.NoError(t, err)
@@ -120,7 +119,7 @@ func TestUpstreamPlain_Exchange_truncated(t *testing.T) {
Network: forward.NetworkAny,
Address: addr,
})
- defer log.OnCloserError(uAny, log.DEBUG)
+ defer testutil.CleanupAndRequireSuccess(t, uAny.Close)
res, nw, err = uAny.Exchange(testutil.ContextWithTimeout(t, testTimeout), req)
require.NoError(t, err)
diff --git a/internal/dnsserver/go.mod b/internal/dnsserver/go.mod
index c3c934a..b2bde61 100644
--- a/internal/dnsserver/go.mod
+++ b/internal/dnsserver/go.mod
@@ -1,22 +1,22 @@
module github.com/AdguardTeam/AdGuardDNS/internal/dnsserver
-go 1.23.4
+go 1.23.6
require (
- github.com/AdguardTeam/golibs v0.30.4
+ github.com/AdguardTeam/golibs v0.32.1
github.com/ameshkov/dnscrypt/v2 v2.3.0
github.com/ameshkov/dnsstamps v1.0.3
github.com/bluele/gcache v0.0.2
github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500
- github.com/miekg/dns v1.1.62
- github.com/panjf2000/ants/v2 v2.10.0
+ github.com/miekg/dns v1.1.63
+ github.com/panjf2000/ants/v2 v2.11.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.2
- github.com/stretchr/testify v1.9.0
- golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
- golang.org/x/net v0.32.0
- golang.org/x/sys v0.28.0
+ github.com/quic-go/quic-go v0.49.0
+ github.com/stretchr/testify v1.10.0
+ golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3
+ golang.org/x/net v0.34.0
+ golang.org/x/sys v0.30.0
)
require (
@@ -26,23 +26,24 @@ 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-20241203143554-1e3fdc7de467 // indirect
+ github.com/google/pprof v0.0.0-20250202011525-fc3143867406 // 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.22.0 // indirect
+ github.com/onsi/ginkgo/v2 v2.22.2 // 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.1 // indirect
+ github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
+ github.com/robfig/cron/v3 v3.0.1 // indirect
go.uber.org/mock v0.5.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
+ golang.org/x/crypto v0.32.0 // indirect
+ golang.org/x/mod v0.23.0 // indirect
+ golang.org/x/sync v0.11.0 // indirect
+ golang.org/x/text v0.22.0 // indirect
+ golang.org/x/time v0.10.0 // indirect
+ golang.org/x/tools v0.29.0 // indirect
+ google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/internal/dnsserver/go.sum b/internal/dnsserver/go.sum
index a7fdf7f..4ad62b7 100644
--- a/internal/dnsserver/go.sum
+++ b/internal/dnsserver/go.sum
@@ -1,4 +1,5 @@
-github.com/AdguardTeam/golibs v0.30.4 h1:zfFX1v4hkOCz6BifkneiBW2PCwSK523kYNr+VwaFrIw=
+github.com/AdguardTeam/golibs v0.32.1 h1:Ajf6Q0k+A9zjFbj8HOzNAbHImrV4JtbT0vwy02D6VeI=
+github.com/AdguardTeam/golibs v0.32.1/go.mod h1:dXRLSsnJQDxOfQVl0ochy1bfk4NgnJQGQdR1YPJdwcw=
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=
@@ -16,7 +17,6 @@ github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500/go.mod h1:S/7n9cop
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
@@ -25,21 +25,24 @@ 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-20241203143554-1e3fdc7de467 h1:keEZFtbLJugfE0qHn+Ge1JCE71spzkchQobDf3mzS/4=
+github.com/google/pprof v0.0.0-20250202011525-fc3143867406 h1:wlQI2cYY0BsWmmPPAnxfQ8SDW0S3Jasn+4B8kXFxprg=
+github.com/google/pprof v0.0.0-20250202011525-fc3143867406/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
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=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
-github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
+github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
+github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
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.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/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
+github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
+github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
+github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
+github.com/panjf2000/ants/v2 v2.11.0 h1:sHrqEwTBQTQ2w6PMvbMfvBtVUuhsaYPzUmAYDLYmJPg=
+github.com/panjf2000/ants/v2 v2.11.0/go.mod h1:V9HhTupTWxcaRmIglJvGwvzqXUTnIZW9uO6q4hAfApw=
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible h1:IWzUvJ72xMjmrjR9q3H1PF+jwdN0uNQiR2t1BLNalyo=
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -48,39 +51,43 @@ 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.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
+github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
+github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
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.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
+github.com/quic-go/quic-go v0.49.0 h1:w5iJHXwHxs1QxyBv1EHKuC50GX5to8mJAxvtnttJp94=
+github.com/quic-go/quic-go v0.49.0/go.mod h1:s2wDnmCdooUQBmQfpUSTCYBl1/D4FcqbULMMkASvR6s=
+github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
+github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
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=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
-github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
-github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.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.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.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=
+golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
+golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
+golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 h1:qNgPs5exUA+G0C96DrPwNrvLSj7GT/9D+3WMWUcUg34=
+golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
+golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
+golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
+golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
+golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
+golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
+golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
+golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
+golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
+golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
+golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
+golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
+google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
+google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/internal/dnsserver/handler.go b/internal/dnsserver/handler.go
new file mode 100644
index 0000000..e197ec5
--- /dev/null
+++ b/internal/dnsserver/handler.go
@@ -0,0 +1,40 @@
+package dnsserver
+
+import (
+ "context"
+
+ "github.com/miekg/dns"
+)
+
+// Handler is an interface that defines how the DNS server would process DNS
+// queries. Inspired by net/http.Server and it's Handler.
+type Handler interface {
+ // ServeDNS processes the request and writes a DNS response to rw. ctx must
+ // contain [*ServerInfo] and [*RequestInfo]. rw and req must not be nil.
+ // req must have exactly one question.
+ ServeDNS(ctx context.Context, rw ResponseWriter, req *dns.Msg) (err error)
+}
+
+// The HandlerFunc type is an adapter to allow the use of ordinary functions
+// as DNS handlers. If f is a function with the appropriate signature,
+// HandlerFunc(f) is a Handler that calls f.
+type HandlerFunc func(context.Context, ResponseWriter, *dns.Msg) (err error)
+
+// type check
+var _ Handler = HandlerFunc(nil)
+
+// ServeDNS implements the [Handler] interface for HandlerFunc.
+func (f HandlerFunc) ServeDNS(ctx context.Context, rw ResponseWriter, req *dns.Msg) (err error) {
+ return f(ctx, rw, req)
+}
+
+// notImplementedHandlerFunc is used if no Handler is configured for a server.
+var notImplementedHandlerFunc HandlerFunc = func(
+ ctx context.Context,
+ w ResponseWriter,
+ r *dns.Msg,
+) (err error) {
+ res := (&dns.Msg{}).SetRcode(r, dns.RcodeNotImplemented)
+
+ return w.WriteMsg(ctx, r, res)
+}
diff --git a/internal/dnsserver/msg.go b/internal/dnsserver/msg.go
index 4fa2e68..a47d4f8 100644
--- a/internal/dnsserver/msg.go
+++ b/internal/dnsserver/msg.go
@@ -18,21 +18,6 @@ func genErrorResponse(req *dns.Msg, code int) (m *dns.Msg) {
return m
}
-// questionData extracts DNS Question data in a safe manner.
-func questionData(m *dns.Msg) (hostname, qType string) {
- if len(m.Question) > 0 {
- q := m.Question[0]
- hostname = q.Name
- if v, ok := dns.TypeToString[q.Qtype]; ok {
- qType = v
- } else {
- qType = fmt.Sprintf("TYPE%d", q.Qtype)
- }
- }
-
- return hostname, qType
-}
-
// packWithPrefix packs a DNS message with a 2-byte prefix with the message
// length by appending it into buf and returns it.
func packWithPrefix(m *dns.Msg, buf []byte) (packed []byte, err error) {
diff --git a/internal/dnsserver/normalize.go b/internal/dnsserver/normalize.go
index 6d4870d..4677b0b 100644
--- a/internal/dnsserver/normalize.go
+++ b/internal/dnsserver/normalize.go
@@ -1,7 +1,7 @@
package dnsserver
import (
- "math/rand"
+ "math/rand/v2"
"github.com/miekg/dns"
)
@@ -144,7 +144,7 @@ func padAnswer(reqOpt, respOpt *dns.OPT) {
// TODO(ameshkov): Consider changing to crypto/rand, need to hold a vote.
// #nosec G404 -- We don't need a real random for a simple padding
- // randomization, pseudo-random is enough.
+ // randomization, pseudorandom is enough.
//
// Note, that we don't check for whether reqOpt.UDPSize() here is smaller
// than resp.Len() + padLen so in theory the padded response may be larger
@@ -152,7 +152,7 @@ func padAnswer(reqOpt, respOpt *dns.OPT) {
// avoiding calling resp.Len().
//
// TODO(ameshkov): Return this check if we optimize resp.Len().
- padLen := rand.Intn(responsePaddingMaxSize-1) + 1
+ padLen := rand.IntN(responsePaddingMaxSize-1) + 1
paddingOpt.Padding = respPadBuf[:padLen:padLen]
}
diff --git a/internal/dnsserver/prometheus/cache.go b/internal/dnsserver/prometheus/cache.go
index 1d102fc..13ee813 100644
--- a/internal/dnsserver/prometheus/cache.go
+++ b/internal/dnsserver/prometheus/cache.go
@@ -2,11 +2,13 @@ package prometheus
import (
"context"
+ "fmt"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/cache"
+ "github.com/AdguardTeam/golibs/container"
+ "github.com/AdguardTeam/golibs/errors"
"github.com/miekg/dns"
"github.com/prometheus/client_golang/prometheus"
- "github.com/prometheus/client_golang/prometheus/promauto"
)
// cacheTypeDefault is a "type" label value for the default LRU cache. In the
@@ -24,31 +26,63 @@ type CacheMetricsListener struct {
// NewCacheMetricsListener returns a new properly initialized
// *CacheMetricsListener. As long as this function registers prometheus
// counters it must be called only once.
-//
-// TODO(a.garipov): Do not use promauto.
-func NewCacheMetricsListener(namespace string) *CacheMetricsListener {
- return &CacheMetricsListener{
- cacheSize: promauto.NewGaugeVec(prometheus.GaugeOpts{
- Name: "size",
+func NewCacheMetricsListener(
+ namespace string,
+ reg prometheus.Registerer,
+) (l *CacheMetricsListener, err error) {
+ const (
+ cacheSize = "size"
+ hitsTotal = "hits_total"
+ missesTotal = "misses_total"
+ )
+
+ l = &CacheMetricsListener{
+ cacheSize: prometheus.NewGaugeVec(prometheus.GaugeOpts{
+ Name: cacheSize,
Namespace: namespace,
Subsystem: subsystemCache,
Help: "The total number items in the cache.",
}, []string{"type"}),
- hitsTotal: promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "hits_total",
+ hitsTotal: prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: hitsTotal,
Namespace: namespace,
Subsystem: subsystemCache,
Help: "The total number of cache hits.",
}, []string{"type"}),
- missesTotal: promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "misses_total",
+ missesTotal: prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: missesTotal,
Namespace: namespace,
Subsystem: subsystemCache,
Help: "The total number of cache misses.",
}, []string{"type"}),
}
+
+ var errs []error
+ collectors := container.KeyValues[string, prometheus.Collector]{{
+ Key: cacheSize,
+ Value: l.cacheSize,
+ }, {
+ Key: hitsTotal,
+ Value: l.hitsTotal,
+ }, {
+ Key: missesTotal,
+ Value: l.missesTotal,
+ }}
+
+ for _, c := range collectors {
+ err = reg.Register(c.Value)
+ if err != nil {
+ errs = append(errs, fmt.Errorf("registering metrics %q: %w", c.Key, err))
+ }
+ }
+
+ if err = errors.Join(errs...); err != nil {
+ return nil, err
+ }
+
+ return l, nil
}
// type check
diff --git a/internal/dnsserver/prometheus/cache_test.go b/internal/dnsserver/prometheus/cache_test.go
index b0aa82b..1057fcb 100644
--- a/internal/dnsserver/prometheus/cache_test.go
+++ b/internal/dnsserver/prometheus/cache_test.go
@@ -8,8 +8,9 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/cache"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
- "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/prometheus"
+ dnssrvprom "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/prometheus"
"github.com/miekg/dns"
+ "github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require"
)
@@ -17,8 +18,13 @@ import (
// normal unit test, we create a cache middleware, emulate a query and then
// check if prom metrics were incremented.
func TestCacheMetricsListener_integration_cache(t *testing.T) {
+ reg := prometheus.NewRegistry()
+ mtrcListener, err := dnssrvprom.NewCacheMetricsListener(testNamespace, reg)
+ require.NoError(t, err)
+
cacheMiddleware := cache.NewMiddleware(&cache.MiddlewareConfig{
- MetricsListener: prometheus.NewCacheMetricsListener(testNamespace),
+ Logger: testLogger,
+ MetricsListener: mtrcListener,
Count: 100,
})
@@ -39,7 +45,7 @@ func TestCacheMetricsListener_integration_cache(t *testing.T) {
req := dnsservertest.CreateMessage(testReqDomain, dns.TypeA)
- err := handlerWithMiddleware.ServeDNS(ctx, nrw, req)
+ err = handlerWithMiddleware.ServeDNS(ctx, nrw, req)
require.NoError(t, err)
dnsservertest.RequireResponse(t, req, nrw.Msg(), 1, dns.RcodeSuccess, false)
}
@@ -47,6 +53,7 @@ func TestCacheMetricsListener_integration_cache(t *testing.T) {
// Now make sure that prometheus metrics were incremented properly.
requireMetrics(
t,
+ reg,
"dns_cache_hits_total",
"dns_cache_misses_total",
"dns_cache_size",
diff --git a/internal/dnsserver/prometheus/forward.go b/internal/dnsserver/prometheus/forward.go
index 59f77ab..01b3cbf 100644
--- a/internal/dnsserver/prometheus/forward.go
+++ b/internal/dnsserver/prometheus/forward.go
@@ -2,15 +2,16 @@ package prometheus
import (
"context"
+ "fmt"
"net"
"sync"
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/forward"
+ "github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/miekg/dns"
"github.com/prometheus/client_golang/prometheus"
- "github.com/prometheus/client_golang/prometheus/promauto"
)
// ForwardMetricsListener implements the [forward.MetricsListener] interface
@@ -33,40 +34,50 @@ type ForwardMetricsListener struct {
// NewForwardMetricsListener returns a properly initialized
// *ForwardMetricsListener expecting to track upsNumHint upstreams. As long as
// this function registers prometheus counters it must be called only once.
-//
-// TODO(a.garipov): Do not use promauto.
-func NewForwardMetricsListener(namespace string, upsNumHint int) (f *ForwardMetricsListener) {
- return &ForwardMetricsListener{
- requestsTotal: promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "request_total",
+func NewForwardMetricsListener(
+ namespace string,
+ reg prometheus.Registerer,
+ upsNumHint int,
+) (f *ForwardMetricsListener, err error) {
+ const (
+ requestsTotal = "request_total"
+ responseRCode = "response_rcode_total"
+ requestDuration = "request_duration_seconds"
+ errorsTotal = "error_total"
+ upstreamStatus = "upstream_status"
+ )
+
+ f = &ForwardMetricsListener{
+ requestsTotal: prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: requestsTotal,
Namespace: namespace,
Subsystem: subsystemForward,
Help: "The number of processed DNS requests.",
}, []string{"to", "network"}),
- responseRCode: promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "response_rcode_total",
+ responseRCode: prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: responseRCode,
Namespace: namespace,
Subsystem: subsystemForward,
Help: "The counter for DNS response codes.",
}, []string{"to", "rcode"}),
- requestDuration: promauto.NewHistogramVec(prometheus.HistogramOpts{
- Name: "request_duration_seconds",
+ requestDuration: prometheus.NewHistogramVec(prometheus.HistogramOpts{
+ Name: requestDuration,
Namespace: namespace,
Subsystem: subsystemForward,
Help: "Time elapsed on processing a DNS query.",
}, []string{"to"}),
- errorsTotal: promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "error_total",
+ errorsTotal: prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: errorsTotal,
Namespace: namespace,
Subsystem: subsystemForward,
Help: "The number of errors occurred when processing a DNS query.",
}, []string{"to", "type"}),
- upstreamStatus: promauto.NewGaugeVec(prometheus.GaugeOpts{
- Name: "upstream_status",
+ upstreamStatus: prometheus.NewGaugeVec(prometheus.GaugeOpts{
+ Name: upstreamStatus,
Namespace: namespace,
Subsystem: subsystemForward,
Help: "Status of the main upstream. 1 is okay, 0 the upstream is backed off",
@@ -76,6 +87,37 @@ func NewForwardMetricsListener(namespace string, upsNumHint int) (f *ForwardMetr
statusGauges: make(map[forward.Upstream]prometheus.Gauge, upsNumHint),
}
+
+ var errs []error
+ collectors := container.KeyValues[string, prometheus.Collector]{{
+ Key: requestsTotal,
+ Value: f.requestsTotal,
+ }, {
+ Key: responseRCode,
+ Value: f.responseRCode,
+ }, {
+ Key: requestDuration,
+ Value: f.requestDuration,
+ }, {
+ Key: errorsTotal,
+ Value: f.errorsTotal,
+ }, {
+ Key: upstreamStatus,
+ Value: f.upstreamStatus,
+ }}
+
+ for _, c := range collectors {
+ err = reg.Register(c.Value)
+ if err != nil {
+ errs = append(errs, fmt.Errorf("registering metrics %q: %w", c.Key, err))
+ }
+ }
+
+ if err = errors.Join(errs...); err != nil {
+ return nil, err
+ }
+
+ return f, nil
}
// type check
diff --git a/internal/dnsserver/prometheus/forward_test.go b/internal/dnsserver/prometheus/forward_test.go
index 5913fa6..58ad5cd 100644
--- a/internal/dnsserver/prometheus/forward_test.go
+++ b/internal/dnsserver/prometheus/forward_test.go
@@ -8,9 +8,9 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/forward"
- "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/prometheus"
- "github.com/AdguardTeam/golibs/logutil/slogutil"
+ dnssrvprom "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/prometheus"
"github.com/miekg/dns"
+ "github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require"
)
@@ -19,15 +19,18 @@ import (
// check if prom metrics were incremented.
func TestForwardMetricsListener_integration_request(t *testing.T) {
srv, addr := dnsservertest.RunDNSServer(t, dnsservertest.NewDefaultHandler())
+ reg := prometheus.NewRegistry()
+ mtrcListener, err := dnssrvprom.NewForwardMetricsListener(testNamespace, reg, 0)
+ require.NoError(t, err)
// Initialize a new forward.Handler and set the metrics listener.
handler := forward.NewHandler(&forward.HandlerConfig{
- Logger: slogutil.NewDiscardLogger(),
+ Logger: testLogger,
UpstreamsAddresses: []*forward.UpstreamPlainConfig{{
Network: forward.NetworkAny,
Address: netip.MustParseAddrPort(addr),
}},
- MetricsListener: prometheus.NewForwardMetricsListener(testNamespace, 0),
+ MetricsListener: mtrcListener,
})
// Prepare a test DNS message and call the handler's ServeDNS function.
@@ -36,12 +39,13 @@ func TestForwardMetricsListener_integration_request(t *testing.T) {
req := dnsservertest.CreateMessage(testReqDomain, dns.TypeA)
rw := dnsserver.NewNonWriterResponseWriter(srv.LocalUDPAddr(), srv.LocalUDPAddr())
- err := handler.ServeDNS(context.Background(), rw, req)
+ err = handler.ServeDNS(context.Background(), rw, req)
require.NoError(t, err)
// Now make sure that prometheus metrics were incremented properly.
requireMetrics(
t,
+ reg,
"dns_forward_request_total",
"dns_forward_request_duration_seconds",
"dns_forward_response_rcode_total",
diff --git a/internal/dnsserver/prometheus/prometheus_test.go b/internal/dnsserver/prometheus/prometheus_test.go
index 3b559aa..431f8dc 100644
--- a/internal/dnsserver/prometheus/prometheus_test.go
+++ b/internal/dnsserver/prometheus/prometheus_test.go
@@ -5,14 +5,13 @@ import (
"testing"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
- "github.com/AdguardTeam/golibs/testutil"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require"
)
-func TestMain(m *testing.M) {
- testutil.DiscardLogOutput(m)
-}
+// testLogger is the common logger for tests.
+var testLogger = slogutil.NewDiscardLogger()
// testNamespace is a test namespace for metrics.
const testNamespace = "dns"
@@ -33,12 +32,12 @@ var testUDPAddr = &net.UDPAddr{
Port: 53,
}
-// requireMetrics accepts a list of metrics names and checks that
-// they exist in the prom registry.
-func requireMetrics(t testing.TB, args ...string) {
+// requireMetrics accepts a list of metrics names and checks that they exist in
+// reg.
+func requireMetrics(t testing.TB, reg *prometheus.Registry, args ...string) {
t.Helper()
- mf, err := prometheus.DefaultGatherer.Gather()
+ mf, err := reg.Gather()
require.NoError(t, err)
require.NotNil(t, mf)
diff --git a/internal/dnsserver/prometheus/ratelimit.go b/internal/dnsserver/prometheus/ratelimit.go
index 5232021..8ff9829 100644
--- a/internal/dnsserver/prometheus/ratelimit.go
+++ b/internal/dnsserver/prometheus/ratelimit.go
@@ -2,13 +2,15 @@ package prometheus
import (
"context"
+ "fmt"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit"
+ "github.com/AdguardTeam/golibs/container"
+ "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/syncutil"
"github.com/miekg/dns"
"github.com/prometheus/client_golang/prometheus"
- "github.com/prometheus/client_golang/prometheus/promauto"
)
// RateLimitMetricsListener implements the [ratelimit.Metrics] interface
@@ -22,24 +24,52 @@ type RateLimitMetricsListener struct {
// *RateLimitMetricsListener. As long as this function registers prometheus
// counters it must be called only once.
//
-// TODO(a.garipov): Do not use promauto.
-func NewRateLimitMetricsListener(namespace string) (l *RateLimitMetricsListener) {
+// TODO(s.chzhen): Use it.
+func NewRateLimitMetricsListener(
+ namespace string,
+ reg prometheus.Registerer,
+) (l *RateLimitMetricsListener, err error) {
+ const (
+ droppedTotalMtrcName = "dropped_total"
+ allowlistedTotalMtrcName = "allowlisted_total"
+ )
+
var (
- droppedTotal = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "dropped_total",
+ droppedTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: droppedTotalMtrcName,
Namespace: namespace,
Subsystem: subsystemRateLimit,
Help: "The total number of rate-limited DNS queries.",
}, []string{"name", "proto", "network", "addr", "type", "family"})
- allowlistedTotal = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "allowlisted_total",
+ allowlistedTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: allowlistedTotalMtrcName,
Namespace: namespace,
Subsystem: subsystemRateLimit,
Help: "The total number of allowlisted DNS queries.",
}, []string{"name", "proto", "network", "addr", "type", "family"})
)
+ var errs []error
+ collectors := container.KeyValues[string, prometheus.Collector]{{
+ Key: droppedTotalMtrcName,
+ Value: droppedTotal,
+ }, {
+ Key: allowlistedTotalMtrcName,
+ Value: allowlistedTotal,
+ }}
+
+ for _, c := range collectors {
+ err = reg.Register(c.Value)
+ if err != nil {
+ errs = append(errs, fmt.Errorf("registering metrics %q: %w", c.Key, err))
+ }
+ }
+
+ if err = errors.Join(errs...); err != nil {
+ return nil, err
+ }
+
return &RateLimitMetricsListener{
dropCounters: syncutil.NewOnceConstructor(
func(k reqLabelMetricKey) (c prometheus.Counter) {
@@ -51,7 +81,7 @@ func NewRateLimitMetricsListener(namespace string) (l *RateLimitMetricsListener)
return k.withLabelValues(allowlistedTotal)
},
),
- }
+ }, nil
}
// type check
diff --git a/internal/dnsserver/prometheus/ratelimit_test.go b/internal/dnsserver/prometheus/ratelimit_test.go
index 97a126c..761a5d5 100644
--- a/internal/dnsserver/prometheus/ratelimit_test.go
+++ b/internal/dnsserver/prometheus/ratelimit_test.go
@@ -8,10 +8,11 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
- "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/prometheus"
+ dnssvcprom "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/prometheus"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit"
"github.com/c2h5oh/datasize"
"github.com/miekg/dns"
+ "github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require"
)
@@ -36,8 +37,13 @@ func TestRateLimiterMetricsListener_integration_cache(t *testing.T) {
IPv6Interval: ivl,
RefuseANY: true,
})
+
+ reg := prometheus.NewRegistry()
+ mtrcListener, err := dnssvcprom.NewRateLimitMetricsListener(testNamespace, reg)
+ require.NoError(t, err)
+
rlMw, err := ratelimit.NewMiddleware(&ratelimit.MiddlewareConfig{
- Metrics: prometheus.NewRateLimitMetricsListener(testNamespace),
+ Metrics: mtrcListener,
RateLimit: rl,
})
require.NoError(t, err)
@@ -68,11 +74,13 @@ func TestRateLimiterMetricsListener_integration_cache(t *testing.T) {
}
// Now make sure that prometheus metrics were incremented properly.
- requireMetrics(t, "dns_ratelimit_dropped_total")
+ requireMetrics(t, reg, "dns_ratelimit_dropped_total")
}
func BenchmarkRateLimitMetricsListener(b *testing.B) {
- l := prometheus.NewRateLimitMetricsListener(testNamespace)
+ reg := prometheus.NewRegistry()
+ l, err := dnssvcprom.NewRateLimitMetricsListener(testNamespace, reg)
+ require.NoError(b, err)
ctx := dnsserver.ContextWithServerInfo(context.Background(), testServerInfo)
req := dnsservertest.CreateMessage(testReqDomain, dns.TypeA)
diff --git a/internal/dnsserver/prometheus/server.go b/internal/dnsserver/prometheus/server.go
index 48aaab7..e37d005 100644
--- a/internal/dnsserver/prometheus/server.go
+++ b/internal/dnsserver/prometheus/server.go
@@ -2,12 +2,14 @@ package prometheus
import (
"context"
+ "fmt"
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
+ "github.com/AdguardTeam/golibs/container"
+ "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/syncutil"
"github.com/prometheus/client_golang/prometheus"
- "github.com/prometheus/client_golang/prometheus/promauto"
)
// ServerMetricsListener implements the [dnsserver.MetricsListener] interface
@@ -55,26 +57,39 @@ func (i srvInfoRCode) withLabelValues(vec *prometheus.CounterVec) (c prometheus.
// NewServerMetricsListener returns a new properly initialized
// *ServerMetricsListener. As long as this function registers prometheus
// counters it must be called only once.
-//
-// TODO(a.garipov): Do not use promauto.
-func NewServerMetricsListener(namespace string) (l *ServerMetricsListener) {
+func NewServerMetricsListener(
+ namespace string,
+ reg prometheus.Registerer,
+) (l *ServerMetricsListener, err error) {
+ const (
+ reqTotalMtrcName = "request_total"
+ reqDurationMtrcName = "request_duration_seconds"
+ reqSizeMtrcName = "request_size_bytes"
+ respSizeMtrcName = "response_size_bytes"
+ respRCodeMtrcName = "response_rcode_total"
+ errTotalMtrcName = "error_total"
+ panicTotalMtrcName = "panic_total"
+ invalidMsgTotalMtrcName = "invalid_msg_total"
+ quicAddrLookupsMtrcName = "quic_addr_validation_lookups"
+ )
+
var (
- requestTotal = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "request_total",
+ requestTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: reqTotalMtrcName,
Namespace: namespace,
Subsystem: subsystemServer,
Help: "The number of processed DNS requests.",
}, []string{"name", "proto", "network", "addr", "type", "family"})
- requestDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{
- Name: "request_duration_seconds",
+ requestDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
+ Name: reqDurationMtrcName,
Namespace: namespace,
Subsystem: subsystemServer,
Help: "Time elapsed on processing a DNS query.",
}, []string{"name", "proto", "addr"})
- requestSize = promauto.NewHistogramVec(prometheus.HistogramOpts{
- Name: "request_size_bytes",
+ requestSize = prometheus.NewHistogramVec(prometheus.HistogramOpts{
+ Name: reqSizeMtrcName,
Namespace: namespace,
Subsystem: subsystemServer,
Help: "Time elapsed on processing a DNS query.",
@@ -83,8 +98,8 @@ func NewServerMetricsListener(namespace string) (l *ServerMetricsListener) {
},
}, []string{"name", "proto", "addr"})
- responseSize = promauto.NewHistogramVec(prometheus.HistogramOpts{
- Name: "response_size_bytes",
+ responseSize = prometheus.NewHistogramVec(prometheus.HistogramOpts{
+ Name: respSizeMtrcName,
Namespace: namespace,
Subsystem: subsystemServer,
Help: "Time elapsed on processing a DNS query.",
@@ -93,42 +108,83 @@ func NewServerMetricsListener(namespace string) (l *ServerMetricsListener) {
},
}, []string{"name", "proto", "addr"})
- responseRCode = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "response_rcode_total",
+ responseRCode = prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: respRCodeMtrcName,
Namespace: namespace,
Subsystem: subsystemServer,
Help: "The counter for DNS response codes.",
}, []string{"name", "proto", "addr", "rcode"})
- errorTotal = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "error_total",
+ errorTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: errTotalMtrcName,
Namespace: namespace,
Subsystem: subsystemServer,
Help: "The number of errors occurred in the DNS server.",
}, []string{"name", "proto", "addr"})
- panicTotal = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "panic_total",
+ panicTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: panicTotalMtrcName,
Namespace: namespace,
Subsystem: subsystemServer,
Help: "The number of panics occurred in the DNS server.",
}, []string{"name", "proto", "addr"})
- invalidMsgTotal = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "invalid_msg_total",
+ invalidMsgTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: invalidMsgTotalMtrcName,
Namespace: namespace,
Subsystem: subsystemServer,
Help: "The number of invalid DNS messages processed by the DNS server.",
}, []string{"name", "proto", "addr"})
+
+ quicAddrValidationCacheLookups = prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: quicAddrLookupsMtrcName,
+ Namespace: namespace,
+ Subsystem: subsystemServer,
+ Help: "The number of QUIC address validation lookups." +
+ "hit=1 means that a cached item was found.",
+ }, []string{"hit"})
)
- quicAddrValidationCacheLookups := promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "quic_addr_validation_lookups",
- Namespace: namespace,
- Subsystem: subsystemServer,
- Help: "The number of QUIC address validation lookups." +
- "hit=1 means that a cached item was found.",
- }, []string{"hit"})
+ var errs []error
+ collectors := container.KeyValues[string, prometheus.Collector]{{
+ Key: reqTotalMtrcName,
+ Value: requestTotal,
+ }, {
+ Key: reqDurationMtrcName,
+ Value: requestDuration,
+ }, {
+ Key: reqSizeMtrcName,
+ Value: requestSize,
+ }, {
+ Key: respSizeMtrcName,
+ Value: responseSize,
+ }, {
+ Key: respRCodeMtrcName,
+ Value: responseRCode,
+ }, {
+ Key: errTotalMtrcName,
+ Value: errorTotal,
+ }, {
+ Key: panicTotalMtrcName,
+ Value: panicTotal,
+ }, {
+ Key: invalidMsgTotalMtrcName,
+ Value: invalidMsgTotal,
+ }, {
+ Key: quicAddrLookupsMtrcName,
+ Value: quicAddrValidationCacheLookups,
+ }}
+
+ for _, c := range collectors {
+ err = reg.Register(c.Value)
+ if err != nil {
+ errs = append(errs, fmt.Errorf("registering metrics %q: %w", c.Key, err))
+ }
+ }
+
+ if err = errors.Join(errs...); err != nil {
+ return nil, err
+ }
return &ServerMetricsListener{
quicAddrValidationCacheLookupsHits: quicAddrValidationCacheLookups.WithLabelValues("1"),
@@ -177,7 +233,7 @@ func NewServerMetricsListener(namespace string) (l *ServerMetricsListener) {
return withSrvInfoLabelValues(responseSize, k)
},
),
- }
+ }, nil
}
// type check
diff --git a/internal/dnsserver/prometheus/server_test.go b/internal/dnsserver/prometheus/server_test.go
index f52a3b1..9e81db9 100644
--- a/internal/dnsserver/prometheus/server_test.go
+++ b/internal/dnsserver/prometheus/server_test.go
@@ -7,9 +7,10 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
- "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/prometheus"
+ dnssvcprom "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/prometheus"
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
+ "github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require"
)
@@ -17,21 +18,26 @@ import (
// normal unit test, we run a test DNS server, send a DNS query, and then
// check that metrics were properly counted.
func TestServerMetricsListener_integration_requestLifetime(t *testing.T) {
+ reg := prometheus.NewRegistry()
+ mtrcListener, err := dnssvcprom.NewServerMetricsListener(testNamespace, reg)
+ require.NoError(t, err)
+
// Initialize the test server and supply the metrics listener. The
// acknowledgment-based protocol TCP is used here to make the test
// less flaky.
- conf := dnsserver.ConfigDNS{
- ConfigBase: dnsserver.ConfigBase{
- Name: "test",
- Addr: "127.0.0.1:0",
- Handler: dnsservertest.NewDefaultHandler(),
- Metrics: prometheus.NewServerMetricsListener(testNamespace),
+ conf := &dnsserver.ConfigDNS{
+ Base: &dnsserver.ConfigBase{
+ BaseLogger: testLogger,
+ Name: "test",
+ Addr: "127.0.0.1:0",
+ Handler: dnsservertest.NewDefaultHandler(),
+ Metrics: mtrcListener,
},
}
srv := dnsserver.NewServerDNS(conf)
// Start the server.
- err := srv.Start(context.Background())
+ err = srv.Start(context.Background())
require.NoError(t, err)
// Make sure the server shuts down in the end.
@@ -58,6 +64,7 @@ func TestServerMetricsListener_integration_requestLifetime(t *testing.T) {
// Now make sure that prometheus metrics were incremented properly.
requireMetrics(
t,
+ reg,
"dns_server_request_total",
"dns_server_request_duration_seconds",
"dns_server_request_size_bytes",
@@ -67,7 +74,9 @@ func TestServerMetricsListener_integration_requestLifetime(t *testing.T) {
}
func BenchmarkServerMetricsListener(b *testing.B) {
- l := prometheus.NewServerMetricsListener(testNamespace)
+ reg := prometheus.NewRegistry()
+ l, err := dnssvcprom.NewServerMetricsListener(testNamespace, reg)
+ require.NoError(b, err)
ctx := dnsserver.ContextWithServerInfo(context.Background(), testServerInfo)
ctx = dnsserver.ContextWithRequestInfo(ctx, &dnsserver.RequestInfo{
diff --git a/internal/dnsserver/querylog/querylog.go b/internal/dnsserver/querylog/querylog.go
index a5af854..cea18e4 100644
--- a/internal/dnsserver/querylog/querylog.go
+++ b/internal/dnsserver/querylog/querylog.go
@@ -6,23 +6,27 @@ import (
"context"
"fmt"
"io"
+ "log/slog"
"strings"
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
- "github.com/AdguardTeam/golibs/log"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/miekg/dns"
)
// LogMiddleware is a simple middleware that prints DNS queries to the log.
// We keep it here to show an example of a middleware.
type LogMiddleware struct {
+ logger *slog.Logger
output io.Writer
}
-// NewLogMiddleware creates a new LogMiddleware with the specified output.
-func NewLogMiddleware(output io.Writer) *LogMiddleware {
+// NewLogMiddleware creates a new LogMiddleware with the specified output. All
+// arguments must not be nil.
+func NewLogMiddleware(output io.Writer, logger *slog.Logger) *LogMiddleware {
return &LogMiddleware{
+ logger: logger,
output: output,
}
}
@@ -30,7 +34,7 @@ func NewLogMiddleware(output io.Writer) *LogMiddleware {
// type check
var _ dnsserver.Middleware = (*LogMiddleware)(nil)
-// Wrap implements the dnsserver.Middleware interface for *LogMiddleware.
+// Wrap implements the [dnsserver.Middleware] interface for *LogMiddleware.
func (l *LogMiddleware) Wrap(h dnsserver.Handler) (wrapped dnsserver.Handler) {
f := func(ctx context.Context, rw dnsserver.ResponseWriter, req *dns.Msg) error {
// Call the next handler and record the response that has been written
@@ -66,14 +70,7 @@ func (l *LogMiddleware) Wrap(h dnsserver.Handler) (wrapped dnsserver.Handler) {
if !ok {
qTypeStr = fmt.Sprintf("TYPE%d", qType)
}
- sb.WriteString(
- fmt.Sprintf("%d %s %s %d ",
- req.Id,
- qTypeStr,
- hostname,
- req.Len(),
- ),
- )
+ sb.WriteString(fmt.Sprintf("%d %s %s %d ", req.Id, qTypeStr, hostname, req.Len()))
// Response data: {rcode} {rsize}
rcode := 0
@@ -93,7 +90,7 @@ func (l *LogMiddleware) Wrap(h dnsserver.Handler) (wrapped dnsserver.Handler) {
// Suppress errors, it's not that important for a query log
_, outErr := l.output.Write([]byte(sb.String()))
if outErr != nil {
- log.Debug("failed to write to the query log: %v", outErr)
+ l.logger.DebugContext(ctx, "writing the query log", slogutil.KeyError, outErr)
}
return err
diff --git a/internal/dnsserver/querylog/querylog_test.go b/internal/dnsserver/querylog/querylog_test.go
index 82ea9d2..e53dc22 100644
--- a/internal/dnsserver/querylog/querylog_test.go
+++ b/internal/dnsserver/querylog/querylog_test.go
@@ -10,6 +10,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/querylog"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/require"
)
@@ -31,7 +32,7 @@ func TestLogMiddleware_Wrap(t *testing.T) {
w := new(bytes.Buffer)
handlerWithMiddlewares := dnsserver.WithMiddlewares(
handler,
- querylog.NewLogMiddleware(w),
+ querylog.NewLogMiddleware(w, slogutil.NewDiscardLogger()),
)
// Create a test DNS request
diff --git a/internal/dnsserver/ratelimit/ratelimit_test.go b/internal/dnsserver/ratelimit/ratelimit_test.go
index adf5f17..a81fca5 100644
--- a/internal/dnsserver/ratelimit/ratelimit_test.go
+++ b/internal/dnsserver/ratelimit/ratelimit_test.go
@@ -10,17 +10,12 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit"
- "github.com/AdguardTeam/golibs/testutil"
"github.com/c2h5oh/datasize"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
-func TestMain(m *testing.M) {
- testutil.DiscardLogOutput(m)
-}
-
func TestRatelimitMiddleware(t *testing.T) {
const (
rps = 10
diff --git a/internal/dnsserver/recorder.go b/internal/dnsserver/recorder.go
deleted file mode 100644
index b90876e..0000000
--- a/internal/dnsserver/recorder.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package dnsserver
-
-import (
- "context"
- "net"
-
- "github.com/AdguardTeam/golibs/errors"
- "github.com/miekg/dns"
-)
-
-// RecorderResponseWriter implements the ResponseWriter interface and simply
-// calls underlying ResponseWriter functions except for the WriteMsg method,
-// which records a clone of the response message that has been written.
-type RecorderResponseWriter struct {
- // rw is the underlying ResponseWriter.
- rw ResponseWriter
-
- // Resp is the response that has been written (if any).
- Resp *dns.Msg
-}
-
-// NewRecorderResponseWriter creates a new instance of RecorderResponseWriter.
-func NewRecorderResponseWriter(rw ResponseWriter) (recw *RecorderResponseWriter) {
- return &RecorderResponseWriter{
- rw: rw,
- }
-}
-
-// type check
-var _ ResponseWriter = (*RecorderResponseWriter)(nil)
-
-// LocalAddr implements the ResponseWriter interface for *RecorderResponseWriter.
-func (r *RecorderResponseWriter) LocalAddr() (addr net.Addr) {
- return r.rw.LocalAddr()
-}
-
-// RemoteAddr implements the ResponseWriter interface for *RecorderResponseWriter.
-func (r *RecorderResponseWriter) RemoteAddr() (addr net.Addr) {
- return r.rw.RemoteAddr()
-}
-
-// WriteMsg implements the ResponseWriter interface for *RecorderResponseWriter.
-func (r *RecorderResponseWriter) WriteMsg(ctx context.Context, req, resp *dns.Msg) (err error) {
- defer func() { err = errors.Annotate(err, "recorder: %w") }()
-
- r.Resp = resp
-
- return r.rw.WriteMsg(ctx, req, resp)
-}
diff --git a/internal/dnsserver/responsewriter.go b/internal/dnsserver/responsewriter.go
new file mode 100644
index 0000000..d48c6d7
--- /dev/null
+++ b/internal/dnsserver/responsewriter.go
@@ -0,0 +1,70 @@
+package dnsserver
+
+import (
+ "context"
+ "net"
+
+ "github.com/AdguardTeam/golibs/errors"
+ "github.com/miekg/dns"
+)
+
+// A ResponseWriter interface is used by a DNS handler to construct a DNS
+// response.
+type ResponseWriter interface {
+ // LocalAddr returns the net.Addr of the server.
+ LocalAddr() net.Addr
+
+ // RemoteAddr returns the net.Addr of the client that sent the current
+ // request.
+ RemoteAddr() net.Addr
+
+ // WriteMsg writes a reply back to the client. Handlers must not modify req
+ // and resp after the call to WriteMsg, since their ResponseWriter
+ // implementation may be a recorder. req and resp must not be nil.
+ //
+ // TODO(a.garipov): Store bytes written to the socket.
+ WriteMsg(ctx context.Context, req, resp *dns.Msg) (err error)
+}
+
+// RecorderResponseWriter implements the [ResponseWriter] interface and simply
+// calls underlying writer's methods except for WriteMsg, which records a clone
+// of the response message that has been written.
+type RecorderResponseWriter struct {
+ // rw is the underlying ResponseWriter.
+ rw ResponseWriter
+
+ // Resp is the response that has been written (if any).
+ Resp *dns.Msg
+}
+
+// NewRecorderResponseWriter creates a new instance of RecorderResponseWriter.
+func NewRecorderResponseWriter(rw ResponseWriter) (recw *RecorderResponseWriter) {
+ return &RecorderResponseWriter{
+ rw: rw,
+ }
+}
+
+// type check
+var _ ResponseWriter = (*RecorderResponseWriter)(nil)
+
+// LocalAddr implements the [ResponseWriter] interface for
+// *RecorderResponseWriter.
+func (r *RecorderResponseWriter) LocalAddr() (addr net.Addr) {
+ return r.rw.LocalAddr()
+}
+
+// RemoteAddr implements the [ResponseWriter] interface for
+// *RecorderResponseWriter.
+func (r *RecorderResponseWriter) RemoteAddr() (addr net.Addr) {
+ return r.rw.RemoteAddr()
+}
+
+// WriteMsg implements the [ResponseWriter] interface for
+// *RecorderResponseWriter.
+func (r *RecorderResponseWriter) WriteMsg(ctx context.Context, req, resp *dns.Msg) (err error) {
+ defer func() { err = errors.Annotate(err, "recorder: %w") }()
+
+ r.Resp = resp
+
+ return r.rw.WriteMsg(ctx, req, resp)
+}
diff --git a/internal/dnsserver/serverbase.go b/internal/dnsserver/serverbase.go
index 5f54f1e..faece17 100644
--- a/internal/dnsserver/serverbase.go
+++ b/internal/dnsserver/serverbase.go
@@ -1,22 +1,41 @@
package dnsserver
import (
+ "cmp"
"context"
+ "fmt"
+ "log/slog"
"net"
"os"
- "runtime/debug"
"sync"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext"
- "github.com/AdguardTeam/golibs/log"
+ "github.com/AdguardTeam/golibs/contextutil"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
+ "github.com/AdguardTeam/golibs/osutil"
+ "github.com/AdguardTeam/golibs/syncutil"
"github.com/miekg/dns"
)
-// ConfigBase contains the necessary minimum that every Server needs to
-// be initialized.
+// ConfigBase contains the necessary minimum that every [Server] needs to be
+// initialized.
+//
+// TODO(a.garipov): Consider splitting and adding appropriate fields to the
+// configs of the separate server types.
type ConfigBase struct {
- // Handler is a handler that processes incoming DNS messages. If not set,
- // the default handler, which returns error response to any query, is used.
+ // BaseLogger is used to create loggers for servers and requests. It should
+ // contain the name of the server. If BaseLogger is nil, [slog.Default] is
+ // used.
+ //
+ // Loggers for requests derived from this logger include the following
+ // fields:
+ // - "qname": the target of the DNS query.
+ // - "qtype": the type of the DNS query.
+ // - "req_id": the 16-bit ID of the message as set by the client.
+ BaseLogger *slog.Logger
+
+ // Handler processes incoming DNS messages. If not set, the default
+ // handler, which returns error responses to all queries, is used.
Handler Handler
// Metrics is the object we use for collecting performance metrics. If not
@@ -24,12 +43,12 @@ type ConfigBase struct {
Metrics MetricsListener
// Disposer is used to help module users reuse parts of DNS responses. If
- // not set, EmptyDisposer is used.
+ // not set, [EmptyDisposer] is used.
Disposer Disposer
- // RequestContext is a ContextConstructor that returns contexts for
- // requests. If not set, the server uses [DefaultContextConstructor].
- RequestContext ContextConstructor
+ // RequestContext is a context constructor that returns contexts for
+ // requests. If not set, the server uses [contextutil.EmptyConstructor].
+ RequestContext contextutil.Constructor
// ListenConfig, when set, is used to set options of connections used by the
// DNS server. If nil, an appropriate default ListenConfig is used.
@@ -42,20 +61,27 @@ type ConfigBase struct {
Network Network
// Name is used for logging, and it may be used for perf counters reporting.
+ // It should not be empty.
Name string
// Addr is the address the server listens to. See [net.Dial] for the
- // documentation on the address format.
+ // documentation on the address format. It must not be empty.
Addr string
}
-// ServerBase implements base methods that every Server implementation uses.
+// ServerBase implements base methods that every [Server] implementation uses.
type ServerBase struct {
+ // baseLogger is the base logger of this server.
+ baseLogger *slog.Logger
+
+ // attrPool is the pool of logging attributes for reuse.
+ attrPool *syncutil.Pool[[]slog.Attr]
+
// handler is a handler that processes incoming DNS messages.
handler Handler
// reqCtx is a function that should return the base context.
- reqCtx ContextConstructor
+ reqCtx contextutil.Constructor
// metrics is the object we use for collecting performance metrics.
metrics MetricsListener
@@ -66,6 +92,9 @@ type ServerBase struct {
// listenConfig is used to set tcpListener and udpListener.
listenConfig netext.ListenConfig
+ // mu protects started, tcpListener, and udpListener.
+ mu *sync.RWMutex
+
// tcpListener is used to accept new TCP connections. It is nil for servers
// that don't use TCP.
tcpListener net.Listener
@@ -74,14 +103,13 @@ type ServerBase struct {
// that don't use UDP.
udpListener net.PacketConn
- // mu protects started, tcpListener, and udpListener.
- mu *sync.RWMutex
-
- // wg tracks active workers (listeners or query processing). Shutdown
- // won't finish until there's at least one active worker.
+ // wg tracks active workers, both listeners and workers processing queries.
+ // Shutdown won't finish until there's at least one active worker.
wg *sync.WaitGroup
// name is used for logging and it may be used for perf counters reporting.
+ //
+ // TODO(a.garipov): Remove eventually.
name string
// addr is the address the server listens to.
@@ -89,84 +117,81 @@ type ServerBase struct {
// network is the network to listen to. It only makes sense for the
// following protocols: [ProtoDNS], [ProtoDNSCrypt], [ProtoDoH].
+ //
+ // TODO(a.garipov): Move into separate servers.
network Network
- // proto is the server protocol.
+ // proto is the protocol of the server.
proto Protocol
+ // started shows if the server has already been started.
started bool
}
// type check
var _ Server = (*ServerBase)(nil)
+// logAttrNum is the number of attributes used by the request loggers
+const logAttrNum = 4
+
// newServerBase creates a new instance of ServerBase and initializes
-// some of its internal properties.
-func newServerBase(proto Protocol, conf ConfigBase) (s *ServerBase) {
- s = &ServerBase{
- handler: conf.Handler,
- reqCtx: conf.RequestContext,
- metrics: conf.Metrics,
- disposer: conf.Disposer,
- listenConfig: conf.ListenConfig,
+// some of its internal properties. proto must be valid. c must not be nil.
+//
+// TODO(a.garipov): Consider either relaxing the requirements, by turning
+// “must” into “should” and returning errors, or validating the configuration
+// contracts explicitly.
+func newServerBase(proto Protocol, c *ConfigBase) (s *ServerBase) {
+ return &ServerBase{
+ baseLogger: cmp.Or(c.BaseLogger, slog.Default()),
+ attrPool: syncutil.NewSlicePool[slog.Attr](logAttrNum),
+ handler: cmp.Or[Handler](c.Handler, notImplementedHandlerFunc),
+ reqCtx: cmp.Or[contextutil.Constructor](
+ c.RequestContext,
+ contextutil.EmptyConstructor{},
+ ),
+ metrics: cmp.Or[MetricsListener](c.Metrics, EmptyMetricsListener{}),
+ disposer: cmp.Or[Disposer](c.Disposer, EmptyDisposer{}),
+ listenConfig: c.ListenConfig,
mu: &sync.RWMutex{},
wg: &sync.WaitGroup{},
- name: conf.Name,
- addr: conf.Addr,
- network: conf.Network,
+ name: c.Name,
+ addr: c.Addr,
+ network: c.Network,
proto: proto,
}
-
- if s.reqCtx == nil {
- s.reqCtx = DefaultContextConstructor{}
- }
-
- if s.metrics == nil {
- s.metrics = &EmptyMetricsListener{}
- }
-
- if s.disposer == nil {
- s.disposer = EmptyDisposer{}
- }
-
- if s.handler == nil {
- s.handler = notImplementedHandlerFunc
- }
-
- return s
}
-// Name implements the [dnsserver.Server] interface for *ServerBase.
+// Name implements the [Server] interface for *ServerBase.
func (s *ServerBase) Name() (name string) {
return s.name
}
-// Proto implements the [dnsserver.Server] interface for *ServerBase.
+// Proto implements the [Server] interface for *ServerBase.
func (s *ServerBase) Proto() (proto Protocol) {
return s.proto
}
-// Network implements the [dnsserver.Server] interface for *ServerBase.
+// Network implements the [Server] interface for *ServerBase.
func (s *ServerBase) Network() (network Network) {
return s.network
}
-// Addr implements the [dnsserver.Server] interface for *ServerBase.
+// Addr implements the [Server] interface for *ServerBase.
func (s *ServerBase) Addr() (addr string) {
return s.addr
}
-// Start implements the [dnsserver.Server] interface for *ServerBase.
+// Start implements the [Server] interface for *ServerBase.
func (s *ServerBase) Start(_ context.Context) (err error) {
panic("*ServerBase must not be used directly")
}
-// Shutdown implements the [dnsserver.Server] interface for *ServerBase.
+// Shutdown implements the [Server] interface for *ServerBase.
func (s *ServerBase) Shutdown(_ context.Context) (err error) {
panic("*ServerBase must not be used directly")
}
-// LocalTCPAddr implements the [dnsserver.Server] interface for *ServerBase.
+// LocalTCPAddr implements the [Server] interface for *ServerBase.
func (s *ServerBase) LocalTCPAddr() (addr net.Addr) {
if s.tcpListener != nil {
return s.tcpListener.Addr()
@@ -175,7 +200,7 @@ func (s *ServerBase) LocalTCPAddr() (addr net.Addr) {
return nil
}
-// LocalUDPAddr implements the [dnsserver.Server] interface for *ServerBase.
+// LocalUDPAddr implements the [Server] interface for *ServerBase.
func (s *ServerBase) LocalUDPAddr() (addr net.Addr) {
if s.udpListener != nil {
return s.udpListener.LocalAddr()
@@ -185,8 +210,10 @@ func (s *ServerBase) LocalUDPAddr() (addr net.Addr) {
}
// requestContext returns a context for one request and adds server information.
-func (s *ServerBase) requestContext() (ctx context.Context, cancel context.CancelFunc) {
- ctx, cancel = s.reqCtx.New()
+func (s *ServerBase) requestContext(
+ parent context.Context,
+) (ctx context.Context, cancel context.CancelFunc) {
+ ctx, cancel = s.reqCtx.New(parent)
ctx = ContextWithServerInfo(ctx, &ServerInfo{
Name: s.name,
Addr: s.addr,
@@ -212,17 +239,26 @@ func (s *ServerBase) serveDNS(ctx context.Context, buf []byte, rw ResponseWriter
}
// serveDNSMsg processes the incoming DNS query and writes the response to the
-// specified ResponseWriter. written is false if no response was written.
+// specified ResponseWriter. req and rw must not be nil. written is false if
+// no response was written.
func (s *ServerBase) serveDNSMsg(
ctx context.Context,
req *dns.Msg,
rw ResponseWriter,
) (written bool) {
- hostname, qType := questionData(req)
- log.Debug("[%d] processing \"%s %s\"", req.Id, qType, hostname)
+ attrsPtr := s.newAttrsSlicePtr(req, rw.RemoteAddr().String())
+ defer s.attrPool.Put(attrsPtr)
+
+ logHdlr := s.baseLogger.Handler().WithAttrs(*attrsPtr)
+ logger := slog.New(logHdlr)
+
+ logger.DebugContext(ctx, "started processing")
+ defer logger.DebugContext(ctx, "finished processing")
+
+ ctx = slogutil.ContextWithLogger(ctx, logger)
recW := NewRecorderResponseWriter(rw)
- s.serveDNSMsgInternal(ctx, req, recW)
+ s.serveDNSMsgInternal(ctx, logger, req, recW)
resp := recW.Resp
written = resp != nil
@@ -241,13 +277,46 @@ func (s *ServerBase) serveDNSMsg(
ResponseSize: respLen,
}, rw)
- log.Debug("[%d]: finished processing \"%s %s\"", req.Id, qType, hostname)
-
s.dispose(rw, resp)
return written
}
+// newAttrsSlicePtr returns a pointer to a slice with the attributes from the
+// DNS request set. Callers should defer returning the slice back to the pool.
+// req must not be nil.
+func (s *ServerBase) newAttrsSlicePtr(req *dns.Msg, raddr string) (attrsPtr *[]slog.Attr) {
+ attrsPtr = s.attrPool.Get()
+
+ attrs := *attrsPtr
+
+ // Optimize bounds checking.
+ _ = attrs[logAttrNum-1]
+
+ qName, qType := questionData(req)
+ attrs[0] = slog.String("qname", qName)
+ attrs[1] = slog.String("qtype", qType)
+ attrs[2] = slog.String("raddr", raddr)
+ attrs[3] = slog.Uint64("req_id", uint64(req.Id))
+
+ return attrsPtr
+}
+
+// questionData extracts DNS Question data in a safe manner. m must not be nil.
+func questionData(m *dns.Msg) (hostname, qType string) {
+ if len(m.Question) > 0 {
+ q := m.Question[0]
+ hostname = q.Name
+ if v, ok := dns.TypeToString[q.Qtype]; ok {
+ qType = v
+ } else {
+ qType = fmt.Sprintf("TYPE%d", q.Qtype)
+ }
+ }
+
+ return hostname, qType
+}
+
// dispose is a helper for disposing a DNS response right after writing it to a
// connection. Disposal of a response is only safe assuming that there is no
// further processing up the stack. Currently, this is only true for plain DNS
@@ -266,25 +335,30 @@ func (s *ServerBase) dispose(rw ResponseWriter, resp *dns.Msg) {
}
// serveDNSMsgInternal serves the DNS request and uses recorder as a
-// ResponseWriter. This method is supposed to be called from serveDNSMsg,
-// the recorded response is used for counting metrics.
+// [ResponseWriter]. This method is supposed to be called from serveDNSMsg, the
+// recorded response is used for counting metrics. logger, req, and rw must not
+// be nil.
+//
+// TODO(a.garipov): Think of a better name or refactor its connections to other
+// methods.
func (s *ServerBase) serveDNSMsgInternal(
ctx context.Context,
+ logger *slog.Logger,
req *dns.Msg,
rw *RecorderResponseWriter,
) {
var resp *dns.Msg
- // Check if we can accept this message
- switch action := s.acceptMsg(req); action {
+ // Check if we can accept this message.
+ switch action, reason := s.acceptMsg(req); action {
case dns.MsgReject:
- log.Debug("[%d] Query format is invalid", req.Id)
+ logger.DebugContext(ctx, "rejected", "reason", reason)
resp = genErrorResponse(req, dns.RcodeFormatError)
case dns.MsgRejectNotImplemented:
- log.Debug("[%d] Rejecting this query", req.Id)
+ logger.DebugContext(ctx, "not implemented", "reason", reason)
resp = genErrorResponse(req, dns.RcodeNotImplemented)
case dns.MsgIgnore:
- log.Debug("[%d] Ignoring this query", req.Id)
+ logger.DebugContext(ctx, "ignoring", "reason", reason)
s.metrics.OnInvalidMsg(ctx)
return
@@ -293,11 +367,10 @@ func (s *ServerBase) serveDNSMsgInternal(
// If resp is not empty at this stage, the request is invalid and we should
// simply exit here.
if resp != nil {
- // Ignore errors and just write the message
- log.Debug("[%d]: writing DNS response code %d", req.Id, resp.Rcode)
+ logger.DebugContext(ctx, "writing response", "rcode", resp.Rcode)
err := rw.WriteMsg(ctx, req, resp)
if err != nil {
- log.Debug("[%d]: error writing a response: %v", req.Id, err)
+ logger.DebugContext(ctx, "error writing reject response", slogutil.KeyError, err)
}
return
@@ -305,7 +378,7 @@ func (s *ServerBase) serveDNSMsgInternal(
err := s.handler.ServeDNS(ctx, rw, req)
if err != nil {
- log.Debug("[%d]: handler returned an error: %s", req.Id, err)
+ logger.DebugContext(ctx, "handler error", slogutil.KeyError, err)
s.metrics.OnError(ctx, err)
resp = genErrorResponse(req, dns.RcodeServerFailure)
@@ -315,7 +388,7 @@ func (s *ServerBase) serveDNSMsgInternal(
err = rw.WriteMsg(ctx, req, resp)
if err != nil {
- log.Debug("[%d]: error writing a response: %s", req.Id, err)
+ logger.DebugContext(ctx, "error writing handler response", slogutil.KeyError, err)
}
}
}
@@ -342,78 +415,82 @@ func addEDE(req, resp *dns.Msg, code uint16, text string) {
})
}
-// acceptMsg checks if we should process the incoming DNS query.
-func (s *ServerBase) acceptMsg(m *dns.Msg) (action dns.MsgAcceptAction) {
- if m.Response {
- log.Debug("[%d]: message rejected since this is a response", m.Id)
-
- return dns.MsgIgnore
+// acceptMsg checks if we should process the incoming DNS query. msg must not be
+// nil.
+func (s *ServerBase) acceptMsg(msg *dns.Msg) (action dns.MsgAcceptAction, reason string) {
+ if msg.Response {
+ return dns.MsgIgnore, "message is a response"
}
- if m.Opcode != dns.OpcodeQuery && m.Opcode != dns.OpcodeNotify {
- log.Debug("[%d]: rejected due to unsupported opcode", m.Opcode)
-
- return dns.MsgRejectNotImplemented
+ if msg.Opcode != dns.OpcodeQuery && msg.Opcode != dns.OpcodeNotify {
+ return dns.MsgRejectNotImplemented, fmt.Sprintf("unsupported opcode %d", msg.Opcode)
}
// There can only be one question in request, unless DNS Cookies are
// involved. See AGDNS-738.
- if len(m.Question) != 1 {
- log.Debug("[%d]: message rejected due to wrong number of questions", m.Id)
-
- return dns.MsgReject
+ if len(msg.Question) != 1 {
+ return dns.MsgReject, "bad number of questions"
}
- // NOTIFY requests can have a SOA in the ANSWER section. See RFC 1996 Section 3.7 and 3.11.
- if len(m.Answer) > 1 {
- log.Debug("[%d]: message rejected due to wrong number of answers", m.Id)
-
- return dns.MsgReject
+ // NOTIFY requests can have a SOA in the ANSWER section. See RFC 1996
+ // Section 3.7 and 3.11.
+ if len(msg.Answer) > 1 {
+ return dns.MsgReject, "bad number of answers"
}
- // IXFR request could have one SOA RR in the NS section. See RFC 1995, section 3.
- if len(m.Ns) > 1 {
- log.Debug("[%d]: message rejected due to wrong number of NS records", m.Id)
-
- return dns.MsgReject
+ // IXFR request could have one SOA RR in the NS section. See RFC 1995,
+ // section 3.
+ if len(msg.Ns) > 1 {
+ return dns.MsgReject, "bad number of ns records"
}
- return dns.MsgAccept
+ return dns.MsgAccept, ""
}
// handlePanicAndExit writes panic info to log, reports it to the registered
-// MetricsListener and calls os.Exit with a positive exit code.
+// [MetricsListener] and calls [os.Exit] with [osutil.ExitCodeFailure].
func (s *ServerBase) handlePanicAndExit(ctx context.Context) {
- if v := recover(); v != nil {
- log.Error(
- "%q(%s://%s): panic encountered, exiting: %v\n%s",
- s.name,
- s.proto,
- s.addr,
- v,
- string(debug.Stack()),
- )
-
- s.metrics.OnPanic(ctx, v)
-
- os.Exit(1)
+ v := recover()
+ if v == nil {
+ return
}
+
+ s.handlePanic(ctx, v)
+
+ os.Exit(osutil.ExitCodeFailure)
+}
+
+// handlePanic is the common panic handler. v should be the recovered value and
+// must not be nil.
+func (s *ServerBase) handlePanic(ctx context.Context, v any) {
+ s.metrics.OnPanic(ctx, v)
+
+ logger, ok := slogutil.LoggerFromContext(ctx)
+ if !ok {
+ logger = s.baseLogger
+ }
+
+ var args []any
+ err, ok := v.(error)
+ if ok {
+ args = []any{slogutil.KeyError, err}
+ } else {
+ args = []any{"value", v}
+ }
+
+ logger.ErrorContext(ctx, "recovered from panic", args...)
+ slogutil.PrintStack(ctx, logger, slog.LevelError)
}
// handlePanicAndRecover writes panic info to log, reports it to the registered
// MetricsListener.
func (s *ServerBase) handlePanicAndRecover(ctx context.Context) {
- if v := recover(); v != nil {
- log.Error(
- "%s %s://%s: panic encountered, recovered: %s\n%s",
- s.name,
- s.addr,
- s.proto,
- v,
- string(debug.Stack()),
- )
- s.metrics.OnPanic(ctx, v)
+ v := recover()
+ if v == nil {
+ return
}
+
+ s.handlePanic(ctx, v)
}
// listenUDP initializes and starts s.udpListener using s.addr. If the TCP
@@ -457,17 +534,18 @@ func (s *ServerBase) listenTCP(ctx context.Context) (err error) {
}
// closeListeners stops UDP and TCP listeners.
-func (s *ServerBase) closeListeners() {
+func (s *ServerBase) closeListeners(ctx context.Context) {
if s.udpListener != nil {
err := s.udpListener.Close()
if err != nil {
- log.Info("[%s]: Failed to close NetworkUDP listener: %v", s.Name(), err)
+ s.baseLogger.InfoContext(ctx, "closing udp listener", slogutil.KeyError, err)
}
}
+
if s.tcpListener != nil {
err := s.tcpListener.Close()
if err != nil {
- log.Info("[%s]: Failed to close NetworkTCP listener: %v", s.Name(), err)
+ s.baseLogger.InfoContext(ctx, "closing tcp listener", slogutil.KeyError, err)
}
}
}
@@ -476,8 +554,9 @@ func (s *ServerBase) closeListeners() {
func (s *ServerBase) waitShutdown(ctx context.Context) (err error) {
// Using this channel to wait until all goroutines finish their work
closed := make(chan struct{})
+
go func() {
- defer log.OnPanic("waitShutdown")
+ defer slogutil.RecoverAndLog(ctx, s.baseLogger)
// wait until all queries are processed
s.wg.Wait()
diff --git a/internal/dnsserver/serverdns.go b/internal/dnsserver/serverdns.go
index 9276f3a..061fca2 100644
--- a/internal/dnsserver/serverdns.go
+++ b/internal/dnsserver/serverdns.go
@@ -10,8 +10,9 @@ import (
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext"
+ "github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
- "github.com/AdguardTeam/golibs/log"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/syncutil"
"github.com/miekg/dns"
"github.com/panjf2000/ants/v2"
@@ -39,14 +40,16 @@ const (
// ConfigDNS is a struct that needs to be passed to NewServerDNS to
// initialize a new ServerDNS instance.
type ConfigDNS struct {
- ConfigBase
+ // Base is the base configuration for this server. It must not be nil and
+ // must be valid.
+ Base *ConfigBase
// ReadTimeout is the net.Conn.SetReadTimeout value for new connections.
- // If not set it defaults to DefaultReadTimeout.
+ // If not set it defaults to [DefaultReadTimeout].
ReadTimeout time.Duration
// WriteTimeout is the net.Conn.SetWriteTimeout value for connections. If
- // not set it defaults to DefaultWriteTimeout.
+ // not set it defaults to [DefaultWriteTimeout].
WriteTimeout time.Duration
// TCPIdleTimeout is the timeout for waiting between multiple queries. If
@@ -68,6 +71,7 @@ type ConfigDNS struct {
TCPSize int
// MaxUDPRespSize is the maximum size of DNS response over UDP protocol.
+ // If not set, [dns.MinMsgSize] is used.
MaxUDPRespSize uint16
// MaxPipelineEnabled, if true, enables TCP pipeline limiting.
@@ -75,6 +79,8 @@ type ConfigDNS struct {
}
// ServerDNS is a plain DNS server (e.g. it supports UDP and TCP protocols).
+//
+// TODO(a.garipov): Consider unembedding ServerBase.
type ServerDNS struct {
*ServerBase
@@ -94,33 +100,41 @@ type ServerDNS struct {
respPool *syncutil.Pool[[]byte]
// tcpConns is a set that is used to track active connections.
- tcpConns map[net.Conn]struct{}
+ tcpConns *container.MapSet[net.Conn]
tcpConnsMu *sync.Mutex
- // TODO(ameshkov, a.garipov): Only save the parameters a server actually
- // needs.
- conf ConfigDNS
+ readTimeout time.Duration
+ tcpIdleTimeout time.Duration
+ writeTimeout time.Duration
+
+ maxPipelineCount uint
+
+ maxUDPRespSize uint16
+
+ maxPipelineEnabled bool
}
// type check
var _ Server = (*ServerDNS)(nil)
-// NewServerDNS creates a new ServerDNS instance.
-func NewServerDNS(conf ConfigDNS) (s *ServerDNS) {
- return newServerDNS(ProtoDNS, conf)
+// NewServerDNS creates a new ServerDNS instance. c must not be nil and must be
+// valid.
+func NewServerDNS(c *ConfigDNS) (s *ServerDNS) {
+ return newServerDNS(ProtoDNS, c)
}
// newServerDNS initializes a new ServerDNS instance with the specified proto.
-// This function is reused in ServerTLS as it is basically a plain DNS-over-TCP
-// server with a TLS layer on top of it.
-func newServerDNS(proto Protocol, conf ConfigDNS) (s *ServerDNS) {
+// This function is reused in [ServerTLS] as it is basically a plain
+// DNS-over-TCP server with a TLS layer on top of it. c must not be nil and
+// must be valid.
+func newServerDNS(proto Protocol, c *ConfigDNS) (s *ServerDNS) {
// Init default settings first.
- conf.ReadTimeout = cmp.Or(conf.ReadTimeout, DefaultReadTimeout)
- conf.WriteTimeout = cmp.Or(conf.WriteTimeout, DefaultWriteTimeout)
- conf.TCPIdleTimeout = cmp.Or(conf.TCPIdleTimeout, DefaultTCPIdleTimeout)
+ c.ReadTimeout = cmp.Or(c.ReadTimeout, DefaultReadTimeout)
+ c.WriteTimeout = cmp.Or(c.WriteTimeout, DefaultWriteTimeout)
+ c.TCPIdleTimeout = cmp.Or(c.TCPIdleTimeout, DefaultTCPIdleTimeout)
// TODO(a.garipov): Return an error instead.
- if t := conf.TCPIdleTimeout; t < 0 || t > MaxTCPIdleTimeout {
+ if t := c.TCPIdleTimeout; t < 0 || t > MaxTCPIdleTimeout {
panic(fmt.Errorf(
"newServerDNS: tcp idle timeout: %w: must be >= 0 and <= %s, got %s",
errors.ErrOutOfRange,
@@ -131,27 +145,34 @@ func newServerDNS(proto Protocol, conf ConfigDNS) (s *ServerDNS) {
// Use dns.MinMsgSize since 99% of DNS queries fit this size, so this is a
// sensible default.
- conf.UDPSize = cmp.Or(conf.UDPSize, dns.MinMsgSize)
- conf.TCPSize = cmp.Or(conf.TCPSize, dns.MinMsgSize)
+ c.UDPSize = cmp.Or(c.UDPSize, dns.MinMsgSize)
+ c.TCPSize = cmp.Or(c.TCPSize, dns.MinMsgSize)
- if conf.ListenConfig == nil {
- conf.ListenConfig = netext.DefaultListenConfigWithOOB(nil)
- }
+ c.Base.ListenConfig = cmp.Or(c.Base.ListenConfig, netext.DefaultListenConfigWithOOB(nil))
s = &ServerDNS{
- ServerBase: newServerBase(proto, conf.ConfigBase),
- workerPool: newPoolNonblocking(),
+ ServerBase: newServerBase(proto, c.Base),
- udpPool: syncutil.NewSlicePool[byte](conf.UDPSize),
- tcpPool: syncutil.NewSlicePool[byte](conf.TCPSize),
+ udpPool: syncutil.NewSlicePool[byte](c.UDPSize),
+ tcpPool: syncutil.NewSlicePool[byte](c.TCPSize),
respPool: syncutil.NewSlicePool[byte](dns.MinMsgSize),
- tcpConns: map[net.Conn]struct{}{},
+ tcpConns: container.NewMapSet[net.Conn](),
tcpConnsMu: &sync.Mutex{},
- conf: conf,
+ readTimeout: c.ReadTimeout,
+ tcpIdleTimeout: c.TCPIdleTimeout,
+ writeTimeout: c.WriteTimeout,
+
+ maxPipelineCount: c.MaxPipelineCount,
+
+ maxUDPRespSize: max(c.MaxUDPRespSize, dns.MinMsgSize),
+
+ maxPipelineEnabled: c.MaxPipelineEnabled,
}
+ s.workerPool = mustNewPoolNonblocking(s.baseLogger)
+
return s
}
@@ -166,7 +187,7 @@ func (s *ServerDNS) Start(ctx context.Context) (err error) {
return ErrServerAlreadyStarted
}
- log.Info("[%s]: Starting the server", s.Name())
+ s.baseLogger.InfoContext(ctx, "starting server")
ctx = ContextWithServerInfo(ctx, &ServerInfo{
Name: s.name,
@@ -202,7 +223,7 @@ func (s *ServerDNS) Start(ctx context.Context) (err error) {
s.started = true
- log.Info("[%s]: Server has been started", s.Name())
+ s.baseLogger.InfoContext(ctx, "server has been started")
return nil
}
@@ -211,20 +232,22 @@ func (s *ServerDNS) Start(ctx context.Context) (err error) {
func (s *ServerDNS) Shutdown(ctx context.Context) (err error) {
defer func() { err = errors.Annotate(err, "shutting down dns server: %w") }()
- err = s.shutdown()
+ s.baseLogger.InfoContext(ctx, "shutting down server")
+
+ err = s.shutdown(ctx)
if err != nil {
- log.Info("[%s]: Failed to shutdown: %v", s.Name(), err)
+ s.baseLogger.WarnContext(ctx, "error while shutting down", slogutil.KeyError, err)
return err
}
- s.unblockTCPConns()
+ s.unblockTCPConns(ctx)
err = s.waitShutdown(ctx)
// Close the workerPool and releases all workers.
s.workerPool.Release()
- log.Info("[%s]: Finished stopping the server", s.Name())
+ s.baseLogger.InfoContext(ctx, "server has been shut down")
return err
}
@@ -236,10 +259,11 @@ func (s *ServerDNS) startServeUDP(ctx context.Context) {
defer s.handlePanicAndExit(ctx)
defer s.wg.Done()
- log.Info("[%s]: Start listening to udp://%s", s.Name(), s.Addr())
+ s.baseLogger.InfoContext(ctx, "starting listening udp")
+
err := s.serveUDP(ctx, s.udpListener)
if err != nil {
- log.Info("[%s]: Finished listening to udp://%s due to %v", s.Name(), s.Addr(), err)
+ s.baseLogger.WarnContext(ctx, "listening udp failed", slogutil.KeyError, err)
}
}
@@ -250,15 +274,15 @@ func (s *ServerDNS) startServeTCP(ctx context.Context) {
defer s.handlePanicAndExit(ctx)
defer s.wg.Done()
- log.Info("[%s]: Start listening to tcp://%s", s.Name(), s.Addr())
+ s.baseLogger.InfoContext(ctx, "starting listening tcp")
err := s.serveTCP(ctx, s.tcpListener)
if err != nil {
- log.Info("[%s]: Finished listening to tcp://%s due to %v", s.Name(), s.Addr(), err)
+ s.baseLogger.WarnContext(ctx, "listening tcp failed", slogutil.KeyError, err)
}
}
// shutdown marks the server as stopped and closes active listeners.
-func (s *ServerDNS) shutdown() (err error) {
+func (s *ServerDNS) shutdown(ctx context.Context) (err error) {
s.mu.Lock()
defer s.mu.Unlock()
@@ -270,21 +294,24 @@ func (s *ServerDNS) shutdown() (err error) {
s.started = false
// Now close all listeners.
- s.closeListeners()
+ s.closeListeners(ctx)
return nil
}
// unblockTCPConns unblocks reads for all active TCP connections.
-func (s *ServerDNS) unblockTCPConns() {
+func (s *ServerDNS) unblockTCPConns(ctx context.Context) {
s.tcpConnsMu.Lock()
defer s.tcpConnsMu.Unlock()
- for conn := range s.tcpConns {
+
+ s.tcpConns.Range(func(conn net.Conn) (cont bool) {
err := conn.SetReadDeadline(time.Unix(1, 0))
if err != nil {
- log.Debug("[%s]: Failed to set read deadline: %v", s.Name(), err)
+ s.baseLogger.WarnContext(ctx, "failed to unblock conn", slogutil.KeyError, err)
}
- }
+
+ return true
+ })
}
// writeDeadlineSetter is an interface for connections that can set write
@@ -306,6 +333,11 @@ func withWriteDeadline(
// sooner.
ctx, cancel := context.WithTimeout(ctx, timeout)
+ logger, ok := slogutil.LoggerFromContext(ctx)
+ if !ok {
+ logger = slogutil.NewDiscardLogger()
+ }
+
defer func() {
cancel()
@@ -313,7 +345,7 @@ func withWriteDeadline(
if err != nil && !errors.Is(err, net.ErrClosed) {
// Consider deadline errors non-critical. Ignore [net.ErrClosed] as
// it is expected to happen when the client closes connections.
- log.Error("dnsserver: removing deadlines: %s", err)
+ logger.WarnContext(ctx, "removing deadlines", slogutil.KeyError, err)
}
}()
@@ -324,7 +356,7 @@ func withWriteDeadline(
if err != nil && !errors.Is(err, net.ErrClosed) {
// Consider deadline errors non-critical. Ignore [net.ErrClosed] as it
// is expected to happen when the client closes connections.
- log.Error("dnsserver: setting deadlines: %s", err)
+ logger.WarnContext(ctx, "setting deadlines", slogutil.KeyError, err)
}
f()
diff --git a/internal/dnsserver/serverdns_test.go b/internal/dnsserver/serverdns_test.go
index 0a7634c..cb6357a 100644
--- a/internal/dnsserver/serverdns_test.go
+++ b/internal/dnsserver/serverdns_test.go
@@ -12,7 +12,6 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
"github.com/AdguardTeam/golibs/errors"
- "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
@@ -376,7 +375,7 @@ func TestServerDNS_integration_udpMsgIgnore(t *testing.T) {
conn, err := net.Dial("udp", addr)
require.Nil(t, err)
- defer log.OnCloserError(conn, log.DEBUG)
+ testutil.CleanupAndRequireSuccess(t, conn.Close)
// Write some crap
_, err = conn.Write([]byte{1, 3, 1, 52, 12, 5, 32, 12})
@@ -473,7 +472,7 @@ func TestServerDNS_integration_tcpMsgIgnore(t *testing.T) {
conn, err := net.Dial("tcp", addr)
require.Nil(t, err)
- defer log.OnCloserError(conn, log.DEBUG)
+ testutil.CleanupAndRequireSuccess(t, conn.Close)
// Write the invalid request
_, err = conn.Write(tc.buf)
diff --git a/internal/dnsserver/serverdnscrypt.go b/internal/dnsserver/serverdnscrypt.go
index 98535a3..9cc5deb 100644
--- a/internal/dnsserver/serverdnscrypt.go
+++ b/internal/dnsserver/serverdnscrypt.go
@@ -1,6 +1,7 @@
package dnsserver
import (
+ "cmp"
"context"
"fmt"
"net"
@@ -8,7 +9,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext"
"github.com/AdguardTeam/golibs/errors"
- "github.com/AdguardTeam/golibs/log"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/ameshkov/dnscrypt/v2"
"github.com/miekg/dns"
)
@@ -16,36 +17,41 @@ import (
// ConfigDNSCrypt is a struct that needs to be passed to NewServerDNSCrypt to
// initialize a new ServerDNSCrypt instance.
type ConfigDNSCrypt struct {
- ConfigBase
+ // Base is the base configuration for this server. It must not be nil
+ // and must be valid.
+ Base *ConfigBase
- // DNSCryptResolverCert is a DNSCrypt server certificate.
- DNSCryptResolverCert *dnscrypt.Cert
+ // ResolverCert is a DNSCrypt server certificate. It must not be nil.
+ ResolverCert *dnscrypt.Cert
- // DNSCryptProviderName is a DNSCrypt provider name (see DNSCrypt spec).
- DNSCryptProviderName string
+ // ProviderName is a DNSCrypt provider name, see DNSCrypt spec. It must not
+ // be empty.
+ ProviderName string
}
// ServerDNSCrypt is a DNSCrypt server implementation.
+//
+// TODO(a.garipov): Consider unembedding ServerBase.
type ServerDNSCrypt struct {
*ServerBase
- dnsCryptServer *dnscrypt.Server
-
- conf ConfigDNSCrypt
+ server *dnscrypt.Server
+ resolverCert *dnscrypt.Cert
+ providerName string
}
// type check
var _ Server = (*ServerDNSCrypt)(nil)
-// NewServerDNSCrypt creates a new instance of ServerDNSCrypt.
-func NewServerDNSCrypt(conf ConfigDNSCrypt) (s *ServerDNSCrypt) {
- if conf.ListenConfig == nil {
- conf.ListenConfig = netext.DefaultListenConfig(nil)
- }
+// NewServerDNSCrypt creates a new instance of ServerDNSCrypt. c must not be
+// nil and must be valid.
+func NewServerDNSCrypt(c *ConfigDNSCrypt) (s *ServerDNSCrypt) {
+ c.Base.ListenConfig = cmp.Or(c.Base.ListenConfig, netext.DefaultListenConfig(nil))
return &ServerDNSCrypt{
- ServerBase: newServerBase(ProtoDNSCrypt, conf.ConfigBase),
- conf: conf,
+ ServerBase: newServerBase(ProtoDNSCrypt, c.Base),
+ resolverCert: c.ResolverCert,
+ providerName: c.ProviderName,
}
}
@@ -65,7 +71,7 @@ func (s *ServerDNSCrypt) Start(ctx context.Context) (err error) {
return ErrServerAlreadyStarted
}
- log.Info("[%s]: Starting the server", s.Name())
+ s.baseLogger.InfoContext(ctx, "starting server")
ctx = ContextWithServerInfo(ctx, &ServerInfo{
Name: s.name,
@@ -74,9 +80,9 @@ func (s *ServerDNSCrypt) Start(ctx context.Context) (err error) {
})
// Create DNSCrypt server with a handler
- s.dnsCryptServer = &dnscrypt.Server{
- ProviderName: s.conf.DNSCryptProviderName,
- ResolverCert: s.conf.DNSCryptResolverCert,
+ s.server = &dnscrypt.Server{
+ ProviderName: s.providerName,
+ ResolverCert: s.resolverCert,
Handler: &dnsCryptHandler{
srv: s,
},
@@ -89,7 +95,7 @@ func (s *ServerDNSCrypt) Start(ctx context.Context) (err error) {
s.started = true
- log.Info("[%s]: Server has been started", s.Name())
+ s.baseLogger.InfoContext(ctx, "server has been started")
return nil
}
@@ -98,16 +104,18 @@ func (s *ServerDNSCrypt) Start(ctx context.Context) (err error) {
func (s *ServerDNSCrypt) Shutdown(ctx context.Context) (err error) {
defer func() { err = errors.Annotate(err, "shutting down dnscrypt server: %w") }()
- log.Info("[%s]: Stopping the server", s.Name())
- err = s.shutdown()
+ s.baseLogger.InfoContext(ctx, "shutting down server")
+
+ err = s.shutdown(ctx)
if err != nil {
- log.Info("[%s]: Failed to shutdown: %v", s.Name(), err)
+ s.baseLogger.WarnContext(ctx, "error while shutting down", slogutil.KeyError, err)
return err
}
- err = s.dnsCryptServer.Shutdown(ctx)
- log.Info("[%s]: Finished stopping the server", s.Name())
+ err = s.server.Shutdown(ctx)
+
+ s.baseLogger.InfoContext(ctx, "server has been shut down")
return err
}
@@ -133,7 +141,7 @@ func (s *ServerDNSCrypt) startServe(ctx context.Context) (err error) {
}
if len(errs) > 0 {
- s.closeListeners()
+ s.closeListeners(ctx)
return fmt.Errorf("creating listeners: %w", errors.Join(errs...))
}
@@ -150,7 +158,7 @@ func (s *ServerDNSCrypt) startServeUDP(ctx context.Context) {
// the application won't be able to continue listening to DoT.
defer s.handlePanicAndExit(ctx)
- log.Info("[%s]: Start listening to udp://%s", s.Name(), s.Addr())
+ s.baseLogger.InfoContext(ctx, "starting listening udp")
// TODO(ameshkov): Add context to the ServeTCP and ServeUDP methods in
// dnscrypt/v3. Or at least add ServeTCPContext and ServeUDPContext
@@ -158,9 +166,9 @@ func (s *ServerDNSCrypt) startServeUDP(ctx context.Context) {
//
// TODO(ameshkov): Redo the dnscrypt module to make it not depend on
// *net.UDPConn and use net.PacketConn instead.
- err := s.dnsCryptServer.ServeUDP(s.udpListener.(*net.UDPConn))
+ err := s.server.ServeUDP(s.udpListener.(*net.UDPConn))
if err != nil {
- log.Info("[%s]: Finished listening to udp://%s due to %v", s.Name(), s.Addr(), err)
+ s.baseLogger.WarnContext(ctx, "listening udp failed", slogutil.KeyError, err)
}
}
@@ -170,19 +178,19 @@ func (s *ServerDNSCrypt) startServeTCP(ctx context.Context) {
// the application won't be able to continue listening to DoT.
defer s.handlePanicAndExit(ctx)
- log.Info("[%s]: Start listening to tcp://%s", s.Name(), s.Addr())
+ s.baseLogger.InfoContext(ctx, "starting listening tcp")
// TODO(ameshkov): Add context to the ServeTCP and ServeUDP methods in
// dnscrypt/v3. Or at least add ServeTCPContext and ServeUDPContext
// methods for now.
- err := s.dnsCryptServer.ServeTCP(s.tcpListener)
+ err := s.server.ServeTCP(s.tcpListener)
if err != nil {
- log.Info("[%s]: Finished listening to tcp://%s due to %v", s.Name(), s.Addr(), err)
+ s.baseLogger.WarnContext(ctx, "listening tcp failed", slogutil.KeyError, err)
}
}
// shutdown marks the server as stopped and closes active listeners.
-func (s *ServerDNSCrypt) shutdown() (err error) {
+func (s *ServerDNSCrypt) shutdown(ctx context.Context) (err error) {
s.mu.Lock()
defer s.mu.Unlock()
@@ -194,7 +202,7 @@ func (s *ServerDNSCrypt) shutdown() (err error) {
s.started = false
// Now close all listeners
- s.closeListeners()
+ s.closeListeners(ctx)
return nil
}
@@ -212,7 +220,7 @@ func (h *dnsCryptHandler) ServeDNS(rw dnscrypt.ResponseWriter, r *dns.Msg) (err
defer func() { err = errors.Annotate(err, "dnscrypt: %w") }()
// TODO(ameshkov): Use the context from the arguments once it's added there.
- ctx, cancel := h.srv.requestContext()
+ ctx, cancel := h.srv.requestContext(context.Background())
defer cancel()
ctx = ContextWithRequestInfo(ctx, &RequestInfo{StartTime: time.Now()})
diff --git a/internal/dnsserver/serverdnscrypt_test.go b/internal/dnsserver/serverdnscrypt_test.go
index 3365b69..e019951 100644
--- a/internal/dnsserver/serverdnscrypt_test.go
+++ b/internal/dnsserver/serverdnscrypt_test.go
@@ -6,12 +6,19 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
+ "github.com/AdguardTeam/golibs/testutil"
"github.com/ameshkov/dnscrypt/v2"
"github.com/ameshkov/dnsstamps"
"github.com/miekg/dns"
"github.com/stretchr/testify/require"
)
+// TODO(a.garipov): Remove when https://github.com/ameshkov/dnscrypt/issues/26
+// is fixed.
+func TestMain(m *testing.M) {
+ testutil.DiscardLogOutput(m)
+}
+
func TestServerDNSCrypt_integration_query(t *testing.T) {
testCases := []struct {
handler dnsserver.Handler
diff --git a/internal/dnsserver/serverdnstcp.go b/internal/dnsserver/serverdnstcp.go
index b8411e4..1596c71 100644
--- a/internal/dnsserver/serverdnstcp.go
+++ b/internal/dnsserver/serverdnstcp.go
@@ -6,20 +6,21 @@ import (
"encoding/binary"
"fmt"
"io"
+ "log/slog"
"net"
"slices"
"sync"
"time"
"github.com/AdguardTeam/golibs/errors"
- "github.com/AdguardTeam/golibs/log"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/syncutil"
"github.com/miekg/dns"
)
// serveTCP runs the TCP serving loop.
func (s *ServerDNS) serveTCP(ctx context.Context, l net.Listener) (err error) {
- defer log.OnCloserError(l, log.DEBUG)
+ defer func() { closeWithLog(ctx, s.baseLogger, "closing tcp listener", l) }()
for s.isStarted() {
err = s.acceptTCPConn(ctx, l)
@@ -56,7 +57,7 @@ func (s *ServerDNS) acceptTCPConn(ctx context.Context, l net.Listener) (err erro
defer s.tcpConnsMu.Unlock()
// Track the connection to allow unblocking reads on shutdown.
- s.tcpConns[conn] = struct{}{}
+ s.tcpConns.Add(conn)
}()
s.wg.Add(1)
@@ -99,30 +100,30 @@ func (s *ServerDNS) serveTCPConn(ctx context.Context, conn net.Conn) {
wg.Wait()
- log.OnCloserError(conn, log.DEBUG)
+ closeWithLog(ctx, s.baseLogger, "closing tcp conn", conn)
s.tcpConnsMu.Lock()
defer s.tcpConnsMu.Unlock()
- delete(s.tcpConns, conn)
+ s.tcpConns.Delete(conn)
}()
defer s.handlePanicAndRecover(ctx)
var msgSema syncutil.Semaphore = syncutil.EmptySemaphore{}
- if s.conf.MaxPipelineEnabled {
- msgSema = syncutil.NewChanSemaphore(s.conf.MaxPipelineCount)
+ if s.maxPipelineEnabled {
+ msgSema = syncutil.NewChanSemaphore(s.maxPipelineCount)
}
// writeMu serializes write deadline setting and writing to conn.
writeMu := &sync.Mutex{}
- timeout := s.conf.ReadTimeout
- idleTimeout := s.conf.TCPIdleTimeout
+ timeout := s.readTimeout
+ idleTimeout := s.tcpIdleTimeout
err := handshake(conn, timeout)
if err != nil {
- s.logReadErr("handshaking", err)
+ s.logReadErr(ctx, "handshaking", err)
return
}
@@ -130,7 +131,7 @@ func (s *ServerDNS) serveTCPConn(ctx context.Context, conn net.Conn) {
for s.isStarted() {
err = s.acceptTCPMsg(conn, wg, writeMu, timeout, msgSema)
if err != nil {
- s.logReadErr("reading from conn", err)
+ s.logReadErr(ctx, "reading from conn", err)
return
}
@@ -142,12 +143,12 @@ func (s *ServerDNS) serveTCPConn(ctx context.Context, conn net.Conn) {
// logReadErr logs err on debug level unless it's trivial ([io.EOF] or
// [net.ErrClosed]).
-func (s *ServerDNS) logReadErr(msg string, err error) {
+func (s *ServerDNS) logReadErr(ctx context.Context, msg string, err error) {
if errors.Is(err, io.EOF) || errors.Is(err, net.ErrClosed) {
return
}
- log.Debug("[%s]: %s: %s", s.Name(), msg, err)
+ s.baseLogger.DebugContext(ctx, msg, slogutil.KeyError, err)
}
// acceptTCPMsg reads and starts processing a single TCP message. If conn is a
@@ -171,7 +172,7 @@ func (s *ServerDNS) acceptTCPMsg(
ri.TLSServerName = cs.ConnectionState().ServerName
}
- reqCtx, reqCancel := s.requestContext()
+ reqCtx, reqCancel := s.requestContext(context.Background())
reqCtx = ContextWithRequestInfo(reqCtx, ri)
err = msgSema.Acquire(reqCtx)
@@ -213,8 +214,8 @@ func (s *ServerDNS) serveTCPMessage(
respPool: s.respPool,
writeMu: writeMu,
conn: conn,
- writeTimeout: s.conf.WriteTimeout,
- idleTimeout: s.conf.TCPIdleTimeout,
+ writeTimeout: s.writeTimeout,
+ idleTimeout: s.tcpIdleTimeout,
}
written := s.serveDNS(ctx, buf, rw)
@@ -223,7 +224,7 @@ func (s *ServerDNS) serveTCPMessage(
// avoid hanging connections. Than might happen if the handler
// rate-limited connections or if we received garbage data instead of
// a DNS query.
- log.OnCloserError(conn, log.DEBUG)
+ slogutil.CloseAndLog(ctx, s.baseLogger, conn, slog.LevelDebug)
}
}
diff --git a/internal/dnsserver/serverdnsudp.go b/internal/dnsserver/serverdnsudp.go
index e334e91..091a747 100644
--- a/internal/dnsserver/serverdnsudp.go
+++ b/internal/dnsserver/serverdnsudp.go
@@ -8,14 +8,13 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext"
"github.com/AdguardTeam/golibs/errors"
- "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/syncutil"
"github.com/miekg/dns"
)
// serveUDP runs the UDP serving loop.
func (s *ServerDNS) serveUDP(ctx context.Context, conn net.PacketConn) (err error) {
- defer log.OnCloserError(conn, log.DEBUG)
+ defer func() { closeWithLog(ctx, s.baseLogger, "closing udp conn", conn) }()
for s.isStarted() {
err = s.acceptUDPMsg(ctx, conn)
@@ -52,7 +51,7 @@ func (s *ServerDNS) acceptUDPMsg(ctx context.Context, conn net.PacketConn) (err
s.wg.Add(1)
// Save the start time here, but create the context inside the goroutine,
- // since s.reqCtx.New can be slow.
+ // since s.requestContext can be slow.
//
// TODO(a.garipov): The slowness is likely due to constant reallocation of
// timers in [context.WithTimeout]. Consider creating an optimized reusable
@@ -60,7 +59,7 @@ func (s *ServerDNS) acceptUDPMsg(ctx context.Context, conn net.PacketConn) (err
startTime := time.Now()
return s.workerPool.Submit(func() {
- reqCtx, reqCancel := s.requestContext()
+ reqCtx, reqCancel := s.requestContext(context.Background())
defer reqCancel()
reqCtx = ContextWithRequestInfo(reqCtx, &RequestInfo{
@@ -86,8 +85,8 @@ func (s *ServerDNS) serveUDPPacket(
respPool: s.respPool,
udpSession: sess,
conn: conn,
- writeTimeout: s.conf.WriteTimeout,
- maxRespSize: s.conf.MaxUDPRespSize,
+ writeTimeout: s.writeTimeout,
+ maxRespSize: s.maxUDPRespSize,
}
s.serveDNS(ctx, buf, rw)
}
@@ -98,7 +97,7 @@ func (s *ServerDNS) readUDPMsg(
conn net.PacketConn,
buf []byte,
) (n int, sess netext.PacketSession, err error) {
- err = conn.SetReadDeadline(time.Now().Add(s.conf.ReadTimeout))
+ err = conn.SetReadDeadline(time.Now().Add(s.readTimeout))
if err != nil {
return 0, nil, err
}
diff --git a/internal/dnsserver/serverhttps.go b/internal/dnsserver/serverhttps.go
index 81427e2..c661118 100644
--- a/internal/dnsserver/serverhttps.go
+++ b/internal/dnsserver/serverhttps.go
@@ -1,11 +1,13 @@
package dnsserver
import (
+ "cmp"
"context"
"crypto/tls"
"encoding/base64"
"fmt"
"io"
+ "log/slog"
"net"
"net/http"
"net/url"
@@ -17,7 +19,8 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/httphdr"
- "github.com/AdguardTeam/golibs/log"
+ "github.com/AdguardTeam/golibs/ioutil"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/miekg/dns"
@@ -55,13 +58,13 @@ 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
-// enabled or not by specifying [ConfigBase.Network]. By default, the server
-// will listen to both HTTP/2 and HTTP/3, but if you set it to NetworkTCP, the
-// server will only use HTTP/2 and NetworkUDP will mean HTTP/3 only.
+// enabled or not by specifying [Base.Network]. By default, the server will
+// listen to both HTTP/2 and HTTP/3, but if you set it to NetworkTCP, the server
+// will only use HTTP/2 and NetworkUDP will mean HTTP/3 only.
type ConfigHTTPS struct {
// 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].
+ // [Base.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
@@ -72,10 +75,12 @@ type ConfigHTTPS struct {
// If it is empty, the server will return 404 for requests like that.
NonDNSHandler http.Handler
- ConfigBase
+ // Base is the base configuration for this server. It must not be nil and
+ // must be valid.
+ Base *ConfigBase
// MaxStreamsPerPeer is the maximum number of concurrent streams that a peer
- // is allowed to open.
+ // is allowed to open. If not set, 100 is used.
MaxStreamsPerPeer int
// QUICLimitsEnabled, if true, enables QUIC limiting.
@@ -86,6 +91,8 @@ type ConfigHTTPS struct {
// and DNS JSON format. Regular DoH (wireformat) will be available at the
// /dns-query location. JSON format will be available at the "/resolve"
// location.
+//
+// TODO(a.garipov): Consider unembedding ServerBase.
type ServerHTTPS struct {
*ServerBase
@@ -103,26 +110,36 @@ type ServerHTTPS struct {
// quicTransport is saved here to close it later.
quicTransport *quic.Transport
- conf ConfigHTTPS
+ tlsConfDefault *tls.Config
+ tlsConfH3 *tls.Config
+
+ nonDNSHandler http.Handler
+
+ maxStreamsPerPeer int
+
+ quicLimitsEnabled bool
}
// type check
var _ Server = (*ServerHTTPS)(nil)
-// NewServerHTTPS creates a new ServerHTTPS instance.
-func NewServerHTTPS(conf ConfigHTTPS) (s *ServerHTTPS) {
- if conf.ListenConfig == nil {
- // Do not enable OOB here, because ListenPacket is only used by HTTP/3,
- // and quic-go sets the necessary flags.
- conf.ListenConfig = netext.DefaultListenConfig(nil)
- }
+// NewServerHTTPS creates a new ServerHTTPS instance. c must not be nil and
+// must be valid.
+func NewServerHTTPS(c *ConfigHTTPS) (s *ServerHTTPS) {
+ // Do not enable OOB here, because ListenPacket is only used by HTTP/3, and
+ // quic-go sets the necessary flags.
+ c.Base.ListenConfig = cmp.Or(c.Base.ListenConfig, netext.DefaultListenConfig(nil))
- s = &ServerHTTPS{
- ServerBase: newServerBase(ProtoDoH, conf.ConfigBase),
- conf: conf,
+ return &ServerHTTPS{
+ ServerBase: newServerBase(ProtoDoH, c.Base),
+ tlsConfDefault: c.TLSConfDefault,
+ tlsConfH3: c.TLSConfH3,
+ nonDNSHandler: c.NonDNSHandler,
+ // NOTE: 100 is the current default in package quic, but set it
+ // explicitly in case that changes in the future.
+ maxStreamsPerPeer: cmp.Or(c.MaxStreamsPerPeer, 100),
+ quicLimitsEnabled: c.QUICLimitsEnabled,
}
-
- return s
}
// Start implements the dnsserver.Server interface for *ServerHTTPS.
@@ -136,7 +153,7 @@ func (s *ServerHTTPS) Start(ctx context.Context) (err error) {
return ErrServerAlreadyStarted
}
- log.Info("[%s]: Starting the server", s.addr)
+ s.baseLogger.InfoContext(ctx, "starting server")
ctx = ContextWithServerInfo(ctx, &ServerInfo{
Name: s.name,
@@ -164,7 +181,7 @@ func (s *ServerHTTPS) Start(ctx context.Context) (err error) {
s.started = true
- log.Info("[%s]: Server has been started", s.Name())
+ s.baseLogger.InfoContext(ctx, "server has been started")
return nil
}
@@ -173,16 +190,18 @@ func (s *ServerHTTPS) Start(ctx context.Context) (err error) {
func (s *ServerHTTPS) Shutdown(ctx context.Context) (err error) {
defer func() { err = errors.Annotate(err, "shutting down doh server: %w") }()
- log.Info("[%s]: Stopping the server", s.Name())
+ s.baseLogger.InfoContext(ctx, "shutting down server")
+
err = s.shutdown(ctx)
if err != nil {
- log.Info("[%s]: Failed to shutdown: %v", s.Name(), err)
+ s.baseLogger.WarnContext(ctx, "error while shutting down", slogutil.KeyError, err)
return err
}
err = s.waitShutdown(ctx)
- log.Info("[%s]: Finished stopping the server", s.Name())
+
+ s.baseLogger.InfoContext(ctx, "server has been shut down")
return err
}
@@ -208,7 +227,7 @@ func (s *ServerHTTPS) startHTTPSServer(ctx context.Context) (err error) {
ReadHeaderTimeout: httpReadTimeout,
WriteTimeout: httpWriteTimeout,
IdleTimeout: httpIdleTimeout,
- ErrorLog: log.StdLog("dnsserver/serverhttps: "+s.name, log.DEBUG),
+ ErrorLog: slog.NewLogLogger(s.baseLogger.Handler(), slog.LevelDebug),
}
// Start the server worker goroutine.
@@ -262,41 +281,41 @@ func (s *ServerHTTPS) shutdown(ctx context.Context) (err error) {
if s.tcpListener != nil {
err = s.tcpListener.Close()
if err != nil {
- log.Info("[%s]: Failed to close NetworkTCP listener: %v", s.Name(), err)
+ s.baseLogger.WarnContext(ctx, "closing tcp listener", slogutil.KeyError, err)
}
}
// Second, shutdown the HTTP server.
err = s.httpServer.Shutdown(ctx)
if err != nil {
- log.Debug("[%s]: http server shutdown: %v", s.Name(), err)
+ s.baseLogger.WarnContext(ctx, "shutting down http server", slogutil.KeyError, err)
}
// Finally, shutdown the HTTP/3 server.
- s.shutdownH3()
+ s.shutdownH3(ctx)
return nil
}
// shutdownH3 shuts down the HTTP/3 server, if enabled, and logs all errors.
-func (s *ServerHTTPS) shutdownH3() {
+func (s *ServerHTTPS) shutdownH3(ctx context.Context) {
if s.h3Server == nil {
return
}
err := s.quicListener.Close()
if err != nil {
- log.Debug("[%s]: quic listener shutdown: %s", s.Name(), err)
+ s.baseLogger.WarnContext(ctx, "closing quic listener", slogutil.KeyError, err)
}
err = s.quicTransport.Close()
if err != nil {
- log.Debug("[%s]: quic transport shutdown: %s", s.Name(), err)
+ s.baseLogger.WarnContext(ctx, "closing quic transport", slogutil.KeyError, err)
}
err = s.h3Server.Close()
if err != nil {
- log.Debug("[%s]: http/3 server shutdown: %s", s.Name(), err)
+ s.baseLogger.WarnContext(ctx, "shutting down http/3 server", slogutil.KeyError, err)
}
}
@@ -310,19 +329,15 @@ func (s *ServerHTTPS) serveHTTPS(ctx context.Context, hs *http.Server, l net.Lis
defer s.handlePanicAndExit(ctx)
scheme := urlutil.SchemeHTTPS
- if s.conf.TLSConfDefault == nil {
+ if s.tlsConfDefault == nil {
scheme = urlutil.SchemeHTTP
}
- u := &url.URL{
- Scheme: scheme,
- Host: s.addr,
- }
- log.Info("[%s]: Start listening to %s", s.name, u)
+ s.baseLogger.InfoContext(ctx, "starting serving http", "scheme", scheme)
err := hs.Serve(l)
if err != nil {
- log.Info("[%s]: Finished listening to %s due to %v", s.name, u, err)
+ s.baseLogger.WarnContext(ctx, "serving http failed", "scheme", scheme, slogutil.KeyError, err)
}
}
@@ -334,20 +349,18 @@ func (s *ServerHTTPS) serveH3(ctx context.Context, hs *http3.Server, ql *quic.Ea
// application won't be able to continue listening to DoH.
defer s.handlePanicAndExit(ctx)
- u := &url.URL{
- Scheme: http3.NextProtoH3,
- Host: s.addr,
- }
- log.Info("[%s]: Start listening to %s", s.name, u)
+ s.baseLogger.InfoContext(ctx, "starting serving http/3")
err := hs.ServeListener(ql)
if err != nil {
- log.Info("[%s]: Finished listening to %s due to %v", s.name, u, err)
+ s.baseLogger.WarnContext(ctx, "serving http/3 failed", slogutil.KeyError, err)
}
}
-// httpHandler is a helper structure that implements http.Handler
-// and holds pointers to ServerHTTPS, net.Listener.
+// httpHandler is a helper structure that implements [http.Handler] and holds
+// pointers to ServerHTTPS and net.Addr.
+//
+// TODO(a.garipov): Think of ways of using [httputil.LogMiddleware] with this.
type httpHandler struct {
srv *ServerHTTPS
localAddr net.Addr
@@ -384,22 +397,13 @@ func (h *httpHandler) remoteAddr(r *http.Request) (addr net.Addr) {
// ServeHTTP implements the http.Handler interface for *httpHandler. It reads
// the DNS data from the request, resolves it, and sends a response.
-//
-// NOTE: r.Context() is only used to control cancelation. To add values to the
-// context, use the BaseContext of this handler's ServerHTTPS.
func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- ctx, cancel := h.srv.requestContext()
+ ctx := r.Context()
+ ctx, cancel := h.srv.requestContext(ctx)
defer cancel()
- if dl, ok := r.Context().Deadline(); ok {
- ctx, cancel = context.WithDeadline(ctx, dl)
- defer cancel()
- }
-
defer h.srv.handlePanicAndRecover(ctx)
- log.Debug("Received a request to %s", r.URL)
-
// TODO(ameshkov): Consider using ants.Pool here.
isDNS, _, _ := isDoH(r)
@@ -409,8 +413,8 @@ func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
- if h.srv.conf.NonDNSHandler != nil {
- h.srv.conf.NonDNSHandler.ServeHTTP(w, r)
+ if hdlr := h.srv.nonDNSHandler; hdlr != nil {
+ hdlr.ServeHTTP(w, r)
} else {
h.srv.metrics.OnInvalidMsg(ctx)
http.Error(w, "", http.StatusNotFound)
@@ -422,7 +426,6 @@ func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
func (h *httpHandler) serveDoH(ctx context.Context, w http.ResponseWriter, r *http.Request) {
m, err := httpRequestToMsg(r)
if err != nil {
- log.Debug("Failed to convert request to a DNS message: %v", err)
h.srv.metrics.OnInvalidMsg(ctx)
http.Error(w, err.Error(), http.StatusBadRequest)
@@ -439,7 +442,6 @@ func (h *httpHandler) serveDoH(ctx context.Context, w http.ResponseWriter, r *ht
// If no response were written, indicate it via an internal server error.
if !written {
- log.Debug("No response has been written by the handler")
http.Error(w, "No response", http.StatusInternalServerError)
return
@@ -452,8 +454,6 @@ func (h *httpHandler) serveDoH(ctx context.Context, w http.ResponseWriter, r *ht
// Write the response to the client
err = h.writeResponse(req, resp, r, w)
if err != nil {
- log.Debug("[%d] Failed to write HTTP response: %v", req.Id, err)
-
// Try writing an error response just in case.
http.Error(w, "Internal error", http.StatusInternalServerError)
@@ -505,7 +505,6 @@ func (h *httpHandler) writeResponse(
w.WriteHeader(http.StatusOK)
// Write the actual response
- log.Debug("[%d] Writing HTTP response", req.Id)
_, err = w.Write(buf)
return err
@@ -520,7 +519,7 @@ func (s *ServerHTTPS) listenTLS(ctx context.Context) (err error) {
}
// Prepare the TLS configuration of the server.
- tlsConf := s.conf.TLSConfDefault
+ tlsConf := s.tlsConfDefault
if tlsConf == nil {
return nil
}
@@ -533,7 +532,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.TLSConfH3
+ tlsConf := s.tlsConfH3
conn, err := s.listenConfig.ListenPacket(ctx, "udp", s.addr)
if err != nil {
@@ -546,7 +545,7 @@ func (s *ServerHTTPS) listenQUIC(ctx context.Context) (err error) {
VerifySourceAddress: v.requiresValidation,
}
- qConf := newServerQUICConfig(s.conf.QUICLimitsEnabled, s.conf.MaxStreamsPerPeer)
+ qConf := newServerQUICConfig(s.quicLimitsEnabled, s.maxStreamsPerPeer)
ql, err := transport.ListenEarly(tlsConf, qConf)
if err != nil {
return fmt.Errorf("listening quic: %w", err)
@@ -596,10 +595,14 @@ func httpRequestToMsg(req *http.Request) (b []byte, err error) {
}
}
-// httpRequestToMsgPost extracts the DNS message from a request body.
+// httpRequestToMsgPost extracts the DNS message from a request body. req must
+// not be nil.
func httpRequestToMsgPost(req *http.Request) (b []byte, err error) {
- buf, err := io.ReadAll(req.Body)
- defer log.OnCloserError(req.Body, log.DEBUG)
+ // TODO(a.garipov): Make the limit configurable.
+ r := ioutil.LimitReader(req.Body, dns.MaxMsgSize)
+ buf, err := io.ReadAll(r)
+ err = errors.WithDeferred(err, req.Body.Close())
+
return buf, err
}
diff --git a/internal/dnsserver/serverhttps_test.go b/internal/dnsserver/serverhttps_test.go
index 53ace00..bf7f766 100644
--- a/internal/dnsserver/serverhttps_test.go
+++ b/internal/dnsserver/serverhttps_test.go
@@ -18,7 +18,6 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
"github.com/AdguardTeam/golibs/httphdr"
- "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
"github.com/quic-go/quic-go"
@@ -140,9 +139,12 @@ func TestServerHTTPS_integration_serveRequests(t *testing.T) {
}
func TestServerHTTPS_integration_nonDNSHandler(t *testing.T) {
+ errCh := make(chan error, 1)
testHandler := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
- _, _ = w.Write([]byte("OK"))
+
+ _, err := io.WriteString(w, "OK")
+ errCh <- err
})
srv, err := dnsservertest.RunLocalHTTPSServer(
@@ -156,11 +158,14 @@ func TestServerHTTPS_integration_nonDNSHandler(t *testing.T) {
return srv.Shutdown(context.Background())
})
- var resp *http.Response
- resp, err = http.Get(fmt.Sprintf("http://%s/test", srv.LocalTCPAddr()))
- defer log.OnCloserError(resp.Body, log.DEBUG)
+ resp, err := http.Get(fmt.Sprintf("http://%s/test", srv.LocalTCPAddr()))
require.NoError(t, err)
+ testutil.CleanupAndRequireSuccess(t, resp.Body.Close)
require.Equal(t, http.StatusOK, resp.StatusCode)
+
+ err, ok := testutil.RequireReceive(t, errCh, testTimeout)
+ require.True(t, ok)
+ require.NoError(t, err)
}
func TestDNSMsgToJSONMsg(t *testing.T) {
@@ -404,7 +409,7 @@ func testDoH3Exchange(
// Send the request and check the response.
httpResp, err := client.Do(httpReq)
require.NoError(t, err)
- defer log.OnCloserError(httpResp.Body, log.DEBUG)
+ testutil.CleanupAndRequireSuccess(t, httpResp.Body.Close)
body, err := io.ReadAll(httpResp.Body)
require.NoError(t, err)
@@ -447,7 +452,7 @@ func mustDoHReq(
httpResp, err := client.Do(httpReq)
require.NoError(t, err)
- defer log.OnCloserError(httpResp.Body, log.DEBUG)
+ testutil.CleanupAndRequireSuccess(t, httpResp.Body.Close)
if tlsConfig != nil && !httpResp.ProtoAtLeast(2, 0) {
t.Fatal(fmt.Errorf("protocol is too old: %s", httpResp.Proto))
diff --git a/internal/dnsserver/serverquic.go b/internal/dnsserver/serverquic.go
index c83c7b0..3f7d4ef 100644
--- a/internal/dnsserver/serverquic.go
+++ b/internal/dnsserver/serverquic.go
@@ -1,18 +1,20 @@
package dnsserver
import (
+ "cmp"
"context"
"crypto/tls"
"encoding/binary"
"fmt"
"io"
+ "log/slog"
"net"
"sync"
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext"
"github.com/AdguardTeam/golibs/errors"
- "github.com/AdguardTeam/golibs/log"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/syncutil"
"github.com/bluele/gcache"
"github.com/miekg/dns"
@@ -74,10 +76,12 @@ type ConfigQUIC struct {
// be set to [NextProtoDoQ].
TLSConfig *tls.Config
- ConfigBase
+ // Base is the base configuration for this server. It must not be nil and
+ // must be valid.
+ Base *ConfigBase
// MaxStreamsPerPeer is the maximum number of concurrent streams that a peer
- // is allowed to open.
+ // is allowed to open. If not set, 100 is used.
MaxStreamsPerPeer int
// QUICLimitsEnabled, if true, enables QUIC limiting.
@@ -85,6 +89,8 @@ type ConfigQUIC struct {
}
// ServerQUIC is a DNS-over-QUIC server implementation.
+//
+// TODO(a.garipov): Consider unembedding ServerBase.
type ServerQUIC struct {
*ServerBase
@@ -107,29 +113,35 @@ type ServerQUIC struct {
// transport is the QUIC transport saved here to close it later.
transport *quic.Transport
- // TODO(a.garipov): Remove this and only save the values a server actually
- // uses.
- conf ConfigQUIC
+ tlsConf *tls.Config
+
+ maxStreamsPerPeer int
+
+ quicLimitsEnabled bool
}
// quicBytePoolSize is the size for the QUIC byte pools.
const quicBytePoolSize = dns.MaxMsgSize
-// NewServerQUIC creates a new ServerQUIC instance.
-func NewServerQUIC(conf ConfigQUIC) (s *ServerQUIC) {
- if conf.ListenConfig == nil {
- // Do not enable OOB here as quic-go will do that on its own.
- conf.ListenConfig = netext.DefaultListenConfig(nil)
- }
+// NewServerQUIC creates a new ServerQUIC instance. c must not be nil and must
+// be valid.
+func NewServerQUIC(c *ConfigQUIC) (s *ServerQUIC) {
+ // Do not enable OOB here as quic-go will do that on its own.
+ c.Base.ListenConfig = cmp.Or(c.Base.ListenConfig, netext.DefaultListenConfig(nil))
s = &ServerQUIC{
- ServerBase: newServerBase(ProtoDoQ, conf.ConfigBase),
- pool: newPoolNonblocking(),
+ ServerBase: newServerBase(ProtoDoQ, c.Base),
reqPool: syncutil.NewSlicePool[byte](quicBytePoolSize),
respPool: syncutil.NewSlicePool[byte](quicBytePoolSize),
- conf: conf,
+ tlsConf: c.TLSConfig,
+ // NOTE: 100 is the current default in package quic, but set it
+ // explicitly in case that changes in the future.
+ maxStreamsPerPeer: cmp.Or(c.MaxStreamsPerPeer, 100),
+ quicLimitsEnabled: c.QUICLimitsEnabled,
}
+ s.pool = mustNewPoolNonblocking(s.baseLogger)
+
return s
}
@@ -143,15 +155,11 @@ func (s *ServerQUIC) Start(ctx context.Context) (err error) {
s.mu.Lock()
defer s.mu.Unlock()
- if s.conf.TLSConfig == nil {
- return errors.Error("tls config is required")
- }
-
if s.started {
return ErrServerAlreadyStarted
}
- log.Info("[%s]: Starting the server", s.name)
+ s.baseLogger.InfoContext(ctx, "starting server")
ctx = ContextWithServerInfo(ctx, &ServerInfo{
Name: s.name,
@@ -171,7 +179,7 @@ func (s *ServerQUIC) Start(ctx context.Context) (err error) {
s.started = true
- log.Info("[%s]: Server has been started", s.Name())
+ s.baseLogger.InfoContext(ctx, "server has been started")
return nil
}
@@ -180,11 +188,11 @@ func (s *ServerQUIC) Start(ctx context.Context) (err error) {
func (s *ServerQUIC) Shutdown(ctx context.Context) (err error) {
defer func() { err = errors.Annotate(err, "shutting down doq server: %w") }()
- log.Info("[%s]: Stopping the server", s.Name())
+ s.baseLogger.InfoContext(ctx, "shutting down server")
- err = s.shutdown()
+ err = s.shutdown(ctx)
if err != nil {
- log.Info("[%s]: Failed to shutdown: %v", s.Name(), err)
+ s.baseLogger.WarnContext(ctx, "error while shutting down", slogutil.KeyError, err)
return err
}
@@ -194,13 +202,13 @@ func (s *ServerQUIC) Shutdown(ctx context.Context) (err error) {
// Close the workerPool and releases all workers.
s.pool.Release()
- log.Info("[%s]: Finished stopping the server", s.Name())
+ s.baseLogger.InfoContext(ctx, "server has been shut down")
return err
}
// shutdown marks the server as stopped and closes active listeners.
-func (s *ServerQUIC) shutdown() (err error) {
+func (s *ServerQUIC) shutdown(ctx context.Context) (err error) {
s.mu.Lock()
defer s.mu.Unlock()
@@ -214,13 +222,13 @@ func (s *ServerQUIC) shutdown() (err error) {
// Now close all listeners.
err = s.quicListener.Close()
if err != nil {
- log.Debug("[%s]: closing quic listener: %s", s.Name(), err)
+ s.baseLogger.DebugContext(ctx, "closing quic listener", slogutil.KeyError, err)
}
// And the transport.
err = s.transport.Close()
if err != nil {
- log.Debug("[%s]: closing quic transport: %s", s.Name(), err)
+ s.baseLogger.DebugContext(ctx, "closing quic transport", slogutil.KeyError, err)
}
return nil
@@ -233,15 +241,11 @@ func (s *ServerQUIC) startServeQUIC(ctx context.Context) {
defer s.handlePanicAndExit(ctx)
defer s.wg.Done()
- log.Info("[%s]: Start listening to quic://%s", s.Name(), s.LocalUDPAddr())
+ s.baseLogger.InfoContext(ctx, "starting listening quic")
+
err := s.serveQUIC(ctx, s.quicListener)
if err != nil {
- log.Info(
- "[%s]: Finished listening to quic://%s due to %v",
- s.Name(),
- s.LocalUDPAddr(),
- err,
- )
+ s.baseLogger.WarnContext(ctx, "listening quic failed", slogutil.KeyError, err)
}
}
@@ -299,7 +303,7 @@ func (s *ServerQUIC) acceptQUICConn(
if err != nil {
// Most likely the workerPool is closed, and we can exit right away.
// Make sure that the connection is closed just in case.
- closeQUICConn(conn, DOQCodeNoError)
+ s.closeQUICConn(ctx, conn, DOQCodeNoError)
return err
}
@@ -321,7 +325,7 @@ func (s *ServerQUIC) serveQUICConnAsync(
err := s.serveQUICConn(ctx, conn)
if !isExpectedQUICErr(err) {
s.metrics.OnError(ctx, err)
- log.Debug("[%s] Error while serving a QUIC conn: %v", s.Name(), err)
+ s.baseLogger.DebugContext(ctx, "serving quic conn", slogutil.KeyError, err)
}
}
@@ -334,7 +338,7 @@ func (s *ServerQUIC) serveQUICConn(ctx context.Context, conn quic.Connection) (e
streamWg.Wait()
// Close the connection to make sure resources are freed.
- closeQUICConn(conn, DOQCodeNoError)
+ s.closeQUICConn(ctx, conn, DOQCodeNoError)
}()
for s.isStarted() {
@@ -372,7 +376,7 @@ func (s *ServerQUIC) serveQUICConn(ctx context.Context, conn quic.Connection) (e
TLSServerName: conn.ConnectionState().TLS.ServerName,
}
- reqCtx, reqCancel := s.requestContext()
+ reqCtx, reqCancel := s.requestContext(context.Background())
reqCtx = ContextWithRequestInfo(reqCtx, ri)
streamWg.Add(1)
@@ -409,7 +413,7 @@ func (s *ServerQUIC) serveQUICStreamAsync(
err := s.serveQUICStream(ctx, stream, conn)
if !isExpectedQUICErr(err) {
s.metrics.OnError(ctx, err)
- log.Debug("[%s] Failed to process a QUIC stream: %v", s.Name(), err)
+ s.baseLogger.DebugContext(ctx, "serving quic stream", slogutil.KeyError, err)
}
}
@@ -423,11 +427,11 @@ func (s *ServerQUIC) serveQUICStream(
// The server MUST send the response on the same stream, and MUST indicate
// through the STREAM FIN mechanism that no further data will be sent on
// that stream.
- defer log.OnCloserError(stream, log.DEBUG)
+ defer slogutil.CloseAndLog(ctx, s.baseLogger, stream, slog.LevelDebug)
msg, err := s.readQUICMsg(ctx, stream)
if err != nil {
- closeQUICConn(conn, DOQCodeProtocolError)
+ s.closeQUICConn(ctx, conn, DOQCodeProtocolError)
return err
}
@@ -437,7 +441,7 @@ func (s *ServerQUIC) serveQUICStream(
// fatal error. It SHOULD forcibly abort the connection using QUIC's
// CONNECTION_CLOSE mechanism and SHOULD use the DoQ error code
// DOQ_PROTOCOL_ERROR.
- closeQUICConn(conn, DOQCodeProtocolError)
+ s.closeQUICConn(ctx, conn, DOQCodeProtocolError)
return ErrProtocol
}
@@ -464,7 +468,7 @@ func (s *ServerQUIC) serveQUICStream(
b, err := packWithPrefix(resp, *bufPtr)
if err != nil {
- closeQUICConn(conn, DOQCodeProtocolError)
+ s.closeQUICConn(ctx, conn, DOQCodeProtocolError)
return err
}
@@ -570,8 +574,8 @@ func (s *ServerQUIC) listenQUIC(ctx context.Context) (err error) {
VerifySourceAddress: v.requiresValidation,
}
- qConf := newServerQUICConfig(s.conf.QUICLimitsEnabled, s.conf.MaxStreamsPerPeer)
- ql, err := transport.Listen(s.conf.TLSConfig, qConf)
+ qConf := newServerQUICConfig(s.quicLimitsEnabled, s.maxStreamsPerPeer)
+ ql, err := transport.Listen(s.tlsConf, qConf)
if err != nil {
return fmt.Errorf("listening quic: %w", err)
}
@@ -684,10 +688,14 @@ func validQUICMsg(req *dns.Msg) (ok bool) {
// closeQUICConn quietly closes the QUIC connection with the specified error
// code and logs if it fails to close the connection.
-func closeQUICConn(conn quic.Connection, code quic.ApplicationErrorCode) {
+func (s *ServerQUIC) closeQUICConn(
+ ctx context.Context,
+ conn quic.Connection,
+ code quic.ApplicationErrorCode,
+) {
err := conn.CloseWithError(code, "")
if err != nil {
- log.Debug("failed to close the QUIC connection: %v", err)
+ s.baseLogger.DebugContext(ctx, "closing quic conn", slogutil.KeyError, err)
}
}
diff --git a/internal/dnsserver/serverquic_test.go b/internal/dnsserver/serverquic_test.go
index f3c1a2b..e04e4ce 100644
--- a/internal/dnsserver/serverquic_test.go
+++ b/internal/dnsserver/serverquic_test.go
@@ -14,7 +14,6 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
"github.com/AdguardTeam/golibs/errors"
- "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
"github.com/quic-go/quic-go"
@@ -219,7 +218,7 @@ func sendQUICMessage(
return nil, fmt.Errorf("opening stream: %w", err)
}
- defer log.OnCloserError(stream, log.ERROR)
+ defer func() { err = errors.WithDeferred(err, stream.Close()) }()
data, err := req.Pack()
if err != nil {
diff --git a/internal/dnsserver/servertls.go b/internal/dnsserver/servertls.go
index ec68a75..93c7da6 100644
--- a/internal/dnsserver/servertls.go
+++ b/internal/dnsserver/servertls.go
@@ -5,35 +5,40 @@ import (
"crypto/tls"
"github.com/AdguardTeam/golibs/errors"
- "github.com/AdguardTeam/golibs/log"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
)
// ConfigTLS is a struct that needs to be passed to NewServerTLS to
// initialize a new ServerTLS instance.
type ConfigTLS struct {
- // TLSConfig is the TLS configuration for TLS.
+ // TLSConfig is the TLS configuration for TLS. It must not be nil.
TLSConfig *tls.Config
- ConfigDNS
+ // DNS is the configuration for the underlying DNS server. It must not be
+ // nil and must be valid.
+ DNS *ConfigDNS
}
-// ServerTLS implements a DNS-over-TLS server.
-// Note that it heavily relies on ServerDNS.
+// ServerTLS implements a DNS-over-TLS server. Note that it heavily relies on
+// ServerDNS.
+//
+// TODO(a.garipov): Consider unembedding ServerDNS.
type ServerTLS struct {
*ServerDNS
- conf ConfigTLS
+ tlsConf *tls.Config
}
// type check
var _ Server = (*ServerTLS)(nil)
-// NewServerTLS creates a new ServerTLS instance.
-func NewServerTLS(conf ConfigTLS) (s *ServerTLS) {
- srv := newServerDNS(ProtoDoT, conf.ConfigDNS)
+// NewServerTLS creates a new ServerTLS instance. c must not be nil and must be
+// valid.
+func NewServerTLS(c *ConfigTLS) (s *ServerTLS) {
+ srv := newServerDNS(ProtoDoT, c.DNS)
s = &ServerTLS{
ServerDNS: srv,
- conf: conf,
+ tlsConf: c.TLSConfig,
}
return s
@@ -46,15 +51,11 @@ func (s *ServerTLS) Start(ctx context.Context) (err error) {
s.mu.Lock()
defer s.mu.Unlock()
- if s.conf.TLSConfig == nil {
- return errors.Error("tls config is required")
- }
-
if s.started {
return ErrServerAlreadyStarted
}
- log.Info("[%s]: Starting the server", s.name)
+ s.baseLogger.InfoContext(ctx, "starting server")
ctx = ContextWithServerInfo(ctx, &ServerInfo{
Name: s.name,
@@ -77,7 +78,7 @@ func (s *ServerTLS) Start(ctx context.Context) (err error) {
// listeners are up.
s.started = true
- log.Info("[%s]: Server has been started", s.Name())
+ s.baseLogger.InfoContext(ctx, "server has been started")
return nil
}
@@ -95,10 +96,11 @@ func (s *ServerTLS) startServeTCP(ctx context.Context) {
// the application won't be able to continue listening to DoT
defer s.handlePanicAndExit(ctx)
- log.Info("[%s]: Start listening to tls://%s", s.Name(), s.Addr())
+ s.baseLogger.InfoContext(ctx, "starting listening tls")
+
err := s.serveTCP(ctx, s.tcpListener)
if err != nil {
- log.Info("[%s]: Finished listening to tls://%s due to %v", s.Name(), s.Addr(), err)
+ s.baseLogger.WarnContext(ctx, "listening tls failed", slogutil.KeyError, err)
}
}
@@ -109,7 +111,7 @@ func (s *ServerTLS) listenTLS(ctx context.Context) (err error) {
return err
}
- s.tcpListener = newTLSListener(l, s.conf.TLSConfig)
+ s.tcpListener = newTLSListener(l, s.tlsConf)
return nil
}
diff --git a/internal/dnsserver/servertls_test.go b/internal/dnsserver/servertls_test.go
index 16af47c..8acae89 100644
--- a/internal/dnsserver/servertls_test.go
+++ b/internal/dnsserver/servertls_test.go
@@ -10,7 +10,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
- "github.com/AdguardTeam/golibs/log"
+ "github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/require"
)
@@ -100,7 +100,7 @@ func TestServerTLS_integration_msgIgnore(t *testing.T) {
conn, err := tls.Dial("tcp", addr.String(), tlsConfig)
require.Nil(t, err)
- defer log.OnCloserError(conn, log.DEBUG)
+ testutil.CleanupAndRequireSuccess(t, conn.Close)
// Write the invalid request
_, err = conn.Write(tc.buf)
@@ -161,7 +161,7 @@ func TestServerTLS_integration_queriesPipelining(t *testing.T) {
conn, err := tls.Dial("tcp", addr.String(), tlsConfig)
require.Nil(t, err)
- defer log.OnCloserError(conn, log.DEBUG)
+ testutil.CleanupAndRequireSuccess(t, conn.Close)
// Second - write multiple queries (let's say 100) and save
// those queries IDs
diff --git a/internal/dnsserver/workerpool.go b/internal/dnsserver/workerpool.go
index b8b3827..00fbfca 100644
--- a/internal/dnsserver/workerpool.go
+++ b/internal/dnsserver/workerpool.go
@@ -1,37 +1,43 @@
package dnsserver
import (
+ "fmt"
+ "log/slog"
"time"
- "github.com/AdguardTeam/golibs/log"
+ "github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/panjf2000/ants/v2"
)
// antsLogger implements the [ants.Logger] interface and writes everything
-// using golibs logger.
-type antsLogger struct{}
+// to its logger.
+type antsLogger struct {
+ logger *slog.Logger
+}
// type check
var _ ants.Logger = (*antsLogger)(nil)
// Printf implements the [ants.Logger] interface for *antsLogger.
func (l *antsLogger) Printf(format string, args ...interface{}) {
- log.Info(format, args...)
+ l.logger.Info("ants pool", slogutil.KeyMessage, fmt.Sprintf(format, args...))
}
-// newPoolNonblocking creates a new instance of [*ants.Pool] configured optimally
-// for using it in DNS servers.
-func newPoolNonblocking() (p *ants.Pool) {
+// mustNewPoolNonblocking creates a new instance of [*ants.Pool] configured
+// optimally for using it in DNS servers. It panics if there are errors.
+// logger must not be nil.
+func mustNewPoolNonblocking(logger *slog.Logger) (p *ants.Pool) {
p, err := ants.NewPool(0, ants.WithOptions(ants.Options{
ExpiryDuration: time.Minute,
PreAlloc: false,
Nonblocking: true,
DisablePurge: false,
- Logger: &antsLogger{},
+ Logger: &antsLogger{
+ logger: logger,
+ },
}))
- if err != nil {
- log.Fatalf("failed to init goroutines workerPool: %v", err)
- }
+ errors.Check(err)
return p
}
diff --git a/internal/dnssvc/config.go b/internal/dnssvc/config.go
index 1c01ad9..272c05c 100644
--- a/internal/dnssvc/config.go
+++ b/internal/dnssvc/config.go
@@ -17,6 +17,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit"
+ "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/initial"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
@@ -28,6 +29,10 @@ import (
// Config is the configuration of the AdGuard DNS service.
type Config struct {
+ // BaseLogger is used to create loggers for the DNS listeners. It must not
+ // be nil.
+ BaseLogger *slog.Logger
+
// Handlers are the handlers to use in this DNS service.
Handlers Handlers
@@ -56,22 +61,27 @@ type Config struct {
// NonDNS is the handler for non-DNS HTTP requests. It must not be nil.
NonDNS http.Handler
+ // PrometheusRegisterer is used to register Prometheus metrics. It must not
+ // be nil.
+ PrometheusRegisterer prometheus.Registerer
+
// MetricsNamespace is a namespace for Prometheus metrics. It must be a
// valid Prometheus metric label.
MetricsNamespace string
// ServerGroups are the DNS server groups. Each element must be non-nil.
- ServerGroups []*agd.ServerGroup
+ ServerGroups []*ServerGroupConfig
// HandleTimeout defines the timeout for the entire handling of a single
// query. It must be greater than zero.
HandleTimeout time.Duration
}
-// NewListenerFunc is the type for DNS listener constructors.
+// NewListenerFunc is the type for DNS listener constructors. All arguments
+// must not be nil.
type NewListenerFunc func(
srv *agd.Server,
- baseConf dnsserver.ConfigBase,
+ baseConf *dnsserver.ConfigBase,
nonDNS http.Handler,
) (l Listener, err error)
@@ -142,6 +152,9 @@ type HandlersConfig struct {
// Handler is the ultimate handler of the DNS query to be wrapped by
// middlewares. It must not be nil.
+ //
+ // TODO(a.garipov): Use the logger from the context throughout the
+ // handling.
Handler dnsserver.Handler
// HashMatcher is the safe-browsing hash matcher for TXT queries. It must
@@ -176,8 +189,8 @@ type HandlersConfig struct {
FilteringGroups map[agd.FilteringGroupID]*agd.FilteringGroup
// ServerGroups are the DNS server groups for which to build handlers. Each
- // element and its servers must be non-nil.
- ServerGroups []*agd.ServerGroup
+ // server group and its servers must be valid and non-nil.
+ ServerGroups []*ServerGroupConfig
// EDEEnabled enables the addition of the Extended DNS Error (EDE) codes in
// the profiles' message constructors.
@@ -192,7 +205,7 @@ type Handlers map[HandlerKey]dnsserver.Handler
// HandlerKey is a key for the [Handlers] map.
type HandlerKey struct {
Server *agd.Server
- ServerGroup *agd.ServerGroup
+ ServerGroup *ServerGroupConfig
}
// CacheConfig is the configuration for the DNS cache.
@@ -226,3 +239,37 @@ const (
CacheTypeSimple
CacheTypeECS
)
+
+// ServerGroupConfig is the configuration for a group of DNS servers all of
+// which use the same filtering settings.
+type ServerGroupConfig struct {
+ // DDR is the configuration for the server group's Discovery Of Designated
+ // Resolvers (DDR) handlers. DDR must not be nil.
+ DDR *DDRConfig
+
+ // DeviceDomains is the list of domain names used to detect device IDs from
+ // clients' server names.
+ DeviceDomains []string
+
+ // Name is the unique name of the server group.
+ Name agd.ServerGroupName
+
+ // FilteringGroup is the ID of the filtering group for this server group.
+ FilteringGroup agd.FilteringGroupID
+
+ // Servers are the settings for servers. Each element must be non-nil.
+ //
+ // TODO(a.garipov): Move servers here as well as ServerConfig.
+ Servers []*agd.Server
+
+ // ProfilesEnabled, if true, enables recognition of user devices and
+ // profiles for this server group.
+ ProfilesEnabled bool
+}
+
+// ServerGroupName is the name of a server group.
+type ServerGroupName string
+
+// DDRConfig is the configuration for the server group's Discovery Of Designated
+// Resolvers (DDR) handlers.
+type DDRConfig = initial.DDRConfig
diff --git a/internal/dnssvc/context.go b/internal/dnssvc/context.go
index 3e054bc..05f9171 100644
--- a/internal/dnssvc/context.go
+++ b/internal/dnssvc/context.go
@@ -5,11 +5,11 @@ import (
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
- "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
+ "github.com/AdguardTeam/golibs/contextutil"
)
-// contextConstructor is a [dnsserver.ContextConstructor] implementation that
-// returns a context with the given timeout as well as a new [agd.RequestID].
+// contextConstructor is a [contextutil.Constructor] implementation that returns
+// a context with the given timeout as well as a new [agd.RequestID].
type contextConstructor struct {
timeout time.Duration
}
@@ -22,13 +22,15 @@ func newContextConstructor(timeout time.Duration) (c *contextConstructor) {
}
// type check
-var _ dnsserver.ContextConstructor = (*contextConstructor)(nil)
+var _ contextutil.Constructor = (*contextConstructor)(nil)
-// New implements the [dnsserver.ContextConstructor] interface for
+// New implements the [contextutil.Constructor] interface for
// *contextConstructor. It returns a context with a new [agd.RequestID] as well
// as its timeout and the corresponding cancelation function.
-func (c *contextConstructor) New() (ctx context.Context, cancel context.CancelFunc) {
- ctx, cancel = context.WithTimeout(context.Background(), c.timeout)
+func (c *contextConstructor) New(
+ parent context.Context,
+) (ctx context.Context, cancel context.CancelFunc) {
+ ctx, cancel = context.WithTimeout(parent, c.timeout)
ctx = agd.WithRequestID(ctx, agd.NewRequestID())
return ctx, cancel
diff --git a/internal/dnssvc/dnssvc.go b/internal/dnssvc/dnssvc.go
index 66c7661..28c4aeb 100644
--- a/internal/dnssvc/dnssvc.go
+++ b/internal/dnssvc/dnssvc.go
@@ -15,6 +15,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext"
dnssrvprom "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/prometheus"
"github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/service"
"github.com/miekg/dns"
)
@@ -56,9 +57,17 @@ func New(c *Config) (svc *Service, err error) {
newListener = NewListener
}
+ mtrcListener, err := dnssrvprom.NewServerMetricsListener(
+ c.MetricsNamespace,
+ c.PrometheusRegisterer,
+ )
+ if err != nil {
+ return nil, fmt.Errorf("metrics listener: %w", err)
+ }
+
errCollListener := &errCollMetricsListener{
errColl: c.ErrColl,
- baseListener: dnssrvprom.NewServerMetricsListener(c.MetricsNamespace),
+ baseListener: mtrcListener,
}
// Configure the service itself.
@@ -87,7 +96,7 @@ func New(c *Config) (svc *Service, err error) {
// newServers creates a slice of servers.
func newServers(
c *Config,
- srvGrp *agd.ServerGroup,
+ srvGrp *ServerGroupConfig,
errCollListener *errCollMetricsListener,
newListener NewListenerFunc,
) (servers []*server, err error) {
@@ -140,7 +149,15 @@ func newListeners(
proto := srv.Protocol
name := listenerName(srv.Name, addr, proto)
- baseConf := dnsserver.ConfigBase{
+ baseConf := &dnsserver.ConfigBase{
+ // TODO(a.garipov): Consider making servers add the address instead
+ // of module users doing that. Including the correct handling of
+ // addresses with zero port.
+ BaseLogger: c.BaseLogger.With(
+ "listener_addr", addr,
+ "listener_name", name,
+ slogutil.KeyPrefix, "dnsserver",
+ ),
Network: dnsserver.NetworkAny,
Handler: handler,
Metrics: errCollListener,
@@ -152,7 +169,6 @@ func newListeners(
c.ConnLimiter,
proto,
),
- Name: name,
Addr: addr,
}
@@ -318,7 +334,7 @@ func (svc *Service) Handle(
// TODO(a.garipov): Replace this in tests with [netext.ListenConfig].
func NewListener(
s *agd.Server,
- baseConf dnsserver.ConfigBase,
+ baseConf *dnsserver.ConfigBase,
nonDNS http.Handler,
) (l Listener, err error) {
defer func() { err = errors.Annotate(err, "listener %q: %w", baseConf.Name) }()
@@ -328,8 +344,8 @@ func NewListener(
switch p := s.Protocol; p {
case agd.ProtoDNS:
udpConf := s.UDPConf
- l = dnsserver.NewServerDNS(dnsserver.ConfigDNS{
- ConfigBase: baseConf,
+ l = dnsserver.NewServerDNS(&dnsserver.ConfigDNS{
+ Base: baseConf,
ReadTimeout: s.ReadTimeout,
WriteTimeout: s.WriteTimeout,
MaxUDPRespSize: udpConf.MaxRespSize,
@@ -339,14 +355,14 @@ func NewListener(
})
case agd.ProtoDNSCrypt:
dcConf := s.DNSCrypt
- l = dnsserver.NewServerDNSCrypt(dnsserver.ConfigDNSCrypt{
- ConfigBase: baseConf,
- DNSCryptProviderName: dcConf.ProviderName,
- DNSCryptResolverCert: dcConf.Cert,
+ l = dnsserver.NewServerDNSCrypt(&dnsserver.ConfigDNSCrypt{
+ Base: baseConf,
+ ProviderName: dcConf.ProviderName,
+ ResolverCert: dcConf.Cert,
})
case agd.ProtoDoH:
- l = dnsserver.NewServerHTTPS(dnsserver.ConfigHTTPS{
- ConfigBase: baseConf,
+ l = dnsserver.NewServerHTTPS(&dnsserver.ConfigHTTPS{
+ Base: baseConf,
TLSConfDefault: s.TLS.Default,
TLSConfH3: s.TLS.H3,
NonDNSHandler: nonDNS,
@@ -354,16 +370,16 @@ func NewListener(
QUICLimitsEnabled: quicConf.QUICLimitsEnabled,
})
case agd.ProtoDoQ:
- l = dnsserver.NewServerQUIC(dnsserver.ConfigQUIC{
+ l = dnsserver.NewServerQUIC(&dnsserver.ConfigQUIC{
TLSConfig: s.TLS.Default,
- ConfigBase: baseConf,
+ Base: baseConf,
MaxStreamsPerPeer: quicConf.MaxStreamsPerPeer,
QUICLimitsEnabled: quicConf.QUICLimitsEnabled,
})
case agd.ProtoDoT:
- l = dnsserver.NewServerTLS(dnsserver.ConfigTLS{
- ConfigDNS: dnsserver.ConfigDNS{
- ConfigBase: baseConf,
+ l = dnsserver.NewServerTLS(&dnsserver.ConfigTLS{
+ DNS: &dnsserver.ConfigDNS{
+ Base: baseConf,
ReadTimeout: s.ReadTimeout,
WriteTimeout: s.WriteTimeout,
MaxPipelineEnabled: tcpConf.MaxPipelineEnabled,
diff --git a/internal/dnssvc/dnssvc_test.go b/internal/dnssvc/dnssvc_test.go
index 6c91802..687b34b 100644
--- a/internal/dnssvc/dnssvc_test.go
+++ b/internal/dnssvc/dnssvc_test.go
@@ -9,20 +9,20 @@ import (
"testing"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
- "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/forward"
"github.com/AdguardTeam/AdGuardDNS/internal/dnssvc"
"github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/dnssvctest"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
+ "github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
-// type check
-var _ agdservice.Refresher = (*forward.Handler)(nil)
+// testLogger is the common logger for tests.
+var testLogger = slogutil.NewDiscardLogger()
// testListener is a [dnssvc.Listener] for tests.
type testListener struct {
@@ -99,7 +99,7 @@ func newTestListener() (tl *testListener) {
func newTestListenerFunc(tl *testListener) (f dnssvc.NewListenerFunc) {
return func(
_ *agd.Server,
- _ dnsserver.ConfigBase,
+ _ *dnsserver.ConfigBase,
_ http.Handler,
) (l dnssvc.Listener, err error) {
return tl, nil
@@ -150,7 +150,7 @@ func TestService_Start(t *testing.T) {
AddrPort: netip.MustParseAddrPort("127.0.0.1:53"),
})
- srvGrp := &agd.ServerGroup{
+ srvGrp := &dnssvc.ServerGroupConfig{
Name: dnssvctest.ServerGroupName,
Servers: []*agd.Server{srv},
}
@@ -161,12 +161,14 @@ func TestService_Start(t *testing.T) {
}
c := &dnssvc.Config{
+ BaseLogger: testLogger,
NewListener: newTestListenerFunc(tl),
Handlers: dnssvc.Handlers{
k: dnsservertest.NewDefaultHandler(),
},
- MetricsNamespace: "test_start",
- ServerGroups: []*agd.ServerGroup{srvGrp},
+ PrometheusRegisterer: prometheus.NewRegistry(),
+ MetricsNamespace: "test_start",
+ ServerGroups: []*dnssvc.ServerGroupConfig{srvGrp},
}
svc, err := dnssvc.New(c)
@@ -204,7 +206,7 @@ func TestNew(t *testing.T) {
}),
}
- srvGrp := &agd.ServerGroup{
+ srvGrp := &dnssvc.ServerGroupConfig{
Name: dnssvctest.ServerGroupName,
Servers: srvs,
}
@@ -220,9 +222,11 @@ func TestNew(t *testing.T) {
}
c := &dnssvc.Config{
- Handlers: handlers,
- MetricsNamespace: "test_new",
- ServerGroups: []*agd.ServerGroup{srvGrp},
+ BaseLogger: testLogger,
+ Handlers: handlers,
+ PrometheusRegisterer: prometheus.NewRegistry(),
+ MetricsNamespace: "test_new",
+ ServerGroups: []*dnssvc.ServerGroupConfig{srvGrp},
}
svc, err := dnssvc.New(c)
diff --git a/internal/dnssvc/handler.go b/internal/dnssvc/handler.go
index 0862e20..2c833d9 100644
--- a/internal/dnssvc/handler.go
+++ b/internal/dnssvc/handler.go
@@ -15,15 +15,21 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/preupstream"
"github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/ratelimitmw"
"github.com/AdguardTeam/AdGuardDNS/internal/ecscache"
- "github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
+
+ // TODO(e.burkov): Move registering of the metrics to another package to
+ // avoid dependency on the metrics package.
+ "github.com/AdguardTeam/AdGuardDNS/internal/metrics"
)
// NewHandlers returns the main DNS handlers wrapped in all necessary
// middlewares. c must not be nil.
func NewHandlers(ctx context.Context, c *HandlersConfig) (handlers Handlers, err error) {
- handler := wrapPreUpstreamMw(ctx, c)
+ handler, err := wrapPreUpstreamMw(ctx, c)
+ if err != nil {
+ return nil, fmt.Errorf("wrapping pre-upstream middleware: %w", err)
+ }
mainMwMtrc, err := newMainMiddlewareMetrics(c)
if err != nil {
@@ -60,12 +66,6 @@ func NewHandlers(ctx context.Context, c *HandlersConfig) (handlers Handlers, err
handler = postInitMw.Wrap(handler)
}
- initMw := initial.New(&initial.Config{
- Logger: c.BaseLogger.With(slogutil.KeyPrefix, "initmw"),
- })
-
- handler = initMw.Wrap(handler)
-
return newHandlersForServers(c, handler)
}
@@ -74,7 +74,10 @@ func NewHandlers(ctx context.Context, c *HandlersConfig) (handlers Handlers, err
//
// TODO(a.garipov): Adapt the cache tests that previously were in package
// preupstream.
-func wrapPreUpstreamMw(ctx context.Context, c *HandlersConfig) (wrapped dnsserver.Handler) {
+func wrapPreUpstreamMw(
+ ctx context.Context,
+ c *HandlersConfig,
+) (wrapped dnsserver.Handler, err error) {
// TODO(a.garipov): Use in other places if necessary.
l := c.BaseLogger.With(slogutil.KeyPrefix, "dnssvc")
@@ -85,9 +88,18 @@ func wrapPreUpstreamMw(ctx context.Context, c *HandlersConfig) (wrapped dnsserve
case CacheTypeSimple:
l.InfoContext(ctx, "plain cache enabled", "count", conf.NoECSCount)
+ var mtrcListener *dnssrvprom.CacheMetricsListener
+ mtrcListener, err = dnssrvprom.NewCacheMetricsListener(
+ c.MetricsNamespace,
+ c.PrometheusRegisterer,
+ )
+ if err != nil {
+ return nil, fmt.Errorf("registering cache metrics: %w", err)
+ }
+
cacheMw := cache.NewMiddleware(&cache.MiddlewareConfig{
- // TODO(a.garipov): Do not use promauto and refactor.
- MetricsListener: dnssrvprom.NewCacheMetricsListener(metrics.Namespace()),
+ Logger: c.BaseLogger.With(slogutil.KeyPrefix, "cache"),
+ MetricsListener: mtrcListener,
Count: conf.NoECSCount,
MinTTL: conf.MinTTL,
OverrideTTL: conf.OverrideCacheTTL,
@@ -102,7 +114,14 @@ func wrapPreUpstreamMw(ctx context.Context, c *HandlersConfig) (wrapped dnsserve
"no_ecs_count", conf.NoECSCount,
)
+ var mtrc ecscache.Metrics
+ mtrc, err = metrics.NewECSCache(c.MetricsNamespace, c.PrometheusRegisterer)
+ if err != nil {
+ return nil, fmt.Errorf("registering ecs cache metrics: %w", err)
+ }
+
cacheMw := ecscache.NewMiddleware(&ecscache.MiddlewareConfig{
+ Metrics: mtrc,
Cloner: c.Cloner,
Logger: c.BaseLogger.With(slogutil.KeyPrefix, "ecscache"),
CacheManager: c.CacheManager,
@@ -124,7 +143,7 @@ func wrapPreUpstreamMw(ctx context.Context, c *HandlersConfig) (wrapped dnsserve
wrapped = preUps.Wrap(wrapped)
- return wrapped
+ return wrapped, nil
}
// newMainMiddlewareMetrics returns a filtering-middleware metrics
@@ -135,7 +154,11 @@ func newMainMiddlewareMetrics(c *HandlersConfig) (mainMwMtrc MainMiddlewareMetri
return mainMwMtrc, nil
}
- mainMwMtrc, err = metrics.NewDefaultMainMiddleware(c.MetricsNamespace, c.PrometheusRegisterer)
+ mainMwMtrc, err = metrics.NewDefaultMainMiddleware(
+ c.BaseLogger.With(slogutil.KeyPrefix, "mainmw_metrics"),
+ c.MetricsNamespace,
+ c.PrometheusRegisterer,
+ )
if err != nil {
return nil, fmt.Errorf("mainmw metrics: %w", err)
}
@@ -145,7 +168,7 @@ 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) {
+func newHandlersForServers(c *HandlersConfig, handler dnsserver.Handler) (handlers Handlers, err error) {
rlMwMtrc, err := metrics.NewDefaultRatelimitMiddleware(
c.MetricsNamespace,
c.PrometheusRegisterer,
@@ -167,13 +190,27 @@ func newHandlersForServers(c *HandlersConfig, h dnsserver.Handler) (handlers Han
)
}
+ initMw := initial.New(&initial.Config{
+ Logger: c.BaseLogger.With(slogutil.KeyPrefix, "initmw"),
+ DDR: srvGrp.DDR,
+ })
+
+ srvGrpHandler := initMw.Wrap(handler)
+
for _, srv := range srvGrp.Servers {
+ srvInfo := &agd.RequestServerInfo{
+ GroupName: srvGrp.Name,
+ Name: srv.Name,
+ DeviceDomains: srvGrp.DeviceDomains,
+ Protocol: srv.Protocol,
+ ProfilesEnabled: srvGrp.ProfilesEnabled,
+ }
+
rlMw := ratelimitmw.New(&ratelimitmw.Config{
Logger: rlMwLogger,
Messages: c.Messages,
FilteringGroup: fltGrp,
- ServerGroup: srvGrp,
- Server: srv,
+ ServerInfo: srvInfo,
StructuredErrors: c.StructuredErrors,
AccessManager: c.AccessManager,
DeviceFinder: newDeviceFinder(c, srvGrp, srv),
@@ -190,7 +227,7 @@ func newHandlersForServers(c *HandlersConfig, h dnsserver.Handler) (handlers Han
ServerGroup: srvGrp,
}
- handlers[k] = rlMw.Wrap(h)
+ handlers[k] = rlMw.Wrap(srvGrpHandler)
}
}
@@ -199,7 +236,7 @@ func newHandlersForServers(c *HandlersConfig, h dnsserver.Handler) (handlers Han
// newDeviceFinder returns a new agd.DeviceFinder for a server based on the
// configuration. All arguments must not be nil.
-func newDeviceFinder(c *HandlersConfig, g *agd.ServerGroup, s *agd.Server) (df agd.DeviceFinder) {
+func newDeviceFinder(c *HandlersConfig, g *ServerGroupConfig, s *agd.Server) (df agd.DeviceFinder) {
if !g.ProfilesEnabled {
return agd.EmptyDeviceFinder{}
}
diff --git a/internal/dnssvc/handler_test.go b/internal/dnssvc/handler_test.go
index d7e5df4..8ffb83a 100644
--- a/internal/dnssvc/handler_test.go
+++ b/internal/dnssvc/handler_test.go
@@ -16,7 +16,6 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/AdGuardDNS/internal/querylog"
- "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
@@ -108,8 +107,8 @@ func TestNewHandlers(t *testing.T) {
AddrPort: dnssvctest.ServerAddrPort,
})
- srvGrp := &agd.ServerGroup{
- DDR: &agd.DDR{
+ srvGrp := &dnssvc.ServerGroupConfig{
+ DDR: &dnssvc.DDRConfig{
Enabled: true,
},
Name: dnssvctest.ServerGroupName,
@@ -151,7 +150,7 @@ func TestNewHandlers(t *testing.T) {
ctx := testutil.ContextWithTimeout(t, dnssvctest.Timeout)
handlers, err := dnssvc.NewHandlers(ctx, &dnssvc.HandlersConfig{
- BaseLogger: slogutil.NewDiscardLogger(),
+ BaseLogger: testLogger,
Cloner: agdtest.NewCloner(),
Cache: tc.cacheConf,
HumanIDParser: agd.NewHumanIDParser(),
@@ -176,7 +175,7 @@ func TestNewHandlers(t *testing.T) {
RuleStat: ruleStat,
MetricsNamespace: path.Base(t.Name()),
FilteringGroups: fltGrps,
- ServerGroups: []*agd.ServerGroup{srvGrp},
+ ServerGroups: []*dnssvc.ServerGroupConfig{srvGrp},
EDEEnabled: true,
})
require.NoError(t, err)
diff --git a/internal/dnssvc/integration_test.go b/internal/dnssvc/integration_test.go
index eae2d60..80953b2 100644
--- a/internal/dnssvc/integration_test.go
+++ b/internal/dnssvc/integration_test.go
@@ -23,10 +23,10 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/AdGuardDNS/internal/querylog"
- "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
+ "github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -186,8 +186,8 @@ func newTestService(
return true, false, nil
}
- srvGrps := []*agd.ServerGroup{{
- DDR: &agd.DDR{
+ srvGrps := []*dnssvc.ServerGroupConfig{{
+ DDR: &dnssvc.DDRConfig{
Enabled: true,
},
DeviceDomains: []string{dnssvctest.DomainForDevices},
@@ -210,7 +210,7 @@ func newTestService(
}
hdlrConf := &dnssvc.HandlersConfig{
- BaseLogger: slogutil.NewDiscardLogger(),
+ BaseLogger: testLogger,
Cache: &dnssvc.CacheConfig{
Type: dnssvc.CacheTypeNone,
},
@@ -256,14 +256,16 @@ func newTestService(
require.NoError(t, err)
c := &dnssvc.Config{
- Handlers: handlers,
- NewListener: newTestListenerFunc(tl),
- Cloner: agdtest.NewCloner(),
- ErrColl: errColl,
- NonDNS: http.NotFoundHandler(),
- MetricsNamespace: path.Base(t.Name()),
- ServerGroups: srvGrps,
- HandleTimeout: dnssvctest.Timeout,
+ BaseLogger: testLogger,
+ Handlers: handlers,
+ NewListener: newTestListenerFunc(tl),
+ Cloner: agdtest.NewCloner(),
+ ErrColl: errColl,
+ NonDNS: http.NotFoundHandler(),
+ PrometheusRegisterer: prometheus.NewRegistry(),
+ MetricsNamespace: path.Base(t.Name()),
+ ServerGroups: srvGrps,
+ HandleTimeout: dnssvctest.Timeout,
}
svc, err = dnssvc.New(c)
diff --git a/internal/dnssvc/internal/initial/initial.go b/internal/dnssvc/internal/initial/initial.go
index 39ad44b..3db10c0 100644
--- a/internal/dnssvc/internal/initial/initial.go
+++ b/internal/dnssvc/internal/initial/initial.go
@@ -16,6 +16,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal"
"github.com/AdguardTeam/AdGuardDNS/internal/optslog"
+ "github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/miekg/dns"
)
@@ -25,6 +26,7 @@ import (
// middleware.
type Middleware struct {
logger *slog.Logger
+ ddr *DDRConfig
}
// Config is the configuration structure for the initial middleware. All fields
@@ -32,6 +34,34 @@ type Middleware struct {
type Config struct {
// Logger is used to log the operation of the middleware.
Logger *slog.Logger
+
+ // DDR is the configuration for the server group's Discovery Of Designated
+ // Resolvers (DDR) handlers. It must not be nil.
+ DDR *DDRConfig
+}
+
+// DDRConfig is the configuration for the server group's Discovery Of Designated
+// Resolvers (DDR) handlers.
+type DDRConfig struct {
+ // DeviceTargets is the set of all domain names, subdomains of which should
+ // be checked for DDR queries with device IDs.
+ DeviceTargets *container.MapSet[string]
+
+ // PublicTargets is the set of all public domain names, DDR queries for
+ // which should be processed.
+ PublicTargets *container.MapSet[string]
+
+ // DeviceRecordTemplates are used to respond to DDR queries from recognized
+ // devices.
+ DeviceRecordTemplates []*dns.SVCB
+
+ // PubilcRecordTemplates are used to respond to DDR queries from
+ // unrecognized devices.
+ PublicRecordTemplates []*dns.SVCB
+
+ // Enabled shows if DDR queries are processed. If it is false, DDR domain
+ // name queries receive an NXDOMAIN response.
+ Enabled bool
}
// New returns a new initial middleware. c must not be nil, and all its fields
@@ -39,6 +69,7 @@ type Config struct {
func New(c *Config) (mw *Middleware) {
return &Middleware{
logger: c.Logger,
+ ddr: c.DDR,
}
}
diff --git a/internal/dnssvc/internal/initial/specialdomain.go b/internal/dnssvc/internal/initial/specialdomain.go
index 7f57960..49970e7 100644
--- a/internal/dnssvc/internal/initial/specialdomain.go
+++ b/internal/dnssvc/internal/initial/specialdomain.go
@@ -116,18 +116,17 @@ func (mw *Middleware) isDDRRequest(ri *agd.RequestInfo) (ok bool) {
return true
}
- return isDDRDomain(ri, host)
+ return mw.isDDRDomain(ri, host)
}
// isDDRDomain returns true if host is a DDR domain.
-func isDDRDomain(ri *agd.RequestInfo, host string) (ok bool) {
+func (mw *Middleware) isDDRDomain(ri *agd.RequestInfo, host string) (ok bool) {
firstLabel, resolverDomain, cut := strings.Cut(host, ".")
if !cut || firstLabel != DDRLabel {
return false
}
- ddr := ri.ServerGroup.DDR
- if ddr.PublicTargets.Has(resolverDomain) {
+ if mw.ddr.PublicTargets.Has(resolverDomain) {
// The client may simply send a DNS SVCB query using the known name of
// the resolver. This query can be issued to the named Encrypted
// Resolver itself or to any other resolver. Unlike the case of
@@ -144,7 +143,7 @@ func isDDRDomain(ri *agd.RequestInfo, host string) (ok bool) {
firstLabel, resolverDomain, cut = strings.Cut(resolverDomain, ".")
if cut && firstLabel == string(dev.ID) {
// A request for the device ID resolver domain.
- return ddr.DeviceTargets.Has(resolverDomain)
+ return mw.ddr.DeviceTargets.Has(resolverDomain)
}
return false
@@ -162,7 +161,7 @@ func (mw *Middleware) handleDDR(
metrics.DNSSvcDDRRequestsTotal.Inc()
- if ri.ServerGroup.DDR.Enabled {
+ if mw.ddr.Enabled {
return rw.WriteMsg(ctx, req, mw.newRespDDR(req, ri))
}
@@ -181,7 +180,7 @@ func (mw *Middleware) handleDDRNoData(
metrics.DNSSvcDDRRequestsTotal.Inc()
- if ri.ServerGroup.DDR.Enabled {
+ if mw.ddr.Enabled {
return rw.WriteMsg(ctx, req, ri.Messages.NewRespRCode(req, dns.RcodeSuccess))
}
@@ -194,11 +193,10 @@ func (mw *Middleware) handleDDRNoData(
func (mw *Middleware) newRespDDR(req *dns.Msg, ri *agd.RequestInfo) (resp *dns.Msg) {
resp = ri.Messages.NewResp(req)
name := req.Question[0].Name
- ddr := ri.ServerGroup.DDR
// TODO(a.garipov): Optimize calls to ri.DeviceData.
if _, dev := ri.DeviceData(); dev != nil {
- for _, rr := range ddr.DeviceRecordTemplates {
+ for _, rr := range mw.ddr.DeviceRecordTemplates {
rr = dns.Copy(rr).(*dns.SVCB)
rr.Hdr.Name = name
rr.Target = string(dev.ID) + "." + rr.Target
@@ -209,7 +207,7 @@ func (mw *Middleware) newRespDDR(req *dns.Msg, ri *agd.RequestInfo) (resp *dns.M
return resp
}
- for _, rr := range ddr.PublicRecordTemplates {
+ for _, rr := range mw.ddr.PublicRecordTemplates {
rr = dns.Copy(rr).(*dns.SVCB)
rr.Hdr.Name = name
diff --git a/internal/dnssvc/internal/initial/specialdomain_test.go b/internal/dnssvc/internal/initial/specialdomain_test.go
index d37ac01..db6e407 100644
--- a/internal/dnssvc/internal/initial/specialdomain_test.go
+++ b/internal/dnssvc/internal/initial/specialdomain_test.go
@@ -123,6 +123,9 @@ func TestMiddleware_Wrap_specialDomain(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
mw := initial.New(&initial.Config{
Logger: slogutil.NewDiscardLogger(),
+ DDR: &initial.DDRConfig{
+ Enabled: false,
+ },
})
h := mw.Wrap(newSpecDomHandler(tc.wantRCode == dns.RcodeSuccess))
@@ -163,7 +166,6 @@ func newSpecDomReqInfo(
ri = &agd.RequestInfo{
Messages: agdtest.NewConstructor(tb),
- ServerGroup: &agd.ServerGroup{},
FilteringGroup: fltGrp,
Host: host,
QClass: dns.ClassINET,
diff --git a/internal/dnssvc/internal/mainmw/error.go b/internal/dnssvc/internal/mainmw/error.go
index d1f079a..187c2b3 100644
--- a/internal/dnssvc/internal/mainmw/error.go
+++ b/internal/dnssvc/internal/mainmw/error.go
@@ -36,5 +36,6 @@ var _ errcoll.SentryReportableError = afterFilteringError{}
// IsSentryReportable implements the [errcoll.SentryReportableError] interface
// for afterFilteringError.
func (err afterFilteringError) IsSentryReportable() (ok bool) {
- return !errors.Is(err.err, context.DeadlineExceeded)
+ return !errors.Is(err.err, context.DeadlineExceeded) &&
+ !errors.Is(err.err, context.Canceled)
}
diff --git a/internal/dnssvc/internal/mainmw/mainmw_test.go b/internal/dnssvc/internal/mainmw/mainmw_test.go
index 57726dd..8353a5f 100644
--- a/internal/dnssvc/internal/mainmw/mainmw_test.go
+++ b/internal/dnssvc/internal/mainmw/mainmw_test.go
@@ -358,11 +358,13 @@ func newContext(
FilteringGroup: &agd.FilteringGroup{
FilterConfig: fltConf,
},
+ ServerInfo: &agd.RequestServerInfo{
+ Protocol: testProto,
+ },
Messages: agdtest.NewConstructor(tb),
RemoteIP: dnssvctest.ClientAddr,
Host: host,
QType: qType,
- Proto: testProto,
})
return ctx
diff --git a/internal/dnssvc/internal/mainmw/record.go b/internal/dnssvc/internal/mainmw/record.go
index 247f4ed..690a102 100644
--- a/internal/dnssvc/internal/mainmw/record.go
+++ b/internal/dnssvc/internal/mainmw/record.go
@@ -45,7 +45,7 @@ func (mw *Middleware) recordQueryInfo(
reqInfo := dnsserver.MustRequestInfoFromContext(ctx)
start := reqInfo.StartTime
- mw.billStat.Record(ctx, devID, reqCtry, reqASN, start, ri.Proto)
+ mw.billStat.Record(ctx, devID, reqCtry, reqASN, start, ri.ServerInfo.Protocol)
if !prof.QueryLogEnabled {
return
@@ -80,7 +80,7 @@ func (mw *Middleware) recordQueryInfo(
ClientASN: reqASN,
RequestType: ri.QType,
ResponseCode: rcode,
- Protocol: ri.Proto,
+ Protocol: ri.ServerInfo.Protocol,
DNSSEC: respDNSSEC,
RemoteIP: clientIP,
}
diff --git a/internal/dnssvc/internal/mainmw/record_internal_test.go b/internal/dnssvc/internal/mainmw/record_internal_test.go
index 7bcadb7..87d0a80 100644
--- a/internal/dnssvc/internal/mainmw/record_internal_test.go
+++ b/internal/dnssvc/internal/mainmw/record_internal_test.go
@@ -184,6 +184,13 @@ func TestMiddleware_recordQueryInfo_respCtry(t *testing.T) {
QueryLogEnabled: true,
},
},
+ ServerInfo: &agd.RequestServerInfo{
+ GroupName: dnssvctest.ServerGroupName,
+ Name: dnssvctest.ServerName,
+ DeviceDomains: []string{dnssvctest.DomainForDevices},
+ Protocol: agd.ProtoDoT,
+ ProfilesEnabled: true,
+ },
QType: tc.req.Question[0].Qtype,
QClass: class,
}
diff --git a/internal/dnssvc/internal/ratelimitmw/access_test.go b/internal/dnssvc/internal/ratelimitmw/access_test.go
index dc5e798..2852562 100644
--- a/internal/dnssvc/internal/ratelimitmw/access_test.go
+++ b/internal/dnssvc/internal/ratelimitmw/access_test.go
@@ -64,10 +64,13 @@ func TestMiddleware_Wrap_access(t *testing.T) {
Logger: slogutil.NewDiscardLogger(),
Messages: agdtest.NewConstructor(t),
FilteringGroup: &agd.FilteringGroup{},
- ServerGroup: &agd.ServerGroup{},
- Server: &agd.Server{
+ ServerInfo: &agd.RequestServerInfo{
+ GroupName: dnssvctest.ServerGroupName,
+ Name: dnssvctest.ServerName,
+ DeviceDomains: []string{dnssvctest.DomainForDevices},
// Use a DoT server to prevent ratelimiting.
- Protocol: agd.ProtoDoT,
+ Protocol: agd.ProtoDoT,
+ ProfilesEnabled: true,
},
StructuredErrors: agdtest.NewSDEConfig(true),
AccessManager: accessMgr,
diff --git a/internal/dnssvc/internal/ratelimitmw/limit.go b/internal/dnssvc/internal/ratelimitmw/limit.go
index d6f24e9..9d809eb 100644
--- a/internal/dnssvc/internal/ratelimitmw/limit.go
+++ b/internal/dnssvc/internal/ratelimitmw/limit.go
@@ -21,7 +21,7 @@ func (mw *Middleware) serveWithRatelimiting(
ri *agd.RequestInfo,
next dnsserver.Handler,
) (err error) {
- if !slices.Contains(mw.protos, ri.Proto) {
+ if !slices.Contains(mw.protos, ri.ServerInfo.Protocol) {
return next.ServeDNS(ctx, rw, req)
}
diff --git a/internal/dnssvc/internal/ratelimitmw/ratelimitmw.go b/internal/dnssvc/internal/ratelimitmw/ratelimitmw.go
index 338dfad..b7f8239 100644
--- a/internal/dnssvc/internal/ratelimitmw/ratelimitmw.go
+++ b/internal/dnssvc/internal/ratelimitmw/ratelimitmw.go
@@ -48,41 +48,46 @@ type Middleware struct {
// Config is the configuration structure for the access and ratelimiting
// middleware. All fields must not be empty.
type Config struct {
- // Logger is used to log the operation of the middleware.
+ // Logger is used to log the operation of the middleware. It must not be
+ // nil.
Logger *slog.Logger
// Messages is used to build the responses specific for a request's context.
+ // It must not be nil.
Messages *dnsmsg.Constructor
// FilteringGroup is the filtering group to which [Config.Server] belongs.
+ // It must not be nil.
FilteringGroup *agd.FilteringGroup
- // ServerGroup is the server group to which [Config.Server] belongs.
- ServerGroup *agd.ServerGroup
-
- // Server is the current server which serves the request.
- Server *agd.Server
+ // ServerInfo contains the information about the server processing the
+ // queries and its server group. It must not be nil.
+ ServerInfo *agd.RequestServerInfo
// StructuredErrors is the configuration for the experimental Structured DNS
- // Errors feature for the profiles' message constructors.
+ // Errors feature for the profiles' message constructors. It must not be
+ // nil.
StructuredErrors *dnsmsg.StructuredDNSErrorsConfig
- // AccessManager is the global access manager.
+ // AccessManager is the global access manager. It must not be nil.
AccessManager access.Interface
// DeviceFinder is used to set the device and profile for a request, if any.
+ // If [Config.ServerInfo.ProfilesEnabled] is true, it must not be nil.
DeviceFinder agd.DeviceFinder
- // ErrColl collects and reports the errors considered non-critical.
+ // ErrColl collects and reports the errors considered non-critical. It must
+ // not be nil.
ErrColl errcoll.Interface
- // GeoIP detects the location of the request source.
+ // GeoIP detects the location of the request source. It must not be nil.
GeoIP geoip.Interface
- // Metrics is a listener for the middleware events.
+ // Metrics is a listener for the middleware events. It must not be nil.
Metrics Metrics
- // Limiter defines whether the query should be dropped or not.
+ // Limiter defines whether the query should be dropped or not. It must not
+ // be nil.
Limiter ratelimit.Interface
// Protocols is a list of protocols this middleware applies ratelimiting
@@ -103,9 +108,7 @@ func New(c *Config) (mw *Middleware) {
// Set the filtering-group and server information here immediately.
return &agd.RequestInfo{
FilteringGroup: c.FilteringGroup,
- ServerGroup: c.ServerGroup,
- Server: c.Server.Name,
- Proto: c.Server.Protocol,
+ ServerInfo: c.ServerInfo,
}
}),
sdeConf: c.StructuredErrors,
diff --git a/internal/ecscache/ecsblocklist.go b/internal/ecscache/ecsblocklist.go
index 8246cc1..3075310 100644
--- a/internal/ecscache/ecsblocklist.go
+++ b/internal/ecscache/ecsblocklist.go
@@ -7,7 +7,6 @@ import "github.com/AdguardTeam/golibs/container"
// FakeECSFQDNs contains all domains that indicate ECS support, but in fact
// don't have one.
var FakeECSFQDNs = container.NewMapSet(
- "0.html-load.com.",
"0231034e888a4837a17fd85b1ab159.ba.tenant.api.powerplatform.com.",
"0272ac85-5199-4024-a555-397c3d825d95.prmutv.co.",
"0b732edd-087f-4c27-b0f6-5ff26d96cdf6.prmutv.co.",
@@ -18,11 +17,11 @@ var FakeECSFQDNs = container.NewMapSet(
"103.chtsite.com.",
"11-alarm-mop.meshare.com.",
"12-alarm-mop.meshare.com.",
+ "123ipip.icu.",
"126.com.",
"126.net.",
"127.net.",
"13-alarm-mop.meshare.com.",
- "1337x.to.",
"14-alarm-mop.meshare.com.",
"1447723502.rsc.cdn77.org.",
"1533248697.rsc.cdn77.org.",
@@ -32,20 +31,14 @@ var FakeECSFQDNs = container.NewMapSet(
"163yun.com.",
"1663719199.rsc.cdn77.org.",
"1688.com.",
- "17de4c1f.akstat.io.",
"1845588971.rsc.cdn77.org.",
"18ea70d2d9a945cfb97d818ba71817dc.pacloudflare.com.",
- "193377-ipv4mte.gr.global.aa-rt.sharepoint.com.",
- "193767-ipv4mte.gr.global.aa-rt.sharepoint.com.",
- "193918-ipv4mte.gr.global.aa-rt.sharepoint.com.",
- "1e3f1c45e0354da8a119ffe5209cab06.pacloudflare.com.",
"2.401402081.west-gcloud.codm.activision.com.",
- "2.api.pof.com.",
"2.realtime.services.box.net.",
- "201205igp.gameloft.com.",
"2acdb9b66bb242618283aadb21ede6c1.pacloudflare.com.",
"2e4b93d1-a8ae-4a89-8885-6109135ac0de.prmutv.co.",
- "2talk.com.",
+ "2gis.ru.",
+ "33ipip.top.",
"360safe.com.",
"3a6b0682-f3e1-4576-a706-5eb4101b9cc3.prmutv.co.",
"3aba5292-ba75-422b-8715-bd21146f7836.prmutv.co.",
@@ -54,14 +47,14 @@ var FakeECSFQDNs = container.NewMapSet(
"4.adsco.re.",
"401402081.west-gcloud.codm.activision.com.",
"4186979c18134d1eae82ec64dbfc9af2.pacloudflare.com.",
- "46a2e5c3c5a64e218b60f2c2ee76b750.pacloudflare.com.",
"4b32bb64ce554875ae3f8836479c89d4.pacloudflare.com.",
+ "507b28fb-2ef1-4c34-8bda-ba32030bb199.prmutv.co.",
"520.jp.",
- "59b517704ce43f0f.cartx.cloud.",
"5d79bce7-5d2b-427e-a6c4-b89b6c7bf048.prmutv.co.",
"5nines.com.",
"6.401402081.west-gcloud.codm.activision.com.",
"6093eccf-6734-4877-ac8b-83d6d0e27b46.prmutv.co.",
+ "64.adsco.re.",
"66yahoo.com.",
"733868e706dd40d3a4a0588fc39b3df8.pacloudflare.com.",
"7c7b02d4bc3d48dd81a7c7738d4de1ab.pacloudflare.com.",
@@ -73,203 +66,14 @@ var FakeECSFQDNs = container.NewMapSet(
"a-api.anthropic.com.",
"a-cdn.anthropic.com.",
"a-m-p.xyz.",
- "a.api.permutive.app.",
- "a.applvn.com.",
+ "a.audrte.com.",
"a.getepic.com.",
+ "a.lulucdn.com.",
"a.nel.cloudflare.com.",
"a.nitropay.com.",
- "a10681260716.cdn.optimizely.com.",
- "a2321.casalemedia.com.",
- "a2322.casalemedia.com.",
- "a2323.casalemedia.com.",
- "a2324.casalemedia.com.",
- "a2325.casalemedia.com.",
- "a2326.casalemedia.com.",
- "a2327.casalemedia.com.",
- "a2328.casalemedia.com.",
- "a2329.casalemedia.com.",
- "a2330.casalemedia.com.",
- "a2331.casalemedia.com.",
- "a2332.casalemedia.com.",
- "a2333.casalemedia.com.",
- "a2334.casalemedia.com.",
- "a2336.casalemedia.com.",
- "a2337.casalemedia.com.",
- "a2338.casalemedia.com.",
- "a2339.casalemedia.com.",
- "a2340.casalemedia.com.",
- "a2341.casalemedia.com.",
- "a2342.casalemedia.com.",
- "a2344.casalemedia.com.",
- "a2345.casalemedia.com.",
- "a2346.casalemedia.com.",
- "a2347.casalemedia.com.",
- "a2348.casalemedia.com.",
- "a2350.casalemedia.com.",
- "a2351.casalemedia.com.",
- "a2352.casalemedia.com.",
- "a2353.casalemedia.com.",
- "a2354.casalemedia.com.",
- "a2355.casalemedia.com.",
- "a2356.casalemedia.com.",
- "a2358.casalemedia.com.",
- "a2359.casalemedia.com.",
- "a2361.casalemedia.com.",
- "a2362.casalemedia.com.",
- "a2363.casalemedia.com.",
- "a2364.casalemedia.com.",
- "a2365.casalemedia.com.",
- "a2366.casalemedia.com.",
- "a2367.casalemedia.com.",
- "a2368.casalemedia.com.",
- "a2369.casalemedia.com.",
- "a2370.casalemedia.com.",
- "a2371.casalemedia.com.",
- "a2372.casalemedia.com.",
- "a2373.casalemedia.com.",
- "a2375.casalemedia.com.",
- "a2377.casalemedia.com.",
- "a2378.casalemedia.com.",
- "a2379.casalemedia.com.",
- "a2380.casalemedia.com.",
- "a2381.casalemedia.com.",
- "a2382.casalemedia.com.",
- "a2383.casalemedia.com.",
- "a2384.casalemedia.com.",
- "a2385.casalemedia.com.",
- "a2386.casalemedia.com.",
- "a2387.casalemedia.com.",
- "a2388.casalemedia.com.",
- "a2389.casalemedia.com.",
- "a2390.casalemedia.com.",
- "a2391.casalemedia.com.",
- "a2392.casalemedia.com.",
- "a2393.casalemedia.com.",
- "a2394.casalemedia.com.",
- "a2395.casalemedia.com.",
- "a2396.casalemedia.com.",
- "a2397.casalemedia.com.",
- "a2398.casalemedia.com.",
- "a2399.casalemedia.com.",
- "a2400.casalemedia.com.",
- "a2402.casalemedia.com.",
- "a2403.casalemedia.com.",
- "a2404.casalemedia.com.",
- "a2405.casalemedia.com.",
- "a2406.casalemedia.com.",
- "a2407.casalemedia.com.",
- "a2408.casalemedia.com.",
- "a2411.casalemedia.com.",
- "a2412.casalemedia.com.",
- "a2413.casalemedia.com.",
- "a2414.casalemedia.com.",
- "a2415.casalemedia.com.",
- "a2416.casalemedia.com.",
- "a2417.casalemedia.com.",
- "a2418.casalemedia.com.",
- "a2419.casalemedia.com.",
- "a2420.casalemedia.com.",
- "a2421.casalemedia.com.",
- "a2422.casalemedia.com.",
- "a2423.casalemedia.com.",
- "a2425.casalemedia.com.",
- "a2426.casalemedia.com.",
- "a2427.casalemedia.com.",
- "a2428.casalemedia.com.",
- "a2429.casalemedia.com.",
- "a2430.casalemedia.com.",
- "a2431.casalemedia.com.",
- "a2432.casalemedia.com.",
- "a2433.casalemedia.com.",
- "a2434.casalemedia.com.",
- "a2435.casalemedia.com.",
- "a2436.casalemedia.com.",
- "a2437.casalemedia.com.",
- "a2438.casalemedia.com.",
- "a2439.casalemedia.com.",
- "a2440.casalemedia.com.",
- "a2441.casalemedia.com.",
- "a2442.casalemedia.com.",
- "a2443.casalemedia.com.",
- "a2444.casalemedia.com.",
- "a2445.casalemedia.com.",
- "a2446.casalemedia.com.",
- "a2447.casalemedia.com.",
- "a2448.casalemedia.com.",
- "a2449.casalemedia.com.",
- "a2450.casalemedia.com.",
- "a2451.casalemedia.com.",
- "a2452.casalemedia.com.",
- "a2453.casalemedia.com.",
- "a2454.casalemedia.com.",
- "a2456.casalemedia.com.",
- "a2457.casalemedia.com.",
- "a2458.casalemedia.com.",
- "a2459.casalemedia.com.",
- "a2460.casalemedia.com.",
- "a2461.casalemedia.com.",
- "a2462.casalemedia.com.",
- "a2463.casalemedia.com.",
- "a2464.casalemedia.com.",
- "a2466.casalemedia.com.",
- "a2467.casalemedia.com.",
- "a2469.casalemedia.com.",
- "a2470.casalemedia.com.",
- "a2471.casalemedia.com.",
- "a2473.casalemedia.com.",
- "a2474.casalemedia.com.",
- "a2475.casalemedia.com.",
- "a2476.casalemedia.com.",
- "a2477.casalemedia.com.",
- "a2478.casalemedia.com.",
- "a2479.casalemedia.com.",
- "a2480.casalemedia.com.",
- "a2481.casalemedia.com.",
- "a2483.casalemedia.com.",
- "a2484.casalemedia.com.",
- "a2485.casalemedia.com.",
- "a2486.casalemedia.com.",
- "a2488.casalemedia.com.",
- "a2489.casalemedia.com.",
- "a2490.casalemedia.com.",
- "a2492.casalemedia.com.",
- "a2493.casalemedia.com.",
- "a2494.casalemedia.com.",
- "a2495.casalemedia.com.",
- "a2496.casalemedia.com.",
- "a2497.casalemedia.com.",
- "a2498.casalemedia.com.",
- "a2499.casalemedia.com.",
- "a2500.casalemedia.com.",
- "a2501.casalemedia.com.",
- "a2502.casalemedia.com.",
- "a2503.casalemedia.com.",
- "a2504.casalemedia.com.",
- "a2505.casalemedia.com.",
- "a2506.casalemedia.com.",
- "a2507.casalemedia.com.",
- "a2508.casalemedia.com.",
- "a2509.casalemedia.com.",
- "a2510.casalemedia.com.",
- "a2511.casalemedia.com.",
- "a2512.casalemedia.com.",
- "a2513.casalemedia.com.",
- "a2514.casalemedia.com.",
- "a2515.casalemedia.com.",
- "a2516.casalemedia.com.",
- "a2517.casalemedia.com.",
- "a2518.casalemedia.com.",
- "a2519.casalemedia.com.",
- "a2521.casalemedia.com.",
- "a2522.casalemedia.com.",
- "a2523.casalemedia.com.",
- "a2524.casalemedia.com.",
- "a2525.casalemedia.com.",
- "a2526.casalemedia.com.",
- "a2527.casalemedia.com.",
- "a2528.casalemedia.com.",
- "a2529.casalemedia.com.",
- "a2681.casalemedia.com.",
+ "a.quora.com.",
+ "a1521dca917ff0e871c935c9253afc0626587b2d.cws.conviva.com.",
+ "a16dda3b33f14e7dbbf0aee44dc53784.pacloudflare.com.",
"a2682.casalemedia.com.",
"a2683.casalemedia.com.",
"a2684.casalemedia.com.",
@@ -289,10 +93,10 @@ var FakeECSFQDNs = container.NewMapSet(
"a2698.casalemedia.com.",
"a2699.casalemedia.com.",
"a2700.casalemedia.com.",
+ "a2701.casalemedia.com.",
"a2702.casalemedia.com.",
"a2703.casalemedia.com.",
"a2704.casalemedia.com.",
- "a2705.casalemedia.com.",
"a2706.casalemedia.com.",
"a2707.casalemedia.com.",
"a2708.casalemedia.com.",
@@ -330,14 +134,12 @@ var FakeECSFQDNs = container.NewMapSet(
"a2740.casalemedia.com.",
"a2741.casalemedia.com.",
"a2742.casalemedia.com.",
- "a2743.casalemedia.com.",
"a2744.casalemedia.com.",
"a2745.casalemedia.com.",
"a2746.casalemedia.com.",
"a2747.casalemedia.com.",
"a2748.casalemedia.com.",
"a2749.casalemedia.com.",
- "a2750.casalemedia.com.",
"a2751.casalemedia.com.",
"a2752.casalemedia.com.",
"a2753.casalemedia.com.",
@@ -346,19 +148,19 @@ var FakeECSFQDNs = container.NewMapSet(
"a2756.casalemedia.com.",
"a2757.casalemedia.com.",
"a2758.casalemedia.com.",
+ "a2759.casalemedia.com.",
"a2760.casalemedia.com.",
"a2761.casalemedia.com.",
"a2762.casalemedia.com.",
"a2763.casalemedia.com.",
"a2764.casalemedia.com.",
- "a2765.casalemedia.com.",
"a2766.casalemedia.com.",
"a2767.casalemedia.com.",
"a2768.casalemedia.com.",
+ "a2769.casalemedia.com.",
"a2770.casalemedia.com.",
"a2771.casalemedia.com.",
"a2772.casalemedia.com.",
- "a2773.casalemedia.com.",
"a2774.casalemedia.com.",
"a2775.casalemedia.com.",
"a2776.casalemedia.com.",
@@ -368,12 +170,12 @@ var FakeECSFQDNs = container.NewMapSet(
"a2780.casalemedia.com.",
"a2781.casalemedia.com.",
"a2782.casalemedia.com.",
+ "a2783.casalemedia.com.",
"a2784.casalemedia.com.",
"a2785.casalemedia.com.",
"a2786.casalemedia.com.",
"a2787.casalemedia.com.",
"a2788.casalemedia.com.",
- "a2790.casalemedia.com.",
"a2791.casalemedia.com.",
"a2793.casalemedia.com.",
"a2794.casalemedia.com.",
@@ -386,7 +188,6 @@ var FakeECSFQDNs = container.NewMapSet(
"a2a5c7f9-3fa0-4182-889a-15aa61acf59b.prmutv.co.",
"a3551.casalemedia.com.",
"a3552.casalemedia.com.",
- "a3553.casalemedia.com.",
"a3554.casalemedia.com.",
"a3555.casalemedia.com.",
"a3556.casalemedia.com.",
@@ -403,6 +204,7 @@ var FakeECSFQDNs = container.NewMapSet(
"a3568.casalemedia.com.",
"a3569.casalemedia.com.",
"a3570.casalemedia.com.",
+ "a3571.casalemedia.com.",
"a3572.casalemedia.com.",
"a3573.casalemedia.com.",
"a3574.casalemedia.com.",
@@ -410,16 +212,17 @@ var FakeECSFQDNs = container.NewMapSet(
"a3576.casalemedia.com.",
"a3577.casalemedia.com.",
"a3578.casalemedia.com.",
+ "a3579.casalemedia.com.",
"a3580.casalemedia.com.",
"a3581.casalemedia.com.",
"a3582.casalemedia.com.",
- "a3583.casalemedia.com.",
"a3584.casalemedia.com.",
"a3585.casalemedia.com.",
"a3586.casalemedia.com.",
"a3587.casalemedia.com.",
"a3588.casalemedia.com.",
"a3589.casalemedia.com.",
+ "a3590.casalemedia.com.",
"a3591.casalemedia.com.",
"a3592.casalemedia.com.",
"a3593.casalemedia.com.",
@@ -431,7 +234,6 @@ var FakeECSFQDNs = container.NewMapSet(
"a3599.casalemedia.com.",
"a3600.casalemedia.com.",
"a3601.casalemedia.com.",
- "a3602.casalemedia.com.",
"a3603.casalemedia.com.",
"a3604.casalemedia.com.",
"a3605.casalemedia.com.",
@@ -443,7 +245,6 @@ var FakeECSFQDNs = container.NewMapSet(
"a3611.casalemedia.com.",
"a3612.casalemedia.com.",
"a3613.casalemedia.com.",
- "a3614.casalemedia.com.",
"a3615.casalemedia.com.",
"a3616.casalemedia.com.",
"a3617.casalemedia.com.",
@@ -455,17 +256,13 @@ var FakeECSFQDNs = container.NewMapSet(
"a3623.casalemedia.com.",
"a3624.casalemedia.com.",
"a3625.casalemedia.com.",
- "a3626.casalemedia.com.",
"a3627.casalemedia.com.",
- "a3628.casalemedia.com.",
"a3629.casalemedia.com.",
"a3630.casalemedia.com.",
"a3631.casalemedia.com.",
"a3632.casalemedia.com.",
"a3633.casalemedia.com.",
"a3634.casalemedia.com.",
- "a3635.casalemedia.com.",
- "a3636.casalemedia.com.",
"a3637.casalemedia.com.",
"a3638.casalemedia.com.",
"a3639.casalemedia.com.",
@@ -474,11 +271,11 @@ var FakeECSFQDNs = container.NewMapSet(
"a3642.casalemedia.com.",
"a3643.casalemedia.com.",
"a3644.casalemedia.com.",
+ "a3645.casalemedia.com.",
"a3646.casalemedia.com.",
"a3647.casalemedia.com.",
"a3648.casalemedia.com.",
- "a3649.casalemedia.com.",
- "a3651.casalemedia.com.",
+ "a3650.casalemedia.com.",
"a3652.casalemedia.com.",
"a3653.casalemedia.com.",
"a3654.casalemedia.com.",
@@ -492,16 +289,14 @@ var FakeECSFQDNs = container.NewMapSet(
"a3662.casalemedia.com.",
"a3663.casalemedia.com.",
"a3664.casalemedia.com.",
- "a3665.casalemedia.com.",
"a3666.casalemedia.com.",
"a3667.casalemedia.com.",
"a3668.casalemedia.com.",
"a3669.casalemedia.com.",
- "a3670.casalemedia.com.",
- "a3671.casalemedia.com.",
"a3672.casalemedia.com.",
"a3673.casalemedia.com.",
"a3674.casalemedia.com.",
+ "a3675.casalemedia.com.",
"a3676.casalemedia.com.",
"a3677.casalemedia.com.",
"a3678.casalemedia.com.",
@@ -555,8 +350,206 @@ var FakeECSFQDNs = container.NewMapSet(
"a3728.casalemedia.com.",
"a3729.casalemedia.com.",
"a3730.casalemedia.com.",
+ "a3881.casalemedia.com.",
+ "a3882.casalemedia.com.",
+ "a3883.casalemedia.com.",
+ "a3884.casalemedia.com.",
+ "a3885.casalemedia.com.",
+ "a3886.casalemedia.com.",
+ "a3887.casalemedia.com.",
+ "a3888.casalemedia.com.",
+ "a3889.casalemedia.com.",
+ "a3890.casalemedia.com.",
+ "a3891.casalemedia.com.",
+ "a3892.casalemedia.com.",
+ "a3893.casalemedia.com.",
+ "a3894.casalemedia.com.",
+ "a3895.casalemedia.com.",
+ "a3896.casalemedia.com.",
+ "a3897.casalemedia.com.",
+ "a3898.casalemedia.com.",
+ "a3899.casalemedia.com.",
+ "a3900.casalemedia.com.",
+ "a3901.casalemedia.com.",
+ "a3902.casalemedia.com.",
+ "a3903.casalemedia.com.",
+ "a3904.casalemedia.com.",
+ "a3905.casalemedia.com.",
+ "a3906.casalemedia.com.",
+ "a3907.casalemedia.com.",
+ "a3908.casalemedia.com.",
+ "a3909.casalemedia.com.",
+ "a3910.casalemedia.com.",
+ "a3911.casalemedia.com.",
+ "a3912.casalemedia.com.",
+ "a3913.casalemedia.com.",
+ "a3914.casalemedia.com.",
+ "a3915.casalemedia.com.",
+ "a3916.casalemedia.com.",
+ "a3918.casalemedia.com.",
+ "a3919.casalemedia.com.",
+ "a3920.casalemedia.com.",
+ "a3922.casalemedia.com.",
+ "a3923.casalemedia.com.",
+ "a3924.casalemedia.com.",
+ "a3925.casalemedia.com.",
+ "a3926.casalemedia.com.",
+ "a3927.casalemedia.com.",
+ "a3928.casalemedia.com.",
+ "a3929.casalemedia.com.",
+ "a3930.casalemedia.com.",
+ "a3931.casalemedia.com.",
+ "a3932.casalemedia.com.",
+ "a3933.casalemedia.com.",
+ "a3934.casalemedia.com.",
+ "a3935.casalemedia.com.",
+ "a3936.casalemedia.com.",
+ "a3937.casalemedia.com.",
+ "a3938.casalemedia.com.",
+ "a3939.casalemedia.com.",
+ "a3940.casalemedia.com.",
+ "a3941.casalemedia.com.",
+ "a3942.casalemedia.com.",
+ "a3943.casalemedia.com.",
+ "a3944.casalemedia.com.",
+ "a3945.casalemedia.com.",
+ "a3946.casalemedia.com.",
+ "a3947.casalemedia.com.",
+ "a3948.casalemedia.com.",
+ "a3949.casalemedia.com.",
+ "a3950.casalemedia.com.",
+ "a3951.casalemedia.com.",
+ "a3952.casalemedia.com.",
+ "a3953.casalemedia.com.",
+ "a3954.casalemedia.com.",
+ "a3955.casalemedia.com.",
+ "a3956.casalemedia.com.",
+ "a3957.casalemedia.com.",
+ "a3958.casalemedia.com.",
+ "a3959.casalemedia.com.",
+ "a3960.casalemedia.com.",
+ "a3961.casalemedia.com.",
+ "a3962.casalemedia.com.",
+ "a3963.casalemedia.com.",
+ "a3964.casalemedia.com.",
+ "a3965.casalemedia.com.",
+ "a3966.casalemedia.com.",
+ "a3967.casalemedia.com.",
+ "a3968.casalemedia.com.",
+ "a3969.casalemedia.com.",
+ "a3970.casalemedia.com.",
+ "a3971.casalemedia.com.",
+ "a3972.casalemedia.com.",
+ "a3973.casalemedia.com.",
+ "a3974.casalemedia.com.",
+ "a3976.casalemedia.com.",
+ "a3977.casalemedia.com.",
+ "a3978.casalemedia.com.",
+ "a3980.casalemedia.com.",
+ "a3981.casalemedia.com.",
+ "a3982.casalemedia.com.",
+ "a3983.casalemedia.com.",
+ "a3984.casalemedia.com.",
+ "a3985.casalemedia.com.",
+ "a3987.casalemedia.com.",
+ "a3988.casalemedia.com.",
+ "a3989.casalemedia.com.",
+ "a3990.casalemedia.com.",
+ "a3991.casalemedia.com.",
+ "a3992.casalemedia.com.",
+ "a3993.casalemedia.com.",
+ "a3994.casalemedia.com.",
+ "a3995.casalemedia.com.",
+ "a3996.casalemedia.com.",
+ "a3997.casalemedia.com.",
+ "a3998.casalemedia.com.",
+ "a3999.casalemedia.com.",
+ "a3ehe.com.",
+ "a4000.casalemedia.com.",
+ "a4001.casalemedia.com.",
+ "a4002.casalemedia.com.",
+ "a4003.casalemedia.com.",
+ "a4004.casalemedia.com.",
+ "a4005.casalemedia.com.",
+ "a4006.casalemedia.com.",
+ "a4007.casalemedia.com.",
+ "a4008.casalemedia.com.",
+ "a4009.casalemedia.com.",
+ "a4010.casalemedia.com.",
+ "a4011.casalemedia.com.",
+ "a4012.casalemedia.com.",
+ "a4013.casalemedia.com.",
+ "a4014.casalemedia.com.",
+ "a4015.casalemedia.com.",
+ "a4016.casalemedia.com.",
+ "a4017.casalemedia.com.",
+ "a4018.casalemedia.com.",
+ "a4019.casalemedia.com.",
+ "a4020.casalemedia.com.",
+ "a4021.casalemedia.com.",
+ "a4022.casalemedia.com.",
+ "a4023.casalemedia.com.",
+ "a4024.casalemedia.com.",
+ "a4025.casalemedia.com.",
+ "a4026.casalemedia.com.",
+ "a4027.casalemedia.com.",
+ "a4028.casalemedia.com.",
+ "a4029.casalemedia.com.",
+ "a4030.casalemedia.com.",
+ "a4031.casalemedia.com.",
+ "a4032.casalemedia.com.",
+ "a4033.casalemedia.com.",
+ "a4034.casalemedia.com.",
+ "a4035.casalemedia.com.",
+ "a4036.casalemedia.com.",
+ "a4037.casalemedia.com.",
+ "a4038.casalemedia.com.",
+ "a4039.casalemedia.com.",
+ "a4041.casalemedia.com.",
+ "a4042.casalemedia.com.",
+ "a4043.casalemedia.com.",
+ "a4045.casalemedia.com.",
+ "a4046.casalemedia.com.",
+ "a4047.casalemedia.com.",
+ "a4048.casalemedia.com.",
+ "a4049.casalemedia.com.",
+ "a4050.casalemedia.com.",
+ "a4051.casalemedia.com.",
+ "a4052.casalemedia.com.",
+ "a4053.casalemedia.com.",
+ "a4054.casalemedia.com.",
+ "a4055.casalemedia.com.",
+ "a4056.casalemedia.com.",
+ "a4057.casalemedia.com.",
+ "a4058.casalemedia.com.",
+ "a4059.casalemedia.com.",
+ "a4060.casalemedia.com.",
+ "a4061.casalemedia.com.",
+ "a4062.casalemedia.com.",
+ "a4063.casalemedia.com.",
+ "a4064.casalemedia.com.",
+ "a4065.casalemedia.com.",
+ "a4066.casalemedia.com.",
+ "a4067.casalemedia.com.",
+ "a4068.casalemedia.com.",
+ "a4069.casalemedia.com.",
+ "a4070.casalemedia.com.",
+ "a4071.casalemedia.com.",
+ "a4072.casalemedia.com.",
+ "a4073.casalemedia.com.",
+ "a4074.casalemedia.com.",
+ "a4077.casalemedia.com.",
+ "a4078.casalemedia.com.",
+ "a4079.casalemedia.com.",
+ "a4080.casalemedia.com.",
+ "a4082.casalemedia.com.",
+ "a4084.casalemedia.com.",
+ "a4085.casalemedia.com.",
+ "a4086.casalemedia.com.",
+ "a4087.casalemedia.com.",
+ "a4088.casalemedia.com.",
+ "a4089.casalemedia.com.",
"a5551.casalemedia.com.",
- "a5555.casalemedia.com.",
"a5556.casalemedia.com.",
"a5557.casalemedia.com.",
"a5558.casalemedia.com.",
@@ -565,10 +558,8 @@ var FakeECSFQDNs = container.NewMapSet(
"a5561.casalemedia.com.",
"a5562.casalemedia.com.",
"a5563.casalemedia.com.",
- "a5565.casalemedia.com.",
- "a5566.casalemedia.com.",
+ "a5564.casalemedia.com.",
"a5567.casalemedia.com.",
- "a5568.casalemedia.com.",
"a5569.casalemedia.com.",
"a5570.casalemedia.com.",
"a5571.casalemedia.com.",
@@ -576,12 +567,12 @@ var FakeECSFQDNs = container.NewMapSet(
"a5573.casalemedia.com.",
"a5574.casalemedia.com.",
"a5575.casalemedia.com.",
+ "a5576.casalemedia.com.",
"a5577.casalemedia.com.",
"a5578.casalemedia.com.",
"a5579.casalemedia.com.",
"a5580.casalemedia.com.",
"a5581.casalemedia.com.",
- "a5582.casalemedia.com.",
"a5583.casalemedia.com.",
"a5584.casalemedia.com.",
"a5585.casalemedia.com.",
@@ -590,12 +581,12 @@ var FakeECSFQDNs = container.NewMapSet(
"a5588.casalemedia.com.",
"a5589.casalemedia.com.",
"a5590.casalemedia.com.",
+ "a5591.casalemedia.com.",
"a5593.casalemedia.com.",
"a5594.casalemedia.com.",
"a5595.casalemedia.com.",
"a5596.casalemedia.com.",
"a5597.casalemedia.com.",
- "a5598.casalemedia.com.",
"a5599.casalemedia.com.",
"a55a84b3-9632-4869-b625-3d8ef43ed18d.prmutv.co.",
"a5600.casalemedia.com.",
@@ -608,7 +599,6 @@ var FakeECSFQDNs = container.NewMapSet(
"a5607.casalemedia.com.",
"a5608.casalemedia.com.",
"a5609.casalemedia.com.",
- "a5610.casalemedia.com.",
"a5611.casalemedia.com.",
"a5612.casalemedia.com.",
"a5613.casalemedia.com.",
@@ -620,10 +610,10 @@ var FakeECSFQDNs = container.NewMapSet(
"a5619.casalemedia.com.",
"a5620.casalemedia.com.",
"a5621.casalemedia.com.",
- "a5622.casalemedia.com.",
"a5623.casalemedia.com.",
"a5624.casalemedia.com.",
"a5625.casalemedia.com.",
+ "a5626.casalemedia.com.",
"a5627.casalemedia.com.",
"a5628.casalemedia.com.",
"a5629.casalemedia.com.",
@@ -633,39 +623,40 @@ var FakeECSFQDNs = container.NewMapSet(
"a5633.casalemedia.com.",
"a5634.casalemedia.com.",
"a5635.casalemedia.com.",
- "a5636.casalemedia.com.",
"a5637.casalemedia.com.",
"a5638.casalemedia.com.",
"a5639.casalemedia.com.",
"a5640.casalemedia.com.",
- "a5641.casalemedia.com.",
+ "a5642.casalemedia.com.",
"a5643.casalemedia.com.",
+ "a5644.casalemedia.com.",
"a5645.casalemedia.com.",
+ "a5647.casalemedia.com.",
"a5648.casalemedia.com.",
"a5649.casalemedia.com.",
+ "a5654.casalemedia.com.",
"a5656.casalemedia.com.",
"a5657.casalemedia.com.",
- "a5667.casalemedia.com.",
+ "a5658.casalemedia.com.",
+ "a5664.casalemedia.com.",
+ "a5665.casalemedia.com.",
"a5668.casalemedia.com.",
"a5671.casalemedia.com.",
"a5672.casalemedia.com.",
"a5673.casalemedia.com.",
- "a5674.casalemedia.com.",
"a5675.casalemedia.com.",
"a5676.casalemedia.com.",
"a5677.casalemedia.com.",
"a5678.casalemedia.com.",
+ "a5679.casalemedia.com.",
"a5680.casalemedia.com.",
"a5681.casalemedia.com.",
"a5682.casalemedia.com.",
- "a5684.casalemedia.com.",
- "a5685.casalemedia.com.",
- "a5686.casalemedia.com.",
+ "a5683.casalemedia.com.",
"a5687.casalemedia.com.",
"a5688.casalemedia.com.",
"a5689.casalemedia.com.",
"a5690.casalemedia.com.",
- "a5691.casalemedia.com.",
"a5692.casalemedia.com.",
"a5693.casalemedia.com.",
"a5694.casalemedia.com.",
@@ -678,69 +669,118 @@ var FakeECSFQDNs = container.NewMapSet(
"a5701.casalemedia.com.",
"a5702.casalemedia.com.",
"a5703.casalemedia.com.",
+ "a5704.casalemedia.com.",
+ "a5705.casalemedia.com.",
"a5706.casalemedia.com.",
- "a5707.casalemedia.com.",
+ "a5708.casalemedia.com.",
+ "a5709.casalemedia.com.",
"a5710.casalemedia.com.",
+ "a5731.casalemedia.com.",
+ "a5732.casalemedia.com.",
+ "a5733.casalemedia.com.",
+ "a5734.casalemedia.com.",
+ "a5735.casalemedia.com.",
+ "a5736.casalemedia.com.",
+ "a5737.casalemedia.com.",
+ "a5738.casalemedia.com.",
+ "a5739.casalemedia.com.",
+ "a5740.casalemedia.com.",
+ "a5742.casalemedia.com.",
+ "a5743.casalemedia.com.",
+ "a5744.casalemedia.com.",
+ "a5745.casalemedia.com.",
+ "a5746.casalemedia.com.",
+ "a5747.casalemedia.com.",
+ "a5748.casalemedia.com.",
+ "a5749.casalemedia.com.",
+ "a5750.casalemedia.com.",
+ "a5751.casalemedia.com.",
+ "a5752.casalemedia.com.",
+ "a5753.casalemedia.com.",
+ "a5754.casalemedia.com.",
+ "a5755.casalemedia.com.",
+ "a5756.casalemedia.com.",
+ "a5757.casalemedia.com.",
+ "a5758.casalemedia.com.",
+ "a5759.casalemedia.com.",
+ "a5760.casalemedia.com.",
+ "a5761.casalemedia.com.",
+ "a5762.casalemedia.com.",
+ "a5763.casalemedia.com.",
+ "a5764.casalemedia.com.",
+ "a5765.casalemedia.com.",
+ "a5766.casalemedia.com.",
+ "a5767.casalemedia.com.",
+ "a5768.casalemedia.com.",
+ "a5769.casalemedia.com.",
+ "a5770.casalemedia.com.",
+ "a5771.casalemedia.com.",
+ "a5772.casalemedia.com.",
+ "a5773.casalemedia.com.",
+ "a5774.casalemedia.com.",
+ "a5775.casalemedia.com.",
"a5776.casalemedia.com.",
- "a5777.casalemedia.com.",
"a5778.casalemedia.com.",
"a5779.casalemedia.com.",
"a5780.casalemedia.com.",
"a5781.casalemedia.com.",
+ "a5782.casalemedia.com.",
"a5783.casalemedia.com.",
"a5784.casalemedia.com.",
+ "a5785.casalemedia.com.",
"a5786.casalemedia.com.",
"a5787.casalemedia.com.",
"a5788.casalemedia.com.",
- "a5789.casalemedia.com.",
"a5790.casalemedia.com.",
"a5791.casalemedia.com.",
+ "a5792.casalemedia.com.",
"a5793.casalemedia.com.",
"a5794.casalemedia.com.",
"a5795.casalemedia.com.",
"a5796.casalemedia.com.",
- "a5797.casalemedia.com.",
"a5798.casalemedia.com.",
+ "a5799.casalemedia.com.",
"a5800.casalemedia.com.",
"a5801.casalemedia.com.",
"a5802.casalemedia.com.",
"a5803.casalemedia.com.",
"a5804.casalemedia.com.",
+ "a5805.casalemedia.com.",
+ "a5806.casalemedia.com.",
"a5807.casalemedia.com.",
"a5809.casalemedia.com.",
"a581.casalemedia.com.",
"a5810.casalemedia.com.",
"a5811.casalemedia.com.",
+ "a5812.casalemedia.com.",
"a5813.casalemedia.com.",
+ "a5814.casalemedia.com.",
"a5815.casalemedia.com.",
+ "a582.casalemedia.com.",
"a583.casalemedia.com.",
"a584.casalemedia.com.",
"a585.casalemedia.com.",
"a586.casalemedia.com.",
"a587.casalemedia.com.",
"a588.casalemedia.com.",
- "a589.casalemedia.com.",
"a590.casalemedia.com.",
"a591.casalemedia.com.",
"a592.casalemedia.com.",
"a593.casalemedia.com.",
"a594.casalemedia.com.",
"a595.casalemedia.com.",
- "a596.casalemedia.com.",
"a597.casalemedia.com.",
"a598.casalemedia.com.",
"a599.casalemedia.com.",
"a600.casalemedia.com.",
"a601.casalemedia.com.",
- "a602.casalemedia.com.",
- "a603.casalemedia.com.",
"a604.casalemedia.com.",
"a605.casalemedia.com.",
"a606.casalemedia.com.",
"a607.casalemedia.com.",
"a608.casalemedia.com.",
- "a609.casalemedia.com.",
"a610.casalemedia.com.",
+ "a611.casalemedia.com.",
"a612.casalemedia.com.",
"a613.casalemedia.com.",
"a614.casalemedia.com.",
@@ -754,7 +794,6 @@ var FakeECSFQDNs = container.NewMapSet(
"a622.casalemedia.com.",
"a623.casalemedia.com.",
"a624.casalemedia.com.",
- "a625.casalemedia.com.",
"a626.casalemedia.com.",
"a627.casalemedia.com.",
"a628.casalemedia.com.",
@@ -769,7 +808,6 @@ var FakeECSFQDNs = container.NewMapSet(
"a637.casalemedia.com.",
"a638.casalemedia.com.",
"a639.casalemedia.com.",
- "a640.casalemedia.com.",
"a921.casalemedia.com.",
"a922.casalemedia.com.",
"a923.casalemedia.com.",
@@ -815,7 +853,6 @@ var FakeECSFQDNs = container.NewMapSet(
"a965.casalemedia.com.",
"a966.casalemedia.com.",
"a967.casalemedia.com.",
- "a968.casalemedia.com.",
"a969.casalemedia.com.",
"a9695278-4085-40b3-9f02-8d4c38a6ff01.prmutv.co.",
"a970.casalemedia.com.",
@@ -828,9 +865,9 @@ var FakeECSFQDNs = container.NewMapSet(
"a977.casalemedia.com.",
"a979.casalemedia.com.",
"a980.casalemedia.com.",
- "a9972578858.cdn.optimizely.com.",
"aa.online-metrix.net.",
"aa.quantummetric.com.",
+ "aa.unioneeu.com.",
"ab.qq.com.",
"aba2c424-419a-4d03-9aed-2dca8a7139e4.prmutv.co.",
"abeacdataonrt-stsdk.vivo.com.cn.",
@@ -840,81 +877,60 @@ var FakeECSFQDNs = container.NewMapSet(
"accela.com.",
"accentuate.io.",
"access.mp.lura.live.",
- "access.ovid.com.",
+ "accessacloud.com.",
"account.box.com.",
- "account.live.com.",
"account.msa.msidentity.com.",
- "account.riotgames.com.",
- "accountapi.agoda.com.",
- "accounts.binance.info.",
- "accounts.illuminateed.net.",
- "accounts.logme.in.",
- "accounts.shopify.com.",
"accurint.com.",
"acdn.tinkoff.ru.",
- "acertb.com.",
"acgvideo.com.",
- "achievements.xboxlive.com.",
"acme-v02.api.letsencrypt.org.",
"acrobits.cz.",
- "acs.smartrg.com.",
- "acsdk.gameyw.easebar.com.",
- "action.dstillery.com.",
- "activate.academy.com.",
"ad1.adfarm1.adition.com.",
- "ad11.adfarm1.adition.com.",
- "ad11p.adfarm1.adition.com.",
- "ad13.adfarm1.adition.com.",
- "ad2.adfarm1.adition.com.",
- "ad3.adfarm1.adition.com.",
- "ad4.adfarm1.adition.com.",
"adapex.io.",
- "adapi3.boomplaymusic.com.",
"adash.man.aliyuncs.com.",
"adblock.telemetry.getadblock.com.",
+ "adc.bmj.com.",
"adcell.com.",
"adder.feeder.co.",
"addictpodcast.com.",
"additionfi.com.",
+ "adebc6b12f2d428abfe2b66ceace1662.pacloudflare.com.",
"adfarm1.adition.com.",
- "adgzs.top.",
"adiam.tech.",
"adition.com.",
"aditude.io.",
- "adm.wsms.haplat.net.",
- "admbk.wsms.haplat.net.",
"admixer.com.",
- "adoric.com.",
+ "adokutcontextual.com.",
"adrs.org.cn.",
+ "ads.chtbl.com.",
"adsco.re.",
"adsolut.in.",
"adtarget.market.",
"adtarget.me.",
- "adtechnacity.com.",
"advantage.purpleguys.com.",
"advantage2.purpleguys.com.",
+ "adventisthealthwest-my.sharepoint.com.",
"adview.com.",
"adx-os.bridgeoos.com.",
"adx-sg-req.anythinktech.com.",
- "aepenergy.sharepoint.com.",
+ "adxpremium.services.",
"aeries.com.",
"afd-cf.www.linkedin.com.",
"afd-lnkd.www.linkedin.com.",
+ "afd-wcs-ramp.www.linkedin.com.",
+ "ag.dns-finder.com.",
"agent-logos.storage.googleapis.com.",
"agent.marketingcloudfx.com.",
+ "agents.fxpasu01.manage.microsoft.us.",
"ai-voice.cloudbirds.cn.",
"aicdn.com.",
"aid.send.microad.jp.",
"aidata.io.",
+ "aiq-in.caranddriver.com.",
"airasia.com.",
"airtory.com.",
- "aisee.tv.",
"ajcloud.net.",
"akrdinfo.cn.",
- "akronchildrens.sharepoint.com.",
- "akulaku.com.",
- "alarm.wsms.haplat.net.",
- "alarmbk.wsms.haplat.net.",
"albany.remotepc.com.",
"album-sg01a.ocloud.heytapmobi.com.",
"alfasense.com.",
@@ -929,16 +945,12 @@ var FakeECSFQDNs = container.NewMapSet(
"alive3.cloudbirds.cn.",
"aliyuncs.com.",
"alpha1-ap-public.val.qq.com.",
- "alpha1-gp-ping-cq2.val.qq.com.",
- "alpha1-gp-ping-gz2.val.qq.com.",
- "alpha1-gp-ping-nj2.val.qq.com.",
- "alpha1-gp-ping-tj2.val.qq.com.",
+ "alpha1-nj-gp2-mix3.val.qq.com.",
"amd.com.",
"amdcopen.m.taobao.com.",
"amdcopen.m.taobao.com.gds.alibabadns.com.",
- "amino01.mom.",
- "amino03.mom.",
"ammarkids.online.",
+ "amp.namequery.com.",
"amp.permutive.com.",
"amplitudelab.usemotion.com.",
"amsterdam.remotepc.com.",
@@ -946,33 +958,31 @@ var FakeECSFQDNs = container.NewMapSet(
"analytics-2.athome.com.",
"analytics-debugger.com.",
"analytics-ingress-global.bitmovin.com.",
- "analytics.apps.seabroadnet.com.",
- "analytics.archive.org.",
"analytics.belk.com.",
- "analytics.edgekey.net.",
"analytics.gnc.com.",
"analytics.languagetoolplus.com.",
"analytics.pdf24.org.",
"analytics.qumucloud.com.",
- "analytics.trovit.com.",
+ "analytics.talbots.com.",
"analyticssystems.net.",
+ "ancestralsupplements.com.",
"android.crashsight.wetest.net.",
"anlian.co.",
"announce.torrentsmd.com.",
- "ant.learnscitech.com.",
"anthropic.com.",
"antstream.com.",
+ "aocde.com.",
"aon.com.",
"api-analytics-us3.zepp.com.",
- "api-app-slow.bitkeep.fun.",
- "api-app-slow.bitkeep.life.",
"api-asyncgw-gcc-teams.usgovtrafficmanager.net.",
+ "api-auth.flysleep.cn.",
"api-auth.zztfly.com.",
"api-decider.vsco.co.",
"api-eu1.hubspot.com.",
- "api-gl.store.heytapmobi.com.",
+ "api-gateway.umami.dev.",
"api-glb-aaps1b.smoot.apple.com.",
"api-glb-aapse1c.smoot.apple.com.",
+ "api-glb-aeun1a.smoot.apple.com.",
"api-glb-aeus2a.smoot.apple.com.",
"api-glb-aeus2b.smoot.apple.com.",
"api-glb-aeuw1b.smoot.apple.com.",
@@ -985,98 +995,116 @@ var FakeECSFQDNs = container.NewMapSet(
"api-glb-ause2c.smoot.apple.com.",
"api-glb-ausw2b.smoot.apple.com.",
"api-glb-ausw2c.smoot.apple.com.",
+ "api-lookup-ause2a.smoot.apple.com.",
"api-mayi.django.t.taobao.com.",
+ "api-mifit-de2.zepp.com.",
"api-mifit-us3.zepp.com.",
- "api-player.musicstylingonline.com.",
"api-pos.titank12.com.",
"api-preview.luckyorange.com.",
+ "api-proxy.conveythis.com.",
+ "api-safari-aapse1c.smoot.apple.com.",
+ "api-safari-aeun1a.smoot.apple.com.",
+ "api-safari-aeun1b.smoot.apple.com.",
+ "api-safari-aeus2a.smoot.apple.com.",
+ "api-safari-aeus2b.smoot.apple.com.",
+ "api-safari-aeuw1b.smoot.apple.com.",
+ "api-safari-ause1a.smoot.apple.com.",
+ "api-safari-ause1b.smoot.apple.com.",
+ "api-safari-ause1c.smoot.apple.com.",
+ "api-safari-ause2a.smoot.apple.com.",
+ "api-safari-ause2b.smoot.apple.com.",
+ "api-safari-ause2c.smoot.apple.com.",
+ "api-safari-ausw2b.smoot.apple.com.",
+ "api-safari-ausw2c.smoot.apple.com.",
+ "api-service.shein.com.cdn.cloudflare.net.",
"api-sh.django.t.taobao.com.gds.alibabadns.com.",
"api-user.dalyfeds.com.",
+ "api.9hits.com.",
+ "api.al-array.com.",
"api.ams.gcc.teams.microsoft.com.",
- "api.axs.com.",
"api.bobble.ai.",
- "api.boldcommerce.com.",
"api.box.com.",
"api.btloader.com.",
- "api.cb-device-intelligence.com.",
+ "api.bugreport.huorong.cn.",
+ "api.ceros.com.",
"api.cloud.tenda.com.cn.",
"api.config-security.com.",
"api.crobox.com.",
"api.crush.163.com.",
+ "api.crxcavator.io.",
"api.dealersocket.com.",
"api.deepl.com.",
"api.eponesh.com.",
"api.glanceapis.com.",
"api.gleap.io.",
- "api.gowish.com.",
"api.gravitec.media.",
- "api.gx.me.",
"api.hetangsmart.com.",
"api.hqt0w.com.",
- "api.icalendars.app.",
"api.ipgeolocation.io.",
- "api.k.163.com.",
+ "api.kingdata.ksyun.com.",
"api.kinogram.best.",
"api.lightboxcdn.com.",
"api.loyalhealth.com.",
"api.lytics.io.",
- "api.mangacoin.net.",
"api.mida.so.",
"api.moyoung.com.",
"api.my.jbi.global.",
+ "api.nkryu17dc.com.",
"api.onedrive.com.",
+ "api.open-meteo.com.",
"api.permutive.app.",
"api.permutive.com.",
"api.pgf-thek63.com.",
"api.playlnk.io.",
+ "api.polaris.al-array.com.",
"api.popin.cc.",
- "api.qingcigame.com.",
"api.queryly.com.",
- "api.qwant.com.",
- "api.rakuten.com.",
"api.reverso.net.",
"api.ringcentral.biz.",
"api.rollbar.com.",
+ "api.scorchads.com.",
"api.smartdeploy.com.",
"api.snapchat.com.",
- "api.swishapps.ai.",
- "api.tapcart.com.",
- "api.textnow.me.",
+ "api.tracking.al-array.com.",
"api.transitapp.com.",
+ "api.tunnelbear.com.",
"api.tx4.pw.adn.cloud.",
- "api.u17tz.com.",
"api.ultimaker.com.",
"api.ultimate-guitar.com.",
"api.unity.com.",
- "api.urbanoutfitters.com.",
"api.us.minga.io.",
+ "api.us2.backdrop.cloud.",
"api.userlike.com.",
"api.vieon.vn.",
+ "api.voicemod.net.",
"api.vsco.co.",
"api.workjam.com.",
+ "api.x1skf.com.",
"api.xiaoyi.com.",
- "api.y41w4.com.",
+ "api.yosmart.com.",
+ "api.zmcyu9ypy.com.",
"api000.backblazeb2.com.",
"api001.backblazeb2.com.",
"api002.backblazeb2.com.",
+ "apibay.org.",
"apiblink.ru.",
"apiisgp.ezvizlife.com.",
"apiisgp.hik-connect.com.",
"apimgmttmgpxfqy6dfjiqfsk6t67i30fgsnfhah4rrjw51coy3.trafficmanager.net.",
"apis.live.net.",
- "apk.v-mate.mobi.",
- "apk.vidmate.net.",
"aplo-evnt.com.",
- "apm.gotokeep.com.",
"app-atl.five9.com.",
"app-eu1.hubspot.com.",
"app-scl.five9.com.",
"app-student.atitesting.com.",
"app.adoric-om.com.",
"app.box.com.",
+ "app.carnow.com.cdn.cloudflare.net.",
"app.cart-bot.net.",
"app.cybba.solutions.",
"app.ee-share.com.",
+ "app.firmguard.com.",
+ "app.jobvite.com.",
"app.paces.jbi.global.",
"app.pendo.qgenda.com.",
"app.retinavue.net.",
@@ -1085,18 +1113,17 @@ var FakeECSFQDNs = container.NewMapSet(
"app.talkjs.com.",
"app.wdesk.com.",
"app.zoom.us.",
+ "app.zoominfo.com.",
"appcafe.gtm.starbucks.com.",
"appcafe.starbucks.com.",
- "appconf.mail.163.com.",
"appdump.nie.easebar.com.",
+ "apphub.tracker.al-array.com.",
"applog.matrix.easebar.com.",
"appocean.media.",
- "apponline.research.qq.com.",
+ "appriver.com.",
"apps.wix.com.",
- "appstoreonrt-stsdk.vivo.com.cn.",
"appstoreort-stsdk.vivo.com.cn.",
- "apro-api.collegeboard.org.",
- "apv-static.minute.ly.",
+ "aralego.net.",
"arenabg.com.",
"aristotleinsight.com.",
"arm-frontdoor-edge-geo.trafficmanager.net.",
@@ -1104,35 +1131,18 @@ var FakeECSFQDNs = container.NewMapSet(
"arms-retcode-sg.aliyuncs.com.",
"as-api.asm.skype.com.",
"as-prod.asyncgw.teams.microsoft.com.",
- "as.footballbros.io.",
+ "as.xiaohongshu.com.",
"asanalytics.booking.com.",
"asheville.remotepc.com.",
"ashleyfurniture.aiproxies.com.",
- "asia-adlog.vivoglobal.com.",
- "asia-browser.vivoglobal.com.",
- "asia-cota.vivoglobal.com.",
"asia-ex-adlog.vivoglobal.com.",
- "asia-gsearch.vivoglobal.com.",
- "asia-news-abroad-backstage-interface.vivoglobal.com.",
- "asia-news-abroad.vivo.com.",
- "asia-p.vivoglobal.com.",
- "asia-ro-up.vivoglobal.com.",
"asia-rommc-api.vivoglobal.com.",
- "asia-romsp-unifyconfig.vivoglobal.com.",
- "asia-st-romsp.vivoglobal.com.",
- "asia-st-sl.vivoglobal.com.",
- "asia-st-sysupgrade.vivoglobal.com.",
- "asia-stp.vivoglobal.com.",
+ "asia-southeast2-idm-corp-prd.cloudfunctions.net.",
"asia-timer-appstore.vivoglobal.com.",
"asia-timesync.vivoglobal.com.",
"asia-upload-appstore.vivoglobal.com.",
"asia-usrsys-api.vivoglobal.com.",
- "asia-vgcmdm-api-tha.vivoglobal.com.",
- "asia-vnote.vivoglobal.com.",
- "asia-vpushonrt-stsdk.vivoglobal.com.",
- "asia-vpushort-stsdk.vivoglobal.com.",
"asia-wifi.vivoglobal.com.",
- "asia.edge.rms.si.riotgames.com.",
"asia.remotepc.com.",
"asm-api-golocal-geo-am-teams.trafficmanager.net.",
"asm-api-golocal-geo-as-teams.trafficmanager.net.",
@@ -1141,15 +1151,11 @@ var FakeECSFQDNs = container.NewMapSet(
"asm-api-prod-geo-am-skype.trafficmanager.net.",
"asm-api-prod-geo-as-skype.trafficmanager.net.",
"asm-api-prod-geo-eu-skype.trafficmanager.net.",
- "asr.openssp.ru.",
"asset.fwcdn3.com.",
"asset.fwpub1.com.",
- "asset.fwscripts.com.",
- "assets.adobetarget.com.",
"assets.api.stairwell.com.",
- "assets.pinterest.com.",
- "assetshare.basspro.com.",
- "assistant-dre.op.hicloud.com.",
+ "assets.ovid.com.",
+ "assets2.procore.com.",
"astemo-am.spectrum.colortokens.com.",
"async-motiondetection-us-1d.oss-us-west-1.aliyuncs.com.",
"asyncim.zoom.us.",
@@ -1159,71 +1165,74 @@ var FakeECSFQDNs = container.NewMapSet(
"atlanta4.remotepc.com.",
"atlassianblog.wpengine.com.",
"atlsbc04ag1.atl.five9.com.",
- "atzscr.itsupport247.net.",
"au-prod.asyncgw.teams.microsoft.com.",
- "au.footballbros.io.",
+ "au.ff.avast.sec.miui.com.",
"auc-collabrtc.officeapps.live.com.",
"auckland.remotepc.com.",
+ "audienceye.com.",
+ "audio-private.canva.com.",
"audio.vivintsky.com.",
+ "audioscrobbler.com.",
"audiostatlog.cc.easebar.com.",
- "augmentation.osi.office.net.",
"australiaeast.api.cognitive.microsoft.com.",
"auth-l7.bereal.com.",
+ "auth.esports.rpg.riotgames.com.",
"auth.jbisumari.org.",
- "auth.laureate.net.",
+ "auth.riotgames.com.",
+ "auth.services.adobe.com.",
+ "automate.itsasap.com.",
"autotrack.studyquicks.com.",
"avalanche.autotrader.co.uk.",
"aws-aus-e-rdvz.g.nssvc.net.",
"aws-bz-s-rdvz.g.nssvc.net.",
+ "aws-us-e-rdvz.g.nssvc.net.",
+ "axb7sdy1ruuf98ot.apple.holadns.com.",
+ "axb7sdy1ruuf98ot.apple.martianinc.co.",
+ "axb7sdy1ruuf98ot.apple.okamiboss.com.",
"az-us-sc-features.netscalergateway.net.",
"azchicago.remotepc.com.",
"azchicago2.remotepc.com.",
"azeus1-client-s.gateway.messenger.live.com.",
- "azioncdn.net.",
"azscus1-client-s.gateway.messenger.live.com.",
- "azure.clmbosean.space.",
"azwcus1-client-s.gateway.messenger.live.com.",
"azwus1-client-s.gateway.messenger.live.com.",
"azwus2-client-s.gateway.messenger.live.com.",
- "b.vx323.com.",
- "b1-nldc1.zemanta.com.",
"b1t-nldc1.zemanta.com.",
"b2b.filesyscrm.com.",
"bablosoft.com.",
"backend-l.deepl.com.",
+ "backend.deepl.com.",
"badambiz.com.",
"badoo.app.",
"bahrain.remotepc.com.",
"baishan-cloud.net.",
"baltimore.remotepc.com.",
"bam.nr-data.net.cdn.cloudflare.net.",
- "bancomermovil.com.",
"bangalore2.remotepc.com.",
"bangalore3.remotepc.com.",
"bangalore4.remotepc.com.",
"bangkok.remotepc.com.",
- "bankozarks-my.sharepoint.com.",
+ "banners-inventory-weighted-geo.b.hyprmx.com.",
"barstoolsports.com.",
"bd1cec50-00d1-4ce9-9572-785857419a1e.prmutv.co.",
- "bdfed.stitch.mlbinfra.com.",
- "bea.gov.",
+ "bdreporting.com.",
+ "beacon.aimtell.com.",
"beacon.qq.com.",
"beacons4.gvt2.com.",
"beacons5.gvt2.com.",
- "becpsn-my.sharepoint.com.",
+ "beanstack.com.",
"bee.tc.easebar.com.",
"beizi.biz.",
"belgium.remotepc.com.",
"belgrad.remotepc.com.",
"bend.remotepc.com.",
+ "betlive.com.",
"bgtfs.transitapp.com.",
"bi-tracker-global.rivergame.net.",
- "biddr.brealtime.com.cdn.cloudflare.net.",
"bidster.net.",
"bifrost.vivaldi.com.",
"bigdata.talkie-ai.com.",
"bik.gov.tr.",
- "bisdus-my.sharepoint.com.",
"bl6pap003.storage.live.com.",
"bl6pap004.storage.live.com.",
"black-cat.crypto.com.",
@@ -1231,10 +1240,8 @@ var FakeECSFQDNs = container.NewMapSet(
"blackbox.dropbox-dns.com.",
"block64.com.",
"bloomerang.co.",
- "bluedot.is.autonavi.com.gds.alibabadns.com.",
"bluffdale.remotepc.com.",
"blz04pap005.storage.live.com.",
- "blz04pap006.storage.live.com.",
"bmaus.bumble.com.",
"bn02pap001.storage.live.com.",
"bnz05pap001.storage.live.com.",
@@ -1244,6 +1251,7 @@ var FakeECSFQDNs = container.NewMapSet(
"boardgamearena.net.",
"bobble.ai.",
"bokep.work.",
+ "bookriot.com.",
"books-analytics-events.apple.com.",
"books-personalization-server.apple.com.",
"booksy.com.",
@@ -1257,48 +1265,43 @@ var FakeECSFQDNs = container.NewMapSet(
"bratislava.remotepc.com.",
"brazilsouth.api.cognitive.microsoft.com.",
"breitbart.com.",
- "bridge.tonapi.io.",
- "broadsimp.site.",
+ "bridgetonpsnj.aristotleinsight.com.",
"broadstreetads.com.",
"broker-ws-prod-cag-sg.vasdgame.com.",
- "bscedge.com.",
- "bsprings.remotepc.com.",
+ "broker.jagat.io.",
"bssrvc66.com.",
- "bsw-ig.criteo.com.",
"bt.moack.co.kr.",
"btrace.qq.com.",
+ "bubble.io.",
"bucharest.remotepc.com.",
"bucharest1.remotepc.com.",
"budapest.remotepc.com.",
- "bugly.qcloud.com.",
"bugreport.huorong.cn.",
"bundler.nice-team.net.",
- "buy.music.apple.com.",
"bytecdn.cn.",
"bytedance.net.",
"c.4dex.io.",
- "c.aklamator.com.",
"c.fakespot.io.",
"c.pub.network.",
+ "c.tadst.com.",
"c1.ttcache.com.",
"c2.ttcache.com.",
"c2c.wechat.com.",
"c3.ttcache.com.",
"c4.ttcache.com.",
- "c7l.cyberhaven.io.",
"c8ee9446-97ed-462f-a5e9-1af66c8e9104.prmutv.co.",
"ca-prod.asyncgw.teams.microsoft.com.",
- "ca.gov.",
+ "ca.pinterest.com.",
"ca.rogers.rcs.telephony.goog.",
"ca000.backblaze.com.",
"ca001.backblaze.com.",
"ca002.backblaze.com.",
+ "ca3-excel-collab.officeapps.live.com.",
"ca80a1adb12a4fbdac5ffcbc944e9a61.pacloudflare.com.",
"cac-collabrtc.officeapps.live.com.",
- "cache.dciwx.com.",
"cachenetworks.com.",
- "caldav.163.com.",
"california.remotepc.com.",
+ "caltech.edu.",
"camstore.vsco.co.",
"canada.remotepc.com.",
"canadacentral.api.cognitive.microsoft.com.",
@@ -1310,21 +1313,24 @@ var FakeECSFQDNs = container.NewMapSet(
"car-cloud-cn.net.",
"cardiff.remotepc.com.",
"care.novaicare.com.",
+ "carrollcountynh.gov.",
+ "cartsee-form-c.cartx.cloud.",
"cartx.cloud.",
"carystudio.com.",
+ "cas-us8.wfs.cloud.",
"cat1.hbwrapper.com.",
"cat2.hbwrapper.com.",
"cat3.hbwrapper.com.",
"cavai.com.",
"cbs.com.",
- "cbsipv4.shuzilm.cn.",
"cc.easebar.com.",
- "cdml02.contentdm.oclc.org.",
"cdn-audio-gcp-media.getepic.com.",
"cdn-cname.pendo.io.",
"cdn-gcp-media-drm.getepic.com.",
"cdn-gcp-media.getepic.com.",
- "cdn-gcp.getepic.com.",
+ "cdn-gpd.x-plarium.com.",
+ "cdn-prod.seismic.com.",
+ "cdn-prod.wdesk.com.",
"cdn-us.algoliaradar.com.",
"cdn-video-gcp-media.getepic.com.",
"cdn.adjust.com.",
@@ -1333,33 +1339,35 @@ var FakeECSFQDNs = container.NewMapSet(
"cdn.chargeafter.com.",
"cdn.conveythis.com.",
"cdn.deepintent.com.",
- "cdn.engine.4dsply.com.",
- "cdn.exitbee.com.",
- "cdn.flashtalking.com.edgekey.net.",
"cdn.ftd.agency.",
- "cdn.gowish.com.",
"cdn.groupbycloud.com.",
"cdn.hw.gcloudcs.com.",
+ "cdn.ingest-lr.com.",
"cdn.instapagemetrics.com.",
- "cdn.jst.ai.",
- "cdn.mscdirect.com.",
+ "cdn.komiku.id.",
+ "cdn.onesignal.com.",
"cdn.overleaf.com.",
- "cdn.pandora.xiaomi.com.",
"cdn.qq.com.",
- "cdn.resonate.com.",
+ "cdn.shopifycdn.net.",
"cdn.sierrapacificgroup.com.",
"cdn.skcrtxr.com.",
"cdn.t-bank-app.ru.",
"cdn.tapdb-dev.com.",
+ "cdn.tbank.ru.",
"cdn.us.minga.io.",
"cdn.uxfeedback.ru.",
- "cdn01.boxcdn.net.",
+ "cdn.web.prd.q4inc.com.cdn.cloudflare.net.",
+ "cdn01.humeysha.com.",
+ "cdn02.humeysha.com.",
+ "cdn1.roadster.com.",
"cdn1.wixdns.net.",
+ "cdn3.onlineaccess1.com.",
"cdngslb.com.",
"cdntm.hsbc.co.uk.",
"cdnwidget.com.",
- "cds.swishapps.ai.",
+ "cds.glanceapis.com.",
"cdsi.signal.org.",
+ "ceconnection.lww.com.",
"cedexis-test.com.",
"cedock.com.",
"cef.com.br.",
@@ -1373,103 +1381,96 @@ var FakeECSFQDNs = container.NewMapSet(
"center07.deltaork.com.",
"center08.deltaork.com.",
"center09.deltaork.com.",
+ "center10.deltaork.com.",
+ "center11.deltaork.com.",
+ "center12.deltaork.com.",
+ "center13.deltaork.com.",
+ "center14.deltaork.com.",
+ "center15.deltaork.com.",
+ "center16.deltaork.com.",
+ "center17.deltaork.com.",
+ "center18.deltaork.com.",
+ "center19.deltaork.com.",
"centralindia.api.cognitive.microsoft.com.",
"centralus.api.cognitive.microsoft.com.",
"centrexit.com.",
- "certs.t-bank-app.ru.",
- "cf-lb-web-spectrum.www.deepl.com.",
+ "cf.iad-03.braze.com.cdn.cloudflare.net.",
"cfa.fidelity.com.",
- "cgi.twns.qq.com.",
+ "cfgc.zztfly.com.",
+ "cform.loyalhealth.com.",
"changehealthcare.com.",
"chargeafter.com.",
- "chat.openai.com.cdn.cloudflare.net.",
- "check.ragpets.com.",
+ "charlotte.remotepc.com.",
+ "charter.asapp.com.",
"check2.lloydsbank.co.uk.",
- "check2.ragpets.com.",
- "checkout-service.global-e.com.",
"chennai.remotepc.com.",
- "chi01pap001.storage.live.com.",
- "chi01pap002.storage.live.com.",
"chicago2.remotepc.com.",
"childrens.zoom.us.",
"china-dayunlinks.obs.cn-south-1.myhuaweicloud.com.",
- "chinacache.net.",
- "chinaccl-a.haplat.net.",
- "chinaccl-b.haplat.net.",
- "chinaccl-c.haplat.net.",
"chrome.com.",
"chtsite.com.",
"chub1.imp.us.contentkeeper.net.",
- "chujingapp.com.",
+ "chubbgroup-my.sharepoint.com.",
"cicd-release-api.dalyfeds.com.",
"cinarra.com.",
+ "cinemark.com.",
+ "cis.citrix.com.",
+ "cisa.gov.",
"cisco.app.box.com.",
+ "cisco.sharepoint.com.",
"citrix-cloud-content.customer.pendo.io.",
"citrix-cloud-data.customer.pendo.io.",
"citrix-sharefile-content.customer.pendo.io.",
"citrix-sharefile-data.customer.pendo.io.",
"citrix.com.",
"cl-data.ads.heytapmobi.com.",
- "classroom6x.gitlab.io.",
"claude.ai.",
"cleveland.remotepc.com.",
- "clevelandclinic-my.sharepoint.com.",
- "click-eu.preclknu.com.",
- "click.pclk.name.",
- "click.preclknu.com.",
+ "click-v4.mainexdircllk.com.",
"clickiocmp.com.",
- "clickstrck.com.",
"client-log.box.com.",
"client-s.gateway.messenger.live.com.",
"client-tracking.omiapp.me.",
"client-updates.lumu.io.",
- "client.mail.163.com.",
- "clientlog3.music.163.com.",
+ "clientlogsf.music.163.com.",
"cloud-agent.policypak.com.",
"cloud-config-service.rtc.aliyuncs.com.",
- "cloud-links.net.",
- "cloud-rest.lenovomm.com.",
- "cloud.huawei.ru.",
"cloud.ibm.com.",
"cloud.vmp.onezapp.com.",
"cloud.zoom.us.",
"cloudbirds.cn.",
"clouddata.turbowarp.org.",
- "cloudflareperf.com.",
+ "cloudflare.com.",
"cloudlinks.cn.",
"cloudscdn.info.",
"cm-x.mgid.com.",
- "cmgate.vip.qq.com.",
"cmpassport.com.",
+ "cms-assets.teacherspayteachers.com.",
"cn-hangzhou.oss-cdn.aliyun-inc.com.",
"cn-mib2high-mbbservices.audi-connect.cn.",
"cn-mib2plus.mbbservices-1a.audi-connect.cn.",
- "cn.gcloudcs.com.",
"cname.pendo.io.",
"cnplug.ttlock.com.",
+ "cnt.xhprograms.site.",
"cnzz.com.",
- "co-vcode-od.vivoglobal.com.",
"codacloud.net.",
"code.etracker.com.",
- "codis-bak.ngb.haplat.net.",
- "codis.ngb.haplat.net.",
- "collabrtc-geo.rtc.trafficmanager.net.",
- "collabrtc.officeapps.live.com.",
+ "code42.com.",
"collect.quickcep.com.",
- "collect.trendyol.com.",
"collector.quillbot.com.",
"collector.vhx.tv.",
"collector.wdp.brave.com.",
"collector.xhamster.com.",
- "collector.xhwear.life.",
+ "collector.xhamster.desi.",
+ "collector.xhprograms.site.",
"columbus.remotepc.com.",
"com.yangyi19.com.",
"cometglobal.cf.t3cloud.pb.com.",
"cometservd1.pb.com.",
+ "comicbook.com.",
+ "commerce.nbcuni.com.",
"common-afd.fe.1drv.com.",
"common-afdrk.fe.1drv.com.",
- "common.ltwebstatic.com.",
- "community.thescore.com.",
"compiles.overleafusercontent.com.",
"config-security.com.",
"config.a-m-p.xyz.",
@@ -1477,15 +1478,13 @@ var FakeECSFQDNs = container.NewMapSet(
"config.config-factory.com.",
"config.content-settings.com.",
"config.office.net.",
+ "config.silk.al-array.com.",
"config.y5en.com.",
"config2.cmpassport.com.",
"configdl.teamviewer.com.",
- "configuration.apple.com.edgekey.net.",
- "connect.garenanow.com.",
- "connect.garmin.com.",
"connectivitycheck.unisoc.com.",
+ "connectivitycheck.unisoc.com.c.yundunwaf5.com.",
"contacts.zoho.com.",
- "contendvc.cnouyi.pizza.",
"content.assist.chromeriver.com.",
"content.bhrpendo.bamboohr.com.",
"content.citizensbankonline.com.",
@@ -1496,19 +1495,20 @@ var FakeECSFQDNs = container.NewMapSet(
"content.ebanking-services.com.",
"content.fisglobal.com.",
"content.gap.com.",
+ "content.guide-app.zoominfo.co.",
"content.help.explorelearning.com.",
"content.maxconnector.com.",
"content.pendo.careporthealth.com.",
"content.pendo.follettdestiny.com.",
"content.pendo.saashr.com.",
- "content.pendo.udsrv.com.",
"content.productinsights.blackline.com.",
"content.readiness.imaginelearning.com.",
- "content22.bancanet.banamex.com.",
+ "content.ssctech.com.",
+ "content.tracking.billtrust.com.",
"content22.bmo.com.",
- "content22.citibankonline.com.",
"content22.citicards.com.",
"content22.online.citi.com.",
+ "contentexchange.me.",
"contentlmsp.student.atitesting.com.",
"context.reverso.net.",
"control-out.mna.qq.com.",
@@ -1520,10 +1520,7 @@ var FakeECSFQDNs = container.NewMapSet(
"core.omiapp.me.",
"cosmos.azure.com.",
"countly.mail.163.com.",
- "coursehero.com.",
- "covers.vitalbook.com.",
"cpisolutions.com.",
- "cpm.adsolut.in.",
"cpm.appocean.media.",
"cpm.aserve1.net.",
"cpm.qortex.ai.",
@@ -1535,22 +1532,18 @@ var FakeECSFQDNs = container.NewMapSet(
"crashlytics.com.",
"crashsight.qq.com.",
"crashsight.wetest.net.",
+ "creative.myavlive.com.",
"creator.pdf24.org.",
"creditmaven.com.",
- "crlocsp.cn.",
"croatia.remotepc.com.",
"crobox.com.",
"crobox.io.",
"crossforward.com.",
"crowd.transitapp.com.",
- "crt.comodoca.com.cdn.cloudflare.net.",
- "crutchfield.com.",
- "crutchfieldonline.com.",
"cs.globalsun.io.",
"cs.nex8.net.",
"cs.playdigo.com.",
"csdn.net.",
- "css.dhresource.com.",
"cstse02.ultipro.com.",
"cstsew02.ultipro.com.",
"cstsn02.ultipro.com.",
@@ -1558,33 +1551,33 @@ var FakeECSFQDNs = container.NewMapSet(
"cti.roku.com.",
"ctmail.com.",
"cts.appmeta.store.",
+ "ctt-er.hnzkclouds.com.",
"cummins.zoom.us.",
+ "custom-dialogs.thescore.com.",
"customer.homedepot.com.",
"cvs.quantummetric.com.",
"cwogzftn.usw.stape.io.",
+ "cybba.solutions.",
+ "cyberghostvpn.com.",
"czechrepublic.remotepc.com.",
- "d.applvn.com.",
"d.docs.live.net.",
"d.pub.network.",
"d2fb08da-1c03-4c8a-978f-ad8a96b4c31f.partner.permutive.app.",
"d2fb08da-1c03-4c8a-978f-ad8a96b4c31f.prmutv.co.",
"d3-pr-cu-tm-secapi.trafficmanager.net.",
"d6691a17-6fdb-4d26-85d6-b3dd27f55f08.prmutv.co.",
- "d82f7a30-751a-4689-b7e9-19336a89ab46.prmutv.co.",
"d837da8d.cloudsrv.minerva-labs.com.",
- "da.footballbros.io.",
+ "da.bridgeoos.com.",
"da.mosspf.com.",
- "da.toponadss.com.",
"dacdn.visualwebsiteoptimizer.com.",
"daemon.nanoleaf.me.",
+ "dal-cdsltm-2b.dataremote.com.",
"dallas.remotepc.com.",
"dallas2.remotepc.com.",
"dallas3.remotepc.com.",
"dallas4.remotepc.com.",
"dallas5.remotepc.com.",
- "dantri.com.vn.",
- "dashboard.meraki.com.",
- "dashi.163.com.",
+ "daraz.com.",
"data-detect.nie.easebar.com.",
"data.analytics.thomsonreuters.com.",
"data.analytics.ux.quickbase.com.",
@@ -1598,94 +1591,99 @@ var FakeECSFQDNs = container.NewMapSet(
"data.guide-app.zoominfo.co.",
"data.guides.oncoursesystems.com.",
"data.hockeystack.com.",
+ "data.info.costar.com.",
"data.investing.com.",
"data.ipd.goto.com.",
"data.kuiniuca.com.",
"data.meitu.com.",
+ "data.nelnet.studentaid.gov.",
"data.p3nd0.sproutsocial.com.",
+ "data.pathfinder.gohighlevel.com.",
"data.pendo-cdn.pluralsight.com.",
"data.pendo-cobalt.westlaw.com.",
"data.pendo-tracking.seismic.com.",
"data.pendo.careporthealth.com.",
+ "data.pendo.everfi.net.",
+ "data.pendo.follettdestiny.com.",
"data.pendo.gomotive.com.",
+ "data.pendo.perfectserve.com.",
"data.pendo.saashr.com.",
"data.pendo.udsrv.com.",
"data.pendoanalytics.dayforcehcm.com.",
+ "data.productanalytics.coconutcalendar.com.",
"data.productinsights.blackline.com.",
"data.queryly.com.",
"data.readiness.imaginelearning.com.",
"data.tracking.billtrust.com.",
+ "data.traffmonetizer.com.",
"data.useranalytics.global.datasite.com.",
"data00.adlooxtracking.com.",
+ "datacollection.uve.weibo.com.",
"datasink.cloudlinks.cn.",
"datasite.com.",
"datatheorem.com.",
- "datatrans.com.",
"dayunlinks.cn.",
- "dc-o.api.leiniao.com.",
"dc.ads.linkedin.com.",
- "dc.cftls.t.co.",
"dc.di.atlas.samsung.com.",
"dc.dqa.samsung.com.",
"dc.sigmob.cn.",
"dc.wondershare.com.",
+ "dca-cdsltm-2b.dataremote.com.",
+ "dcs-live-uc1.mp.lura.live.",
+ "dcs-live-ue1.mp.lura.live.",
+ "dcs-live-ue4.mp.lura.live.",
+ "dcs-live-uw1.mp.lura.live.",
"dcs-live.mp.lura.live.",
"dcs-mcdn.mp.lura.live.",
"dcs-png.mp.lura.live.",
"dcs-vod.mp.lura.live.",
- "dcs.cloud.samknows.com.",
- "dcs110-mcdn.mp.lura.live.",
"ddata.huntingtonbank.com.",
"ddnsclient.ivview.net.",
"de-prod.asyncgw.teams.microsoft.com.",
"de.dt.rcs.telephony.goog.",
"dealmoon.com.",
- "debug.nordvpn.com.",
"dec-collabrtc.officeapps.live.com.",
"decagon.ai.",
"delivery.upremium.asia.",
- "deliveryhero.io.",
"dell-prod.actioniq.mr-in.com.",
"delta.quantummetric.com.",
- "demeter-int-ecom-collect.trendyol.com.",
- "demeter-tr-core-collect.trendyol.com.",
"denver.remotepc.com.",
"dev.av380.net.",
"dev.visualwebsiteoptimizer.com.",
+ "devc.flysleep.cn.",
"devc.zztfly.com.",
"deviceapi.ca1.absolute.com.",
"deviceops.hstgps.com.",
- "dewu.com.",
- "dfaklj.tech.",
"dhs.gov.",
"dialpad.com.",
+ "diauth.garmin.com.",
"dicontent.ckapis.com.",
"dict.deepl.com.",
- "dict.ntes53.netease.com.",
- "dictvip-business.youdao.com.",
- "digiapp.vietcombank.com.vn.",
"digiboy.ir.",
"digitalcardservice.com.",
- "dinosaurgame.app.",
"dir.4.401402081.west-gcloud.codm.activision.com.",
+ "direct.quic-core-proxy-useast4-v3.useast4.byteglb.com.",
+ "direct.quic-proxy-gcpsg-v3.gcpsg.byteglb.com.",
+ "discord.easebar.com.",
"discovery.ringcentral.biz.",
"dispatcher.omiapp.me.",
"dispatchosglobal.yuanshen.com.",
"distservp1.pb.com.",
"divide.dalyfeds.com.",
+ "dl.teamviewer.com.",
"dl2.discordapp.net.",
"dls-udc.dqa.samsung.com.",
"dls.di.atlas.samsung.com.",
"dm-us.hybrid.ai.",
- "dm.wo.com.cn.",
+ "dm597cl.cloudatacdn.com.",
"dmv.ca.gov.",
- "dmwww.geo.dmcdn.net.",
"dns-e.ns4v.icu.",
"dns-tunnel-check.googlezip.net.",
+ "dns.adguard.com.",
+ "dns.cloudflare.com.",
"dns.rubyfish.cn.",
"dns1.nettica.com.",
"dns101.register.com.",
- "dns23.llnwi.net.",
"doceditor.wrike.com.",
"docs.live.net.",
"docs.zoom.us.",
@@ -1694,20 +1692,18 @@ var FakeECSFQDNs = container.NewMapSet(
"donewyork1.remotepc.com.",
"donewyork2.remotepc.com.",
"donewyork3.remotepc.com.",
+ "dorkbox.com.",
"dosfo1.remotepc.com.",
"dosfo2.remotepc.com.",
"douyin.starrydyn.com.",
+ "dowjones-prod.actioniq.mr-in.com.",
"download.2.401402081.west-gcloud.codm.activision.com.",
- "download.lenovo.com.edgekey.net.",
"downloadcenter.genetec.com.",
- "doximity-res.cloudinary.com.",
+ "downloads.preyproject.com.",
"dp.barclaysus.com.",
"dpf.authorize.net.",
- "dpool.sina.com.cn.",
"dreame.tech.",
"drfdisvc.walmart.com.",
- "drsquatch.com.",
- "ds-cdn.com.",
"ds.gsma.com.",
"dsa-eu.hybrid.ai.",
"dsm01pap001.storage.live.com.",
@@ -1718,15 +1714,11 @@ var FakeECSFQDNs = container.NewMapSet(
"dsm01pap009.storage.live.com.",
"dsm04pap002.storage.live.com.",
"dsp-trk.eskimi.com.",
- "dsp-trvm.eskimi.com.",
- "dspcluster.adfarm1.adition.com.",
- "dspx.tv.",
"dstillery.com.",
+ "dsu-b.shalltry.com.",
"dtscout.com.",
"dubai.remotepc.com.",
"dublin.remotepc.com.",
- "dun.163yun.com.",
- "dypnsapi.aliyuncs.com.",
"dz.cyberhaven.io.",
"dzfread.cn.",
"e-189.21cn.com.",
@@ -1734,6 +1726,7 @@ var FakeECSFQDNs = container.NewMapSet(
"e.cdnwidget.com.",
"e.userflow.com.",
"e.viously.com.",
+ "e11.ultipro.com.",
"e1cef1f0-495f-4973-ba1c-880786e73a66.prmutv.co.",
"e2c1.gcp.gvt2.com.",
"e2c10.gcp.gvt2.com.",
@@ -1874,15 +1867,15 @@ var FakeECSFQDNs = container.NewMapSet(
"e43.ultipro.com.",
"e488cdb0-e7cb-4d91-9648-60d437d8e491.prmutv.co.",
"e5de3d23065c4748b155c28e6fa36f3e.pacloudflare.com.",
- "ea.footballbros.io.",
"eafddirect.msedge.net.",
"eago9.cyberhaven.io.",
"easeus.com.",
"eastasia.api.cognitive.microsoft.com.",
- "eastmoney.com.",
"eastus.api.cognitive.microsoft.com.",
"eastus2.api.cognitive.microsoft.com.",
+ "ecatholic.com.",
"ecom.wixapps.net.",
+ "ecosec.on.epicgames.com.cdn.cloudflare.net.",
"ecs-gallatin-c2s.trafficmanager.net.",
"ed.link.",
"edevice.toshiba-solutions.com.",
@@ -1890,26 +1883,22 @@ var FakeECSFQDNs = container.NewMapSet(
"edgedl.me.gvt1.com.",
"edgelocation.ivanticloud.com.",
"editor.wix.com.",
- "editorial.femaledaily.com.",
"edna.id.",
- "education-certification.youdao.com.",
"ee-share.com.",
"efercro.com.",
"efs.ultipro.com.",
"egateway.ultipro.com.",
+ "ehmc-my.sharepoint.com.",
"ei.dyn-rev.app.",
- "eic-ngfts.lge.com.",
- "elaracaring.sharepoint.com.",
- "elemecdn.com.",
- "em-frontend-assets.airtrfx.com.",
"emailaptitude.com.",
+ "emailopen.com.",
"empower-retirement.com.",
+ "empower.com.",
"endpointprotector.com.",
"engage.wixapps.net.",
"engagementapi.skype.com.",
"enplug.com.",
"ent.box.com.",
- "entitlement.auth.adobe.com.",
"envoy-ios-prod.getepic.com.",
"envysion.com.",
"epdg.vowifi.cspire.com.",
@@ -1917,9 +1906,9 @@ var FakeECSFQDNs = container.NewMapSet(
"eponesh.com.",
"eportal.fda.gov.ph.",
"epubgame.com.",
+ "erpapimanagementservice.azure-api.net.",
"errortracking.deepl.com.",
"esignlive.com.",
- "essence.com.",
"etracker.com.",
"etsv2.datalake.gameloft.com.",
"eu-aa.online-metrix.net.",
@@ -1927,42 +1916,35 @@ var FakeECSFQDNs = container.NewMapSet(
"eu-prod.asyncgw.teams.microsoft.com.",
"eu-push.api.intl.miui.com.",
"eu.global.market.xiaomi.com.",
- "eu.lers.loyalty.riotgames.com.",
"eu.statusapi.micloud.xiaomi.net.",
"eu.whatfix.com.",
- "eu1.badoo.com.",
- "eu1.bumble.com.",
"eu1a-excel-collab.officeapps.live.com.",
"eu1a-powerpoint-collab.officeapps.live.com.",
+ "eu1a-word-collab.officeapps.live.com.",
"eu2a-excel-collab.officeapps.live.com.",
"eu2a-powerpoint-collab.officeapps.live.com.",
+ "eu2a-word-collab.officeapps.live.com.",
+ "eu2s-excel-collab.officeapps.live.com.",
"eu4-excel-collab.officeapps.live.com.",
"eu4-powerpoint-collab.officeapps.live.com.",
- "euc-collabrtc-geo.rtc.trafficmanager.net.",
- "euc-collabrtc.officeapps.live.com.",
+ "eu4-word-collab.officeapps.live.com.",
"euc-excel-collab.officeapps.live.com.",
"euc-powerpoint-collab.officeapps.live.com.",
"euc-word-collab.officeapps.live.com.",
- "euler-saas-cn.heytapmobi.com.",
"europe-west1-skyuk-uk-pa-tds-prod.cloudfunctions.net.",
"europe.remotepc.com.",
- "eus2.his.arc.azure.com.",
"euw1.chat.si.riotgames.com.",
"eve.gameloft.com.",
"event.evtm.53.com.",
"events.glanceapis.com.",
- "events.polarbyte.com.",
- "events.swishapps.ai.",
"ex-adreq-asia.vivoglobal.com.",
"exappupgrade.vivoglobal.com.",
+ "excel-collab-geo.ocs.trafficmanager.net.",
"excel-collab.officeapps.live.com.",
- "exceptionless.io.",
- "exit.streamoptim.com.",
"exodus.desync.com.",
"exp.host.",
- "experimentation.deepl.com.",
- "exponential.com.",
- "ext.wisesecapp.com.",
+ "expedia.quantummetric.com.",
+ "expo.olo.com.",
"extension.faro.speechify.dev.",
"extension.savvy.security.",
"external-ams2-1.xx.fbcdn.net.",
@@ -1972,6 +1954,7 @@ var FakeECSFQDNs = container.NewMapSet(
"external-atl3-3.xx.fbcdn.net.",
"external-bos5-1.xx.fbcdn.net.",
"external-den2-1.xx.fbcdn.net.",
+ "external-det1-1.xx.fbcdn.net.",
"external-dfw5-1.xx.fbcdn.net.",
"external-dfw5-2.xx.fbcdn.net.",
"external-hou1-1.xx.fbcdn.net.",
@@ -1989,83 +1972,76 @@ var FakeECSFQDNs = container.NewMapSet(
"external-man2-1.xx.fbcdn.net.",
"external-mia3-1.xx.fbcdn.net.",
"external-mia3-2.xx.fbcdn.net.",
+ "external-mia3-3.xx.fbcdn.net.",
"external-msp1-1.xx.fbcdn.net.",
"external-ord5-1.xx.fbcdn.net.",
"external-ord5-2.xx.fbcdn.net.",
"external-ord5-3.xx.fbcdn.net.",
"external-phx1-1.xx.fbcdn.net.",
- "external-qro1-1.xx.fbcdn.net.",
"external-sea1-1.xx.fbcdn.net.",
"external-sjc3-1.xx.fbcdn.net.",
- "eyewind.cn.",
"f002.backblazeb2.com.",
+ "f590d326fefb4f688b74b268f60589a7.pacloudflare.com.",
"f9tg.com.",
- "fa000000074.resources.office.net.",
- "fa000000132.resources.office.net.",
- "fa000000137.resources.office.net.",
+ "fa3fca7ce79f4b81a39f216e916397d5.pacloudflare.com.",
"faas.marktplaats.nl.",
- "factor.reg.163.com.",
"factset.com.",
"fanatics.ent.box.com.",
"fanduel.quantummetric.com.",
"fanyiegg.youdao.com.",
- "fastlane.rubiconproject.com.",
"fdccpaadaptor.forddirectservices.com.",
+ "fdl.flysleep.cn.",
"fdl.zztfly.com.",
- "fe.xiaohongshu.com.",
"fed.federate365.com.",
+ "feedbackify.com.",
"feeder.co.",
"feelinsonice.l.google.com.",
- "fef.amsub0302.manage.microsoft.com.",
- "fef.fxpasu01.manage.microsoft.us.",
- "fef.msua09.manage.microsoft.com.",
- "fef.msub06.manage.microsoft.com.",
- "fef.msub07.manage.microsoft.com.",
- "feikua.net.",
+ "felixistderbeste.de.",
"fema.gov.",
- "femaledaily.com.",
"fengkongcloud.com.",
"fgwn01.ultipro.com.",
+ "fhs.play.king.com.",
"fi.telephony.goog.",
"field59.com.",
- "fifa15.content.easports.com.",
- "fikloshk1.com.",
"fil-pusa01.app.blackbaud.net.",
- "filemail.com.",
"files.jotform.com.",
- "files.pcpitstop.com.",
"filesyscrm.com.",
+ "filter.revrtb.net.",
"finalsite.com.",
"finalsite.net.",
- "fincen.gov.",
+ "fire.irs.gov.",
+ "firebase-auth.vsco.co.",
"fireeye.com.",
+ "firmguard.com.",
+ "fiscal.treasury.gov.",
"five9.com.",
- "fiverr-res.cloudinary.com.",
- "flashcards.vitalsource.com.",
+ "fl.cambiumtds.com.",
"flashjoin.net.",
"flip.to.",
"flixcdn.com.",
+ "floors.nitropay.com.",
"flypgs.com.",
+ "flyspirit.sharepoint.com.",
"fm.printaudit.com.",
"fn.us.ipqscdn.com.",
"fo.iemiq.com.",
"foisonad.com.",
- "fontawesome.com.cdn.cloudflare.net.",
"forcesafesearch.google.com.",
- "form.jotform.com.",
"forms-eu1.hscollectedforms.net.",
"forms-eu1.hsforms.com.",
"forms-eu1.hubspot.com.",
- "forms.hsforms.com.",
"fortisimperious.com.",
"fortworth.remotepc.com.",
- "foundation-ipv4.youdao.com.",
+ "fp-be-proximus.rcs.telephony.goog.",
"fp-ca-bell.rcs.telephony.goog.",
"fp-ca-telus.rcs.telephony.goog.",
"fp-de-carrier-vodafone.rcs.telephony.goog.",
"fp-de-telefonica.rcs.telephony.goog.",
+ "fp-gb-3.rcs.telephony.goog.",
"fp-gb-ee.rcs.telephony.goog.",
+ "fp-gb-o2.rcs.telephony.goog.",
"fp-us-att.rcs.telephony.goog.",
+ "fp-us-carrier-dish.rcs.telephony.goog.",
"fp-us-carrier-spectrum.rcs.telephony.goog.",
"fp-us-cspire.rcs.telephony.goog.",
"fp-us-tmobile.rcs.telephony.goog.",
@@ -2085,51 +2061,46 @@ var FakeECSFQDNs = container.NewMapSet(
"fr-prod.asyncgw.teams.microsoft.com.",
"fran.frvr.com.",
"francecentral.api.cognitive.microsoft.com.",
- "franecki.net.",
"frankfurt.remotepc.com.",
- "free.publictracker.xyz.",
"fremont.remotepc.com.",
- "freseniusmedicalcare.com.",
"fs.ultiproworkplace.com.",
+ "ft-sentry.hetangsmart.com.",
"ftke02.ultipro.com.",
"ftkew02.ultipro.com.",
"ftkn01.ultipro.com.",
"ftkn02.ultipro.com.",
"fxltsbl.com.",
+ "g.mongobrain.app.",
"g10498469755.co.",
- "g1584674684.co.",
- "g9hc4.cn.",
+ "g4.algbid.app.",
+ "g4.bidbrain.app.",
+ "g4.mongobrain.app.",
+ "g4.rtbrain.app.",
"ga.badambiz.com.",
- "galaxyappstore.com.",
- "game.zuiqiangyingyu.net.",
"gameloft.com.",
"gamemonkey.org.",
"gamepigeon.net.",
"gateway.ultiproworkplace.com.",
- "gb-vodafone.rcs.telephony.goog.",
"gb.ee.rcs.telephony.goog.",
"gb.o2.rcs.telephony.goog.",
- "gbc-word-edit.officeapps.live.com.",
"gccmod.ecs.office.com.",
"gcdn.co.",
+ "gce-beacons.gcp.gvt2.com.",
"gcloud.qq.com.",
"gcloudcs.com.",
"gcloudsdk.com.",
"gcs.sc-cdn.net.",
- "gdc-reqs-a.ngb.haplat.net.",
"gdid.datalake.gameloft.com.",
"geappl.io.",
"geckoterminal.com.",
- "geico-sync.quantummetric.com.",
"geniusmonkey.com.",
"geo-dra.platform.hicloud.com.",
- "geoip.apps.getjoy.ai.",
"geoplugin.net.",
- "geovn0mhn4u98k.josyliving.com.",
"germanywestcentral.api.cognitive.microsoft.com.",
+ "get-xmore-link3s.com.",
"getadmiral.com.",
"getbutton.io.",
- "getjoy.ai.",
+ "getgreenshot.org.",
"gettopple.com.",
"getui.net.",
"gifer.com.",
@@ -2137,71 +2108,72 @@ var FakeECSFQDNs = container.NewMapSet(
"gigabyte.com.",
"gitlab.com.",
"gla.gameloft.com.",
- "global-tokenserver-la.headline.uodoo.com.",
"global.datasite.com.",
"globalsigncdn.com.cdn.cloudflare.net.",
"globalsun.io.",
+ "globalvideoquery.fortinet.net.",
"gme.qcloud.com.",
- "gnc.com.",
- "gnss-eph.oss-cn-hangzhou.aliyuncs.com.",
- "go.activengage.com.",
+ "gmfinancial.com.",
+ "gms.machineq.net.",
"go.pdfforge.org.",
"goaffpro.com.",
+ "gohighlevel.com.",
"gonines.com.",
"google.org.",
"googledomains.com.",
"googlesync.permutive.com.",
+ "googls-api.com.",
"gosport.remotepc.com.",
"gov-bam.nr-data.net.",
"gov-bam.nr-data.net.cdn.cloudflare.net.",
- "gowish.com.",
"grab.zoom.us.",
"gravite.net.",
"gravitec.net.",
"greenville.remotepc.com.",
+ "grist.org.",
"group-ib.com.",
"grow.me.",
"grpc.vivintsky.com.",
- "gslb.xiaohongshu.com.",
- "gspe19-ssl.ls.apple.com.",
+ "gs.eponesh.com.",
"gtm.deepl.com.",
"gtm.vividseats.com.",
- "gw5.push.mcp.weibo.cn.",
+ "guidingcare.com.",
"gwadar.cn.",
"gyazo.com.",
"gz0.googleusercontent.com.",
"h-5h8i3ud8.online-metrix.net.",
"h-adp.online-metrix.net.",
- "h-bestbuy.online-metrix.net.",
"h-discover.online-metrix.net.",
- "h-e04kqxof.online-metrix.net.",
"h-ebay.online-metrix.net.",
"h-homedepot.online-metrix.net.",
"h-online.citi.online-metrix.net.",
+ "h-rbs-bank.online-metrix.net.",
"h-sdk.online-metrix.net.",
"h-signifyd.online-metrix.net.",
"h-v60nf4oj-qfp.online-metrix.net.",
"h-walmart.online-metrix.net.",
"h.app.wdesk.com.",
"h.online-metrix.net.",
- "h30-deploy.obviyo.net.",
+ "h104216-dcdn.mp.lura.live.",
+ "h104216-fcdn.mp.lura.live.",
+ "h104216-gcdn.mp.lura.live.",
+ "h104216-hcdn.mp.lura.live.",
+ "h104216-kcdn.mp.lura.live.",
+ "h107833-ecdn.mp.lura.live.",
"h64.online-metrix.net.",
+ "h72-ms-prod.netease.com.",
"hamina.remotepc.com.",
"hanoi.remotepc.com.",
"hapsee.cn.",
"hapseemate.cn.",
+ "harlandclarke.com.",
"harry.lu.",
"hawaii.remotepc.com.",
"hbopenbid-apac-v2.pubmnet.com.",
"hbwrapper.com.",
- "hds.ksc.kaspersky.com.",
"hecheck.bitmyanmar.info.",
- "hello.idocdn.com.",
"helloid.com.",
- "help-ar.apple.com.edgekey.net.",
- "henryco-my.sharepoint.com.",
"hetangsmart.com.",
- "heylink.me.",
"heytapdownload.com.",
"heytapmobi.com.",
"hft-prod.actioniq.mr-in.com.",
@@ -2213,158 +2185,160 @@ var FakeECSFQDNs = container.NewMapSet(
"hk.gcloudcs.com.",
"hk.ntp.org.cn.",
"hk.wechat.com.",
+ "hnzkclouds.com.",
"holykjvbible.com.",
"home.highwire.org.",
+ "homeadvisor.com.",
+ "homedepot-app.quantummetric.com.",
"homedepot.quantummetric.com.",
"honeywell.com.",
+ "hongkong.remotepc.com.",
"hornetsecurity.com.",
"hpfal.deepl.com.",
- "hpkaj.deepl.com.",
"hpplay.cn.",
+ "hpslb.deepl.com.",
+ "hrsa.gov.",
"hstgps.com.",
"html.it.",
"html5.qq.com.",
- "htms.heytapmobi.com.",
"httpdns.y5en.com.",
"huan.tv.",
"huaweicloud.com.",
"hubble.netease.com.",
- "hubcloud.com.cn.",
"hubspotemail.net.",
"huion.cn.",
- "hulkapps-wishlist.nyc3.digitaloceanspaces.com.",
+ "humix.com.cdn.cloudflare.net.",
"huorong.cn.",
"hw.gcloudcs.com.",
"hwapps-o.api.leiniao.com.",
"hwtvmanage-o.api.leiniao.com.",
"hybrid.ai.",
- "hzmklvdieo.com.",
"i-sg01a.ocloud.heytapmobi.com.",
"i.comfrt.com.",
+ "i.discogs.com.",
"i.inspi-dsp.com.",
"i.magazine.heytapmobi.com.",
"i.one-bid.com.",
"i.qchannel03.cn.",
- "i6-vn.weather.oppomobile.com.",
+ "i.voe.sx.",
+ "i.vsco.co.",
"ialicdn.com.",
"iappscontent.courts.state.ny.us.",
+ "iberiaparishsdla.aristotleinsight.com.",
"ibm.account.box.com.",
"ibm.box.com.",
"ibsrv.net.",
- "icalendars.app.",
- "icanhazip.tacticalrmm.io.",
"icare.infranetgroup.com.",
+ "ice.gov.",
"ichano.cn.",
"iconmonstr.com.",
"id-telkom.rcs.telephony.goog.",
"id-timer-appstore.vivoglobal.com.",
- "id.xhwear.life.",
+ "id.3plearning.com.",
+ "id.xhprograms.site.",
"idahofalls.remotepc.com.",
"iddallas1.remotepc.com.",
"iddenver.remotepc.com.",
- "ideafyi.oss-us-west-1.aliyuncs.com.",
"idlondon.remotepc.com.",
"idnewyork1.remotepc.com.",
- "idocdn.com.",
+ "idqqimg.com.",
"idr.cdnwidget.com.",
"ids.cdnwidget.com.",
"iemiq.com.",
"igame.gcloudcs.com.",
"ijoysoftconnect.com.",
- "ikki.youdao.com.",
- "illuminate.zendesk.com.",
"ilog-sea-aliyun.alipayplus.com.",
+ "im.vsco.co.",
+ "image-auto-captioning-computer-vision.cognitiveservices.azure.com.",
"image-sc.richrelevance.com.",
- "image-service.onefootball.com.",
"image.myqcloud.com.",
"image.online.adp.com.",
+ "images.fptplay53.net.",
+ "images.spot.im.",
"imagetrendelite.com.",
- "imeclient.openspeech.cn.",
- "img.cdn4dd.com.",
- "img.onesignal.com.",
- "img.shields.io.",
+ "imap.earthlink.net.",
"img2021.navyfederal.org.",
"img9.target.com.",
"imghst-de.com.",
"imgs.signifyd.com.",
- "imotech.site.",
- "imou-sg-ali-online-paas-iot-private-picture.oss-ap-southeast-1.aliyuncs.com.",
- "imou-sg3-ali-online-paas-private-picture.oss-ap-southeast-1.aliyuncs.com.",
- "imoulife.com.",
+ "immomo.com.",
+ "imodules.com.",
+ "imoim.net.",
"imp.admeking.com.",
"impactify.media.",
"imptrk.siteplug.com.",
+ "ims-prod07.adobelogin.com.",
"in-api.asm.skype.com.",
"in-prod.asyncgw.teams.microsoft.com.",
- "in-vpushonrt-stsdk.vivoglobal.com.",
"in.global.market.xiaomi.com.",
"in.gov.",
+ "in.pinterest.com.",
"in.visitors.live.",
"inc-collabrtc.officeapps.live.com.",
"indianapolis.remotepc.com.",
"inf.miui.com.",
+ "infoldgames.com.",
+ "infrastructure-command-api.newrelic.com.",
+ "ingov.zendesk.com.",
"inneraudioms.cc.easebar.com.",
"innity.com.",
"innity.net.",
- "ins-tgrfs7t3.ias.tencent-cloud.net.",
+ "insightadz.com.",
+ "insights-collector.cell.nr-data.net.",
"inskinad.com.",
"inspi-dsp.com.",
- "inspirebrands-sync.quantummetric.com.",
"inspirebrands.quantummetric.com.",
"instantmessaging-pa-jms-ap.googleapis.com.",
"instantmessaging-pa-jms-eu.googleapis.com.",
"instantmessaging-pa-jms-preprod-us.googleapis.com.",
"instantmessaging-pa-jms-us.googleapis.com.",
"instatus.com.",
- "internetdownloadmanager.com.",
- "intl-im-conn.iq.com.",
"intuit.zoom.us.",
"ios.crashsight.wetest.net.",
"iosack.tuisong.baidu.com.",
+ "iot.hillrom.com.",
"iot.services-edge.paloaltonetworks.com.",
+ "iowa-uscentral-replayclient-x20na.easebar.com.",
"iowa.remotepc.com.",
"ip-api.com.",
+ "ip-tools.tanjingpaas.com.",
"ipfs.io.",
- "ipmeta.io.",
"iprofiles.apple.com.",
"iprom.net.",
"ipv4.cadc.absolute.com.",
"ipv4.geojs.io.",
+ "ipv4.rer.lol.",
"ipv4.sdiptest.com.",
"ipv4.tracker.harry.lu.",
"ipv6-3.sdiptest.com.",
+ "ipv6.shuzilm.cn.",
"iq.com.",
"iscorp.com.",
"istanbul.remotepc.com.",
"istatmenus.app.",
+ "isuzu01-my.sharepoint.com.",
+ "ita-free.www.deepl.com.",
"italynorth.api.cognitive.microsoft.com.",
"itc.cn.",
"itch.io.",
"itch.zone.",
- "itemorder.com.",
"itm.cloud.com.",
"itoon.org.",
"ittpx.eskimi.com.",
"itzmx.com.",
"ivalua.com.",
- "ivt.np.community.playstation.net.",
"ivview.com.",
"ivview.net.",
"izatcloud.net.",
- "j.6sc.co.",
"jabfm.org.",
- "jackhenry.com.",
"japanwest.api.cognitive.microsoft.com.",
- "jbhunt-my.sharepoint.com.",
+ "jazzpharmaceuticals.cyberhaven.io.",
"jdadelivers.com.",
"jdcloud.com.",
"jhahosted.com.",
- "jishiyuchat.com.",
+ "jigsaw.vitalsource.com.",
"jitsu.com.",
"johannesburg.remotepc.com.",
- "josyliving.com.",
- "jotfor.ms.",
- "journals.plos.org.",
"jp-prod.asyncgw.teams.microsoft.com.",
"jp.cinarra.com.",
"jp1.chat.si.riotgames.com.",
@@ -2381,40 +2355,34 @@ var FakeECSFQDNs = container.NewMapSet(
"js-eu1.hsleadflows.net.",
"js-eu1.hubspot.com.",
"js.eruptr.io.",
- "js.volumental.com.",
- "jsonatm.broker.tplay.qq.com.",
+ "jsapi.lightboxcdn.com.",
"jss.starbucks.com.",
"jssprod-starbucks.trafficmanager.net.",
"jtt.rsmjournals.com.",
"junglefrog.com.",
"justice.gov.",
"jxappgtw.jhahosted.com.",
- "k.163.com.",
- "k.apiairasia.com.",
- "k8s1-event-tracker-fr.lb.indexww.com.",
- "k8s1-event-tracker-la.lb.indexww.com.",
"k8s1-event-tracker-ny.lb.indexww.com.",
"k8s1-event-tracker-sg.lb.indexww.com.",
- "k8s1-event-tracker-sj.lb.indexww.com.",
- "k8s1-event-tracker-ty.lb.indexww.com.",
"k8s1-event-tracker-va.lb.indexww.com.",
- "k8s1-la-ext-haproxy.lb.indexww.com.",
"k8s1-ny-ext-haproxy.lb.indexww.com.",
"k8s1-sj-ext-haproxy.lb.indexww.com.",
+ "ka-aus1.contentsquare.net.",
"kajicam.com.",
+ "kids.britannica.com.",
"kiev.remotepc.com.",
+ "kiprotect.com.",
"kirkland.zoom.us.",
- "kitchenaid.com.",
+ "kit-uploads.fontawesome.com.",
"kiwisizing.com.",
"klagenfurt.remotepc.com.",
"knightlab.com.",
"knoxville.remotepc.com.",
"komect.com.",
"komiku.id.",
- "kopipait.store.",
"koreacentral.api.cognitive.microsoft.com.",
+ "kps2yp94aqw5yi5d2.ay.delivery.",
"kstatic.googleusercontent.com.",
- "kumcams.com.",
"kunlunaq.com.",
"kunlunar.com.",
"kunlunca.com.",
@@ -2427,7 +2395,6 @@ var FakeECSFQDNs = container.NewMapSet(
"kurogame.xyz.",
"kwimgs.com.",
"kzhi.tech.",
- "l.deepintent.com.",
"la.remotepc.com.",
"la1.chat.si.riotgames.com.",
"la10.remotepc.com.",
@@ -2436,7 +2403,6 @@ var FakeECSFQDNs = container.NewMapSet(
"la2.remotepc.com.",
"la3.remotepc.com.",
"la4.remotepc.com.",
- "la4lbg.uae2grp.ucweb.com.",
"la8.remotepc.com.",
"la9.remotepc.com.",
"labtech.myitpros.com.",
@@ -2444,24 +2410,23 @@ var FakeECSFQDNs = container.NewMapSet(
"lalapush.com.",
"lan.sdk.linkedin.com.",
"lansing.remotepc.com.",
- "lansweeper.com.",
"larksuite.com.",
+ "last.fm.",
+ "lavasoft.com.",
"lax.remotepc.com.",
"layerxsecurity.com.",
"lazpay-fe-kyc-module-file.oss-ap-southeast-1.aliyuncs.com.",
- "lb-sin.mgid.com.",
"ldap.google.com.",
- "ldgslb.com.",
"ldmnq.com.",
"leadmanagerfx.com.",
+ "legacy.com.cdn.cloudflare.net.",
+ "lego.report-uri.com.",
"leihuo.netease.com.",
"leiniao.com.",
"levect.com.",
"level10gc.com.",
- "leveldata.poki.io.",
- "lianmeng.360.cn.",
+ "lexicon.33across.com.",
"liblynx.com.",
- "libretexts.org.",
"license.gonative.io.",
"license.litespeedtech.com.",
"license.unity3d.com.",
@@ -2472,54 +2437,37 @@ var FakeECSFQDNs = container.NewMapSet(
"lima.remotepc.com.",
"lineicons.com.",
"linguee.com.",
- "link-ga-hz-azure.yunxinfw.com.",
"link-vision-picture-sg-v2.oss-ap-southeast-1.aliyuncs.com.",
+ "linkedin.com.cdn.cloudflare.net.",
"lisbon.remotepc.com.",
"lissabon.remotepc.com.",
"list.tronlink.org.",
"litedev.sgp.hik-connect.com.",
"litespeedtech.com.",
+ "littler-my.sharepoint.com.",
"live.126.net.",
- "live.ngb.haplat.net.",
"live.shopee.com.br.",
- "live2.ngb.haplat.net.",
- "live3.ngb.haplat.net.",
- "live4.ngb.haplat.net.",
- "live5.ngb.haplat.net.",
- "live6.ngb.haplat.net.",
- "livect.haplat.net.",
- "livekilleenisd.sharepoint.com.",
- "livemediahost.com.",
- "liveoversea10.ngb.haplat.net.",
- "liveoversea5.ngb.haplat.net.",
- "liveoversea6.ngb.haplat.net.",
- "liveoversea7.ngb.haplat.net.",
- "liveoversea8.ngb.haplat.net.",
- "liveoversea9.ngb.haplat.net.",
- "liverpool.groupbycloud.com.",
+ "liveutmb-my.sharepoint.com.",
"ljubljana.remotepc.com.",
"loaduk.betfred.com.",
"loandepot.zoom.us.",
"local.adguard.org.",
- "local.info.g9hc4.cn.",
"log-api.newrelic.com.cdn.cloudflare.net.",
- "log-auth.zztfly.com.",
+ "log-auth.flysleep.cn.",
"log.getadblock.com.",
"log.mile.so.",
"log.webmaxlogger.net.",
- "log.xiaoyi.com.",
"log.zoom.us.",
- "log1.cmpassport.com.",
"log2.cmpassport.com.",
"logging-service-prod.getepic.com.",
"logging.mp.lura.live.",
"loggly.com.",
- "login.ringcentral.com.",
+ "login.cbc.ca.",
+ "login.pointclickcare.com.",
"login.teamviewer.com.",
- "login.vivaldi.net.",
+ "login6.cambiumtds.com.",
+ "logus.xiaoyi.com.",
"logx.optimizely.com.",
- "lokalise.co.",
- "lol.sw.game.qq.com.",
"london.remotepc.com.",
"london2.remotepc.com.",
"london3.remotepc.com.",
@@ -2528,42 +2476,46 @@ var FakeECSFQDNs = container.NewMapSet(
"london6.remotepc.com.",
"london8.remotepc.com.",
"long.tv.",
- "look.360.cn.",
+ "lotus-dsp.ru.",
"lpa.ds.gsma.com.",
"lplfinancial.app.box.com.",
"lsagentrelay.lansweeper.com.",
"ltfl.librarything.com.",
- "ludashi.com.",
+ "ltmpt.id.",
"luxembourg.remotepc.com.",
+ "ly200-cdn.com.",
"lycraservice-pa-cam-prod.googleapis.com.",
"lyric.alarmnet.com.",
+ "m.sogo.com.",
"m1.ubianet.com.",
- "m110601-fcdn.mp.lura.live.",
"m2.ubianet.com.",
- "m3.d.meituan.net.",
+ "m2uenlnen6g1spdnv2516hv5f56rd3hm-data.customer.pendo.io.",
"m3.ubianet.com.",
"m4.ubianet.com.",
"m5.ubianet.com.",
+ "m6.d.meituan.net.",
"m6.ubianet.com.",
"macclog-as.rj.link.",
"madrid.remotepc.com.",
"maers.adrs.org.cn.",
- "magichue.net.",
+ "magic.link.",
"maidenhead.remotepc.com.",
"mail.superhuman.com.",
"mailinblue.com.",
"maintenanceconnection.com.",
"malware-filter.gitlab.io.",
"mam.manage.microsoft.us.",
- "manage-selfhost.microsoft.com.",
"manage.wix.com.",
+ "manage1.esna.com.",
+ "management-2.dataremote.com.",
"management.azure.com.",
"management.privatelink.azure.com.",
"manassas.remotepc.com.",
"manchester.remotepc.com.",
- "marketplace.canva.com.",
+ "markets.jpmorgan.com.",
"marmot-cloud.com.",
"marseille.remotepc.com.",
+ "marvelrivals.com.",
"master1.teamviewer.com.",
"master10.teamviewer.com.",
"master11.teamviewer.com.",
@@ -2573,18 +2525,20 @@ var FakeECSFQDNs = container.NewMapSet(
"master15.teamviewer.com.",
"master16.teamviewer.com.",
"master2.teamviewer.com.",
- "master3.teamviewer.com.",
"master4.teamviewer.com.",
"master5.teamviewer.com.",
"master6.teamviewer.com.",
"master7.teamviewer.com.",
"master8.teamviewer.com.",
"master9.teamviewer.com.",
- "match.adfarm1.adition.com.",
- "max-l.mediav.com.",
+ "match.contentexchange.me.",
+ "matrix.netease.com.",
+ "maxfinishseveral.com.",
"mbboauth-1c.prd.cn.vwg-connect.cn.",
"mcallen.remotepc.com.",
+ "mcds.dalyfeds.com.",
"mcount.easebar.com.",
+ "mdap.tngdigital.com.my.",
"meari-oss-us.oss-us-west-1.aliyuncs.com.",
"meari-us.oss-us-west-1.aliyuncs.com.",
"medellin.remotepc.com.",
@@ -2600,12 +2554,11 @@ var FakeECSFQDNs = container.NewMapSet(
"media-bos5-1.cdn.whatsapp.net.",
"media-bru2-1.cdn.whatsapp.net.",
"media-cdg4-1.cdn.whatsapp.net.",
- "media-cdg4-2.cdn.whatsapp.net.",
- "media-cdg4-3.cdn.whatsapp.net.",
"media-cgk1-1.cdn.whatsapp.net.",
"media-cgk1-2.cdn.whatsapp.net.",
"media-cgk2-1.cdn.whatsapp.net.",
"media-den2-1.cdn.whatsapp.net.",
+ "media-det1-1.cdn.whatsapp.net.",
"media-dfw5-1.cdn.whatsapp.net.",
"media-dfw5-2.cdn.whatsapp.net.",
"media-dus1-1.cdn.whatsapp.net.",
@@ -2628,7 +2581,6 @@ var FakeECSFQDNs = container.NewMapSet(
"media-iad3-1.cdn.whatsapp.net.",
"media-iad3-2.cdn.whatsapp.net.",
"media-ist1-1.cdn.whatsapp.net.",
- "media-ist1-2.cdn.whatsapp.net.",
"media-kul2-1.cdn.whatsapp.net.",
"media-kul2-2.cdn.whatsapp.net.",
"media-kul3-1.cdn.whatsapp.net.",
@@ -2641,13 +2593,12 @@ var FakeECSFQDNs = container.NewMapSet(
"media-lhr6-2.cdn.whatsapp.net.",
"media-lhr8-1.cdn.whatsapp.net.",
"media-lhr8-2.cdn.whatsapp.net.",
- "media-lim1-1.cdn.whatsapp.net.",
- "media-lis1-1.cdn.whatsapp.net.",
"media-los2-1.cdn.whatsapp.net.",
"media-man2-1.cdn.whatsapp.net.",
"media-mct1-1.cdn.whatsapp.net.",
"media-mia3-1.cdn.whatsapp.net.",
"media-mia3-2.cdn.whatsapp.net.",
+ "media-mia3-3.cdn.whatsapp.net.",
"media-msp1-1.cdn.whatsapp.net.",
"media-mty2-1.cdn.whatsapp.net.",
"media-muc2-1.cdn.whatsapp.net.",
@@ -2660,7 +2611,6 @@ var FakeECSFQDNs = container.NewMapSet(
"media-qro1-2.cdn.whatsapp.net.",
"media-scl2-1.cdn.whatsapp.net.",
"media-sea1-1.cdn.whatsapp.net.",
- "media-shxixix-yijia21.sn7oss.ctyunxs.cn.",
"media-sin11-1.cdn.whatsapp.net.",
"media-sin11-2.cdn.whatsapp.net.",
"media-sin2-1.cdn.whatsapp.net.",
@@ -2670,60 +2620,51 @@ var FakeECSFQDNs = container.NewMapSet(
"media-sin6-3.cdn.whatsapp.net.",
"media-sin6-4.cdn.whatsapp.net.",
"media-sjc3-1.cdn.whatsapp.net.",
- "media-sof1-1.cdn.whatsapp.net.",
"media-vie1-1.cdn.whatsapp.net.",
- "media.defense.gov.",
- "media.pearsoncmg.com.",
+ "media-yyz1-1.cdn.whatsapp.net.",
"media.ringcentral.com.",
"media.superhuman.com.",
"media.tinkoff.ru.",
"mediacloud.xiaohongshu.com.",
"mediav.com.",
"melbourne.remotepc.com.",
- "members.onepeloton.com.",
"memphis.remotepc.com.",
- "messaging-api.shopifyapps.com.",
"metadata.decagon.ai.",
- "metric-api.cell.nr-data.net.",
- "metric.picodiglobal.com.",
"metrics-dre.dt.dbankcloud.cn.",
"metrics-dre.dt.dbankcloud.com.",
"metrics-dre.dt.hihonorcloud.com.",
+ "metrics.aimetric.net.",
"metrics5.data.hicloud.com.",
"mexicocity.remotepc.com.",
+ "mf.b37mrtl.ru.",
"mgbsdknaeast.matrix.easebar.com.",
"mgtv.com.",
"miami.remotepc.com.",
"miami2.remotepc.com.",
- "miami3.remotepc.com.",
- "miami4.remotepc.com.",
"mib2clu8.car-cloud-cn.net.",
"microad.jp.",
- "microfun.cn.",
"microvirt.com.",
"mid4.linkedin.com.",
"mida.so.",
- "migu.cn.",
+ "middleburycsin.aristotleinsight.com.",
"milan.remotepc.com.",
+ "milestoneinternet.com.cdn.cloudflare.net.",
+ "milwaukeetool.com.",
"mimir2.vivaldi.com.",
"min-api.cryptocompare.com.",
- "mini.browser.360.cn.",
"mintkeyboard.com.",
- "mirror5.internetdownloadmanager.com.",
"mixi.media.",
- "mobavenue.com.",
- "mobile-api.opentable.com.",
- "mobile-bank.cdn-tinkoff.ru.",
+ "mm.fcix.net.",
"mobile-collector.newrelic.com.cdn.cloudflare.net.",
"mobile-l7.bereal.com.",
"mobile-protect-api.securetheorem.com.",
- "mobile.shuzilm.cn.",
- "mobileapi.us.afterpay.com.",
"mobiledataplan-pa.googleapis.com.",
"mobilemaps-pa-gz.googleapis.com.",
"mobilemaps.googleapis.com.",
+ "mobilesettings1.okta-emea.com.",
"modelportrait.xiaohongshu.com.",
"modesto.remotepc.com.",
+ "mon0-misc-lf.amemv.com.",
"moni-onrt-stsdk.vivo.com.cn.",
"monitor.fraudblocker.com.",
"monitoring.getelevar.com.",
@@ -2734,61 +2675,62 @@ var FakeECSFQDNs = container.NewMapSet(
"morningstar.zoom.us.",
"motiondetection-us-1d.oss-us-west-1.aliyuncs.com.",
"motiondetection-us-7d.oss-us-west-1.aliyuncs.com.",
- "motiondetection-us.oss-us-west-1.aliyuncs.com.",
"mouser.com.",
- "mp.360.cn.",
- "mrisoftware.com.",
- "ms.applvn.com.",
+ "mqtt.strongline.commure.com.",
"ms1app.pb.com.",
- "ms4.applvn.com.",
"msdl.microsoft.com.",
"msf.3g.qq.com.",
"msg-img-hk.oss-cn-hongkong.aliyuncs.com.",
- "mssdk22-normal-useast1a.tiktokv.com.",
- "mssdk22-normal-useast2a.tiktokv.com.",
- "mtrace.qq.com.",
+ "msp.meituan.com.",
+ "mstate-my.sharepoint.com.",
"mumbai.remotepc.com.",
+ "mumu.nie.netease.com.",
"munich.remotepc.com.",
"muscache.cn.",
"music.163.com.163jiasu.com.",
+ "musical.ly.",
"musicps.p2p.qq.com.",
"musicpunch.p2p.qq.com.",
"musicstylingonline.com.",
- "mw.footballbros.io.",
+ "musteritemsilcisi.co.",
+ "mvision-us.moodmedia.com.",
"mx-vcode-od.vivoglobal.com.",
"mx.amx.rcs.telephony.goog.",
- "mx.pinterest.com.",
"mxp-pusa01.app.blackbaud.net.",
"mxptint.net.",
"my.canva.site.",
- "my.dealersocket.com.",
"my.getadmiral.com.",
"my.jbi.global.",
"my.nalpeiron.com.",
+ "my.olo.com.",
+ "mydrive.connect.aig.",
"myisolved.com.",
"myporn.club.",
"myqcloud.com.",
- "mystery-game-tile.poki.io.",
- "myworkdaycdn.com.cn.",
- "n-1-ashx.ad-m.net.",
- "n-2-ashx.ad-m.net.",
- "n-7-ashx.ad-m.net.",
+ "myvscloud.com.",
+ "n22.ultipro.com.",
"n33.ultipro.com.",
"n35.ultipro.com.",
- "na113.epm.cyberark.com.",
+ "na.account.riotgames.com.",
+ "na.vg.ac.pvp.net.cdn.cloudflare.net.",
"na2.chat.si.riotgames.com.",
"nab.com.au.",
"najva.com.",
- "nam.veta.naver.com.",
+ "nalpeiron.com.",
"namequery.com.",
"naperville.remotepc.com.",
+ "napps.zoom.us.",
"nashville.remotepc.com.",
"nationalmap.gov.",
- "nationalreview.com.",
- "nativecos.com.",
- "nc-pod1-smp-device.apple.com.",
+ "native.binance.info.",
+ "navan.com.",
+ "nawzryhwatm.broker.amsoveasea.com.",
+ "nbxc.com.",
"nc.com.",
"ncentral.centrexit.com.",
+ "nclkbnh4ojwsxl77.apple.holadns.com.",
+ "nclkbnh4ojwsxl77.apple.martianinc.co.",
+ "nclkbnh4ojwsxl77.apple.okamiboss.com.",
"nearme.com.cn.",
"nechicago.remotepc.com.",
"neonataltherapists.com.",
@@ -2801,7 +2743,6 @@ var FakeECSFQDNs = container.NewMapSet(
"network-check.sybo.net.",
"newcontinuum.net.",
"neworleans.remotepc.com.",
- "news-af-2.feednews.com.",
"news-af.feednews.com.",
"news-client.apple.com.",
"news-events.apple.com.",
@@ -2812,24 +2753,22 @@ var FakeECSFQDNs = container.NewMapSet(
"newyork.remotepc.com.",
"newyork2.remotepc.com.",
"newyork3.remotepc.com.",
- "nex.163.com.",
+ "nexrtb.com.",
"nexstar.amp.permutive.com.",
- "nextinsure.com.",
"nexx360.io.",
"ng1.angus.mrisoftware.com.",
"ngb.haplat.net.",
+ "nhwimp.izooto.com.",
"nice-team.net.",
"nie.netease.com.",
- "ninjakiwi.com.",
- "nist.gov.",
"nitroapps.co.",
"nitropay.com.",
+ "nitrotype.com.",
"noc.computerhelpnj.com.",
"node.setupad.com.",
"nokia.com.",
"nordcurrent.com.",
"northcentralus.api.cognitive.microsoft.com.",
- "northerntool.com.",
"northeurope.api.cognitive.microsoft.com.",
"norwayeast.api.cognitive.microsoft.com.",
"notes-analytics-events.apple.com.",
@@ -2882,6 +2821,7 @@ var FakeECSFQDNs = container.NewMapSet(
"ntes53.netease.com.",
"ntp.aliyun.com.",
"ntp.arlo.com.",
+ "ntp.arlo.com.cdn.cloudflare.net.",
"ntp.org.cn.",
"ntp1.aliyun.com.",
"ntp1.huan.tv.",
@@ -2890,6 +2830,7 @@ var FakeECSFQDNs = container.NewMapSet(
"ntp4.aliyun.com.",
"ntp5.aliyun.com.",
"ntp6.aliyun.com.",
+ "ntp7.aliyun.com.",
"nuremberg.remotepc.com.",
"nv.gov.",
"nvu-prd.mqtt.ivanticloud.com.",
@@ -2897,61 +2838,62 @@ var FakeECSFQDNs = container.NewMapSet(
"nwr.mmcdn.com.",
"nwr.static.mmcdn.com.",
"nws-platform.zoom.us.",
+ "nws.zoom.us.",
"nwsalert.onelouder.com.",
"nycourts.gov.",
"nycrt.marphezis.com.",
"obihai.telephony.goog.",
"obs.ap-southeast-3.myhuaweicloud.com.",
- "obs.line-apps.com.",
"observability-l7.bereal.com.",
"observability.bereal.com.",
"obsproject.com.",
"obus-dc20058-cn.heytapmobi.com.",
"obus-dc20123-cn.heytapmobi.com.",
+ "obus-dc20157-cn.heytapmobi.com.",
"obus-dctech-cn.heytapmobi.com.",
"oclc.org.",
"ocloud.oppomobile.com.",
"ocps-xfer.kronos.net.",
"ocsp.identrust.com.",
+ "odds.dc1.ovid.com.",
"odrs.fda.gov.ph.",
"odw7bf.dood.video.",
"oec22-normal-alisg.tokopediax.com.",
+ "oec22-normal-useast2a.tiktokv.com.",
"office.microsoft.com.",
- "officepreviewredir.microsoft.com.",
- "offline-pkg-api.dalyfeds.com.",
"ogma-l7.bereal.com.",
- "ogsvc.pgoriginad.com.",
"ojp.gov.",
"okko.tv.",
"ollama.com.",
+ "omaha.formlabs.com.",
"omiapp.me.",
"omitech.site.",
"on-hwapps-o.api.leiniao.com.",
+ "one-line.com.",
"one.newrelic.com.",
+ "one.one.one.one.",
"onekey1.cmpassport.com.",
"oneplus.net.",
"onethingpcs.com.",
"onezapp.com.",
- "online-store-web.shopifyapps.com.",
- "online.kugou.com.",
"onlinewebfonts.com.",
"op.mykonf.com.",
"opamarketplace.com.",
- "open.acgtracker.com.",
"open.oppomobile.com.",
+ "openathens.ovid.com.",
"opencmp.net.",
"opendsp.ru.",
"opex-service-cn.allawntech.com.",
"oppo.com.",
"oppomobile.com.",
+ "opps-api.getwarmly.com.",
"optimize.ulinq.asia.",
"optimize.urekamedia.com.",
"optimizely.com.",
+ "optumads.com.",
"orangehire.com.au.",
- "orderdeadline.com.",
"oregon.remotepc.com.",
"origin.fe-image-cache-ttp.useast8.byteglb.com.",
- "originplatform.com.",
"orlando.remotepc.com.",
"os7lm.6kvses.com.",
"osaka.remotepc.com.",
@@ -2963,23 +2905,15 @@ var FakeECSFQDNs = container.NewMapSet(
"oss-us-east-1.aliyuncs.com.",
"oss-us-west-1.aliyuncs.com.",
"otc.t-systems.com.",
- "otlp.nr-data.net.",
"ott.deepl.com.",
"overleaf.com.",
"overleafusercontent.com.",
- "overseasccl-a.haplat.net.",
- "overseasccl-b.haplat.net.",
- "overseasccl-c.haplat.net.",
- "overseasccl-d.haplat.net.",
- "overseasccl-major-a.haplat.net.",
- "overseasccl-major-b.haplat.net.",
- "overseasccl-major-c.haplat.net.",
"ovh.maxhost.io.",
"p.adlooxtracking.com.",
"p.vsco.co.",
"p0-pu-private-useast8.tiktokv.us.",
"p107609.cedexis-test.com.",
- "p18-buy.itunes.apple.com.",
+ "p14.d.meituan.net.",
"p20304.cedexis-test.com.",
"p20305.cedexis-test.com.",
"p20306.cedexis-test.com.",
@@ -2989,12 +2923,10 @@ var FakeECSFQDNs = container.NewMapSet(
"p20311.cedexis-test.com.",
"p20314.cedexis-test.com.",
"p20315.cedexis-test.com.",
- "p23-buy.itunes.apple.com.",
"p2p-cal-2.anker-in.com.",
"p2p-cal-3.anker-in.com.",
"p2p-cal.anker-in.com.",
"p2p-ohi-2.anker-in.com.",
- "p2p-sgp.anker-in.com.",
"p2p-vir.anker-in.com.",
"p2p.qq.com.",
"p2p2.cloudbirds.cn.",
@@ -3016,18 +2948,18 @@ var FakeECSFQDNs = container.NewMapSet(
"p34856.cedexis-test.com.",
"p34858.cedexis-test.com.",
"p35883.cedexis-test.com.",
- "p36-buy.itunes.apple.com.",
- "p37-buy.itunes.apple.com.",
- "p38635.cedexis-test.com.",
"p39604.cedexis-test.com.",
"p40255.cedexis-test.com.",
+ "p40256.cedexis-test.com.",
"p40259.cedexis-test.com.",
"p40264.cedexis-test.com.",
+ "p40265.cedexis-test.com.",
"p40266.cedexis-test.com.",
"p40267.cedexis-test.com.",
"p40480.cedexis-test.com.",
"p40488.cedexis-test.com.",
"p40491.cedexis-test.com.",
+ "p40952.cedexis-test.com.",
"p41237.cedexis-test.com.",
"p41238.cedexis-test.com.",
"p41905.cedexis-test.com.",
@@ -3038,9 +2970,7 @@ var FakeECSFQDNs = container.NewMapSet(
"p48436.cedexis-test.com.",
"p4p.arenabg.com.",
"p52066.cedexis-test.com.",
- "p56-buy.itunes.apple.com.",
"p56745.cedexis-test.com.",
- "p7-buy.itunes.apple.com.",
"p76593.cedexis-test.com.",
"p86075.cedexis-test.com.",
"p86077.cedexis-test.com.",
@@ -3053,46 +2983,42 @@ var FakeECSFQDNs = container.NewMapSet(
"pai.googlezip.net.",
"palermo.remotepc.com.",
"palm.tech.",
+ "panera-app.quantummetric.com.",
"panorama.wixapps.net.",
"paris.remotepc.com.",
"partnerboost.com.",
"pasadena.remotepc.com.",
"passportalmsp.com.",
- "patagonia-us.attn.tv.",
+ "pay.classy.org.",
"pay.datatrans.com.",
+ "paydns.wechat.com.",
"payment.api.speechify.com.",
"payment.omiapp.me.",
"pbdlsp1.pb.com.",
"pbe1.chat.si.riotgames.com.",
"pbs.btloader.com.",
- "pc-store.lenovomm.cn.",
"pc.crashsight.wetest.net.",
"pc.perfsight.wetest.net.",
"pcdn.brave.com.",
+ "pcm-apim-eastus-01.azure-api.net.",
"pd.cdnwidget.com.",
"pdf24.org.",
"pdfforge.org.",
- "peakpx.com.",
+ "pe0733.ci.managedwhitelisting.com.",
"peopleadmin.com.",
"perf-eu1.hsforms.com.",
"perfsight.qq.com.",
"perfsight.wetest.net.",
"perkspot-api.perkspot.com.",
"permutive.businessinsider.com.",
- "permutive.wired.com.",
- "perr.brightvpn.com.",
+ "permutive.newyorker.com.",
"pf.intuit.com.",
"pharos.studyquicks.com.",
"phoenix.remotepc.com.",
"phoenix2.remotepc.com.",
"phonebridge.zoho.com.",
- "photoroom.com.",
"phx02pap002.storage.live.com.",
- "phx02pap003.storage.live.com.",
- "phx02pap004.storage.live.com.",
- "phx02pap005.storage.live.com.",
"phx02pap006.storage.live.com.",
- "phx02pap008.storage.live.com.",
"pi2850.ci.managedwhitelisting.com.",
"pic.rutubelist.ru.",
"piicmgvmss.polaris.com.",
@@ -3101,48 +3027,48 @@ var FakeECSFQDNs = container.NewMapSet(
"ping.getadblock.com.",
"pingler.com.",
"pingma.qq.com.",
- "pingmesh.bigo.sg.",
"pitk.unioneeu.com.",
"pittsburgh.remotepc.com.",
"pix.cdnwidget.com.",
"pixel-sync.trafficmanager.net.",
- "pixel-us-west.rubiconproject.com.",
- "pixel.adlooxtracking.com.",
"pixel.gliacloud.com.",
- "pizzaedition.one.",
- "pk-live.cn.",
+ "pks.dp.holadns.com.",
+ "pks.dp.martianinc.co.",
+ "pks.dp.okamiboss.com.",
"pla-prod-scu-apim-01.azure-api.net.",
- "platform-alib.linkedin.cn.w.kunlunaq.com.",
+ "planner.cloud.microsoft.",
+ "platform-alib.linkedin.cn.",
"playstream.media.",
- "plotline.so.",
"plrm.zone.",
- "plt-gw-us.xiaoyi.com.",
+ "plt-api-us.xiaoyi.com.",
"pm.geniusmonkey.com.",
- "pods.officeapps.live.com.",
"poizon.com.",
"polandcentral.api.cognitive.microsoft.com.",
+ "polarcdn-terrax.com.",
"polling.zoom.us.",
"polymarket.com.",
"pop-convert.com.",
- "popmenucloud.com.",
"popt.in.",
- "portal.us.ubianet.com.",
"portals.mobi.",
"portland.remotepc.com.",
"posthog.com.",
"pov.spectrum.net.",
"powerpoint-collab.officeapps.live.com.",
+ "pp.cadc.absolute.com.",
+ "pp.ringcentral.biz.",
"ppgames.net.",
"ppos.com.",
+ "prebid-ny.casalemedia.com.",
+ "prebid-sj.casalemedia.com.",
"prebid-va.casalemedia.com.",
+ "prebid.anyclip.com.",
"prebid.trustedstack.com.",
"prebidserver.pixfuture.com.",
+ "prediction.cmab.optimizely.com.",
"premium.xvpn.io.",
"printaudit.com.",
- "printfriendly.com.",
"privy.io.",
"pro-glswish-aks-tm.trafficmanager.net.",
- "pro-swishapps-aks-tm.trafficmanager.net.",
"procore.com.",
"prod-client-api.v.aaplimg.com.",
"prod-default.lb.logrocket.network.",
@@ -3158,9 +3084,7 @@ var FakeECSFQDNs = container.NewMapSet(
"prod.api.letsencrypt.org.",
"production.kabutoservices.com.",
"profiler-collector.dalyfeds.com.",
- "project-limelight.com.",
- "projects.gitlab.io.",
- "promotions.newegg.com.",
+ "prolearning.nwea.org.",
"proquest.com.",
"protonvpn.com.",
"provaltech.com.",
@@ -3170,20 +3094,24 @@ var FakeECSFQDNs = container.NewMapSet(
"pub.affilimateapis.com.",
"pub.network.",
"public-api.uxfeedback.ru.",
+ "public-cdn-s3-us-west-2.oss-us-east-1.aliyuncs.com.",
"publicfaas.vasdgame.com.",
"publictracker.xyz.",
+ "pubsub.checkvideo.net.",
"puffer.6.401402081.west-gcloud.codm.activision.com.",
+ "pull-acdn-int.s.bytefcdn-oversea.com.",
"pull-cmaf-f77-sg01.tiktokcdn.com.",
"pull-cmaf-f77-tt01.tiktokcdn-us.com.",
"pull-cmaf-f77-tt02.tiktokcdn-us.com.",
"pull-cmaf-f77-tt03.fcdn.eu.tiktokcdn.com.",
"pull-cmaf-f77-tt03.tiktokcdn.com.",
"pull-cmaf-f77-va01.tiktokcdn.com.",
- "pull-hls-f77-sg01.tiktokcdn.com.",
+ "pull-f5-gcp01.tiktokcdn.com.c.bytefcdn-oversea.com.",
+ "pull-f5-tt04.tiktokrow-cdn.com.",
+ "pull-o5-va01.tiktokcdn.com.c.bytefcdn-oversea.com.",
+ "pull-q5-va01.tiktokrow-cdn.com.c.bytefcdn-oversea.com.",
"punch.p2p.qq.com.",
- "push-ads-cn.heytapmobi.com.",
"push-row.zui.com.",
- "push-rtmp-l95.douyincdn.com.ctdns.cn.",
"push.omiapp.me.",
"pushcrew.com.",
"pushimg.com.",
@@ -3212,18 +3140,13 @@ var FakeECSFQDNs = container.NewMapSet(
"qikify.com.",
"qiniudns.com.",
"qiniup.com.",
- "qiyukf.com.",
"qookkagames.com.",
+ "qpic.cn.",
"qq.com.cn.",
"qualcomm.cn.",
"qualcomm.com.",
- "qualys.ca.",
- "qualys.com.",
- "qualys.eu.",
- "qualys.in.",
- "quantamagazine.org.",
- "quantil.com.",
"quantummetric.com.",
+ "questdiagnostics.sharepoint.com.",
"quicinc.com.",
"quickcep.com.",
"qxwz.com.",
@@ -3231,220 +3154,216 @@ var FakeECSFQDNs = container.NewMapSet(
"r.intake-lr.com.",
"r.logr-ingest.com.",
"r.logrocket.io.",
+ "r.lr-hv-in.com.",
"r.lr-in-prod.com.",
"r.lr-in.com.",
"r.lr-ingest.com.",
"r.lr-ingest.io.",
"r.lr-intake.com.",
"r.lrkt-in.com.",
- "r.office.microsoft.com.",
"r.superhuman.com.",
- "r1---sn-a5mekn6r.c.2mdn.net.",
+ "r0ckeet.com.",
+ "r1---sn-5uaeznes.c.2mdn.net.",
+ "r1---sn-5ualdnsk.c.2mdn.net.",
+ "r1---sn-a5msen76.c.2mdn.net.",
"r1---sn-a5msener.c.2mdn.net.",
- "r1---sn-a5msenle.c.2mdn.net.",
+ "r1---sn-ab5l6ndy.c.2mdn.net.",
"r1---sn-ab5l6nk6.c.2mdn.net.",
"r1---sn-ab5l6nkd.c.2mdn.net.",
"r1---sn-ab5l6nr6.c.2mdn.net.",
"r1---sn-ab5l6nrd.c.2mdn.net.",
- "r1---sn-ab5l6nrl.c.2mdn.net.",
+ "r1---sn-ab5l6nrk.c.2mdn.net.",
+ "r1---sn-ab5l6nrr.c.2mdn.net.",
"r1---sn-ab5l6nrs.c.2mdn.net.",
- "r1---sn-ab5l6nrz.c.2mdn.net.",
"r1---sn-ab5sznzd.c.2mdn.net.",
- "r1---sn-ab5sznze.c.2mdn.net.",
"r1---sn-ab5sznzk.c.2mdn.net.",
+ "r1---sn-ab5sznzl.c.2mdn.net.",
"r1---sn-ab5sznzr.c.2mdn.net.",
"r1---sn-ab5sznzs.c.2mdn.net.",
"r1---sn-ab5sznzy.c.2mdn.net.",
+ "r1---sn-hp57ynl6.c.2mdn.net.",
+ "r1---sn-hp57ynly.c.2mdn.net.",
+ "r1---sn-nx57ynsk.c.2mdn.net.",
"r1---sn-p5qddn7r.c.2mdn.net.",
+ "r1---sn-p5qlsndr.c.2mdn.net.",
"r1---sn-p5qs7n6d.c.2mdn.net.",
"r1---sn-p5qs7nsk.c.2mdn.net.",
+ "r1---sn-p5qs7nsr.c.2mdn.net.",
"r1---sn-p5qs7nzr.c.2mdn.net.",
"r1---sn-q4fl6n6y.c.2mdn.net.",
- "r1---sn-q4fl6n6z.c.2mdn.net.",
- "r1---sn-q4fl6nd7.c.2mdn.net.",
- "r1---sn-q4flrney.c.2mdn.net.",
- "r1---sn-q4flrnez.c.2mdn.net.",
- "r1---sn-q4fzen7r.c.2mdn.net.",
"r1---sn-vgqskn66.c.2mdn.net.",
"r1---sn-vgqskn67.c.2mdn.net.",
- "r1---sn-vgqskn6d.c.2mdn.net.",
- "r1---sn-vgqskn6s.c.2mdn.net.",
- "r1---sn-vgqsknes.c.2mdn.net.",
- "r1---sn-vgqsknld.c.2mdn.net.",
- "r1---sn-vgqsknly.c.2mdn.net.",
- "r1---sn-vgqskns7.c.2mdn.net.",
- "r1---sn-vgqsknse.c.2mdn.net.",
- "r1---sn-vgqsknz7.c.2mdn.net.",
+ "r1---sn-vgqsknlk.c.2mdn.net.",
+ "r1---sn-vgqsknlr.c.2mdn.net.",
"r1---sn-vgqsknzl.c.2mdn.net.",
"r1---sn-vgqsknzs.c.2mdn.net.",
+ "r1---sn-vgqsknzy.c.2mdn.net.",
+ "r1---sn-vgqsrn67.c.2mdn.net.",
"r1---sn-vgqsrn6e.c.2mdn.net.",
"r1---sn-vgqsrne6.c.2mdn.net.",
"r1---sn-vgqsrnl6.c.2mdn.net.",
- "r1---sn-vgqsrnld.c.2mdn.net.",
"r1---sn-vgqsrnls.c.2mdn.net.",
- "r1---sn-vgqsrnlz.c.2mdn.net.",
- "r1---sn-vgqsrnsy.c.2mdn.net.",
+ "r1---sn-vgqsrnsd.c.2mdn.net.",
"r1---sn-vgqsrnzd.c.2mdn.net.",
- "r1---sn-vgqsrnzr.c.2mdn.net.",
- "r1---sn-vgqsrnzs.c.2mdn.net.",
+ "r1---sn-vgqsrnzk.c.2mdn.net.",
"r1---sn-vgqsrnzz.c.2mdn.net.",
+ "r2---sn-a5mlrnlz.c.2mdn.net.",
"r2---sn-ab5l6ndr.c.2mdn.net.",
- "r2---sn-ab5l6ndy.c.2mdn.net.",
"r2---sn-ab5l6nk6.c.2mdn.net.",
"r2---sn-ab5l6nkd.c.2mdn.net.",
"r2---sn-ab5l6nr6.c.2mdn.net.",
"r2---sn-ab5l6nrd.c.2mdn.net.",
"r2---sn-ab5l6nrk.c.2mdn.net.",
- "r2---sn-ab5l6nrl.c.2mdn.net.",
"r2---sn-ab5l6nrr.c.2mdn.net.",
"r2---sn-ab5l6nrs.c.2mdn.net.",
"r2---sn-ab5l6nrz.c.2mdn.net.",
"r2---sn-ab5sznz6.c.2mdn.net.",
"r2---sn-ab5sznzd.c.2mdn.net.",
+ "r2---sn-ab5sznze.c.2mdn.net.",
"r2---sn-ab5sznzk.c.2mdn.net.",
"r2---sn-ab5sznzl.c.2mdn.net.",
+ "r2---sn-ab5sznzr.c.2mdn.net.",
+ "r2---sn-ab5sznzs.c.2mdn.net.",
"r2---sn-ab5sznzy.c.2mdn.net.",
+ "r2---sn-ab5sznzz.c.2mdn.net.",
"r2---sn-ajab55-55.c.2mdn.net.",
- "r2---sn-hp57ynly.c.2mdn.net.",
- "r2---sn-n2uxaxjvh-j5xs.gvt1.com.",
- "r2---sn-p5qddn7r.c.2mdn.net.",
+ "r2---sn-nh5gujvh-h4xe.gvt1.com.",
+ "r2---sn-p5qddn7d.c.2mdn.net.",
"r2---sn-p5qlsn6l.c.2mdn.net.",
- "r2---sn-p5qlsn7d.c.2mdn.net.",
- "r2---sn-p5qlsn7l.c.2mdn.net.",
- "r2---sn-p5qs7nsr.c.2mdn.net.",
- "r2---sn-p5qs7nzy.c.2mdn.net.",
- "r2---sn-q4fl6n6s.c.2mdn.net.",
- "r2---sn-q4fl6ndz.c.2mdn.net.",
- "r2---sn-q4fl6nsd.c.2mdn.net.",
- "r2---sn-q4fl6nzy.c.2mdn.net.",
+ "r2---sn-p5qlsn7s.c.2mdn.net.",
+ "r2---sn-p5qlsndr.c.2mdn.net.",
+ "r2---sn-p5qlsny6.c.2mdn.net.",
+ "r2---sn-p5qs7nsk.c.2mdn.net.",
+ "r2---sn-p5qs7nzk.c.2mdn.net.",
+ "r2---sn-p5qs7nzr.c.2mdn.net.",
+ "r2---sn-q4fl6n66.c.2mdn.net.",
+ "r2---sn-q4flrnek.c.2mdn.net.",
+ "r2---sn-q4flrnl6.c.2mdn.net.",
"r2---sn-q4flrnsl.c.2mdn.net.",
- "r2---sn-q4fzen7e.c.2mdn.net.",
- "r2---sn-q4fzene7.c.2mdn.net.",
- "r2---sn-q4fzenee.c.2mdn.net.",
+ "r2---sn-q4fzen7y.c.2mdn.net.",
"r2---sn-vgqskn66.c.2mdn.net.",
"r2---sn-vgqskn6s.c.2mdn.net.",
"r2---sn-vgqskn6z.c.2mdn.net.",
- "r2---sn-vgqsknek.c.2mdn.net.",
- "r2---sn-vgqsknz7.c.2mdn.net.",
- "r2---sn-vgqsrn66.c.2mdn.net.",
+ "r2---sn-vgqsknld.c.2mdn.net.",
+ "r2---sn-vgqsknlr.c.2mdn.net.",
+ "r2---sn-vgqsknsk.c.2mdn.net.",
+ "r2---sn-vgqsknzs.c.2mdn.net.",
+ "r2---sn-vgqsknzz.c.2mdn.net.",
"r2---sn-vgqsrn67.c.2mdn.net.",
"r2---sn-vgqsrn6z.c.2mdn.net.",
- "r2---sn-vgqsrne6.c.2mdn.net.",
- "r2---sn-vgqsrnls.c.2mdn.net.",
+ "r2---sn-vgqsrnez.c.2mdn.net.",
+ "r2---sn-vgqsrnll.c.2mdn.net.",
"r2---sn-vgqsrnlz.c.2mdn.net.",
- "r2---sn-vgqsrnz7.c.2mdn.net.",
+ "r2---sn-vgqsrnsr.c.2mdn.net.",
"r2---sn-vgqsrnzd.c.2mdn.net.",
- "r2---sn-vgqsrnzs.c.2mdn.net.",
+ "r2---sn-vgqsrnzk.c.2mdn.net.",
+ "r2---sn-vgqsrnzr.c.2mdn.net.",
+ "r2---sn-vgqsrnzz.c.2mdn.net.",
+ "r3---sn-5uaeznes.c.2mdn.net.",
+ "r3---sn-5uaeznlz.c.2mdn.net.",
+ "r3---sn-5ualdnsy.c.2mdn.net.",
+ "r3---sn-5ualdnze.c.2mdn.net.",
+ "r3---sn-a5mekndl.c.2mdn.net.",
"r3---sn-ab5l6ndy.c.2mdn.net.",
- "r3---sn-ab5l6nk6.c.2mdn.net.",
"r3---sn-ab5l6nkd.c.2mdn.net.",
"r3---sn-ab5l6nr6.c.2mdn.net.",
- "r3---sn-ab5l6nrd.c.2mdn.net.",
"r3---sn-ab5l6nrk.c.2mdn.net.",
"r3---sn-ab5l6nrl.c.2mdn.net.",
- "r3---sn-ab5l6nrr.c.2mdn.net.",
"r3---sn-ab5l6nrs.c.2mdn.net.",
"r3---sn-ab5l6nrz.c.2mdn.net.",
- "r3---sn-ab5sznz6.c.2mdn.net.",
"r3---sn-ab5sznzd.c.2mdn.net.",
+ "r3---sn-ab5sznze.c.2mdn.net.",
"r3---sn-ab5sznzk.c.2mdn.net.",
- "r3---sn-ab5sznzl.c.2mdn.net.",
"r3---sn-ab5sznzr.c.2mdn.net.",
"r3---sn-ab5sznzs.c.2mdn.net.",
"r3---sn-ab5sznzz.c.2mdn.net.",
- "r3---sn-hp57kndk.c.2mdn.net.",
- "r3---sn-p5qddn7d.c.2mdn.net.",
+ "r3---sn-hp57kn6y.c.2mdn.net.",
+ "r3---sn-jxopj-nh4e.gvt1.com.",
+ "r3---sn-nx57ynsk.c.2mdn.net.",
"r3---sn-p5qddn7k.c.2mdn.net.",
- "r3---sn-p5qddn7r.c.2mdn.net.",
- "r3---sn-p5qlsn7s.c.2mdn.net.",
- "r3---sn-p5qlsndk.c.2mdn.net.",
- "r3---sn-p5qs7n6d.c.2mdn.net.",
- "r3---sn-p5qs7nsr.c.2mdn.net.",
+ "r3---sn-p5qs7nsk.c.2mdn.net.",
+ "r3---sn-p5qs7nzr.c.2mdn.net.",
"r3---sn-p5qs7nzy.c.2mdn.net.",
- "r3---sn-q4fl6ns6.c.2mdn.net.",
- "r3---sn-q4flrnel.c.2mdn.net.",
- "r3---sn-q4flrnld.c.2mdn.net.",
+ "r3---sn-q4fl6n66.c.2mdn.net.",
+ "r3---sn-q4fl6nsl.c.2mdn.net.",
+ "r3---sn-q4flrney.c.2mdn.net.",
"r3---sn-q4flrnlz.c.2mdn.net.",
"r3---sn-q4flrnsl.c.2mdn.net.",
- "r3---sn-q4flrnss.c.2mdn.net.",
"r3---sn-q4fzen7l.c.2mdn.net.",
+ "r3---sn-q4fzenee.c.2mdn.net.",
"r3---sn-vgqskn66.c.2mdn.net.",
- "r3---sn-vgqskn67.c.2mdn.net.",
+ "r3---sn-vgqskn6d.c.2mdn.net.",
"r3---sn-vgqskn6s.c.2mdn.net.",
- "r3---sn-vgqskned.c.2mdn.net.",
- "r3---sn-vgqsknes.c.2mdn.net.",
- "r3---sn-vgqsknlk.c.2mdn.net.",
- "r3---sn-vgqsknll.c.2mdn.net.",
- "r3---sn-vgqsknlz.c.2mdn.net.",
- "r3---sn-vgqskns7.c.2mdn.net.",
+ "r3---sn-vgqsknld.c.2mdn.net.",
+ "r3---sn-vgqsknlr.c.2mdn.net.",
+ "r3---sn-vgqsknly.c.2mdn.net.",
"r3---sn-vgqsknse.c.2mdn.net.",
"r3---sn-vgqsknsk.c.2mdn.net.",
- "r3---sn-vgqsknz7.c.2mdn.net.",
- "r3---sn-vgqsknzd.c.2mdn.net.",
- "r3---sn-vgqsknzk.c.2mdn.net.",
"r3---sn-vgqsknzl.c.2mdn.net.",
- "r3---sn-vgqsknzr.c.2mdn.net.",
"r3---sn-vgqsknzs.c.2mdn.net.",
- "r3---sn-vgqsknzy.c.2mdn.net.",
"r3---sn-vgqsrn6l.c.2mdn.net.",
- "r3---sn-vgqsrnl6.c.2mdn.net.",
- "r3---sn-vgqsrnll.c.2mdn.net.",
- "r3---sn-vgqsrnls.c.2mdn.net.",
- "r3---sn-vgqsrnlz.c.2mdn.net.",
- "r3---sn-vgqsrns6.c.2mdn.net.",
+ "r3---sn-vgqsrnlk.c.2mdn.net.",
+ "r3---sn-vgqsrnsr.c.2mdn.net.",
+ "r3---sn-vgqsrnz7.c.2mdn.net.",
"r3---sn-vgqsrnzd.c.2mdn.net.",
- "r3---sn-vgqsrnzz.c.2mdn.net.",
- "r4---sn-a5mekndl.c.2mdn.net.",
- "r4---sn-ab5l6ndy.c.2mdn.net.",
+ "r3---sn-vgqsrnzk.c.2mdn.net.",
+ "r4---sn-5ualdnll.c.2mdn.net.",
+ "r4---sn-ab5l6ndr.c.2mdn.net.",
"r4---sn-ab5l6nk6.c.2mdn.net.",
- "r4---sn-ab5l6nkd.c.2mdn.net.",
"r4---sn-ab5l6nr6.c.2mdn.net.",
"r4---sn-ab5l6nrd.c.2mdn.net.",
"r4---sn-ab5l6nrk.c.2mdn.net.",
"r4---sn-ab5l6nrl.c.2mdn.net.",
"r4---sn-ab5l6nrr.c.2mdn.net.",
"r4---sn-ab5l6nrz.c.2mdn.net.",
- "r4---sn-ab5sznld.c.2mdn.net.",
"r4---sn-ab5sznly.c.2mdn.net.",
+ "r4---sn-ab5sznz6.c.2mdn.net.",
"r4---sn-ab5sznzd.c.2mdn.net.",
"r4---sn-ab5sznze.c.2mdn.net.",
"r4---sn-ab5sznzk.c.2mdn.net.",
"r4---sn-ab5sznzl.c.2mdn.net.",
"r4---sn-ab5sznzr.c.2mdn.net.",
"r4---sn-ab5sznzy.c.2mdn.net.",
- "r4---sn-ab5sznzz.c.2mdn.net.",
- "r4---sn-p5qddn76.c.2mdn.net.",
- "r4---sn-p5qddn7z.c.2mdn.net.",
- "r4---sn-p5qlsn6l.c.2mdn.net.",
- "r4---sn-p5qlsnrr.c.2mdn.net.",
+ "r4---sn-hp57knds.c.2mdn.net.",
+ "r4---sn-p5qlsn76.c.2mdn.net.",
+ "r4---sn-p5qlsn7d.c.2mdn.net.",
+ "r4---sn-p5qlsn7s.c.2mdn.net.",
+ "r4---sn-p5qlsndr.c.2mdn.net.",
"r4---sn-p5qs7n6d.c.2mdn.net.",
+ "r4---sn-p5qs7nsk.c.2mdn.net.",
+ "r4---sn-p5qs7nzk.c.2mdn.net.",
+ "r4---sn-p5qs7nzr.c.2mdn.net.",
"r4---sn-q4fl6n66.c.2mdn.net.",
"r4---sn-q4fl6n6d.c.2mdn.net.",
- "r4---sn-q4fl6nd7.c.2mdn.net.",
- "r4---sn-q4fl6ndl.c.2mdn.net.",
- "r4---sn-q4fl6nz7.c.2mdn.net.",
- "r4---sn-q4flrn7k.c.2mdn.net.",
+ "r4---sn-q4fl6nlz.c.2mdn.net.",
+ "r4---sn-q4fl6ns7.c.2mdn.net.",
+ "r4---sn-q4flrn7r.c.2mdn.net.",
+ "r4---sn-q4flrne7.c.2mdn.net.",
+ "r4---sn-q4flrnez.c.2mdn.net.",
"r4---sn-q4flrnld.c.2mdn.net.",
- "r4---sn-q4flrnss.c.2mdn.net.",
+ "r4---sn-q4flrnlz.c.2mdn.net.",
+ "r4---sn-q4fzen7l.c.2mdn.net.",
"r4---sn-vgqskn66.c.2mdn.net.",
- "r4---sn-vgqskn6d.c.2mdn.net.",
"r4---sn-vgqskn6s.c.2mdn.net.",
+ "r4---sn-vgqskned.c.2mdn.net.",
"r4---sn-vgqskns7.c.2mdn.net.",
"r4---sn-vgqsknz7.c.2mdn.net.",
- "r4---sn-vgqsknze.c.2mdn.net.",
- "r4---sn-vgqsknzk.c.2mdn.net.",
+ "r4---sn-vgqsknzr.c.2mdn.net.",
"r4---sn-vgqsknzs.c.2mdn.net.",
- "r4---sn-vgqsknzz.c.2mdn.net.",
- "r4---sn-vgqsrn67.c.2mdn.net.",
"r4---sn-vgqsrn6z.c.2mdn.net.",
- "r4---sn-vgqsrnls.c.2mdn.net.",
+ "r4---sn-vgqsrnes.c.2mdn.net.",
+ "r4---sn-vgqsrnlk.c.2mdn.net.",
"r4---sn-vgqsrnlz.c.2mdn.net.",
- "r4---sn-vgqsrns6.c.2mdn.net.",
- "r4---sn-vgqsrnzd.c.2mdn.net.",
+ "r4---sn-vgqsrnsd.c.2mdn.net.",
+ "r4---sn-vgqsrnz6.c.2mdn.net.",
+ "r4---sn-vgqsrnz7.c.2mdn.net.",
"r4---sn-vgqsrnzk.c.2mdn.net.",
- "r4---sn-vgqsrnzs.c.2mdn.net.",
"r4---sn-vgqsrnzz.c.2mdn.net.",
"r4.visualwebsiteoptimizer.com.",
+ "r5---sn-5uaezned.c.2mdn.net.",
+ "r5---sn-5ualdns6.c.2mdn.net.",
+ "r5---sn-ab5l6ndr.c.2mdn.net.",
+ "r5---sn-ab5l6ndy.c.2mdn.net.",
"r5---sn-ab5l6nk6.c.2mdn.net.",
"r5---sn-ab5l6nkd.c.2mdn.net.",
"r5---sn-ab5l6nr6.c.2mdn.net.",
@@ -3459,52 +3378,53 @@ var FakeECSFQDNs = container.NewMapSet(
"r5---sn-ab5sznze.c.2mdn.net.",
"r5---sn-ab5sznzk.c.2mdn.net.",
"r5---sn-ab5sznzl.c.2mdn.net.",
- "r5---sn-ab5sznzr.c.2mdn.net.",
- "r5---sn-ajab55-55.c.2mdn.net.",
- "r5---sn-p5qddn7d.c.2mdn.net.",
- "r5---sn-p5qddn7k.c.2mdn.net.",
+ "r5---sn-ab5sznzs.c.2mdn.net.",
+ "r5---sn-ab5sznzy.c.2mdn.net.",
+ "r5---sn-p5qddn76.c.2mdn.net.",
"r5---sn-p5qlsn6l.c.2mdn.net.",
- "r5---sn-p5qlsn7d.c.2mdn.net.",
"r5---sn-p5qlsn7l.c.2mdn.net.",
"r5---sn-p5qlsn7s.c.2mdn.net.",
- "r5---sn-p5qlsnrr.c.2mdn.net.",
- "r5---sn-p5qlsny6.c.2mdn.net.",
+ "r5---sn-p5qs7n6d.c.2mdn.net.",
+ "r5---sn-p5qs7nsk.c.2mdn.net.",
"r5---sn-p5qs7nzk.c.2mdn.net.",
"r5---sn-p5qs7nzy.c.2mdn.net.",
- "r5---sn-q4fl6nss.c.2mdn.net.",
- "r5---sn-q4flrnsd.c.2mdn.net.",
- "r5---sn-q4flrnsl.c.2mdn.net.",
+ "r5---sn-q4fl6nd7.c.2mdn.net.",
+ "r5---sn-q4fl6nsk.c.2mdn.net.",
+ "r5---sn-q4flrn7k.c.2mdn.net.",
+ "r5---sn-q4flrnsk.c.2mdn.net.",
"r5---sn-q4fzen7l.c.2mdn.net.",
"r5---sn-qjp5q5-55.c.2mdn.net.",
+ "r5---sn-vgqskn66.c.2mdn.net.",
"r5---sn-vgqskn67.c.2mdn.net.",
- "r5---sn-vgqskn6d.c.2mdn.net.",
- "r5---sn-vgqskn6s.c.2mdn.net.",
- "r5---sn-vgqsknz7.c.2mdn.net.",
- "r5---sn-vgqsknzs.c.2mdn.net.",
- "r5---sn-vgqsrn66.c.2mdn.net.",
- "r5---sn-vgqsrn67.c.2mdn.net.",
- "r5---sn-vgqsrn6z.c.2mdn.net.",
+ "r5---sn-vgqsknld.c.2mdn.net.",
+ "r5---sn-vgqsknlk.c.2mdn.net.",
+ "r5---sn-vgqsknll.c.2mdn.net.",
+ "r5---sn-vgqsknls.c.2mdn.net.",
+ "r5---sn-vgqsknly.c.2mdn.net.",
+ "r5---sn-vgqsknz6.c.2mdn.net.",
+ "r5---sn-vgqsknzd.c.2mdn.net.",
+ "r5---sn-vgqsknze.c.2mdn.net.",
+ "r5---sn-vgqsknzk.c.2mdn.net.",
+ "r5---sn-vgqsknzr.c.2mdn.net.",
+ "r5---sn-vgqsknzz.c.2mdn.net.",
"r5---sn-vgqsrne6.c.2mdn.net.",
- "r5---sn-vgqsrned.c.2mdn.net.",
- "r5---sn-vgqsrnez.c.2mdn.net.",
- "r5---sn-vgqsrnl6.c.2mdn.net.",
+ "r5---sn-vgqsrnes.c.2mdn.net.",
+ "r5---sn-vgqsrnld.c.2mdn.net.",
+ "r5---sn-vgqsrnlk.c.2mdn.net.",
"r5---sn-vgqsrnls.c.2mdn.net.",
- "r5---sn-vgqsrnlz.c.2mdn.net.",
- "r5---sn-vgqsrnsr.c.2mdn.net.",
- "r5---sn-vgqsrnsy.c.2mdn.net.",
"r5---sn-vgqsrnzd.c.2mdn.net.",
- "r5---sn-vgqsrnzy.c.2mdn.net.",
+ "r5---sn-vgqsrnzk.c.2mdn.net.",
"r5.visualwebsiteoptimizer.com.",
"r6.visualwebsiteoptimizer.com.",
+ "raccorp.sharepoint.com.",
"radar.cedexis.com.",
"radar.com.",
"radyushin.com.",
- "railway.app.",
"raleigh.remotepc.com.",
+ "randomhouse.app.box.com.",
"rba-screen.healthsafe-id.com.",
"rba.onehealthcareid.com.",
"rbdata.boostymark.com.",
- "rbhs7ex3.onequince.com.",
"rbm-us.storage.googleapis.com.",
"rbmeuulvihtwm2eltjhwimi2.httpschecker.net.",
"rcs-acs-att-us.jibe.google.com.",
@@ -3515,7 +3435,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rcs-copper-us.googleapis.com.",
"rcs.telephony.goog.",
"rctiplus.id.",
- "rd.com.",
"readingplus.com.",
"realtime-data-api.transitapp.com.",
"realtime.luckyorange.com.",
@@ -3523,53 +3442,56 @@ var FakeECSFQDNs = container.NewMapSet(
"recombee.com.",
"recruiting.ultipro.com.",
"recruiting2.ultipro.com.",
- "reflector.makerbot.com.",
- "refpaucqkl.top.",
+ "referralrock.com.",
"regions.com.",
"registration.prna01.cmdagent.trafficmanager.net.",
- "reichelcormier.bid.",
- "related.queryly.com.",
"relay.shhnowisnottheti.me.",
- "relieffoot.com.",
"remote-config.gslb.sgw.shopeemobile.com.",
"remote.control4.com.",
"repo.zabbix.com.",
+ "requality.android.shouji.sogou.com.",
"request-global.czilladx.com.",
- "request.adx.ws.",
- "res-5.cloudinary.com.",
+ "request.czilladx.com.",
"resideo.com.",
"resource.digitalinsight.com.",
+ "restaurantguru.com.",
+ "restauth.opentable.com.",
"restproxy-analytics.ascendlearning.com.",
"restrict.youtube.com.",
"restrictmoderate.youtube.com.",
- "retailrocket.net.",
"retcode-us-west-1.arms.aliyuncs.com.",
"rethinkad.com.",
"retinavue.net.",
"retirementpartner.com.",
- "retrobowl25.com.",
- "reverso.net.",
"ri9864.ci.managedwhitelisting.com.",
"richrelevance.com.",
- "ridge.com.",
+ "riot-geo.pas.si.riotgames.com.",
"ripamatic.com.",
"rivergame.net.",
"riverside.remotepc.com.",
"rl.progressive.com.",
"rl.quantummetric.com.",
"rlm.haokan.mobi.",
+ "rmm.trustapex.com.",
+ "rmm2.jmark.com.",
"rms-dra.platform.dbankcloud.com.",
"rn-resource-app.xiaohongshu.com.",
- "roaming-eu.officeapps.live.com.",
+ "robokiller.com.",
"roborock.com.",
+ "rocketsutoledo-my.sharepoint.com.",
+ "rockhillssch.aristotleinsight.com.",
"rockylinux.org.",
+ "romsp-unifyconfig.vivo.com.cn.",
+ "roockmobile.com.",
+ "routeone.net.",
"router.teamviewer.com.",
"roxy.azurefd.net.",
"rpt.cedexis.com.",
- "rq.upgrade.cmpc.cmcm.com.",
- "rq.wh.cmcm.com.",
"rr1---sn-0nnpbo5a-bggl.googlevideo.com.",
"rr1---sn-0op8v4h5pox-cbgl.googlevideo.com.",
+ "rr1---sn-2aqu-hoaly.googlevideo.com.",
+ "rr1---sn-2aqu-hoas7.googlevideo.com.",
+ "rr1---sn-2aqu-hoasz.googlevideo.com.",
"rr1---sn-2imern76.googlevideo.com.",
"rr1---sn-2imern7d.googlevideo.com.",
"rr1---sn-2imeyn7k.googlevideo.com.",
@@ -3577,6 +3499,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-2napbiu-p5ie.gvt1.com.",
"rr1---sn-2oaig5-55.googlevideo.com.",
"rr1---sn-2pmxapm0n-gpjl.googlevideo.com.",
+ "rr1---sn-2pmxapm0n-gpjs.googlevideo.com.",
"rr1---sn-30a7rne6.googlevideo.com.",
"rr1---sn-30a7rned.googlevideo.com.",
"rr1---sn-30a7rnek.googlevideo.com.",
@@ -3584,8 +3507,8 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-30a7ynek.googlevideo.com.",
"rr1---sn-30a7yner.googlevideo.com.",
"rr1---sn-30a7yney.googlevideo.com.",
- "rr1---sn-30a7ynl7.googlevideo.com.",
"rr1---sn-3jpm-hjpe.googlevideo.com.",
+ "rr1---sn-3jpm-hjpe.gvt1.com.",
"rr1---sn-4g5e6ns6.googlevideo.com.",
"rr1---sn-4g5e6ns7.googlevideo.com.",
"rr1---sn-4g5e6nsd.googlevideo.com.",
@@ -3608,7 +3531,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-4g5edndk.googlevideo.com.",
"rr1---sn-4g5edndl.googlevideo.com.",
"rr1---sn-4g5edndr.googlevideo.com.",
- "rr1---sn-4g5ednds.googlevideo.com.",
"rr1---sn-4g5edndy.googlevideo.com.",
"rr1---sn-4g5edndz.googlevideo.com.",
"rr1---sn-4g5ednkl.googlevideo.com.",
@@ -3630,15 +3552,20 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-4g5lznek.googlevideo.com.",
"rr1---sn-4g5lzner.googlevideo.com.",
"rr1---sn-4g5lznes.googlevideo.com.",
+ "rr1---sn-4g5lzney.googlevideo.com.",
"rr1---sn-4g5lznez.googlevideo.com.",
"rr1---sn-4g5lznl6.googlevideo.com.",
"rr1---sn-4g5lznl7.googlevideo.com.",
- "rr1---sn-4g5lznl7.gvt1.com.",
"rr1---sn-4g5lznle.googlevideo.com.",
"rr1---sn-4g5lznls.googlevideo.com.",
"rr1---sn-4g5lznlz.googlevideo.com.",
"rr1---sn-4pgnuapbiu-hiul.googlevideo.com.",
- "rr1---sn-5axnug5-hxm6.googlevideo.com.",
+ "rr1---sn-5abxgpxuxaxjvh-9n4e.googlevideo.com.",
+ "rr1---sn-5abxgpxuxaxjvh-9n4l.googlevideo.com.",
+ "rr1---sn-5abxgpxuxaxjvh-9n4s.googlevideo.com.",
+ "rr1---sn-5abxgpxuxaxjvh-9n4z.googlevideo.com.",
+ "rr1---sn-5abxgpxuxaxjvh-j1az.googlevideo.com.",
+ "rr1---sn-5goeenez.googlevideo.com.",
"rr1---sn-5gxo-in8l.googlevideo.com.",
"rr1---sn-5gxo-in8s.googlevideo.com.",
"rr1---sn-5hne6n6e.googlevideo.com.",
@@ -3648,12 +3575,11 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-5hne6nsk.googlevideo.com.",
"rr1---sn-5hne6nsr.googlevideo.com.",
"rr1---sn-5hne6nsy.googlevideo.com.",
- "rr1---sn-5hne6nsy.gvt1.com.",
"rr1---sn-5hne6nsz.googlevideo.com.",
"rr1---sn-5hne6nz6.googlevideo.com.",
"rr1---sn-5hne6nzd.googlevideo.com.",
"rr1---sn-5hne6nzk.googlevideo.com.",
- "rr1---sn-5hne6nzk.gvt1.com.",
+ "rr1---sn-5hne6nzs.googlevideo.com.",
"rr1---sn-5hne6nzy.googlevideo.com.",
"rr1---sn-5hnednss.googlevideo.com.",
"rr1---sn-5hnednsz.googlevideo.com.",
@@ -3681,7 +3607,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-5uaeznse.googlevideo.com.",
"rr1---sn-5uaeznsl.googlevideo.com.",
"rr1---sn-5uaeznss.googlevideo.com.",
- "rr1---sn-5uaezny6.googlevideo.com.",
"rr1---sn-5uaeznyz.googlevideo.com.",
"rr1---sn-5uaeznze.googlevideo.com.",
"rr1---sn-5ualdnle.googlevideo.com.",
@@ -3700,8 +3625,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-5ualdnsz.googlevideo.com.",
"rr1---sn-5ualdnz7.googlevideo.com.",
"rr1---sn-5ualdnze.googlevideo.com.",
- "rr1---sn-8qj-nbo66.googlevideo.com.",
- "rr1---sn-8qj-nbo6y.googlevideo.com.",
"rr1---sn-8xgp1vo-2iae7.googlevideo.com.",
"rr1---sn-8xgp1vo-a5ml.googlevideo.com.",
"rr1---sn-8xgp1vo-ab56.googlevideo.com.",
@@ -3710,14 +3633,15 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-8xgp1vo-ab5l.googlevideo.com.",
"rr1---sn-8xgp1vo-ab5s.googlevideo.com.",
"rr1---sn-8xgp1vo-ab5z.googlevideo.com.",
- "rr1---sn-8xgp1vo-p5ie.googlevideo.com.",
- "rr1---sn-8xgp1vo-poql.googlevideo.com.",
+ "rr1---sn-8xgp1vo-p5qe.googlevideo.com.",
+ "rr1---sn-8xgp1vo-p5qe7.googlevideo.com.",
+ "rr1---sn-8xgp1vo-p5qee.googlevideo.com.",
+ "rr1---sn-8xgp1vo-p5qs.googlevideo.com.",
+ "rr1---sn-8xgp1vo-p5qy.googlevideo.com.",
"rr1---sn-8xgp1vo-vgqe.googlevideo.com.",
"rr1---sn-8xgp1vo-xfge.googlevideo.com.",
- "rr1---sn-8xgp1vo-xfgl.googlevideo.com.",
"rr1---sn-8xgp1vo-xfgs.googlevideo.com.",
"rr1---sn-9gv76n7e.googlevideo.com.",
- "rr1---sn-9gv76n7l.googlevideo.com.",
"rr1---sn-9gv76n7s.googlevideo.com.",
"rr1---sn-9gv76n7z.googlevideo.com.",
"rr1---sn-9gv7ene6.googlevideo.com.",
@@ -3728,34 +3652,28 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-9gv7zn7y.googlevideo.com.",
"rr1---sn-a5m7lnl6.googlevideo.com.",
"rr1---sn-a5m7lnld.googlevideo.com.",
- "rr1---sn-a5m7lnld.gvt1.com.",
"rr1---sn-a5mekn6d.googlevideo.com.",
"rr1---sn-a5mekn6k.googlevideo.com.",
"rr1---sn-a5mekn6l.googlevideo.com.",
- "rr1---sn-a5mekn6l.gvt1.com.",
"rr1---sn-a5mekn6r.googlevideo.com.",
"rr1---sn-a5mekn6s.googlevideo.com.",
- "rr1---sn-a5mekn6s.gvt1.com.",
"rr1---sn-a5mekn6z.googlevideo.com.",
- "rr1---sn-a5mekn6z.gvt1.com.",
"rr1---sn-a5meknd6.googlevideo.com.",
"rr1---sn-a5meknde.googlevideo.com.",
"rr1---sn-a5mekndl.googlevideo.com.",
- "rr1---sn-a5mekndl.gvt1.com.",
"rr1---sn-a5meknds.googlevideo.com.",
"rr1---sn-a5mekndz.googlevideo.com.",
"rr1---sn-a5meknsd.googlevideo.com.",
"rr1---sn-a5meknsy.googlevideo.com.",
+ "rr1---sn-a5meknzk.googlevideo.com.",
"rr1---sn-a5meknzl.googlevideo.com.",
"rr1---sn-a5meknzr.googlevideo.com.",
- "rr1---sn-a5meknzr.gvt1.com.",
"rr1---sn-a5meknzs.googlevideo.com.",
"rr1---sn-a5mlrnek.googlevideo.com.",
"rr1---sn-a5mlrnl6.googlevideo.com.",
"rr1---sn-a5mlrnll.googlevideo.com.",
"rr1---sn-a5mlrnls.googlevideo.com.",
"rr1---sn-a5mlrnlz.googlevideo.com.",
- "rr1---sn-a5mlrnlz.gvt1.com.",
"rr1---sn-a5msen76.googlevideo.com.",
"rr1---sn-a5msen7l.googlevideo.com.",
"rr1---sn-a5msen7s.googlevideo.com.",
@@ -3764,15 +3682,12 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-a5msener.googlevideo.com.",
"rr1---sn-a5msenes.googlevideo.com.",
"rr1---sn-a5msenl7.googlevideo.com.",
- "rr1---sn-a5msenl7.gvt1.com.",
"rr1---sn-a5msenle.googlevideo.com.",
"rr1---sn-a5msenll.googlevideo.com.",
"rr1---sn-ab5l6ndr.googlevideo.com.",
"rr1---sn-ab5l6ndy.googlevideo.com.",
"rr1---sn-ab5l6nk6.googlevideo.com.",
- "rr1---sn-ab5l6nk6.gvt1.com.",
"rr1---sn-ab5l6nkd.googlevideo.com.",
- "rr1---sn-ab5l6nkd.gvt1.com.",
"rr1---sn-ab5l6nr6.googlevideo.com.",
"rr1---sn-ab5l6nrd.googlevideo.com.",
"rr1---sn-ab5l6nrk.googlevideo.com.",
@@ -3784,11 +3699,9 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-ab5sznly.googlevideo.com.",
"rr1---sn-ab5sznz6.googlevideo.com.",
"rr1---sn-ab5sznzd.googlevideo.com.",
- "rr1---sn-ab5sznzd.gvt1.com.",
"rr1---sn-ab5sznze.googlevideo.com.",
- "rr1---sn-ab5sznze.gvt1.com.",
"rr1---sn-ab5sznzk.googlevideo.com.",
- "rr1---sn-ab5sznzk.gvt1.com.",
+ "rr1---sn-ab5sznzl.googlevideo.com.",
"rr1---sn-ab5sznzr.googlevideo.com.",
"rr1---sn-ab5sznzs.googlevideo.com.",
"rr1---sn-ab5sznzy.googlevideo.com.",
@@ -3803,11 +3716,9 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-aigl6nsd.googlevideo.com.",
"rr1---sn-aigl6nsk.googlevideo.com.",
"rr1---sn-aigl6nsr.googlevideo.com.",
- "rr1---sn-aigl6nz7.googlevideo.com.",
"rr1---sn-aigl6nze.googlevideo.com.",
"rr1---sn-aigl6nzk.googlevideo.com.",
"rr1---sn-aigl6nzl.googlevideo.com.",
- "rr1---sn-aigl6nzl.gvt1.com.",
"rr1---sn-aigl6nzr.googlevideo.com.",
"rr1---sn-aigl6nzs.googlevideo.com.",
"rr1---sn-aigzrn76.googlevideo.com.",
@@ -3826,17 +3737,14 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-aigzrnz7.googlevideo.com.",
"rr1---sn-aigzrnze.googlevideo.com.",
"rr1---sn-aj5ua5-5c.googlevideo.com.",
- "rr1---sn-ajab55-55.googlevideo.com.",
"rr1---sn-avbpj-cq5e.googlevideo.com.",
- "rr1---sn-bg5oqxjvh-50nz.googlevideo.com.",
"rr1---sn-bg5oqxjvh-jg2s.googlevideo.com.",
"rr1---sn-bg5oqxjvh-xa2s.googlevideo.com.",
- "rr1---sn-c0q7lns7.googlevideo.com.",
- "rr1---sn-c0q7lnsl.googlevideo.com.",
- "rr1---sn-c0q7lnz7.googlevideo.com.",
+ "rr1---sn-bvvbaxivnuxqjvhj5nu-hp5e.googlevideo.com.",
+ "rr1---sn-bvvbaxivnuxqjvhj5nu-hp5l.googlevideo.com.",
+ "rr1---sn-bvvbaxivnuxqjvhj5nu-hp5s.googlevideo.com.",
"rr1---sn-cvb7lne7.googlevideo.com.",
"rr1---sn-cvb7lnee.googlevideo.com.",
- "rr1---sn-cvb7lnls.googlevideo.com.",
"rr1---sn-cvb7lnlz.googlevideo.com.",
"rr1---sn-cvb7sn7r.googlevideo.com.",
"rr1---sn-fpnjoxu-h3xl.googlevideo.com.",
@@ -3844,45 +3752,34 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-gpuuxg-hxhl.googlevideo.com.",
"rr1---sn-gpuuxg-hxhs.googlevideo.com.",
"rr1---sn-gpuuxg-hxhz.googlevideo.com.",
- "rr1---sn-hjoj-jaul.googlevideo.com.",
"rr1---sn-hjoj-poul.googlevideo.com.",
"rr1---sn-hoa7kn76.googlevideo.com.",
"rr1---sn-hoa7kn7z.googlevideo.com.",
"rr1---sn-hoa7rn76.googlevideo.com.",
"rr1---sn-hoa7rn7z.googlevideo.com.",
"rr1---sn-hp57kn6r.googlevideo.com.",
- "rr1---sn-hp57kn6r.gvt1.com.",
"rr1---sn-hp57kn6y.googlevideo.com.",
- "rr1---sn-hp57kn6y.gvt1.com.",
"rr1---sn-hp57knd6.googlevideo.com.",
- "rr1---sn-hp57knd6.gvt1.com.",
"rr1---sn-hp57kndd.googlevideo.com.",
- "rr1---sn-hp57kndd.gvt1.com.",
"rr1---sn-hp57kndk.googlevideo.com.",
- "rr1---sn-hp57kndk.gvt1.com.",
"rr1---sn-hp57kndr.googlevideo.com.",
- "rr1---sn-hp57kndr.gvt1.com.",
"rr1---sn-hp57knds.googlevideo.com.",
"rr1---sn-hp57knds.gvt1.com.",
"rr1---sn-hp57kndy.googlevideo.com.",
- "rr1---sn-hp57kndy.gvt1.com.",
"rr1---sn-hp57kndz.googlevideo.com.",
"rr1---sn-hp57yn7r.googlevideo.com.",
"rr1---sn-hp57yn7y.googlevideo.com.",
"rr1---sn-hp57yne7.googlevideo.com.",
"rr1---sn-hp57ynee.googlevideo.com.",
"rr1---sn-hp57ynl6.googlevideo.com.",
+ "rr1---sn-hp57ynlr.googlevideo.com.",
"rr1---sn-hp57ynly.googlevideo.com.",
- "rr1---sn-hp57ynly.gvt1.com.",
"rr1---sn-hp57yns7.googlevideo.com.",
"rr1---sn-hp57ynse.googlevideo.com.",
- "rr1---sn-hp57ynse.gvt1.com.",
"rr1---sn-hp57ynsl.googlevideo.com.",
"rr1---sn-hp57ynss.googlevideo.com.",
- "rr1---sn-huxaqvv-ubqe.googlevideo.com.",
- "rr1---sn-huxaqvv-ubqe.gvt1.com.",
- "rr1---sn-huxaqvv-ubql.googlevideo.com.",
- "rr1---sn-huxaqvv-ubql.gvt1.com.",
+ "rr1---sn-hpqfxnu-oaxe.googlevideo.com.",
+ "rr1---sn-hpqfxnu-oaxl.googlevideo.com.",
"rr1---sn-hxgpu-qufs.googlevideo.com.",
"rr1---sn-i3b7kn6s.googlevideo.com.",
"rr1---sn-i3b7knld.googlevideo.com.",
@@ -3892,27 +3789,15 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-i3b7knse.googlevideo.com.",
"rr1---sn-i3b7knsl.googlevideo.com.",
"rr1---sn-i3b7knzl.googlevideo.com.",
- "rr1---sn-i3b7knzs.googlevideo.com.",
"rr1---sn-i3belne6.googlevideo.com.",
"rr1---sn-i3belney.googlevideo.com.",
- "rr1---sn-i3belnl6.googlevideo.com.",
"rr1---sn-i3belnl7.googlevideo.com.",
- "rr1---sn-i3belnll.googlevideo.com.",
- "rr1---sn-i3belnls.googlevideo.com.",
"rr1---sn-i3belnlz.googlevideo.com.",
"rr1---sn-i3bssn7e.googlevideo.com.",
- "rr1---sn-i5f5ppuxa-ioal.googlevideo.com.",
- "rr1---sn-i5f5ppuxa-ioas.googlevideo.com.",
"rr1---sn-jn2pgx4pcxg-w5os.googlevideo.com.",
"rr1---sn-jn2pgx4pcxg-w5oz.googlevideo.com.",
"rr1---sn-jvhj5nu-2iae.googlevideo.com.",
- "rr1---sn-jvhj5nu-2ial.googlevideo.com.",
"rr1---sn-jvhj5nu-nh4e.googlevideo.com.",
- "rr1---sn-jvhj5nu-nh4s.googlevideo.com.",
- "rr1---sn-jvhj5nu-qufe.googlevideo.com.",
- "rr1---sn-jvhj5nu-qufl.googlevideo.com.",
- "rr1---sn-jvhj5nu-qufs.googlevideo.com.",
- "rr1---sn-jvhj5nu-qufz.googlevideo.com.",
"rr1---sn-jvooxqouf3-cqaz.googlevideo.com.",
"rr1---sn-jxopj-n5oe.googlevideo.com.",
"rr1---sn-muxa-2iae.googlevideo.com.",
@@ -3933,7 +3818,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-nh5gujvh-h4xe.gvt1.com.",
"rr1---sn-npoe7ndl.googlevideo.com.",
"rr1---sn-npoe7nds.googlevideo.com.",
- "rr1---sn-npoe7nds.gvt1.com.",
"rr1---sn-npoe7ne6.googlevideo.com.",
"rr1---sn-npoe7ne7.googlevideo.com.",
"rr1---sn-npoe7ned.googlevideo.com.",
@@ -3958,6 +3842,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-npoeenee.googlevideo.com.",
"rr1---sn-npoeenek.googlevideo.com.",
"rr1---sn-npoeener.googlevideo.com.",
+ "rr1---sn-npoeeney.googlevideo.com.",
"rr1---sn-npoeenez.googlevideo.com.",
"rr1---sn-npoeenl7.googlevideo.com.",
"rr1---sn-npoeenle.googlevideo.com.",
@@ -3966,26 +3851,27 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-npoeenly.googlevideo.com.",
"rr1---sn-npoeens7.googlevideo.com.",
"rr1---sn-npoldn76.googlevideo.com.",
- "rr1---sn-npoldn7d.googlevideo.com.",
"rr1---sn-npoldn7e.googlevideo.com.",
"rr1---sn-npoldn7l.googlevideo.com.",
"rr1---sn-npoldn7s.googlevideo.com.",
- "rr1---sn-npoldn7s.gvt1.com.",
"rr1---sn-npoldn7y.googlevideo.com.",
"rr1---sn-npoldn7z.googlevideo.com.",
"rr1---sn-npoldne7.googlevideo.com.",
- "rr1---sn-nuagpm-nuae.googlevideo.com.",
"rr1---sn-nv0uixgo-5ual.googlevideo.com.",
"rr1---sn-nx57ynlk.googlevideo.com.",
"rr1---sn-nx57ynsd.googlevideo.com.",
"rr1---sn-nx57ynse.googlevideo.com.",
"rr1---sn-nx57ynsk.googlevideo.com.",
+ "rr1---sn-nx57ynsk.gvt1.com.",
"rr1---sn-nx57ynsl.googlevideo.com.",
"rr1---sn-nx57ynss.googlevideo.com.",
"rr1---sn-nx57ynsz.googlevideo.com.",
"rr1---sn-nx5s7n76.googlevideo.com.",
"rr1---sn-nx5s7n7d.googlevideo.com.",
+ "rr1---sn-nx5s7n7s.googlevideo.com.",
"rr1---sn-nx5s7n7y.googlevideo.com.",
+ "rr1---sn-nx5s7n7z.googlevideo.com.",
+ "rr1---sn-nx5s7nee.googlevideo.com.",
"rr1---sn-nx5s7nel.googlevideo.com.",
"rr1---sn-o097znsd.googlevideo.com.",
"rr1---sn-o097znse.googlevideo.com.",
@@ -3993,6 +3879,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-o097znsl.googlevideo.com.",
"rr1---sn-o097znsr.googlevideo.com.",
"rr1---sn-o097znss.googlevideo.com.",
+ "rr1---sn-o097znsz.googlevideo.com.",
"rr1---sn-o097znz7.googlevideo.com.",
"rr1---sn-o097znzd.googlevideo.com.",
"rr1---sn-o097znze.googlevideo.com.",
@@ -4019,86 +3906,79 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-p5qlsny6.googlevideo.com.",
"rr1---sn-p5qs7n6d.googlevideo.com.",
"rr1---sn-p5qs7nsk.googlevideo.com.",
+ "rr1---sn-p5qs7nsk.gvt1.com.",
"rr1---sn-p5qs7nsr.googlevideo.com.",
- "rr1---sn-p5qs7nsr.gvt1.com.",
"rr1---sn-p5qs7nzk.googlevideo.com.",
"rr1---sn-p5qs7nzr.googlevideo.com.",
"rr1---sn-p5qs7nzy.googlevideo.com.",
- "rr1---sn-p5qs7nzy.gvt1.com.",
"rr1---sn-paapovpnjxou0gt-nual.googlevideo.com.",
"rr1---sn-paapovpnjxou0gt-nuas.googlevideo.com.",
+ "rr1---sn-paapovpnjxou0gt-nuaz.googlevideo.com.",
"rr1---sn-pjnpu-5hfe.googlevideo.com.",
"rr1---sn-pobpb-poql.googlevideo.com.",
"rr1---sn-q4fl6n66.googlevideo.com.",
- "rr1---sn-q4fl6n66.gvt1.com.",
"rr1---sn-q4fl6n6d.googlevideo.com.",
+ "rr1---sn-q4fl6n6d.gvt1.com.",
"rr1---sn-q4fl6n6r.googlevideo.com.",
+ "rr1---sn-q4fl6n6r.gvt1.com.",
"rr1---sn-q4fl6n6s.googlevideo.com.",
- "rr1---sn-q4fl6n6s.gvt1.com.",
"rr1---sn-q4fl6n6y.googlevideo.com.",
- "rr1---sn-q4fl6n6y.gvt1.com.",
"rr1---sn-q4fl6n6z.googlevideo.com.",
- "rr1---sn-q4fl6n6z.gvt1.com.",
"rr1---sn-q4fl6nd7.googlevideo.com.",
+ "rr1---sn-q4fl6nd7.gvt1.com.",
"rr1---sn-q4fl6nde.googlevideo.com.",
"rr1---sn-q4fl6nde.gvt1.com.",
"rr1---sn-q4fl6ndl.googlevideo.com.",
- "rr1---sn-q4fl6ndl.gvt1.com.",
"rr1---sn-q4fl6nds.googlevideo.com.",
- "rr1---sn-q4fl6nds.gvt1.com.",
"rr1---sn-q4fl6ndz.googlevideo.com.",
"rr1---sn-q4fl6nlz.googlevideo.com.",
"rr1---sn-q4fl6ns6.googlevideo.com.",
- "rr1---sn-q4fl6ns6.gvt1.com.",
"rr1---sn-q4fl6ns7.googlevideo.com.",
"rr1---sn-q4fl6nsd.googlevideo.com.",
"rr1---sn-q4fl6nsk.googlevideo.com.",
- "rr1---sn-q4fl6nsk.gvt1.com.",
"rr1---sn-q4fl6nsl.googlevideo.com.",
- "rr1---sn-q4fl6nsl.gvt1.com.",
"rr1---sn-q4fl6nsr.googlevideo.com.",
"rr1---sn-q4fl6nsr.gvt1.com.",
"rr1---sn-q4fl6nss.googlevideo.com.",
"rr1---sn-q4fl6nsy.googlevideo.com.",
+ "rr1---sn-q4fl6nsy.gvt1.com.",
+ "rr1---sn-q4fl6nz6.googlevideo.com.",
"rr1---sn-q4fl6nz7.googlevideo.com.",
- "rr1---sn-q4fl6nz7.gvt1.com.",
"rr1---sn-q4fl6nzy.googlevideo.com.",
"rr1---sn-q4fl6nzy.gvt1.com.",
"rr1---sn-q4flrn7k.googlevideo.com.",
"rr1---sn-q4flrn7r.googlevideo.com.",
"rr1---sn-q4flrn7y.googlevideo.com.",
"rr1---sn-q4flrne6.googlevideo.com.",
+ "rr1---sn-q4flrne7.googlevideo.com.",
"rr1---sn-q4flrnee.googlevideo.com.",
+ "rr1---sn-q4flrnee.gvt1.com.",
"rr1---sn-q4flrnek.googlevideo.com.",
"rr1---sn-q4flrnel.googlevideo.com.",
- "rr1---sn-q4flrner.googlevideo.com.",
"rr1---sn-q4flrnes.googlevideo.com.",
"rr1---sn-q4flrney.googlevideo.com.",
+ "rr1---sn-q4flrney.gvt1.com.",
"rr1---sn-q4flrnez.googlevideo.com.",
- "rr1---sn-q4flrnez.gvt1.com.",
"rr1---sn-q4flrnl6.googlevideo.com.",
- "rr1---sn-q4flrnl6.gvt1.com.",
"rr1---sn-q4flrnl7.googlevideo.com.",
"rr1---sn-q4flrnld.googlevideo.com.",
- "rr1---sn-q4flrnld.gvt1.com.",
"rr1---sn-q4flrnle.googlevideo.com.",
+ "rr1---sn-q4flrnle.gvt1.com.",
"rr1---sn-q4flrnlz.googlevideo.com.",
- "rr1---sn-q4flrnlz.gvt1.com.",
"rr1---sn-q4flrnsd.googlevideo.com.",
- "rr1---sn-q4flrnsd.gvt1.com.",
"rr1---sn-q4flrnsk.googlevideo.com.",
- "rr1---sn-q4flrnsk.gvt1.com.",
"rr1---sn-q4flrnsl.googlevideo.com.",
"rr1---sn-q4flrnsl.gvt1.com.",
"rr1---sn-q4flrnss.googlevideo.com.",
- "rr1---sn-q4fzen7e.googlevideo.com.",
"rr1---sn-q4fzen7l.googlevideo.com.",
"rr1---sn-q4fzen7l.gvt1.com.",
- "rr1---sn-q4fzen7r.googlevideo.com.",
"rr1---sn-q4fzen7s.googlevideo.com.",
+ "rr1---sn-q4fzen7s.gvt1.com.",
"rr1---sn-q4fzen7y.googlevideo.com.",
"rr1---sn-q4fzene7.googlevideo.com.",
+ "rr1---sn-q4fzene7.gvt1.com.",
"rr1---sn-q4fzenee.googlevideo.com.",
+ "rr1---sn-q4fzenee.gvt1.com.",
"rr1---sn-qjp5q5-55.googlevideo.com.",
"rr1---sn-qxo7rn7k.googlevideo.com.",
"rr1---sn-qxo7rn7r.googlevideo.com.",
@@ -4106,17 +3986,15 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-qxoedn7k.googlevideo.com.",
"rr1---sn-qxoedne7.googlevideo.com.",
"rr1---sn-qxoednee.googlevideo.com.",
- "rr1---sn-uxmqx2uv4po4v-50nl.googlevideo.com.",
"rr1---sn-v53a5oqnji-4oul.googlevideo.com.",
"rr1---sn-v5goxu-jhi6.googlevideo.com.",
"rr1---sn-v5goxu-jhil.googlevideo.com.",
"rr1---sn-v5goxu-jhiz.googlevideo.com.",
+ "rr1---sn-v5goxu-jhiz.gvt1.com.",
"rr1---sn-vgqskn66.googlevideo.com.",
"rr1---sn-vgqskn67.googlevideo.com.",
"rr1---sn-vgqskn6d.googlevideo.com.",
- "rr1---sn-vgqskn6d.gvt1.com.",
"rr1---sn-vgqskn6s.googlevideo.com.",
- "rr1---sn-vgqskn6s.gvt1.com.",
"rr1---sn-vgqskn6z.googlevideo.com.",
"rr1---sn-vgqskne6.googlevideo.com.",
"rr1---sn-vgqskned.googlevideo.com.",
@@ -4127,14 +4005,12 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-vgqsknlk.googlevideo.com.",
"rr1---sn-vgqsknll.googlevideo.com.",
"rr1---sn-vgqsknlr.googlevideo.com.",
- "rr1---sn-vgqsknlr.gvt1.com.",
"rr1---sn-vgqsknls.googlevideo.com.",
"rr1---sn-vgqsknly.googlevideo.com.",
+ "rr1---sn-vgqsknlz.googlevideo.com.",
"rr1---sn-vgqskns7.googlevideo.com.",
- "rr1---sn-vgqsknse.googlevideo.com.",
"rr1---sn-vgqsknsk.googlevideo.com.",
"rr1---sn-vgqsknz6.googlevideo.com.",
- "rr1---sn-vgqsknz6.gvt1.com.",
"rr1---sn-vgqsknz7.googlevideo.com.",
"rr1---sn-vgqsknzd.googlevideo.com.",
"rr1---sn-vgqsknze.googlevideo.com.",
@@ -4147,10 +4023,8 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-vgqsrn66.googlevideo.com.",
"rr1---sn-vgqsrn67.googlevideo.com.",
"rr1---sn-vgqsrn6e.googlevideo.com.",
- "rr1---sn-vgqsrn6e.gvt1.com.",
"rr1---sn-vgqsrn6l.googlevideo.com.",
"rr1---sn-vgqsrn6z.googlevideo.com.",
- "rr1---sn-vgqsrn6z.gvt1.com.",
"rr1---sn-vgqsrne6.googlevideo.com.",
"rr1---sn-vgqsrned.googlevideo.com.",
"rr1---sn-vgqsrnek.googlevideo.com.",
@@ -4160,7 +4034,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-vgqsrnld.googlevideo.com.",
"rr1---sn-vgqsrnlk.googlevideo.com.",
"rr1---sn-vgqsrnll.googlevideo.com.",
- "rr1---sn-vgqsrnll.gvt1.com.",
"rr1---sn-vgqsrnls.googlevideo.com.",
"rr1---sn-vgqsrnlz.googlevideo.com.",
"rr1---sn-vgqsrns6.googlevideo.com.",
@@ -4168,35 +4041,55 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-vgqsrnsr.googlevideo.com.",
"rr1---sn-vgqsrnsy.googlevideo.com.",
"rr1---sn-vgqsrnz6.googlevideo.com.",
- "rr1---sn-vgqsrnz6.gvt1.com.",
"rr1---sn-vgqsrnz7.googlevideo.com.",
- "rr1---sn-vgqsrnz7.gvt1.com.",
"rr1---sn-vgqsrnzd.googlevideo.com.",
"rr1---sn-vgqsrnzk.googlevideo.com.",
"rr1---sn-vgqsrnzr.googlevideo.com.",
"rr1---sn-vgqsrnzs.googlevideo.com.",
"rr1---sn-vgqsrnzy.googlevideo.com.",
"rr1---sn-vgqsrnzz.googlevideo.com.",
+ "rr1---sn-vgqsrnzz.gvt1.com.",
"rr1---sn-vnix5o-28ql.googlevideo.com.",
"rr1---sn-voxoxu-v3jl.googlevideo.com.",
"rr1---sn-voxoxu-v3js.googlevideo.com.",
"rr1---sn-xo5-co5l.googlevideo.com.",
- "rr1.sn-q4fl6n6r.googlevideo.com.",
- "rr1.sn-q4fl6nss.googlevideo.com.",
- "rr1.sn-q4flrne6.googlevideo.com.",
- "rr1.sn-q4flrnee.googlevideo.com.",
- "rr1.sn-q4flrnl7.googlevideo.com.",
- "rr1.sn-q4flrnsl.googlevideo.com.",
+ "rr1.sn-4g5e6nze.googlevideo.com.",
+ "rr1.sn-4g5edndk.googlevideo.com.",
+ "rr1.sn-5hnekn7s.googlevideo.com.",
+ "rr1.sn-aigl6nsd.googlevideo.com.",
+ "rr1.sn-hp57knds.googlevideo.com.",
+ "rr1.sn-p5qs7nsk.googlevideo.com.",
+ "rr1.sn-q4fl6n6y.googlevideo.com.",
+ "rr1.sn-q4fl6nsd.googlevideo.com.",
+ "rr1.sn-q4fl6nsl.googlevideo.com.",
+ "rr1.sn-q4fl6nz7.googlevideo.com.",
+ "rr1.sn-q4flrne7.googlevideo.com.",
+ "rr1.sn-q4flrnl6.googlevideo.com.",
+ "rr1.sn-q4flrnss.googlevideo.com.",
+ "rr1.sn-q4fzen7l.googlevideo.com.",
+ "rr1.sn-q4fzenee.googlevideo.com.",
+ "rr1.sn-vgqsrnzz.googlevideo.com.",
+ "rr10---sn-8xgp1vo-ab56.googlevideo.com.",
+ "rr10---sn-8xgp1vo-ab5d.googlevideo.com.",
+ "rr10---sn-8xgp1vo-p5qee.googlevideo.com.",
+ "rr11---sn-8xgp1vo-ab56.googlevideo.com.",
+ "rr11---sn-8xgp1vo-p5qee.googlevideo.com.",
"rr2---sn-0nnpbo5a-bggl.googlevideo.com.",
"rr2---sn-0op8v4h5pox-cbgl.googlevideo.com.",
+ "rr2---sn-2aqu-hoaly.googlevideo.com.",
+ "rr2---sn-2aqu-hoasz.googlevideo.com.",
+ "rr2---sn-2aqu-jxcr.googlevideo.com.",
"rr2---sn-2imern76.googlevideo.com.",
"rr2---sn-2imern7d.googlevideo.com.",
- "rr2---sn-2imern7d.gvt1.com.",
"rr2---sn-2imeyn7k.googlevideo.com.",
"rr2---sn-2napbiu-p5ie.googlevideo.com.",
"rr2---sn-2napbiu-p5ie.gvt1.com.",
+ "rr2---sn-2oaig5-55.googlevideo.com.",
"rr2---sn-2pmxapm0n-gpjl.googlevideo.com.",
+ "rr2---sn-2pmxapm0n-gpjs.googlevideo.com.",
"rr2---sn-30a7rne6.googlevideo.com.",
+ "rr2---sn-30a7rned.googlevideo.com.",
+ "rr2---sn-30a7rnek.googlevideo.com.",
"rr2---sn-30a7rner.googlevideo.com.",
"rr2---sn-30a7ynek.googlevideo.com.",
"rr2---sn-30a7yner.googlevideo.com.",
@@ -4241,7 +4134,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-4g5ednss.googlevideo.com.",
"rr2---sn-4g5ednsy.googlevideo.com.",
"rr2---sn-4g5ednsz.googlevideo.com.",
- "rr2---sn-4g5ednz7.googlevideo.com.",
"rr2---sn-4g5lzne6.googlevideo.com.",
"rr2---sn-4g5lzned.googlevideo.com.",
"rr2---sn-4g5lznek.googlevideo.com.",
@@ -4255,7 +4147,10 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-4g5lznls.googlevideo.com.",
"rr2---sn-4g5lznlz.googlevideo.com.",
"rr2---sn-4pgnuapbiu-hiul.googlevideo.com.",
- "rr2---sn-5axnug5-hxm6.googlevideo.com.",
+ "rr2---sn-5abxgpxuxaxjvh-9n4l.googlevideo.com.",
+ "rr2---sn-5abxgpxuxaxjvh-9n4s.googlevideo.com.",
+ "rr2---sn-5abxgpxuxaxjvh-9n4z.googlevideo.com.",
+ "rr2---sn-5abxgpxuxaxjvh-j1az.googlevideo.com.",
"rr2---sn-5gxo-in8l.googlevideo.com.",
"rr2---sn-5gxo-in8s.googlevideo.com.",
"rr2---sn-5hne6n6e.googlevideo.com.",
@@ -4267,10 +4162,8 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-5hne6nsy.googlevideo.com.",
"rr2---sn-5hne6nsz.googlevideo.com.",
"rr2---sn-5hne6nz6.googlevideo.com.",
- "rr2---sn-5hne6nz6.gvt1.com.",
"rr2---sn-5hne6nzd.googlevideo.com.",
- "rr2---sn-5hne6nzk.googlevideo.com.",
- "rr2---sn-5hne6nzk.gvt1.com.",
+ "rr2---sn-5hne6nzs.googlevideo.com.",
"rr2---sn-5hne6nzy.googlevideo.com.",
"rr2---sn-5hnednss.googlevideo.com.",
"rr2---sn-5hnednsz.googlevideo.com.",
@@ -4279,10 +4172,10 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-5hnekn7l.googlevideo.com.",
"rr2---sn-5hnekn7s.googlevideo.com.",
"rr2---sn-5hnekn7z.googlevideo.com.",
+ "rr2---sn-5hneknee.googlevideo.com.",
"rr2---sn-5hneknek.googlevideo.com.",
"rr2---sn-5hneknes.googlevideo.com.",
"rr2---sn-5jn5a5n35-5ojs.googlevideo.com.",
- "rr2---sn-5pgnugx5h-hn2s.googlevideo.com.",
"rr2---sn-5pgnugx5h-hn2z.googlevideo.com.",
"rr2---sn-5uaezndd.googlevideo.com.",
"rr2---sn-5uaezne6.googlevideo.com.",
@@ -4293,6 +4186,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-5uaeznl6.googlevideo.com.",
"rr2---sn-5uaeznld.googlevideo.com.",
"rr2---sn-5uaeznls.googlevideo.com.",
+ "rr2---sn-5uaeznlz.googlevideo.com.",
"rr2---sn-5uaezns7.googlevideo.com.",
"rr2---sn-5uaeznse.googlevideo.com.",
"rr2---sn-5uaeznsl.googlevideo.com.",
@@ -4316,8 +4210,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-5ualdnsz.googlevideo.com.",
"rr2---sn-5ualdnz7.googlevideo.com.",
"rr2---sn-5ualdnze.googlevideo.com.",
- "rr2---sn-8qj-nbo66.googlevideo.com.",
- "rr2---sn-8qj-nbo6y.googlevideo.com.",
"rr2---sn-8xgp1vo-2iae7.googlevideo.com.",
"rr2---sn-8xgp1vo-a5ml.googlevideo.com.",
"rr2---sn-8xgp1vo-ab56.googlevideo.com.",
@@ -4326,49 +4218,42 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-8xgp1vo-ab5l.googlevideo.com.",
"rr2---sn-8xgp1vo-ab5s.googlevideo.com.",
"rr2---sn-8xgp1vo-ab5z.googlevideo.com.",
- "rr2---sn-8xgp1vo-p5ie.googlevideo.com.",
- "rr2---sn-8xgp1vo-poql.googlevideo.com.",
+ "rr2---sn-8xgp1vo-p5qe7.googlevideo.com.",
+ "rr2---sn-8xgp1vo-p5qee.googlevideo.com.",
+ "rr2---sn-8xgp1vo-p5ql.googlevideo.com.",
+ "rr2---sn-8xgp1vo-p5qy.googlevideo.com.",
"rr2---sn-8xgp1vo-vgqe.googlevideo.com.",
"rr2---sn-8xgp1vo-xfge.googlevideo.com.",
"rr2---sn-8xgp1vo-xfgl.googlevideo.com.",
"rr2---sn-8xgp1vo-xfgs.googlevideo.com.",
"rr2---sn-9gv76n7e.googlevideo.com.",
- "rr2---sn-9gv76n7l.googlevideo.com.",
"rr2---sn-9gv76n7s.googlevideo.com.",
"rr2---sn-9gv76n7z.googlevideo.com.",
"rr2---sn-9gv7ene6.googlevideo.com.",
"rr2---sn-9gv7ened.googlevideo.com.",
"rr2---sn-9gv7zn76.googlevideo.com.",
"rr2---sn-9gv7zn7e.googlevideo.com.",
+ "rr2---sn-9gv7zn7r.googlevideo.com.",
"rr2---sn-9gv7zn7y.googlevideo.com.",
"rr2---sn-a5m7lnl6.googlevideo.com.",
- "rr2---sn-a5m7lnl6.gvt1.com.",
"rr2---sn-a5m7lnld.googlevideo.com.",
- "rr2---sn-a5m7lnld.gvt1.com.",
"rr2---sn-a5mekn6d.googlevideo.com.",
- "rr2---sn-a5mekn6d.gvt1.com.",
"rr2---sn-a5mekn6k.googlevideo.com.",
"rr2---sn-a5mekn6k.gvt1.com.",
"rr2---sn-a5mekn6l.googlevideo.com.",
"rr2---sn-a5mekn6r.googlevideo.com.",
- "rr2---sn-a5mekn6r.gvt1.com.",
"rr2---sn-a5mekn6s.googlevideo.com.",
"rr2---sn-a5mekn6z.googlevideo.com.",
- "rr2---sn-a5mekn6z.gvt1.com.",
"rr2---sn-a5meknd6.googlevideo.com.",
"rr2---sn-a5meknde.googlevideo.com.",
"rr2---sn-a5mekndl.googlevideo.com.",
- "rr2---sn-a5mekndl.gvt1.com.",
"rr2---sn-a5meknds.googlevideo.com.",
- "rr2---sn-a5meknds.gvt1.com.",
"rr2---sn-a5mekndz.googlevideo.com.",
"rr2---sn-a5meknsd.googlevideo.com.",
"rr2---sn-a5meknsy.googlevideo.com.",
"rr2---sn-a5meknzk.googlevideo.com.",
- "rr2---sn-a5meknzk.gvt1.com.",
"rr2---sn-a5meknzl.googlevideo.com.",
"rr2---sn-a5meknzr.googlevideo.com.",
- "rr2---sn-a5meknzr.gvt1.com.",
"rr2---sn-a5meknzs.googlevideo.com.",
"rr2---sn-a5mlrnek.googlevideo.com.",
"rr2---sn-a5mlrnl6.googlevideo.com.",
@@ -4403,25 +4288,24 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-ab5sznz6.googlevideo.com.",
"rr2---sn-ab5sznzd.googlevideo.com.",
"rr2---sn-ab5sznze.googlevideo.com.",
- "rr2---sn-ab5sznze.gvt1.com.",
"rr2---sn-ab5sznzk.googlevideo.com.",
"rr2---sn-ab5sznzl.googlevideo.com.",
"rr2---sn-ab5sznzr.googlevideo.com.",
- "rr2---sn-ab5sznzr.gvt1.com.",
"rr2---sn-ab5sznzs.googlevideo.com.",
"rr2---sn-ab5sznzy.googlevideo.com.",
"rr2---sn-ab5sznzz.googlevideo.com.",
"rr2---sn-aigl6n6s.googlevideo.com.",
"rr2---sn-aigl6ned.googlevideo.com.",
"rr2---sn-aigl6nek.googlevideo.com.",
+ "rr2---sn-aigl6ner.googlevideo.com.",
"rr2---sn-aigl6ney.googlevideo.com.",
"rr2---sn-aigl6nl7.googlevideo.com.",
"rr2---sn-aigl6ns6.googlevideo.com.",
+ "rr2---sn-aigl6ns6.gvt1.com.",
"rr2---sn-aigl6nsd.googlevideo.com.",
"rr2---sn-aigl6nsk.googlevideo.com.",
"rr2---sn-aigl6nsr.googlevideo.com.",
"rr2---sn-aigl6nz7.googlevideo.com.",
- "rr2---sn-aigl6nz7.gvt1.com.",
"rr2---sn-aigl6nze.googlevideo.com.",
"rr2---sn-aigl6nzk.googlevideo.com.",
"rr2---sn-aigl6nzl.googlevideo.com.",
@@ -4434,9 +4318,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-aigzrn7l.googlevideo.com.",
"rr2---sn-aigzrn7s.googlevideo.com.",
"rr2---sn-aigzrn7z.googlevideo.com.",
- "rr2---sn-aigzrnld.googlevideo.com.",
"rr2---sn-aigzrnse.googlevideo.com.",
- "rr2---sn-aigzrnsl.googlevideo.com.",
"rr2---sn-aigzrnsr.googlevideo.com.",
"rr2---sn-aigzrnss.googlevideo.com.",
"rr2---sn-aigzrnsz.googlevideo.com.",
@@ -4445,16 +4327,13 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-aj5ua5-5c.googlevideo.com.",
"rr2---sn-ajab55-55.googlevideo.com.",
"rr2---sn-avbpj-cq5e.googlevideo.com.",
- "rr2---sn-bg5oqxjvh-50nz.googlevideo.com.",
"rr2---sn-bg5oqxjvh-jg2s.googlevideo.com.",
"rr2---sn-bg5oqxjvh-xa2s.googlevideo.com.",
- "rr2---sn-c0q7lnly.googlevideo.com.",
- "rr2---sn-c0q7lns7.googlevideo.com.",
- "rr2---sn-c0q7lnsl.googlevideo.com.",
- "rr2---sn-c0q7lnz7.googlevideo.com.",
+ "rr2---sn-bvvbaxivnuxqjvhj5nu-hp5e.googlevideo.com.",
+ "rr2---sn-bvvbaxivnuxqjvhj5nu-hp5l.googlevideo.com.",
+ "rr2---sn-bvvbaxivnuxqjvhj5nu-hp5s.googlevideo.com.",
"rr2---sn-cvb7lne7.googlevideo.com.",
"rr2---sn-cvb7lnee.googlevideo.com.",
- "rr2---sn-cvb7lnls.googlevideo.com.",
"rr2---sn-cvb7lnlz.googlevideo.com.",
"rr2---sn-cvb7sn7k.googlevideo.com.",
"rr2---sn-cvb7sn7r.googlevideo.com.",
@@ -4468,72 +4347,51 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-hjoj-jaul.googlevideo.com.",
"rr2---sn-hjoj-poul.googlevideo.com.",
"rr2---sn-hoa7kn76.googlevideo.com.",
- "rr2---sn-hoa7kn7z.googlevideo.com.",
"rr2---sn-hoa7rn76.googlevideo.com.",
"rr2---sn-hoa7rn7z.googlevideo.com.",
"rr2---sn-hp57kn6r.googlevideo.com.",
- "rr2---sn-hp57kn6r.gvt1.com.",
"rr2---sn-hp57kn6y.googlevideo.com.",
"rr2---sn-hp57knd6.googlevideo.com.",
- "rr2---sn-hp57knd6.gvt1.com.",
"rr2---sn-hp57kndd.googlevideo.com.",
- "rr2---sn-hp57kndd.gvt1.com.",
"rr2---sn-hp57kndk.googlevideo.com.",
"rr2---sn-hp57kndr.googlevideo.com.",
- "rr2---sn-hp57kndr.gvt1.com.",
"rr2---sn-hp57knds.googlevideo.com.",
- "rr2---sn-hp57knds.gvt1.com.",
"rr2---sn-hp57kndy.googlevideo.com.",
"rr2---sn-hp57kndz.googlevideo.com.",
- "rr2---sn-hp57kndz.gvt1.com.",
"rr2---sn-hp57yn7r.googlevideo.com.",
"rr2---sn-hp57yn7y.googlevideo.com.",
- "rr2---sn-hp57yn7y.gvt1.com.",
"rr2---sn-hp57yne7.googlevideo.com.",
"rr2---sn-hp57ynee.googlevideo.com.",
"rr2---sn-hp57ynl6.googlevideo.com.",
- "rr2---sn-hp57ynl6.gvt1.com.",
"rr2---sn-hp57ynlr.googlevideo.com.",
"rr2---sn-hp57ynly.googlevideo.com.",
+ "rr2---sn-hp57yns7.googlevideo.com.",
"rr2---sn-hp57ynse.googlevideo.com.",
"rr2---sn-hp57ynsl.googlevideo.com.",
"rr2---sn-hp57ynss.googlevideo.com.",
- "rr2---sn-hp57ynss.gvt1.com.",
- "rr2---sn-huxaqvv-ubqe.googlevideo.com.",
- "rr2---sn-huxaqvv-ubqe.gvt1.com.",
- "rr2---sn-huxaqvv-ubql.googlevideo.com.",
- "rr2---sn-huxaqvv-ubql.gvt1.com.",
+ "rr2---sn-hpqfxnu-oaxe.googlevideo.com.",
+ "rr2---sn-hpqfxnu-oaxl.googlevideo.com.",
"rr2---sn-hxgpu-qufs.googlevideo.com.",
"rr2---sn-i3b7kn6s.googlevideo.com.",
"rr2---sn-i3b7knld.googlevideo.com.",
"rr2---sn-i3b7knlk.googlevideo.com.",
"rr2---sn-i3b7kns6.googlevideo.com.",
- "rr2---sn-i3b7knsd.googlevideo.com.",
"rr2---sn-i3b7knse.googlevideo.com.",
"rr2---sn-i3b7knzl.googlevideo.com.",
"rr2---sn-i3b7knzs.googlevideo.com.",
"rr2---sn-i3belne6.googlevideo.com.",
- "rr2---sn-i3belnl6.googlevideo.com.",
+ "rr2---sn-i3belney.googlevideo.com.",
"rr2---sn-i3belnl7.googlevideo.com.",
"rr2---sn-i3belnll.googlevideo.com.",
"rr2---sn-i3belnls.googlevideo.com.",
- "rr2---sn-i3belnlz.googlevideo.com.",
"rr2---sn-i3bssn7e.googlevideo.com.",
- "rr2---sn-i5f5ppuxa-ioas.googlevideo.com.",
"rr2---sn-jn2pgx4pcxg-w5os.googlevideo.com.",
"rr2---sn-jn2pgx4pcxg-w5oz.googlevideo.com.",
"rr2---sn-jvhj5nu-2iae.googlevideo.com.",
- "rr2---sn-jvhj5nu-2ial.googlevideo.com.",
- "rr2---sn-jvhj5nu-2ias.googlevideo.com.",
- "rr2---sn-jvhj5nu-nh4e.googlevideo.com.",
- "rr2---sn-jvhj5nu-nh4s.googlevideo.com.",
- "rr2---sn-jvhj5nu-qufe.googlevideo.com.",
- "rr2---sn-jvhj5nu-qufz.googlevideo.com.",
"rr2---sn-jvooxqouf3-cqaz.googlevideo.com.",
"rr2---sn-jxopj-n5oe.googlevideo.com.",
"rr2---sn-jxopj-nh4e.googlevideo.com.",
"rr2---sn-jxopj-nh4e.gvt1.com.",
- "rr2---sn-muxa-2iae.googlevideo.com.",
"rr2---sn-n2uxaxjvh-j5xl.googlevideo.com.",
"rr2---sn-n2uxaxjvh-j5xl.gvt1.com.",
"rr2---sn-n2uxaxjvh-j5xs.googlevideo.com.",
@@ -4544,7 +4402,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-n4v7snll.googlevideo.com.",
"rr2---sn-n4v7snlr.googlevideo.com.",
"rr2---sn-n4v7snls.googlevideo.com.",
- "rr2---sn-n4v7snly.googlevideo.com.",
"rr2---sn-n4v7sns7.googlevideo.com.",
"rr2---sn-n4v7snse.googlevideo.com.",
"rr2---sn-nh5gujvh-h4xe.googlevideo.com.",
@@ -4568,7 +4425,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-npoe7nsl.googlevideo.com.",
"rr2---sn-npoe7nsr.googlevideo.com.",
"rr2---sn-npoe7nss.googlevideo.com.",
- "rr2---sn-npoe7nsy.googlevideo.com.",
"rr2---sn-npoe7nz7.googlevideo.com.",
"rr2---sn-npoeene6.googlevideo.com.",
"rr2---sn-npoeened.googlevideo.com.",
@@ -4591,10 +4447,10 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-npoldn7y.googlevideo.com.",
"rr2---sn-npoldn7z.googlevideo.com.",
"rr2---sn-npoldne7.googlevideo.com.",
- "rr2---sn-nuagpm-nuae.googlevideo.com.",
+ "rr2---sn-ntq7yned.googlevideo.com.",
+ "rr2---sn-ntq7yney.googlevideo.com.",
"rr2---sn-nv0uixgo-5ual.googlevideo.com.",
"rr2---sn-nx57ynlk.googlevideo.com.",
- "rr2---sn-nx57ynlk.gvt1.com.",
"rr2---sn-nx57ynsd.googlevideo.com.",
"rr2---sn-nx57ynse.googlevideo.com.",
"rr2---sn-nx57ynse.gvt1.com.",
@@ -4605,6 +4461,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-nx57ynsz.googlevideo.com.",
"rr2---sn-nx5s7n76.googlevideo.com.",
"rr2---sn-nx5s7n7d.googlevideo.com.",
+ "rr2---sn-nx5s7n7s.googlevideo.com.",
"rr2---sn-nx5s7n7y.googlevideo.com.",
"rr2---sn-nx5s7n7z.googlevideo.com.",
"rr2---sn-nx5s7nee.googlevideo.com.",
@@ -4616,6 +4473,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-o097znss.googlevideo.com.",
"rr2---sn-o097znsz.googlevideo.com.",
"rr2---sn-o097znz7.googlevideo.com.",
+ "rr2---sn-o097znzd.googlevideo.com.",
"rr2---sn-o097znze.googlevideo.com.",
"rr2---sn-o097znzk.googlevideo.com.",
"rr2---sn-o097znzr.googlevideo.com.",
@@ -4623,6 +4481,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-oxgpj-5ace.googlevideo.com.",
"rr2---sn-p5qddn76.googlevideo.com.",
"rr2---sn-p5qddn7d.googlevideo.com.",
+ "rr2---sn-p5qddn7k.googlevideo.com.",
"rr2---sn-p5qddn7r.googlevideo.com.",
"rr2---sn-p5qddn7z.googlevideo.com.",
"rr2---sn-p5qlsn6l.googlevideo.com.",
@@ -4645,104 +4504,93 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-p5qs7nzy.googlevideo.com.",
"rr2---sn-paapovpnjxou0gt-nual.googlevideo.com.",
"rr2---sn-paapovpnjxou0gt-nuas.googlevideo.com.",
+ "rr2---sn-paapovpnjxou0gt-nuaz.googlevideo.com.",
"rr2---sn-pjnpu-5hfe.googlevideo.com.",
"rr2---sn-pobpb-poql.googlevideo.com.",
"rr2---sn-q4fl6n66.googlevideo.com.",
- "rr2---sn-q4fl6n66.gvt1.com.",
"rr2---sn-q4fl6n6d.googlevideo.com.",
"rr2---sn-q4fl6n6d.gvt1.com.",
"rr2---sn-q4fl6n6r.googlevideo.com.",
- "rr2---sn-q4fl6n6r.gvt1.com.",
"rr2---sn-q4fl6n6s.googlevideo.com.",
"rr2---sn-q4fl6n6y.googlevideo.com.",
"rr2---sn-q4fl6n6y.gvt1.com.",
"rr2---sn-q4fl6n6z.googlevideo.com.",
+ "rr2---sn-q4fl6n6z.gvt1.com.",
"rr2---sn-q4fl6nd7.googlevideo.com.",
- "rr2---sn-q4fl6nd7.gvt1.com.",
"rr2---sn-q4fl6nde.googlevideo.com.",
"rr2---sn-q4fl6nde.gvt1.com.",
"rr2---sn-q4fl6ndl.googlevideo.com.",
- "rr2---sn-q4fl6ndl.gvt1.com.",
"rr2---sn-q4fl6nds.googlevideo.com.",
"rr2---sn-q4fl6ndz.googlevideo.com.",
"rr2---sn-q4fl6ndz.gvt1.com.",
"rr2---sn-q4fl6nlz.googlevideo.com.",
"rr2---sn-q4fl6ns6.googlevideo.com.",
- "rr2---sn-q4fl6ns6.gvt1.com.",
"rr2---sn-q4fl6ns7.googlevideo.com.",
"rr2---sn-q4fl6ns7.gvt1.com.",
"rr2---sn-q4fl6nsd.googlevideo.com.",
- "rr2---sn-q4fl6nsd.gvt1.com.",
"rr2---sn-q4fl6nsk.googlevideo.com.",
- "rr2---sn-q4fl6nsk.gvt1.com.",
"rr2---sn-q4fl6nsl.googlevideo.com.",
"rr2---sn-q4fl6nsr.googlevideo.com.",
+ "rr2---sn-q4fl6nsr.gvt1.com.",
"rr2---sn-q4fl6nss.googlevideo.com.",
"rr2---sn-q4fl6nsy.googlevideo.com.",
+ "rr2---sn-q4fl6nsy.gvt1.com.",
"rr2---sn-q4fl6nz6.googlevideo.com.",
"rr2---sn-q4fl6nz6.gvt1.com.",
"rr2---sn-q4fl6nz7.googlevideo.com.",
+ "rr2---sn-q4fl6nz7.gvt1.com.",
"rr2---sn-q4fl6nzy.googlevideo.com.",
"rr2---sn-q4fl6nzy.gvt1.com.",
"rr2---sn-q4flrn7k.googlevideo.com.",
"rr2---sn-q4flrn7r.googlevideo.com.",
"rr2---sn-q4flrn7y.googlevideo.com.",
"rr2---sn-q4flrne6.googlevideo.com.",
+ "rr2---sn-q4flrne6.gvt1.com.",
"rr2---sn-q4flrne7.googlevideo.com.",
"rr2---sn-q4flrnee.googlevideo.com.",
- "rr2---sn-q4flrnee.gvt1.com.",
"rr2---sn-q4flrnek.googlevideo.com.",
"rr2---sn-q4flrnel.googlevideo.com.",
- "rr2---sn-q4flrnel.gvt1.com.",
"rr2---sn-q4flrner.googlevideo.com.",
"rr2---sn-q4flrnes.googlevideo.com.",
"rr2---sn-q4flrney.googlevideo.com.",
"rr2---sn-q4flrnez.googlevideo.com.",
- "rr2---sn-q4flrnez.gvt1.com.",
"rr2---sn-q4flrnl6.googlevideo.com.",
+ "rr2---sn-q4flrnl6.gvt1.com.",
"rr2---sn-q4flrnl7.googlevideo.com.",
"rr2---sn-q4flrnld.googlevideo.com.",
"rr2---sn-q4flrnle.googlevideo.com.",
"rr2---sn-q4flrnlz.googlevideo.com.",
"rr2---sn-q4flrnsd.googlevideo.com.",
"rr2---sn-q4flrnsk.googlevideo.com.",
- "rr2---sn-q4flrnsk.gvt1.com.",
"rr2---sn-q4flrnsl.googlevideo.com.",
"rr2---sn-q4flrnsl.gvt1.com.",
"rr2---sn-q4flrnss.googlevideo.com.",
"rr2---sn-q4flrnss.gvt1.com.",
"rr2---sn-q4fzen7e.googlevideo.com.",
- "rr2---sn-q4fzen7e.gvt1.com.",
- "rr2---sn-q4fzen7r.googlevideo.com.",
- "rr2---sn-q4fzen7r.gvt1.com.",
+ "rr2---sn-q4fzen7l.googlevideo.com.",
+ "rr2---sn-q4fzen7l.gvt1.com.",
"rr2---sn-q4fzen7s.googlevideo.com.",
"rr2---sn-q4fzen7y.googlevideo.com.",
- "rr2---sn-q4fzen7y.gvt1.com.",
"rr2---sn-q4fzene7.googlevideo.com.",
"rr2---sn-q4fzene7.gvt1.com.",
"rr2---sn-q4fzenee.googlevideo.com.",
"rr2---sn-qjp5q5-55.googlevideo.com.",
"rr2---sn-qxo7rn7k.googlevideo.com.",
- "rr2---sn-qxo7rn7r.googlevideo.com.",
"rr2---sn-qxo7rn7y.googlevideo.com.",
"rr2---sn-qxoedn7k.googlevideo.com.",
"rr2---sn-qxoedne7.googlevideo.com.",
"rr2---sn-qxoednee.googlevideo.com.",
- "rr2---sn-u1hp55-5c.googlevideo.com.",
- "rr2---sn-u1hp55-5c.gvt1.com.",
- "rr2---sn-uxmqx2uv4po4v-50nl.googlevideo.com.",
"rr2---sn-v53a5oqnji-4oul.googlevideo.com.",
"rr2---sn-v5goxu-jhi6.googlevideo.com.",
- "rr2---sn-v5goxu-jhi6.gvt1.com.",
"rr2---sn-v5goxu-jhil.googlevideo.com.",
"rr2---sn-v5goxu-jhiz.googlevideo.com.",
+ "rr2---sn-v5goxu-jhiz.gvt1.com.",
"rr2---sn-vgqskn66.googlevideo.com.",
+ "rr2---sn-vgqskn66.gvt1.com.",
"rr2---sn-vgqskn67.googlevideo.com.",
"rr2---sn-vgqskn6d.googlevideo.com.",
"rr2---sn-vgqskn6s.googlevideo.com.",
- "rr2---sn-vgqskn6s.gvt1.com.",
"rr2---sn-vgqskn6z.googlevideo.com.",
- "rr2---sn-vgqskn6z.gvt1.com.",
"rr2---sn-vgqskne6.googlevideo.com.",
"rr2---sn-vgqskned.googlevideo.com.",
"rr2---sn-vgqsknek.googlevideo.com.",
@@ -4757,48 +4605,35 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-vgqsknlz.googlevideo.com.",
"rr2---sn-vgqskns7.googlevideo.com.",
"rr2---sn-vgqsknse.googlevideo.com.",
- "rr2---sn-vgqsknz6.googlevideo.com.",
- "rr2---sn-vgqsknz6.gvt1.com.",
+ "rr2---sn-vgqsknsk.googlevideo.com.",
"rr2---sn-vgqsknz7.googlevideo.com.",
"rr2---sn-vgqsknzd.googlevideo.com.",
"rr2---sn-vgqsknze.googlevideo.com.",
- "rr2---sn-vgqsknze.gvt1.com.",
"rr2---sn-vgqsknzk.googlevideo.com.",
"rr2---sn-vgqsknzl.googlevideo.com.",
"rr2---sn-vgqsknzr.googlevideo.com.",
"rr2---sn-vgqsknzs.googlevideo.com.",
"rr2---sn-vgqsknzy.googlevideo.com.",
- "rr2---sn-vgqsknzy.gvt1.com.",
+ "rr2---sn-vgqsknzz.googlevideo.com.",
"rr2---sn-vgqsrn66.googlevideo.com.",
- "rr2---sn-vgqsrn66.gvt1.com.",
"rr2---sn-vgqsrn67.googlevideo.com.",
"rr2---sn-vgqsrn6e.googlevideo.com.",
- "rr2---sn-vgqsrn6e.gvt1.com.",
- "rr2---sn-vgqsrn6l.googlevideo.com.",
- "rr2---sn-vgqsrn6l.gvt1.com.",
"rr2---sn-vgqsrn6z.googlevideo.com.",
"rr2---sn-vgqsrne6.googlevideo.com.",
"rr2---sn-vgqsrned.googlevideo.com.",
"rr2---sn-vgqsrnek.googlevideo.com.",
"rr2---sn-vgqsrnes.googlevideo.com.",
"rr2---sn-vgqsrnez.googlevideo.com.",
+ "rr2---sn-vgqsrnl6.googlevideo.com.",
"rr2---sn-vgqsrnld.googlevideo.com.",
- "rr2---sn-vgqsrnld.gvt1.com.",
- "rr2---sn-vgqsrnlk.googlevideo.com.",
"rr2---sn-vgqsrnll.googlevideo.com.",
"rr2---sn-vgqsrnls.googlevideo.com.",
- "rr2---sn-vgqsrnls.gvt1.com.",
"rr2---sn-vgqsrnlz.googlevideo.com.",
"rr2---sn-vgqsrns6.googlevideo.com.",
- "rr2---sn-vgqsrns6.gvt1.com.",
- "rr2---sn-vgqsrnsd.googlevideo.com.",
"rr2---sn-vgqsrnsr.googlevideo.com.",
- "rr2---sn-vgqsrnsy.googlevideo.com.",
"rr2---sn-vgqsrnz6.googlevideo.com.",
- "rr2---sn-vgqsrnz6.gvt1.com.",
"rr2---sn-vgqsrnz7.googlevideo.com.",
"rr2---sn-vgqsrnzd.googlevideo.com.",
- "rr2---sn-vgqsrnzd.gvt1.com.",
"rr2---sn-vgqsrnzk.googlevideo.com.",
"rr2---sn-vgqsrnzr.googlevideo.com.",
"rr2---sn-vgqsrnzs.googlevideo.com.",
@@ -4809,21 +4644,25 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-voxoxu-v3jl.googlevideo.com.",
"rr2---sn-voxoxu-v3js.googlevideo.com.",
"rr2---sn-xo5-co5l.googlevideo.com.",
- "rr2.sn-5hnednss.googlevideo.com.",
+ "rr2.sn-5hnekn7l.googlevideo.com.",
+ "rr2.sn-hgn7rn7r.googlevideo.com.",
"rr2.sn-hp57knds.googlevideo.com.",
- "rr2.sn-q4flrnek.googlevideo.com.",
- "rr2.sn-q4flrnsk.googlevideo.com.",
+ "rr2.sn-p5qs7nsk.googlevideo.com.",
+ "rr2.sn-q4fl6n6y.googlevideo.com.",
+ "rr2.sn-q4fl6nd7.googlevideo.com.",
+ "rr2.sn-q4fl6ndl.googlevideo.com.",
+ "rr2.sn-q4fl6nds.googlevideo.com.",
+ "rr2.sn-q4flrnl7.googlevideo.com.",
+ "rr2.sn-q4fzen7l.googlevideo.com.",
+ "rr2.sn-vgqsrnzz.googlevideo.com.",
"rr3---sn-0nnpbo5a-bggl.googlevideo.com.",
+ "rr3---sn-2aqu-hoas7.googlevideo.com.",
"rr3---sn-2imern76.googlevideo.com.",
- "rr3---sn-2imern76.gvt1.com.",
"rr3---sn-2imern7d.googlevideo.com.",
- "rr3---sn-2imern7d.gvt1.com.",
"rr3---sn-2imeyn7k.googlevideo.com.",
- "rr3---sn-2imeyn7k.gvt1.com.",
- "rr3---sn-2napbiu-p5ie.googlevideo.com.",
- "rr3---sn-2napbiu-p5ie.gvt1.com.",
- "rr3---sn-2oaig5-55.googlevideo.com.",
+ "rr3---sn-2pmxapm0n-gpjs.googlevideo.com.",
"rr3---sn-30a7rne6.googlevideo.com.",
+ "rr3---sn-30a7rned.googlevideo.com.",
"rr3---sn-30a7rnek.googlevideo.com.",
"rr3---sn-30a7rner.googlevideo.com.",
"rr3---sn-30a7ynek.googlevideo.com.",
@@ -4831,6 +4670,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-30a7yney.googlevideo.com.",
"rr3---sn-30a7ynl7.googlevideo.com.",
"rr3---sn-3jpm-hjpe.googlevideo.com.",
+ "rr3---sn-3jpm-hjpe.gvt1.com.",
"rr3---sn-4g5e6ns6.googlevideo.com.",
"rr3---sn-4g5e6ns7.googlevideo.com.",
"rr3---sn-4g5e6nsd.googlevideo.com.",
@@ -4882,6 +4722,11 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-4g5lznle.googlevideo.com.",
"rr3---sn-4g5lznls.googlevideo.com.",
"rr3---sn-4g5lznlz.googlevideo.com.",
+ "rr3---sn-5abxgpxuxaxjvh-9n4e.googlevideo.com.",
+ "rr3---sn-5abxgpxuxaxjvh-9n4l.googlevideo.com.",
+ "rr3---sn-5abxgpxuxaxjvh-9n4z.googlevideo.com.",
+ "rr3---sn-5abxgpxuxaxjvh-j1az.googlevideo.com.",
+ "rr3---sn-5goeenes.googlevideo.com.",
"rr3---sn-5hne6n6e.googlevideo.com.",
"rr3---sn-5hne6n6l.googlevideo.com.",
"rr3---sn-5hne6ns6.googlevideo.com.",
@@ -4912,6 +4757,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-5uaeznel.googlevideo.com.",
"rr3---sn-5uaeznes.googlevideo.com.",
"rr3---sn-5uaeznez.googlevideo.com.",
+ "rr3---sn-5uaeznl6.googlevideo.com.",
"rr3---sn-5uaeznld.googlevideo.com.",
"rr3---sn-5uaeznls.googlevideo.com.",
"rr3---sn-5uaeznlz.googlevideo.com.",
@@ -4927,7 +4773,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-5ualdnlr.googlevideo.com.",
"rr3---sn-5ualdnls.googlevideo.com.",
"rr3---sn-5ualdns6.googlevideo.com.",
- "rr3---sn-5ualdns7.googlevideo.com.",
"rr3---sn-5ualdnsd.googlevideo.com.",
"rr3---sn-5ualdnse.googlevideo.com.",
"rr3---sn-5ualdnsk.googlevideo.com.",
@@ -4938,7 +4783,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-5ualdnsz.googlevideo.com.",
"rr3---sn-5ualdnz7.googlevideo.com.",
"rr3---sn-5ualdnze.googlevideo.com.",
- "rr3---sn-8qj-nbo66.googlevideo.com.",
"rr3---sn-8xgp1vo-2iae7.googlevideo.com.",
"rr3---sn-8xgp1vo-ab56.googlevideo.com.",
"rr3---sn-8xgp1vo-ab5d.googlevideo.com.",
@@ -4946,13 +4790,14 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-8xgp1vo-ab5l.googlevideo.com.",
"rr3---sn-8xgp1vo-ab5s.googlevideo.com.",
"rr3---sn-8xgp1vo-ab5z.googlevideo.com.",
- "rr3---sn-8xgp1vo-p5ie.googlevideo.com.",
+ "rr3---sn-8xgp1vo-p5qe7.googlevideo.com.",
+ "rr3---sn-8xgp1vo-p5qee.googlevideo.com.",
+ "rr3---sn-8xgp1vo-p5ql.googlevideo.com.",
+ "rr3---sn-8xgp1vo-p5qs.googlevideo.com.",
"rr3---sn-8xgp1vo-vgqe.googlevideo.com.",
- "rr3---sn-8xgp1vo-xfge.googlevideo.com.",
"rr3---sn-8xgp1vo-xfgl.googlevideo.com.",
"rr3---sn-8xgp1vo-xfgs.googlevideo.com.",
"rr3---sn-9gv76n7e.googlevideo.com.",
- "rr3---sn-9gv76n7l.googlevideo.com.",
"rr3---sn-9gv76n7s.googlevideo.com.",
"rr3---sn-9gv76n7z.googlevideo.com.",
"rr3---sn-9gv7ene6.googlevideo.com.",
@@ -4964,56 +4809,43 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-a5m7lnl6.googlevideo.com.",
"rr3---sn-a5m7lnld.googlevideo.com.",
"rr3---sn-a5mekn6d.googlevideo.com.",
- "rr3---sn-a5mekn6d.gvt1.com.",
+ "rr3---sn-a5mekn6k.googlevideo.com.",
"rr3---sn-a5mekn6l.googlevideo.com.",
"rr3---sn-a5mekn6r.googlevideo.com.",
- "rr3---sn-a5mekn6r.gvt1.com.",
"rr3---sn-a5mekn6s.googlevideo.com.",
- "rr3---sn-a5mekn6s.gvt1.com.",
"rr3---sn-a5mekn6z.googlevideo.com.",
"rr3---sn-a5meknd6.googlevideo.com.",
- "rr3---sn-a5meknd6.gvt1.com.",
"rr3---sn-a5meknde.googlevideo.com.",
"rr3---sn-a5mekndl.googlevideo.com.",
"rr3---sn-a5meknds.googlevideo.com.",
"rr3---sn-a5mekndz.googlevideo.com.",
- "rr3---sn-a5mekndz.gvt1.com.",
"rr3---sn-a5meknsd.googlevideo.com.",
"rr3---sn-a5meknsy.googlevideo.com.",
"rr3---sn-a5meknzk.googlevideo.com.",
+ "rr3---sn-a5meknzk.gvt1.com.",
"rr3---sn-a5meknzl.googlevideo.com.",
"rr3---sn-a5meknzr.googlevideo.com.",
- "rr3---sn-a5meknzr.gvt1.com.",
"rr3---sn-a5meknzs.googlevideo.com.",
"rr3---sn-a5mlrnek.googlevideo.com.",
"rr3---sn-a5mlrnl6.googlevideo.com.",
- "rr3---sn-a5mlrnl6.gvt1.com.",
"rr3---sn-a5mlrnll.googlevideo.com.",
- "rr3---sn-a5mlrnll.gvt1.com.",
"rr3---sn-a5mlrnls.googlevideo.com.",
- "rr3---sn-a5mlrnls.gvt1.com.",
"rr3---sn-a5mlrnlz.googlevideo.com.",
- "rr3---sn-a5mlrnlz.gvt1.com.",
"rr3---sn-a5msen76.googlevideo.com.",
"rr3---sn-a5msen7l.googlevideo.com.",
"rr3---sn-a5msen7s.googlevideo.com.",
"rr3---sn-a5msen7z.googlevideo.com.",
- "rr3---sn-a5msen7z.gvt1.com.",
"rr3---sn-a5msenek.googlevideo.com.",
"rr3---sn-a5msener.googlevideo.com.",
"rr3---sn-a5msenes.googlevideo.com.",
- "rr3---sn-a5msenes.gvt1.com.",
"rr3---sn-a5msenl7.googlevideo.com.",
- "rr3---sn-a5msenl7.gvt1.com.",
"rr3---sn-a5msenle.googlevideo.com.",
+ "rr3---sn-a5msenle.gvt1.com.",
"rr3---sn-a5msenll.googlevideo.com.",
- "rr3---sn-a5msenll.gvt1.com.",
- "rr3---sn-ab5l6ndr.googlevideo.com.",
"rr3---sn-ab5l6ndy.googlevideo.com.",
"rr3---sn-ab5l6nk6.googlevideo.com.",
"rr3---sn-ab5l6nkd.googlevideo.com.",
"rr3---sn-ab5l6nr6.googlevideo.com.",
- "rr3---sn-ab5l6nr6.gvt1.com.",
"rr3---sn-ab5l6nrd.googlevideo.com.",
"rr3---sn-ab5l6nrk.googlevideo.com.",
"rr3---sn-ab5l6nrl.googlevideo.com.",
@@ -5024,7 +4856,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-ab5sznly.googlevideo.com.",
"rr3---sn-ab5sznz6.googlevideo.com.",
"rr3---sn-ab5sznzd.googlevideo.com.",
- "rr3---sn-ab5sznzd.gvt1.com.",
"rr3---sn-ab5sznze.googlevideo.com.",
"rr3---sn-ab5sznzk.googlevideo.com.",
"rr3---sn-ab5sznzl.googlevideo.com.",
@@ -5044,9 +4875,9 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-aigl6nsr.googlevideo.com.",
"rr3---sn-aigl6nz7.googlevideo.com.",
"rr3---sn-aigl6nze.googlevideo.com.",
- "rr3---sn-aigl6nze.gvt1.com.",
"rr3---sn-aigl6nzk.googlevideo.com.",
"rr3---sn-aigl6nzl.googlevideo.com.",
+ "rr3---sn-aigl6nzr.googlevideo.com.",
"rr3---sn-aigl6nzs.googlevideo.com.",
"rr3---sn-aigzrn76.googlevideo.com.",
"rr3---sn-aigzrn7d.googlevideo.com.",
@@ -5063,12 +4894,11 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-aigzrnsz.googlevideo.com.",
"rr3---sn-aigzrnz7.googlevideo.com.",
"rr3---sn-aigzrnze.googlevideo.com.",
+ "rr3---sn-aj5ua5-5c.googlevideo.com.",
"rr3---sn-ajab55-55.googlevideo.com.",
- "rr3---sn-bg5oqxjvh-50nz.googlevideo.com.",
- "rr3---sn-c0q7lnly.googlevideo.com.",
- "rr3---sn-c0q7lns7.googlevideo.com.",
- "rr3---sn-c0q7lnsl.googlevideo.com.",
- "rr3---sn-c0q7lnz7.googlevideo.com.",
+ "rr3---sn-bvvbaxivnuxqjvhj5nu-hp5e.googlevideo.com.",
+ "rr3---sn-bvvbaxivnuxqjvhj5nu-hp5l.googlevideo.com.",
+ "rr3---sn-bvvbaxivnuxqjvhj5nu-hp5s.googlevideo.com.",
"rr3---sn-cvb7lne7.googlevideo.com.",
"rr3---sn-cvb7lnlz.googlevideo.com.",
"rr3---sn-cvb7sn7r.googlevideo.com.",
@@ -5081,39 +4911,24 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-hoa7rn76.googlevideo.com.",
"rr3---sn-hoa7rn7z.googlevideo.com.",
"rr3---sn-hp57kn6r.googlevideo.com.",
- "rr3---sn-hp57kn6r.gvt1.com.",
"rr3---sn-hp57kn6y.googlevideo.com.",
- "rr3---sn-hp57kn6y.gvt1.com.",
"rr3---sn-hp57knd6.googlevideo.com.",
"rr3---sn-hp57kndd.googlevideo.com.",
"rr3---sn-hp57kndk.googlevideo.com.",
- "rr3---sn-hp57kndk.gvt1.com.",
"rr3---sn-hp57kndr.googlevideo.com.",
- "rr3---sn-hp57kndr.gvt1.com.",
"rr3---sn-hp57knds.googlevideo.com.",
- "rr3---sn-hp57knds.gvt1.com.",
- "rr3---sn-hp57kndy.googlevideo.com.",
- "rr3---sn-hp57kndy.gvt1.com.",
"rr3---sn-hp57kndz.googlevideo.com.",
- "rr3---sn-hp57kndz.gvt1.com.",
"rr3---sn-hp57yn7r.googlevideo.com.",
- "rr3---sn-hp57yn7r.gvt1.com.",
"rr3---sn-hp57yn7y.googlevideo.com.",
"rr3---sn-hp57yne7.googlevideo.com.",
"rr3---sn-hp57ynee.googlevideo.com.",
"rr3---sn-hp57ynl6.googlevideo.com.",
- "rr3---sn-hp57ynl6.gvt1.com.",
"rr3---sn-hp57ynlr.googlevideo.com.",
"rr3---sn-hp57ynly.googlevideo.com.",
"rr3---sn-hp57yns7.googlevideo.com.",
- "rr3---sn-hp57yns7.gvt1.com.",
"rr3---sn-hp57ynse.googlevideo.com.",
- "rr3---sn-hp57ynse.gvt1.com.",
"rr3---sn-hp57ynsl.googlevideo.com.",
- "rr3---sn-hp57ynsl.gvt1.com.",
"rr3---sn-hp57ynss.googlevideo.com.",
- "rr3---sn-huxaqvv-ubqe.googlevideo.com.",
- "rr3---sn-huxaqvv-ubqe.gvt1.com.",
"rr3---sn-i3b7kn6s.googlevideo.com.",
"rr3---sn-i3b7knld.googlevideo.com.",
"rr3---sn-i3b7knlk.googlevideo.com.",
@@ -5123,8 +4938,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-i3b7knsl.googlevideo.com.",
"rr3---sn-i3b7knzl.googlevideo.com.",
"rr3---sn-i3b7knzs.googlevideo.com.",
- "rr3---sn-i3belne6.googlevideo.com.",
- "rr3---sn-i3belney.googlevideo.com.",
"rr3---sn-i3belnl6.googlevideo.com.",
"rr3---sn-i3belnl7.googlevideo.com.",
"rr3---sn-i3belnll.googlevideo.com.",
@@ -5134,18 +4947,10 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-jn2pgx4pcxg-w5os.googlevideo.com.",
"rr3---sn-jn2pgx4pcxg-w5oz.googlevideo.com.",
"rr3---sn-jvhj5nu-2iae.googlevideo.com.",
- "rr3---sn-jvhj5nu-2ial.googlevideo.com.",
- "rr3---sn-jvhj5nu-2ias.googlevideo.com.",
- "rr3---sn-jvhj5nu-nh4e.googlevideo.com.",
- "rr3---sn-jvhj5nu-nh4s.googlevideo.com.",
- "rr3---sn-jvhj5nu-qufe.googlevideo.com.",
- "rr3---sn-jvhj5nu-qufl.googlevideo.com.",
- "rr3---sn-jvhj5nu-qufs.googlevideo.com.",
- "rr3---sn-jvhj5nu-qufz.googlevideo.com.",
+ "rr3---sn-jvhj5nu-quf6.googlevideo.com.",
"rr3---sn-jxopj-n5oe.googlevideo.com.",
"rr3---sn-jxopj-nh4e.googlevideo.com.",
"rr3---sn-jxopj-nh4e.gvt1.com.",
- "rr3---sn-n4v7snee.googlevideo.com.",
"rr3---sn-n4v7sney.googlevideo.com.",
"rr3---sn-n4v7snl7.googlevideo.com.",
"rr3---sn-n4v7snll.googlevideo.com.",
@@ -5168,7 +4973,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-npoe7nlz.googlevideo.com.",
"rr3---sn-npoe7ns6.googlevideo.com.",
"rr3---sn-npoe7ns7.googlevideo.com.",
- "rr3---sn-npoe7ns7.gvt1.com.",
"rr3---sn-npoe7nsd.googlevideo.com.",
"rr3---sn-npoe7nsk.googlevideo.com.",
"rr3---sn-npoe7nsl.googlevideo.com.",
@@ -5197,20 +5001,23 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-npoldn7y.googlevideo.com.",
"rr3---sn-npoldn7z.googlevideo.com.",
"rr3---sn-npoldne7.googlevideo.com.",
+ "rr3---sn-ntq7yns7.googlevideo.com.",
+ "rr3---sn-ntqe6nes.googlevideo.com.",
"rr3---sn-nx57ynlk.googlevideo.com.",
"rr3---sn-nx57ynsd.googlevideo.com.",
"rr3---sn-nx57ynse.googlevideo.com.",
"rr3---sn-nx57ynsk.googlevideo.com.",
+ "rr3---sn-nx57ynsk.gvt1.com.",
"rr3---sn-nx57ynsl.googlevideo.com.",
"rr3---sn-nx57ynss.googlevideo.com.",
"rr3---sn-nx57ynsz.googlevideo.com.",
- "rr3---sn-nx57ynsz.gvt1.com.",
"rr3---sn-nx5s7n76.googlevideo.com.",
"rr3---sn-nx5s7n7d.googlevideo.com.",
+ "rr3---sn-nx5s7n7s.googlevideo.com.",
"rr3---sn-nx5s7n7y.googlevideo.com.",
+ "rr3---sn-nx5s7n7z.googlevideo.com.",
"rr3---sn-nx5s7nee.googlevideo.com.",
- "rr3---sn-nx5s7nel.googlevideo.com.",
- "rr3---sn-nx5s7nel.gvt1.com.",
+ "rr3---sn-o097znsd.googlevideo.com.",
"rr3---sn-o097znse.googlevideo.com.",
"rr3---sn-o097znsk.googlevideo.com.",
"rr3---sn-o097znsl.googlevideo.com.",
@@ -5231,7 +5038,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-p5qlsn6l.googlevideo.com.",
"rr3---sn-p5qlsn76.googlevideo.com.",
"rr3---sn-p5qlsn7d.googlevideo.com.",
- "rr3---sn-p5qlsn7d.gvt1.com.",
"rr3---sn-p5qlsn7l.googlevideo.com.",
"rr3---sn-p5qlsn7s.googlevideo.com.",
"rr3---sn-p5qlsnd6.googlevideo.com.",
@@ -5243,6 +5049,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-p5qlsny6.googlevideo.com.",
"rr3---sn-p5qs7n6d.googlevideo.com.",
"rr3---sn-p5qs7nsk.googlevideo.com.",
+ "rr3---sn-p5qs7nsk.gvt1.com.",
"rr3---sn-p5qs7nsr.googlevideo.com.",
"rr3---sn-p5qs7nzk.googlevideo.com.",
"rr3---sn-p5qs7nzr.googlevideo.com.",
@@ -5252,25 +5059,19 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-q4fl6n66.gvt1.com.",
"rr3---sn-q4fl6n6d.googlevideo.com.",
"rr3---sn-q4fl6n6r.googlevideo.com.",
- "rr3---sn-q4fl6n6r.gvt1.com.",
"rr3---sn-q4fl6n6s.googlevideo.com.",
"rr3---sn-q4fl6n6y.googlevideo.com.",
"rr3---sn-q4fl6n6y.gvt1.com.",
"rr3---sn-q4fl6n6z.googlevideo.com.",
"rr3---sn-q4fl6n6z.gvt1.com.",
"rr3---sn-q4fl6nd7.googlevideo.com.",
- "rr3---sn-q4fl6nd7.gvt1.com.",
+ "rr3---sn-q4fl6nde.googlevideo.com.",
"rr3---sn-q4fl6ndl.googlevideo.com.",
"rr3---sn-q4fl6nds.googlevideo.com.",
- "rr3---sn-q4fl6nds.gvt1.com.",
"rr3---sn-q4fl6ndz.googlevideo.com.",
- "rr3---sn-q4fl6ndz.gvt1.com.",
"rr3---sn-q4fl6nlz.googlevideo.com.",
- "rr3---sn-q4fl6nlz.gvt1.com.",
"rr3---sn-q4fl6ns6.googlevideo.com.",
- "rr3---sn-q4fl6ns6.gvt1.com.",
"rr3---sn-q4fl6ns7.googlevideo.com.",
- "rr3---sn-q4fl6ns7.gvt1.com.",
"rr3---sn-q4fl6nsd.googlevideo.com.",
"rr3---sn-q4fl6nsk.googlevideo.com.",
"rr3---sn-q4fl6nsl.googlevideo.com.",
@@ -5279,13 +5080,10 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-q4fl6nss.googlevideo.com.",
"rr3---sn-q4fl6nsy.googlevideo.com.",
"rr3---sn-q4fl6nz6.googlevideo.com.",
- "rr3---sn-q4fl6nz6.gvt1.com.",
"rr3---sn-q4fl6nz7.googlevideo.com.",
- "rr3---sn-q4fl6nz7.gvt1.com.",
"rr3---sn-q4fl6nzy.googlevideo.com.",
"rr3---sn-q4flrn7k.googlevideo.com.",
"rr3---sn-q4flrn7r.googlevideo.com.",
- "rr3---sn-q4flrn7r.gvt1.com.",
"rr3---sn-q4flrn7y.googlevideo.com.",
"rr3---sn-q4flrne6.googlevideo.com.",
"rr3---sn-q4flrne7.googlevideo.com.",
@@ -5293,14 +5091,15 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-q4flrnek.googlevideo.com.",
"rr3---sn-q4flrnel.googlevideo.com.",
"rr3---sn-q4flrner.googlevideo.com.",
+ "rr3---sn-q4flrner.gvt1.com.",
"rr3---sn-q4flrnes.googlevideo.com.",
"rr3---sn-q4flrney.googlevideo.com.",
"rr3---sn-q4flrnez.googlevideo.com.",
- "rr3---sn-q4flrnez.gvt1.com.",
"rr3---sn-q4flrnl6.googlevideo.com.",
"rr3---sn-q4flrnl6.gvt1.com.",
"rr3---sn-q4flrnl7.googlevideo.com.",
"rr3---sn-q4flrnld.googlevideo.com.",
+ "rr3---sn-q4flrnld.gvt1.com.",
"rr3---sn-q4flrnle.googlevideo.com.",
"rr3---sn-q4flrnlz.googlevideo.com.",
"rr3---sn-q4flrnlz.gvt1.com.",
@@ -5308,19 +5107,15 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-q4flrnsk.googlevideo.com.",
"rr3---sn-q4flrnsk.gvt1.com.",
"rr3---sn-q4flrnsl.googlevideo.com.",
- "rr3---sn-q4flrnsl.gvt1.com.",
"rr3---sn-q4flrnss.googlevideo.com.",
"rr3---sn-q4flrnss.gvt1.com.",
"rr3---sn-q4fzen7e.googlevideo.com.",
+ "rr3---sn-q4fzen7e.gvt1.com.",
"rr3---sn-q4fzen7l.googlevideo.com.",
- "rr3---sn-q4fzen7r.googlevideo.com.",
- "rr3---sn-q4fzen7r.gvt1.com.",
+ "rr3---sn-q4fzen7l.gvt1.com.",
"rr3---sn-q4fzen7s.googlevideo.com.",
- "rr3---sn-q4fzen7s.gvt1.com.",
"rr3---sn-q4fzen7y.googlevideo.com.",
- "rr3---sn-q4fzen7y.gvt1.com.",
"rr3---sn-q4fzene7.googlevideo.com.",
- "rr3---sn-q4fzene7.gvt1.com.",
"rr3---sn-q4fzenee.googlevideo.com.",
"rr3---sn-q4fzenee.gvt1.com.",
"rr3---sn-qjp5q5-55.googlevideo.com.",
@@ -5330,8 +5125,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-qxoedn7k.googlevideo.com.",
"rr3---sn-qxoedne7.googlevideo.com.",
"rr3---sn-qxoednee.googlevideo.com.",
- "rr3---sn-u1hp55-5c.googlevideo.com.",
- "rr3---sn-u1hp55-5c.gvt1.com.",
"rr3---sn-v5goxu-jhil.googlevideo.com.",
"rr3---sn-vgqskn66.googlevideo.com.",
"rr3---sn-vgqskn67.googlevideo.com.",
@@ -5347,76 +5140,75 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-vgqsknlk.googlevideo.com.",
"rr3---sn-vgqsknll.googlevideo.com.",
"rr3---sn-vgqsknlr.googlevideo.com.",
- "rr3---sn-vgqsknlr.gvt1.com.",
+ "rr3---sn-vgqsknls.googlevideo.com.",
+ "rr3---sn-vgqsknly.googlevideo.com.",
"rr3---sn-vgqsknlz.googlevideo.com.",
"rr3---sn-vgqskns7.googlevideo.com.",
- "rr3---sn-vgqskns7.gvt1.com.",
"rr3---sn-vgqsknse.googlevideo.com.",
"rr3---sn-vgqsknsk.googlevideo.com.",
"rr3---sn-vgqsknz6.googlevideo.com.",
"rr3---sn-vgqsknz7.googlevideo.com.",
- "rr3---sn-vgqsknz7.gvt1.com.",
"rr3---sn-vgqsknzd.googlevideo.com.",
"rr3---sn-vgqsknze.googlevideo.com.",
"rr3---sn-vgqsknzk.googlevideo.com.",
"rr3---sn-vgqsknzl.googlevideo.com.",
"rr3---sn-vgqsknzr.googlevideo.com.",
- "rr3---sn-vgqsknzr.gvt1.com.",
"rr3---sn-vgqsknzs.googlevideo.com.",
"rr3---sn-vgqsknzy.googlevideo.com.",
"rr3---sn-vgqsknzz.googlevideo.com.",
"rr3---sn-vgqsrn66.googlevideo.com.",
"rr3---sn-vgqsrn67.googlevideo.com.",
- "rr3---sn-vgqsrn67.gvt1.com.",
"rr3---sn-vgqsrn6e.googlevideo.com.",
"rr3---sn-vgqsrn6l.googlevideo.com.",
"rr3---sn-vgqsrn6z.googlevideo.com.",
"rr3---sn-vgqsrne6.googlevideo.com.",
"rr3---sn-vgqsrned.googlevideo.com.",
- "rr3---sn-vgqsrned.gvt1.com.",
"rr3---sn-vgqsrnek.googlevideo.com.",
"rr3---sn-vgqsrnes.googlevideo.com.",
"rr3---sn-vgqsrnez.googlevideo.com.",
"rr3---sn-vgqsrnl6.googlevideo.com.",
- "rr3---sn-vgqsrnl6.gvt1.com.",
"rr3---sn-vgqsrnld.googlevideo.com.",
"rr3---sn-vgqsrnlk.googlevideo.com.",
- "rr3---sn-vgqsrnlk.gvt1.com.",
"rr3---sn-vgqsrnll.googlevideo.com.",
"rr3---sn-vgqsrnls.googlevideo.com.",
"rr3---sn-vgqsrnlz.googlevideo.com.",
"rr3---sn-vgqsrns6.googlevideo.com.",
"rr3---sn-vgqsrnsd.googlevideo.com.",
"rr3---sn-vgqsrnsr.googlevideo.com.",
+ "rr3---sn-vgqsrnsr.gvt1.com.",
"rr3---sn-vgqsrnsy.googlevideo.com.",
"rr3---sn-vgqsrnz6.googlevideo.com.",
"rr3---sn-vgqsrnz7.googlevideo.com.",
"rr3---sn-vgqsrnzd.googlevideo.com.",
- "rr3---sn-vgqsrnzd.gvt1.com.",
"rr3---sn-vgqsrnzk.googlevideo.com.",
"rr3---sn-vgqsrnzr.googlevideo.com.",
"rr3---sn-vgqsrnzs.googlevideo.com.",
"rr3---sn-vgqsrnzy.googlevideo.com.",
"rr3---sn-vgqsrnzz.googlevideo.com.",
- "rr3.sn-5hne6nzd.googlevideo.com.",
"rr3.sn-5hnednsz.googlevideo.com.",
- "rr3.sn-5hnekn7z.googlevideo.com.",
- "rr3.sn-5hneknek.googlevideo.com.",
- "rr3.sn-q4fl6n6z.googlevideo.com.",
- "rr3.sn-q4flrnes.googlevideo.com.",
- "rr3.sn-q4fzen7y.googlevideo.com.",
+ "rr3.sn-hp57knds.googlevideo.com.",
+ "rr3.sn-ntqe6nes.googlevideo.com.",
+ "rr3.sn-p5qs7nsk.googlevideo.com.",
+ "rr3.sn-q4fl6n6r.googlevideo.com.",
+ "rr3.sn-q4fl6ndl.googlevideo.com.",
+ "rr3.sn-q4fl6nss.googlevideo.com.",
+ "rr3.sn-q4fzen7l.googlevideo.com.",
+ "rr3.sn-vgqsrnzz.googlevideo.com.",
"rr4---sn-0nnpbo5a-bggl.googlevideo.com.",
+ "rr4---sn-2aqu-hoas7.googlevideo.com.",
+ "rr4---sn-2aqu-hoasz.googlevideo.com.",
+ "rr4---sn-2aqu-jxcr.googlevideo.com.",
"rr4---sn-2imern76.googlevideo.com.",
"rr4---sn-2imern76.gvt1.com.",
"rr4---sn-2imern7d.googlevideo.com.",
- "rr4---sn-2imern7d.gvt1.com.",
"rr4---sn-2imeyn7k.googlevideo.com.",
- "rr4---sn-2imeyn7k.gvt1.com.",
"rr4---sn-2oaig5-55.googlevideo.com.",
+ "rr4---sn-30a7rne6.googlevideo.com.",
"rr4---sn-30a7rned.googlevideo.com.",
"rr4---sn-30a7rnek.googlevideo.com.",
"rr4---sn-30a7rner.googlevideo.com.",
"rr4---sn-30a7ynek.googlevideo.com.",
+ "rr4---sn-30a7yner.googlevideo.com.",
"rr4---sn-30a7ynl7.googlevideo.com.",
"rr4---sn-4g5e6ns6.googlevideo.com.",
"rr4---sn-4g5e6ns7.googlevideo.com.",
@@ -5452,7 +5244,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-4g5ednse.googlevideo.com.",
"rr4---sn-4g5ednsk.googlevideo.com.",
"rr4---sn-4g5ednsl.googlevideo.com.",
- "rr4---sn-4g5ednsr.googlevideo.com.",
"rr4---sn-4g5ednss.googlevideo.com.",
"rr4---sn-4g5ednsy.googlevideo.com.",
"rr4---sn-4g5ednsz.googlevideo.com.",
@@ -5469,6 +5260,12 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-4g5lznle.googlevideo.com.",
"rr4---sn-4g5lznls.googlevideo.com.",
"rr4---sn-4g5lznlz.googlevideo.com.",
+ "rr4---sn-5abxgpxuxaxjvh-9n4e.googlevideo.com.",
+ "rr4---sn-5abxgpxuxaxjvh-9n4l.googlevideo.com.",
+ "rr4---sn-5abxgpxuxaxjvh-9n4z.googlevideo.com.",
+ "rr4---sn-5abxgpxuxaxjvh-j1az.googlevideo.com.",
+ "rr4---sn-5goeenes.googlevideo.com.",
+ "rr4---sn-5goeenez.googlevideo.com.",
"rr4---sn-5hne6n6e.googlevideo.com.",
"rr4---sn-5hne6n6l.googlevideo.com.",
"rr4---sn-5hne6ns6.googlevideo.com.",
@@ -5479,7 +5276,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-5hne6nsz.googlevideo.com.",
"rr4---sn-5hne6nz6.googlevideo.com.",
"rr4---sn-5hne6nzd.googlevideo.com.",
- "rr4---sn-5hne6nzd.gvt1.com.",
"rr4---sn-5hne6nzk.googlevideo.com.",
"rr4---sn-5hne6nzs.googlevideo.com.",
"rr4---sn-5hne6nzy.googlevideo.com.",
@@ -5492,6 +5288,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-5hnekn7z.googlevideo.com.",
"rr4---sn-5hneknee.googlevideo.com.",
"rr4---sn-5hneknek.googlevideo.com.",
+ "rr4---sn-5hneknes.googlevideo.com.",
"rr4---sn-5pgnugx5h-hn2z.googlevideo.com.",
"rr4---sn-5uaezndd.googlevideo.com.",
"rr4---sn-5uaezne6.googlevideo.com.",
@@ -5526,19 +5323,20 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-5ualdnsz.googlevideo.com.",
"rr4---sn-5ualdnz7.googlevideo.com.",
"rr4---sn-5ualdnze.googlevideo.com.",
- "rr4---sn-8qj-i5o6k.googlevideo.com.",
- "rr4---sn-8qj-nbo66.googlevideo.com.",
"rr4---sn-8xgp1vo-2iae7.googlevideo.com.",
"rr4---sn-8xgp1vo-ab56.googlevideo.com.",
"rr4---sn-8xgp1vo-ab5d.googlevideo.com.",
"rr4---sn-8xgp1vo-ab5e.googlevideo.com.",
"rr4---sn-8xgp1vo-ab5s.googlevideo.com.",
"rr4---sn-8xgp1vo-ab5z.googlevideo.com.",
- "rr4---sn-8xgp1vo-p5ie.googlevideo.com.",
+ "rr4---sn-8xgp1vo-p5qee.googlevideo.com.",
+ "rr4---sn-8xgp1vo-p5ql.googlevideo.com.",
+ "rr4---sn-8xgp1vo-p5qs.googlevideo.com.",
+ "rr4---sn-8xgp1vo-p5qy.googlevideo.com.",
+ "rr4---sn-8xgp1vo-vgqe.googlevideo.com.",
"rr4---sn-8xgp1vo-xfge.googlevideo.com.",
"rr4---sn-8xgp1vo-xfgl.googlevideo.com.",
"rr4---sn-9gv76n7e.googlevideo.com.",
- "rr4---sn-9gv76n7l.googlevideo.com.",
"rr4---sn-9gv76n7s.googlevideo.com.",
"rr4---sn-9gv76n7z.googlevideo.com.",
"rr4---sn-9gv7ene6.googlevideo.com.",
@@ -5550,78 +5348,61 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-a5m7lnl6.googlevideo.com.",
"rr4---sn-a5m7lnl6.gvt1.com.",
"rr4---sn-a5m7lnld.googlevideo.com.",
- "rr4---sn-a5m7lnld.gvt1.com.",
"rr4---sn-a5mekn6d.googlevideo.com.",
- "rr4---sn-a5mekn6d.gvt1.com.",
"rr4---sn-a5mekn6k.googlevideo.com.",
"rr4---sn-a5mekn6l.googlevideo.com.",
- "rr4---sn-a5mekn6l.gvt1.com.",
"rr4---sn-a5mekn6r.googlevideo.com.",
- "rr4---sn-a5mekn6r.gvt1.com.",
"rr4---sn-a5mekn6s.googlevideo.com.",
- "rr4---sn-a5mekn6s.gvt1.com.",
"rr4---sn-a5mekn6z.googlevideo.com.",
"rr4---sn-a5mekn6z.gvt1.com.",
"rr4---sn-a5meknd6.googlevideo.com.",
"rr4---sn-a5meknde.googlevideo.com.",
- "rr4---sn-a5meknde.gvt1.com.",
"rr4---sn-a5mekndl.googlevideo.com.",
"rr4---sn-a5meknds.googlevideo.com.",
"rr4---sn-a5mekndz.googlevideo.com.",
"rr4---sn-a5meknsd.googlevideo.com.",
"rr4---sn-a5meknsy.googlevideo.com.",
+ "rr4---sn-a5meknzk.googlevideo.com.",
"rr4---sn-a5meknzl.googlevideo.com.",
"rr4---sn-a5meknzr.googlevideo.com.",
- "rr4---sn-a5meknzr.gvt1.com.",
"rr4---sn-a5meknzs.googlevideo.com.",
- "rr4---sn-a5meknzs.gvt1.com.",
"rr4---sn-a5mlrnek.googlevideo.com.",
"rr4---sn-a5mlrnl6.googlevideo.com.",
"rr4---sn-a5mlrnll.googlevideo.com.",
- "rr4---sn-a5mlrnll.gvt1.com.",
"rr4---sn-a5mlrnls.googlevideo.com.",
- "rr4---sn-a5mlrnls.gvt1.com.",
"rr4---sn-a5mlrnlz.googlevideo.com.",
"rr4---sn-a5msen76.googlevideo.com.",
- "rr4---sn-a5msen76.gvt1.com.",
"rr4---sn-a5msen7l.googlevideo.com.",
"rr4---sn-a5msen7s.googlevideo.com.",
"rr4---sn-a5msen7z.googlevideo.com.",
+ "rr4---sn-a5msenek.googlevideo.com.",
"rr4---sn-a5msener.googlevideo.com.",
- "rr4---sn-a5msener.gvt1.com.",
"rr4---sn-a5msenes.googlevideo.com.",
- "rr4---sn-a5msenes.gvt1.com.",
"rr4---sn-a5msenl7.googlevideo.com.",
- "rr4---sn-a5msenl7.gvt1.com.",
"rr4---sn-a5msenle.googlevideo.com.",
"rr4---sn-a5msenll.googlevideo.com.",
"rr4---sn-ab5l6ndr.googlevideo.com.",
"rr4---sn-ab5l6ndy.googlevideo.com.",
"rr4---sn-ab5l6nk6.googlevideo.com.",
- "rr4---sn-ab5l6nk6.gvt1.com.",
"rr4---sn-ab5l6nkd.googlevideo.com.",
"rr4---sn-ab5l6nr6.googlevideo.com.",
- "rr4---sn-ab5l6nr6.gvt1.com.",
"rr4---sn-ab5l6nrd.googlevideo.com.",
"rr4---sn-ab5l6nrk.googlevideo.com.",
"rr4---sn-ab5l6nrl.googlevideo.com.",
"rr4---sn-ab5l6nrr.googlevideo.com.",
"rr4---sn-ab5l6nrz.googlevideo.com.",
- "rr4---sn-ab5sznld.googlevideo.com.",
"rr4---sn-ab5sznly.googlevideo.com.",
"rr4---sn-ab5sznz6.googlevideo.com.",
"rr4---sn-ab5sznzd.googlevideo.com.",
"rr4---sn-ab5sznze.googlevideo.com.",
- "rr4---sn-ab5sznze.gvt1.com.",
"rr4---sn-ab5sznzk.googlevideo.com.",
"rr4---sn-ab5sznzl.googlevideo.com.",
- "rr4---sn-ab5sznzl.gvt1.com.",
"rr4---sn-ab5sznzr.googlevideo.com.",
"rr4---sn-ab5sznzs.googlevideo.com.",
"rr4---sn-ab5sznzy.googlevideo.com.",
"rr4---sn-ab5sznzz.googlevideo.com.",
- "rr4---sn-ab5sznzz.gvt1.com.",
"rr4---sn-aigl6n6s.googlevideo.com.",
+ "rr4---sn-aigl6ned.googlevideo.com.",
"rr4---sn-aigl6nek.googlevideo.com.",
"rr4---sn-aigl6ner.googlevideo.com.",
"rr4---sn-aigl6ney.googlevideo.com.",
@@ -5629,11 +5410,11 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-aigl6ns6.googlevideo.com.",
"rr4---sn-aigl6nsd.googlevideo.com.",
"rr4---sn-aigl6nsk.googlevideo.com.",
- "rr4---sn-aigl6nsk.gvt1.com.",
"rr4---sn-aigl6nsr.googlevideo.com.",
"rr4---sn-aigl6nz7.googlevideo.com.",
"rr4---sn-aigl6nze.googlevideo.com.",
"rr4---sn-aigl6nzk.googlevideo.com.",
+ "rr4---sn-aigl6nzk.gvt1.com.",
"rr4---sn-aigl6nzl.googlevideo.com.",
"rr4---sn-aigl6nzr.googlevideo.com.",
"rr4---sn-aigl6nzs.googlevideo.com.",
@@ -5653,53 +5434,40 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-aigzrnz7.googlevideo.com.",
"rr4---sn-aigzrnze.googlevideo.com.",
"rr4---sn-ajab55-55.googlevideo.com.",
- "rr4---sn-bg5oqxjvh-50nz.googlevideo.com.",
- "rr4---sn-c0q7lnly.googlevideo.com.",
- "rr4---sn-c0q7lns7.googlevideo.com.",
- "rr4---sn-c0q7lnsl.googlevideo.com.",
- "rr4---sn-c0q7lnz7.googlevideo.com.",
+ "rr4---sn-bvvbaxivnuxqjvhj5nu-hp5e.googlevideo.com.",
+ "rr4---sn-bvvbaxivnuxqjvhj5nu-hp5l.googlevideo.com.",
+ "rr4---sn-bvvbaxivnuxqjvhj5nu-hp5s.googlevideo.com.",
"rr4---sn-cvb7lne7.googlevideo.com.",
"rr4---sn-cvb7lnee.googlevideo.com.",
"rr4---sn-cvb7lnlz.googlevideo.com.",
"rr4---sn-cvb7sn7k.googlevideo.com.",
"rr4---sn-cvb7sn7r.googlevideo.com.",
+ "rr4---sn-hgn7rn7r.googlevideo.com.",
"rr4---sn-hjoj-gq0l.googlevideo.com.",
"rr4---sn-hoa7kn76.googlevideo.com.",
"rr4---sn-hoa7kn7z.googlevideo.com.",
"rr4---sn-hoa7rn76.googlevideo.com.",
"rr4---sn-hoa7rn7z.googlevideo.com.",
"rr4---sn-hp57kn6r.googlevideo.com.",
- "rr4---sn-hp57kn6r.gvt1.com.",
"rr4---sn-hp57kn6y.googlevideo.com.",
"rr4---sn-hp57knd6.googlevideo.com.",
- "rr4---sn-hp57knd6.gvt1.com.",
"rr4---sn-hp57kndd.googlevideo.com.",
"rr4---sn-hp57kndr.googlevideo.com.",
- "rr4---sn-hp57kndr.gvt1.com.",
"rr4---sn-hp57knds.googlevideo.com.",
- "rr4---sn-hp57knds.gvt1.com.",
"rr4---sn-hp57kndy.googlevideo.com.",
- "rr4---sn-hp57kndy.gvt1.com.",
"rr4---sn-hp57kndz.googlevideo.com.",
- "rr4---sn-hp57kndz.gvt1.com.",
"rr4---sn-hp57yn7r.googlevideo.com.",
"rr4---sn-hp57yn7y.googlevideo.com.",
"rr4---sn-hp57yne7.googlevideo.com.",
"rr4---sn-hp57ynee.googlevideo.com.",
"rr4---sn-hp57ynl6.googlevideo.com.",
"rr4---sn-hp57ynlr.googlevideo.com.",
- "rr4---sn-hp57ynlr.gvt1.com.",
"rr4---sn-hp57ynly.googlevideo.com.",
- "rr4---sn-hp57ynly.gvt1.com.",
"rr4---sn-hp57yns7.googlevideo.com.",
+ "rr4---sn-hp57ynse.googlevideo.com.",
"rr4---sn-hp57ynsl.googlevideo.com.",
"rr4---sn-hp57ynss.googlevideo.com.",
- "rr4---sn-hp57ynss.gvt1.com.",
- "rr4---sn-huxaqvv-ubqe.googlevideo.com.",
- "rr4---sn-huxaqvv-ubqe.gvt1.com.",
- "rr4---sn-i3b7kn6s.googlevideo.com.",
"rr4---sn-i3b7knld.googlevideo.com.",
- "rr4---sn-i3b7knlk.googlevideo.com.",
"rr4---sn-i3b7kns6.googlevideo.com.",
"rr4---sn-i3b7knsd.googlevideo.com.",
"rr4---sn-i3b7knse.googlevideo.com.",
@@ -5707,25 +5475,15 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-i3b7knzl.googlevideo.com.",
"rr4---sn-i3b7knzs.googlevideo.com.",
"rr4---sn-i3belne6.googlevideo.com.",
- "rr4---sn-i3belney.googlevideo.com.",
- "rr4---sn-i3belnl6.googlevideo.com.",
"rr4---sn-i3belnl7.googlevideo.com.",
- "rr4---sn-i3belnll.googlevideo.com.",
- "rr4---sn-i3belnlz.googlevideo.com.",
+ "rr4---sn-i3belnls.googlevideo.com.",
"rr4---sn-i3bssn7e.googlevideo.com.",
"rr4---sn-jn2pgx4pcxg-w5os.googlevideo.com.",
"rr4---sn-jvhj5nu-2iae.googlevideo.com.",
- "rr4---sn-jvhj5nu-2ial.googlevideo.com.",
- "rr4---sn-jvhj5nu-2ias.googlevideo.com.",
- "rr4---sn-jvhj5nu-nh4e.googlevideo.com.",
- "rr4---sn-jvhj5nu-nh4s.googlevideo.com.",
- "rr4---sn-jvhj5nu-qufe.googlevideo.com.",
- "rr4---sn-jvhj5nu-qufl.googlevideo.com.",
- "rr4---sn-jvhj5nu-qufs.googlevideo.com.",
- "rr4---sn-jvhj5nu-qufz.googlevideo.com.",
"rr4---sn-jxopj-n5oe.googlevideo.com.",
"rr4---sn-jxopj-nh4e.googlevideo.com.",
"rr4---sn-jxopj-nh4e.gvt1.com.",
+ "rr4---sn-muxa-2iae.googlevideo.com.",
"rr4---sn-n4v7snee.googlevideo.com.",
"rr4---sn-n4v7sney.googlevideo.com.",
"rr4---sn-n4v7snl7.googlevideo.com.",
@@ -5735,9 +5493,9 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-n4v7snly.googlevideo.com.",
"rr4---sn-n4v7sns7.googlevideo.com.",
"rr4---sn-n4v7snse.googlevideo.com.",
- "rr4---sn-n4v7snse.gvt1.com.",
"rr4---sn-npoe7ndl.googlevideo.com.",
"rr4---sn-npoe7nds.googlevideo.com.",
+ "rr4---sn-npoe7ne6.googlevideo.com.",
"rr4---sn-npoe7ne7.googlevideo.com.",
"rr4---sn-npoe7ned.googlevideo.com.",
"rr4---sn-npoe7nek.googlevideo.com.",
@@ -5748,8 +5506,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-npoe7nl6.googlevideo.com.",
"rr4---sn-npoe7nlz.googlevideo.com.",
"rr4---sn-npoe7ns6.googlevideo.com.",
- "rr4---sn-npoe7ns7.googlevideo.com.",
- "rr4---sn-npoe7ns7.gvt1.com.",
"rr4---sn-npoe7nsd.googlevideo.com.",
"rr4---sn-npoe7nsk.googlevideo.com.",
"rr4---sn-npoe7nsl.googlevideo.com.",
@@ -5765,7 +5521,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-npoeeney.googlevideo.com.",
"rr4---sn-npoeenez.googlevideo.com.",
"rr4---sn-npoeenl7.googlevideo.com.",
- "rr4---sn-npoeenle.googlevideo.com.",
"rr4---sn-npoeenlk.googlevideo.com.",
"rr4---sn-npoeenll.googlevideo.com.",
"rr4---sn-npoeenly.googlevideo.com.",
@@ -5778,24 +5533,21 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-npoldn7y.googlevideo.com.",
"rr4---sn-npoldn7z.googlevideo.com.",
"rr4---sn-npoldne7.googlevideo.com.",
- "rr4---sn-ntq7yney.googlevideo.com.",
+ "rr4---sn-ntq7yner.googlevideo.com.",
"rr4---sn-nx57ynlk.googlevideo.com.",
- "rr4---sn-nx57ynlk.gvt1.com.",
"rr4---sn-nx57ynsd.googlevideo.com.",
"rr4---sn-nx57ynse.googlevideo.com.",
"rr4---sn-nx57ynsk.googlevideo.com.",
+ "rr4---sn-nx57ynsk.gvt1.com.",
"rr4---sn-nx57ynsl.googlevideo.com.",
- "rr4---sn-nx57ynss.googlevideo.com.",
- "rr4---sn-nx57ynss.gvt1.com.",
"rr4---sn-nx57ynsz.googlevideo.com.",
- "rr4---sn-nx57ynsz.gvt1.com.",
"rr4---sn-nx5s7n76.googlevideo.com.",
"rr4---sn-nx5s7n7d.googlevideo.com.",
"rr4---sn-nx5s7n7y.googlevideo.com.",
"rr4---sn-nx5s7n7z.googlevideo.com.",
"rr4---sn-nx5s7nee.googlevideo.com.",
- "rr4---sn-nx5s7nee.gvt1.com.",
"rr4---sn-nx5s7nel.googlevideo.com.",
+ "rr4---sn-o097znsd.googlevideo.com.",
"rr4---sn-o097znse.googlevideo.com.",
"rr4---sn-o097znsk.googlevideo.com.",
"rr4---sn-o097znsl.googlevideo.com.",
@@ -5816,7 +5568,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-p5qlsn6l.googlevideo.com.",
"rr4---sn-p5qlsn76.googlevideo.com.",
"rr4---sn-p5qlsn7d.googlevideo.com.",
- "rr4---sn-p5qlsn7d.gvt1.com.",
"rr4---sn-p5qlsn7l.googlevideo.com.",
"rr4---sn-p5qlsn7s.googlevideo.com.",
"rr4---sn-p5qlsnd6.googlevideo.com.",
@@ -5825,30 +5576,23 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-p5qlsndz.googlevideo.com.",
"rr4---sn-p5qlsnrl.googlevideo.com.",
"rr4---sn-p5qlsnrr.googlevideo.com.",
- "rr4---sn-p5qlsnrr.gvt1.com.",
"rr4---sn-p5qlsny6.googlevideo.com.",
"rr4---sn-p5qs7n6d.googlevideo.com.",
"rr4---sn-p5qs7nsk.googlevideo.com.",
+ "rr4---sn-p5qs7nsk.gvt1.com.",
"rr4---sn-p5qs7nsr.googlevideo.com.",
"rr4---sn-p5qs7nzk.googlevideo.com.",
"rr4---sn-p5qs7nzr.googlevideo.com.",
"rr4---sn-p5qs7nzy.googlevideo.com.",
"rr4---sn-q4fl6n66.googlevideo.com.",
"rr4---sn-q4fl6n6d.googlevideo.com.",
- "rr4---sn-q4fl6n6d.gvt1.com.",
"rr4---sn-q4fl6n6r.googlevideo.com.",
- "rr4---sn-q4fl6n6r.gvt1.com.",
"rr4---sn-q4fl6n6s.googlevideo.com.",
- "rr4---sn-q4fl6n6s.gvt1.com.",
"rr4---sn-q4fl6n6y.googlevideo.com.",
- "rr4---sn-q4fl6n6y.gvt1.com.",
"rr4---sn-q4fl6n6z.googlevideo.com.",
"rr4---sn-q4fl6nd7.googlevideo.com.",
- "rr4---sn-q4fl6nd7.gvt1.com.",
"rr4---sn-q4fl6nde.googlevideo.com.",
- "rr4---sn-q4fl6nde.gvt1.com.",
"rr4---sn-q4fl6ndl.googlevideo.com.",
- "rr4---sn-q4fl6ndl.gvt1.com.",
"rr4---sn-q4fl6nds.googlevideo.com.",
"rr4---sn-q4fl6nds.gvt1.com.",
"rr4---sn-q4fl6ndz.googlevideo.com.",
@@ -5858,54 +5602,47 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-q4fl6ns6.gvt1.com.",
"rr4---sn-q4fl6ns7.googlevideo.com.",
"rr4---sn-q4fl6nsd.googlevideo.com.",
- "rr4---sn-q4fl6nsd.gvt1.com.",
"rr4---sn-q4fl6nsk.googlevideo.com.",
"rr4---sn-q4fl6nsl.googlevideo.com.",
"rr4---sn-q4fl6nsr.googlevideo.com.",
"rr4---sn-q4fl6nss.googlevideo.com.",
- "rr4---sn-q4fl6nss.gvt1.com.",
"rr4---sn-q4fl6nsy.googlevideo.com.",
- "rr4---sn-q4fl6nsy.gvt1.com.",
"rr4---sn-q4fl6nz6.googlevideo.com.",
- "rr4---sn-q4fl6nz6.gvt1.com.",
"rr4---sn-q4fl6nz7.googlevideo.com.",
- "rr4---sn-q4fl6nz7.gvt1.com.",
"rr4---sn-q4fl6nzy.googlevideo.com.",
"rr4---sn-q4flrn7k.googlevideo.com.",
"rr4---sn-q4flrn7k.gvt1.com.",
"rr4---sn-q4flrn7r.googlevideo.com.",
"rr4---sn-q4flrn7y.googlevideo.com.",
- "rr4---sn-q4flrn7y.gvt1.com.",
"rr4---sn-q4flrne6.googlevideo.com.",
"rr4---sn-q4flrne6.gvt1.com.",
"rr4---sn-q4flrne7.googlevideo.com.",
"rr4---sn-q4flrnee.googlevideo.com.",
"rr4---sn-q4flrnek.googlevideo.com.",
- "rr4---sn-q4flrnel.googlevideo.com.",
"rr4---sn-q4flrner.googlevideo.com.",
+ "rr4---sn-q4flrnes.googlevideo.com.",
"rr4---sn-q4flrney.googlevideo.com.",
"rr4---sn-q4flrney.gvt1.com.",
"rr4---sn-q4flrnez.googlevideo.com.",
- "rr4---sn-q4flrnez.gvt1.com.",
"rr4---sn-q4flrnl6.googlevideo.com.",
"rr4---sn-q4flrnl7.googlevideo.com.",
+ "rr4---sn-q4flrnl7.gvt1.com.",
"rr4---sn-q4flrnld.googlevideo.com.",
"rr4---sn-q4flrnld.gvt1.com.",
"rr4---sn-q4flrnle.googlevideo.com.",
+ "rr4---sn-q4flrnle.gvt1.com.",
+ "rr4---sn-q4flrnlz.googlevideo.com.",
"rr4---sn-q4flrnsd.googlevideo.com.",
"rr4---sn-q4flrnsk.googlevideo.com.",
"rr4---sn-q4flrnsl.googlevideo.com.",
"rr4---sn-q4flrnss.googlevideo.com.",
"rr4---sn-q4flrnss.gvt1.com.",
"rr4---sn-q4fzen7e.googlevideo.com.",
- "rr4---sn-q4fzen7e.gvt1.com.",
"rr4---sn-q4fzen7l.googlevideo.com.",
"rr4---sn-q4fzen7l.gvt1.com.",
- "rr4---sn-q4fzen7r.googlevideo.com.",
- "rr4---sn-q4fzen7r.gvt1.com.",
"rr4---sn-q4fzen7s.googlevideo.com.",
+ "rr4---sn-q4fzen7s.gvt1.com.",
"rr4---sn-q4fzen7y.googlevideo.com.",
- "rr4---sn-q4fzen7y.gvt1.com.",
"rr4---sn-q4fzene7.googlevideo.com.",
"rr4---sn-q4fzenee.googlevideo.com.",
"rr4---sn-q4fzenee.gvt1.com.",
@@ -5916,14 +5653,11 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-qxoedn7k.googlevideo.com.",
"rr4---sn-qxoedne7.googlevideo.com.",
"rr4---sn-qxoednee.googlevideo.com.",
- "rr4---sn-u1hp55-5c.googlevideo.com.",
- "rr4---sn-u1hp55-5c.gvt1.com.",
"rr4---sn-v5goxu-jhil.googlevideo.com.",
+ "rr4---sn-v5goxu-jhil.gvt1.com.",
"rr4---sn-vgqskn66.googlevideo.com.",
- "rr4---sn-vgqskn66.gvt1.com.",
"rr4---sn-vgqskn67.googlevideo.com.",
"rr4---sn-vgqskn6d.googlevideo.com.",
- "rr4---sn-vgqskn6d.gvt1.com.",
"rr4---sn-vgqskn6s.googlevideo.com.",
"rr4---sn-vgqskn6z.googlevideo.com.",
"rr4---sn-vgqskne6.googlevideo.com.",
@@ -5935,74 +5669,71 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-vgqsknlk.googlevideo.com.",
"rr4---sn-vgqsknll.googlevideo.com.",
"rr4---sn-vgqsknlr.googlevideo.com.",
- "rr4---sn-vgqsknlr.gvt1.com.",
"rr4---sn-vgqsknls.googlevideo.com.",
"rr4---sn-vgqsknly.googlevideo.com.",
"rr4---sn-vgqsknlz.googlevideo.com.",
"rr4---sn-vgqskns7.googlevideo.com.",
"rr4---sn-vgqsknse.googlevideo.com.",
+ "rr4---sn-vgqsknse.gvt1.com.",
"rr4---sn-vgqsknsk.googlevideo.com.",
"rr4---sn-vgqsknz6.googlevideo.com.",
"rr4---sn-vgqsknz7.googlevideo.com.",
"rr4---sn-vgqsknzd.googlevideo.com.",
- "rr4---sn-vgqsknzd.gvt1.com.",
"rr4---sn-vgqsknze.googlevideo.com.",
"rr4---sn-vgqsknzk.googlevideo.com.",
- "rr4---sn-vgqsknzk.gvt1.com.",
"rr4---sn-vgqsknzl.googlevideo.com.",
"rr4---sn-vgqsknzr.googlevideo.com.",
+ "rr4---sn-vgqsknzr.gvt1.com.",
"rr4---sn-vgqsknzs.googlevideo.com.",
"rr4---sn-vgqsknzy.googlevideo.com.",
- "rr4---sn-vgqsknzy.gvt1.com.",
"rr4---sn-vgqsknzz.googlevideo.com.",
- "rr4---sn-vgqsknzz.gvt1.com.",
"rr4---sn-vgqsrn66.googlevideo.com.",
- "rr4---sn-vgqsrn66.gvt1.com.",
"rr4---sn-vgqsrn67.googlevideo.com.",
"rr4---sn-vgqsrn6e.googlevideo.com.",
"rr4---sn-vgqsrn6l.googlevideo.com.",
"rr4---sn-vgqsrn6z.googlevideo.com.",
"rr4---sn-vgqsrne6.googlevideo.com.",
- "rr4---sn-vgqsrne6.gvt1.com.",
"rr4---sn-vgqsrned.googlevideo.com.",
"rr4---sn-vgqsrnek.googlevideo.com.",
"rr4---sn-vgqsrnes.googlevideo.com.",
"rr4---sn-vgqsrnez.googlevideo.com.",
+ "rr4---sn-vgqsrnl6.googlevideo.com.",
"rr4---sn-vgqsrnld.googlevideo.com.",
"rr4---sn-vgqsrnlk.googlevideo.com.",
- "rr4---sn-vgqsrnlk.gvt1.com.",
"rr4---sn-vgqsrnll.googlevideo.com.",
"rr4---sn-vgqsrnls.googlevideo.com.",
"rr4---sn-vgqsrnlz.googlevideo.com.",
"rr4---sn-vgqsrns6.googlevideo.com.",
"rr4---sn-vgqsrnsd.googlevideo.com.",
- "rr4---sn-vgqsrnsd.gvt1.com.",
"rr4---sn-vgqsrnsr.googlevideo.com.",
+ "rr4---sn-vgqsrnsy.googlevideo.com.",
"rr4---sn-vgqsrnz6.googlevideo.com.",
+ "rr4---sn-vgqsrnz7.googlevideo.com.",
"rr4---sn-vgqsrnzd.googlevideo.com.",
"rr4---sn-vgqsrnzk.googlevideo.com.",
"rr4---sn-vgqsrnzr.googlevideo.com.",
- "rr4---sn-vgqsrnzr.gvt1.com.",
"rr4---sn-vgqsrnzs.googlevideo.com.",
"rr4---sn-vgqsrnzy.googlevideo.com.",
"rr4---sn-vgqsrnzz.googlevideo.com.",
- "rr4---sn-vgqsrnzz.gvt1.com.",
- "rr4.sn-4g5lznes.googlevideo.com.",
- "rr4.sn-5hneknee.googlevideo.com.",
- "rr4.sn-q4fl6nlz.googlevideo.com.",
- "rr4.sn-q4fl6nzy.googlevideo.com.",
- "rr4.sn-q4flrnsd.googlevideo.com.",
- "rr4.sn-q4fzen7r.googlevideo.com.",
+ "rr4.sn-hp57knds.googlevideo.com.",
+ "rr4.sn-p5qs7nsk.googlevideo.com.",
+ "rr4.sn-q4fl6nsd.googlevideo.com.",
+ "rr4.sn-q4flrn7r.googlevideo.com.",
+ "rr4.sn-q4fzen7l.googlevideo.com.",
+ "rr4.sn-vgqsrnzz.googlevideo.com.",
"rr5---sn-0nnpbo5a-bggl.googlevideo.com.",
+ "rr5---sn-2aqu-hoas7.googlevideo.com.",
"rr5---sn-2imern76.googlevideo.com.",
- "rr5---sn-2imern76.gvt1.com.",
"rr5---sn-2imern7d.googlevideo.com.",
"rr5---sn-2imern7d.gvt1.com.",
"rr5---sn-2imeyn7k.googlevideo.com.",
"rr5---sn-2oaig5-55.googlevideo.com.",
"rr5---sn-30a7rne6.googlevideo.com.",
+ "rr5---sn-30a7rned.googlevideo.com.",
"rr5---sn-30a7rnek.googlevideo.com.",
"rr5---sn-30a7rner.googlevideo.com.",
+ "rr5---sn-30a7ynek.googlevideo.com.",
+ "rr5---sn-30a7yner.googlevideo.com.",
"rr5---sn-30a7yney.googlevideo.com.",
"rr5---sn-30a7ynl7.googlevideo.com.",
"rr5---sn-4g5e6ns6.googlevideo.com.",
@@ -6056,19 +5787,22 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-4g5lznle.googlevideo.com.",
"rr5---sn-4g5lznls.googlevideo.com.",
"rr5---sn-4g5lznlz.googlevideo.com.",
+ "rr5---sn-5abxgpxuxaxjvh-9n4e.googlevideo.com.",
+ "rr5---sn-5abxgpxuxaxjvh-9n4l.googlevideo.com.",
+ "rr5---sn-5abxgpxuxaxjvh-j1az.googlevideo.com.",
+ "rr5---sn-5goeenez.googlevideo.com.",
"rr5---sn-5hne6n6e.googlevideo.com.",
"rr5---sn-5hne6n6l.googlevideo.com.",
"rr5---sn-5hne6ns6.googlevideo.com.",
+ "rr5---sn-5hne6nsd.googlevideo.com.",
"rr5---sn-5hne6nsk.googlevideo.com.",
"rr5---sn-5hne6nsr.googlevideo.com.",
"rr5---sn-5hne6nsy.googlevideo.com.",
"rr5---sn-5hne6nsz.googlevideo.com.",
"rr5---sn-5hne6nz6.googlevideo.com.",
- "rr5---sn-5hne6nz6.gvt1.com.",
"rr5---sn-5hne6nzd.googlevideo.com.",
"rr5---sn-5hne6nzk.googlevideo.com.",
"rr5---sn-5hne6nzs.googlevideo.com.",
- "rr5---sn-5hne6nzs.gvt1.com.",
"rr5---sn-5hne6nzy.googlevideo.com.",
"rr5---sn-5hnednss.googlevideo.com.",
"rr5---sn-5hnednsz.googlevideo.com.",
@@ -6105,28 +5839,27 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-5ualdns6.googlevideo.com.",
"rr5---sn-5ualdns7.googlevideo.com.",
"rr5---sn-5ualdnsd.googlevideo.com.",
- "rr5---sn-5ualdnse.googlevideo.com.",
"rr5---sn-5ualdnsk.googlevideo.com.",
"rr5---sn-5ualdnsl.googlevideo.com.",
"rr5---sn-5ualdnsr.googlevideo.com.",
"rr5---sn-5ualdnss.googlevideo.com.",
"rr5---sn-5ualdnsy.googlevideo.com.",
- "rr5---sn-5ualdnsz.googlevideo.com.",
"rr5---sn-5ualdnz7.googlevideo.com.",
"rr5---sn-5ualdnze.googlevideo.com.",
- "rr5---sn-8qj-nbo66.googlevideo.com.",
"rr5---sn-8xgp1vo-2iae7.googlevideo.com.",
- "rr5---sn-8xgp1vo-a5me.googlevideo.com.",
"rr5---sn-8xgp1vo-ab56.googlevideo.com.",
"rr5---sn-8xgp1vo-ab5d.googlevideo.com.",
"rr5---sn-8xgp1vo-ab5e.googlevideo.com.",
"rr5---sn-8xgp1vo-ab5s.googlevideo.com.",
"rr5---sn-8xgp1vo-ab5z.googlevideo.com.",
- "rr5---sn-8xgp1vo-p5ie.googlevideo.com.",
+ "rr5---sn-8xgp1vo-p5qe.googlevideo.com.",
+ "rr5---sn-8xgp1vo-p5qee.googlevideo.com.",
+ "rr5---sn-8xgp1vo-p5ql.googlevideo.com.",
+ "rr5---sn-8xgp1vo-p5qs.googlevideo.com.",
+ "rr5---sn-8xgp1vo-p5qy.googlevideo.com.",
"rr5---sn-8xgp1vo-vgqe.googlevideo.com.",
- "rr5---sn-8xgp1vo-xfge.googlevideo.com.",
"rr5---sn-8xgp1vo-xfgl.googlevideo.com.",
- "rr5---sn-9gv76n7l.googlevideo.com.",
+ "rr5---sn-9gv76n7e.googlevideo.com.",
"rr5---sn-9gv76n7s.googlevideo.com.",
"rr5---sn-9gv76n7z.googlevideo.com.",
"rr5---sn-9gv7ene6.googlevideo.com.",
@@ -6138,46 +5871,37 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-a5m7lnl6.googlevideo.com.",
"rr5---sn-a5m7lnld.googlevideo.com.",
"rr5---sn-a5mekn6d.googlevideo.com.",
- "rr5---sn-a5mekn6d.gvt1.com.",
"rr5---sn-a5mekn6k.googlevideo.com.",
"rr5---sn-a5mekn6l.googlevideo.com.",
- "rr5---sn-a5mekn6l.gvt1.com.",
"rr5---sn-a5mekn6r.googlevideo.com.",
"rr5---sn-a5mekn6s.googlevideo.com.",
"rr5---sn-a5mekn6z.googlevideo.com.",
"rr5---sn-a5meknd6.googlevideo.com.",
"rr5---sn-a5meknde.googlevideo.com.",
- "rr5---sn-a5meknde.gvt1.com.",
"rr5---sn-a5mekndl.googlevideo.com.",
"rr5---sn-a5meknds.googlevideo.com.",
- "rr5---sn-a5meknds.gvt1.com.",
"rr5---sn-a5mekndz.googlevideo.com.",
"rr5---sn-a5meknsd.googlevideo.com.",
"rr5---sn-a5meknsy.googlevideo.com.",
"rr5---sn-a5meknzk.googlevideo.com.",
- "rr5---sn-a5meknzk.gvt1.com.",
+ "rr5---sn-a5meknzl.googlevideo.com.",
"rr5---sn-a5meknzr.googlevideo.com.",
"rr5---sn-a5meknzs.googlevideo.com.",
- "rr5---sn-a5meknzs.gvt1.com.",
"rr5---sn-a5mlrnek.googlevideo.com.",
"rr5---sn-a5mlrnl6.googlevideo.com.",
- "rr5---sn-a5mlrnl6.gvt1.com.",
"rr5---sn-a5mlrnll.googlevideo.com.",
"rr5---sn-a5mlrnls.googlevideo.com.",
- "rr5---sn-a5mlrnls.gvt1.com.",
"rr5---sn-a5mlrnlz.googlevideo.com.",
- "rr5---sn-a5mlrnlz.gvt1.com.",
+ "rr5---sn-a5msen76.googlevideo.com.",
"rr5---sn-a5msen7l.googlevideo.com.",
"rr5---sn-a5msen7s.googlevideo.com.",
"rr5---sn-a5msen7z.googlevideo.com.",
"rr5---sn-a5msenek.googlevideo.com.",
"rr5---sn-a5msener.googlevideo.com.",
- "rr5---sn-a5msener.gvt1.com.",
"rr5---sn-a5msenes.googlevideo.com.",
"rr5---sn-a5msenl7.googlevideo.com.",
"rr5---sn-a5msenle.googlevideo.com.",
"rr5---sn-a5msenll.googlevideo.com.",
- "rr5---sn-a5msenll.gvt1.com.",
"rr5---sn-ab5l6ndr.googlevideo.com.",
"rr5---sn-ab5l6ndy.googlevideo.com.",
"rr5---sn-ab5l6nk6.googlevideo.com.",
@@ -6187,16 +5911,13 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-ab5l6nrk.googlevideo.com.",
"rr5---sn-ab5l6nrl.googlevideo.com.",
"rr5---sn-ab5l6nrr.googlevideo.com.",
- "rr5---sn-ab5l6nrr.gvt1.com.",
"rr5---sn-ab5l6nrs.googlevideo.com.",
"rr5---sn-ab5l6nrz.googlevideo.com.",
"rr5---sn-ab5sznld.googlevideo.com.",
"rr5---sn-ab5sznly.googlevideo.com.",
"rr5---sn-ab5sznz6.googlevideo.com.",
"rr5---sn-ab5sznzd.googlevideo.com.",
- "rr5---sn-ab5sznzd.gvt1.com.",
"rr5---sn-ab5sznze.googlevideo.com.",
- "rr5---sn-ab5sznze.gvt1.com.",
"rr5---sn-ab5sznzk.googlevideo.com.",
"rr5---sn-ab5sznzl.googlevideo.com.",
"rr5---sn-ab5sznzr.googlevideo.com.",
@@ -6206,7 +5927,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-aigl6n6s.googlevideo.com.",
"rr5---sn-aigl6ned.googlevideo.com.",
"rr5---sn-aigl6nek.googlevideo.com.",
- "rr5---sn-aigl6nek.gvt1.com.",
"rr5---sn-aigl6ner.googlevideo.com.",
"rr5---sn-aigl6ney.googlevideo.com.",
"rr5---sn-aigl6nl7.googlevideo.com.",
@@ -6215,12 +5935,12 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-aigl6nsk.googlevideo.com.",
"rr5---sn-aigl6nsr.googlevideo.com.",
"rr5---sn-aigl6nz7.googlevideo.com.",
+ "rr5---sn-aigl6nz7.gvt1.com.",
"rr5---sn-aigl6nze.googlevideo.com.",
"rr5---sn-aigl6nzk.googlevideo.com.",
"rr5---sn-aigl6nzl.googlevideo.com.",
"rr5---sn-aigl6nzr.googlevideo.com.",
"rr5---sn-aigl6nzs.googlevideo.com.",
- "rr5---sn-aigl6nzs.gvt1.com.",
"rr5---sn-aigzrn76.googlevideo.com.",
"rr5---sn-aigzrn7d.googlevideo.com.",
"rr5---sn-aigzrn7e.googlevideo.com.",
@@ -6238,46 +5958,34 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-aigzrnze.googlevideo.com.",
"rr5---sn-aj5ua5-5c.googlevideo.com.",
"rr5---sn-ajab55-55.googlevideo.com.",
- "rr5---sn-bg5oqxjvh-50nz.googlevideo.com.",
- "rr5---sn-c0q7lns7.googlevideo.com.",
- "rr5---sn-c0q7lnz7.googlevideo.com.",
+ "rr5---sn-bvvbaxivnuxqjvhj5nu-hp5e.googlevideo.com.",
+ "rr5---sn-bvvbaxivnuxqjvhj5nu-hp5l.googlevideo.com.",
"rr5---sn-cvb7lne7.googlevideo.com.",
"rr5---sn-cvb7lnee.googlevideo.com.",
- "rr5---sn-cvb7lnls.googlevideo.com.",
- "rr5---sn-cvb7lnlz.googlevideo.com.",
"rr5---sn-cvb7sn7k.googlevideo.com.",
"rr5---sn-cvb7sn7r.googlevideo.com.",
- "rr5---sn-hgn7rn7r.googlevideo.com.",
"rr5---sn-hoa7kn76.googlevideo.com.",
"rr5---sn-hoa7kn7z.googlevideo.com.",
"rr5---sn-hoa7rn76.googlevideo.com.",
"rr5---sn-hoa7rn7z.googlevideo.com.",
"rr5---sn-hp57kn6r.googlevideo.com.",
- "rr5---sn-hp57kn6r.gvt1.com.",
"rr5---sn-hp57kn6y.googlevideo.com.",
- "rr5---sn-hp57kn6y.gvt1.com.",
"rr5---sn-hp57knd6.googlevideo.com.",
- "rr5---sn-hp57knd6.gvt1.com.",
"rr5---sn-hp57kndd.googlevideo.com.",
- "rr5---sn-hp57kndd.gvt1.com.",
"rr5---sn-hp57kndk.googlevideo.com.",
- "rr5---sn-hp57kndk.gvt1.com.",
"rr5---sn-hp57kndr.googlevideo.com.",
"rr5---sn-hp57knds.googlevideo.com.",
"rr5---sn-hp57knds.gvt1.com.",
"rr5---sn-hp57kndy.googlevideo.com.",
- "rr5---sn-hp57kndy.gvt1.com.",
"rr5---sn-hp57kndz.googlevideo.com.",
"rr5---sn-hp57yn7r.googlevideo.com.",
- "rr5---sn-hp57yn7r.gvt1.com.",
"rr5---sn-hp57yn7y.googlevideo.com.",
- "rr5---sn-hp57yn7y.gvt1.com.",
"rr5---sn-hp57yne7.googlevideo.com.",
"rr5---sn-hp57ynee.googlevideo.com.",
"rr5---sn-hp57ynl6.googlevideo.com.",
- "rr5---sn-hp57ynl6.gvt1.com.",
"rr5---sn-hp57ynlr.googlevideo.com.",
"rr5---sn-hp57ynly.googlevideo.com.",
+ "rr5---sn-hp57yns7.googlevideo.com.",
"rr5---sn-hp57ynse.googlevideo.com.",
"rr5---sn-hp57ynsl.googlevideo.com.",
"rr5---sn-hp57ynss.googlevideo.com.",
@@ -6285,27 +5993,18 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-i3b7knld.googlevideo.com.",
"rr5---sn-i3b7knlk.googlevideo.com.",
"rr5---sn-i3b7kns6.googlevideo.com.",
+ "rr5---sn-i3b7knsd.googlevideo.com.",
"rr5---sn-i3b7knse.googlevideo.com.",
"rr5---sn-i3b7knsl.googlevideo.com.",
"rr5---sn-i3b7knzl.googlevideo.com.",
"rr5---sn-i3b7knzs.googlevideo.com.",
"rr5---sn-i3belne6.googlevideo.com.",
- "rr5---sn-i3belney.googlevideo.com.",
"rr5---sn-i3belnl6.googlevideo.com.",
"rr5---sn-i3belnl7.googlevideo.com.",
- "rr5---sn-i3belnll.googlevideo.com.",
"rr5---sn-i3belnls.googlevideo.com.",
- "rr5---sn-i3belnlz.googlevideo.com.",
"rr5---sn-i3bssn7e.googlevideo.com.",
"rr5---sn-jn2pgx4pcxg-w5os.googlevideo.com.",
"rr5---sn-jvhj5nu-2iae.googlevideo.com.",
- "rr5---sn-jvhj5nu-2ias.googlevideo.com.",
- "rr5---sn-jvhj5nu-nh4e.googlevideo.com.",
- "rr5---sn-jvhj5nu-nh4s.googlevideo.com.",
- "rr5---sn-jvhj5nu-qufe.googlevideo.com.",
- "rr5---sn-jvhj5nu-qufl.googlevideo.com.",
- "rr5---sn-jvhj5nu-qufs.googlevideo.com.",
- "rr5---sn-jvhj5nu-qufz.googlevideo.com.",
"rr5---sn-jxopj-n5oe.googlevideo.com.",
"rr5---sn-jxopj-nh4e.googlevideo.com.",
"rr5---sn-jxopj-nh4e.gvt1.com.",
@@ -6315,6 +6014,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-n4v7snl7.googlevideo.com.",
"rr5---sn-n4v7snll.googlevideo.com.",
"rr5---sn-n4v7snlr.googlevideo.com.",
+ "rr5---sn-n4v7snls.googlevideo.com.",
"rr5---sn-n4v7snly.googlevideo.com.",
"rr5---sn-n4v7sns7.googlevideo.com.",
"rr5---sn-n4v7snse.googlevideo.com.",
@@ -6347,35 +6047,32 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-npoeeney.googlevideo.com.",
"rr5---sn-npoeenez.googlevideo.com.",
"rr5---sn-npoeenl7.googlevideo.com.",
+ "rr5---sn-npoeenle.googlevideo.com.",
"rr5---sn-npoeenlk.googlevideo.com.",
"rr5---sn-npoeenll.googlevideo.com.",
- "rr5---sn-npoeenly.googlevideo.com.",
"rr5---sn-npoeens7.googlevideo.com.",
"rr5---sn-npoldn76.googlevideo.com.",
"rr5---sn-npoldn7d.googlevideo.com.",
- "rr5---sn-npoldn7d.gvt1.com.",
"rr5---sn-npoldn7e.googlevideo.com.",
"rr5---sn-npoldn7l.googlevideo.com.",
"rr5---sn-npoldn7s.googlevideo.com.",
"rr5---sn-npoldn7y.googlevideo.com.",
+ "rr5---sn-npoldn7z.googlevideo.com.",
"rr5---sn-npoldne7.googlevideo.com.",
"rr5---sn-nx57ynlk.googlevideo.com.",
"rr5---sn-nx57ynsd.googlevideo.com.",
- "rr5---sn-nx57ynsd.gvt1.com.",
+ "rr5---sn-nx57ynse.googlevideo.com.",
"rr5---sn-nx57ynsk.googlevideo.com.",
"rr5---sn-nx57ynsk.gvt1.com.",
"rr5---sn-nx57ynsl.googlevideo.com.",
- "rr5---sn-nx57ynsl.gvt1.com.",
"rr5---sn-nx57ynss.googlevideo.com.",
- "rr5---sn-nx57ynsz.googlevideo.com.",
- "rr5---sn-nx57ynsz.gvt1.com.",
"rr5---sn-nx5s7n76.googlevideo.com.",
+ "rr5---sn-nx5s7n7d.googlevideo.com.",
+ "rr5---sn-nx5s7n7s.googlevideo.com.",
"rr5---sn-nx5s7n7y.googlevideo.com.",
"rr5---sn-nx5s7n7z.googlevideo.com.",
"rr5---sn-nx5s7nee.googlevideo.com.",
- "rr5---sn-nx5s7nee.gvt1.com.",
"rr5---sn-nx5s7nel.googlevideo.com.",
- "rr5---sn-nx5s7nel.gvt1.com.",
"rr5---sn-o097znsd.googlevideo.com.",
"rr5---sn-o097znse.googlevideo.com.",
"rr5---sn-o097znsk.googlevideo.com.",
@@ -6387,9 +6084,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-o097znzd.googlevideo.com.",
"rr5---sn-o097znze.googlevideo.com.",
"rr5---sn-o097znzk.googlevideo.com.",
- "rr5---sn-o097znzk.gvt1.com.",
"rr5---sn-o097znzr.googlevideo.com.",
- "rr5---sn-o097znzr.gvt1.com.",
"rr5---sn-oj5hn5-55.googlevideo.com.",
"rr5---sn-p5qddn76.googlevideo.com.",
"rr5---sn-p5qddn7d.googlevideo.com.",
@@ -6400,7 +6095,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-p5qlsn76.googlevideo.com.",
"rr5---sn-p5qlsn7d.googlevideo.com.",
"rr5---sn-p5qlsn7l.googlevideo.com.",
- "rr5---sn-p5qlsn7l.gvt1.com.",
"rr5---sn-p5qlsn7s.googlevideo.com.",
"rr5---sn-p5qlsnd6.googlevideo.com.",
"rr5---sn-p5qlsndk.googlevideo.com.",
@@ -6408,19 +6102,18 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-p5qlsndz.googlevideo.com.",
"rr5---sn-p5qlsnrl.googlevideo.com.",
"rr5---sn-p5qlsnrr.googlevideo.com.",
- "rr5---sn-p5qlsnrr.gvt1.com.",
"rr5---sn-p5qlsny6.googlevideo.com.",
"rr5---sn-p5qs7n6d.googlevideo.com.",
"rr5---sn-p5qs7nsk.googlevideo.com.",
+ "rr5---sn-p5qs7nsk.gvt1.com.",
"rr5---sn-p5qs7nsr.googlevideo.com.",
- "rr5---sn-p5qs7nsr.gvt1.com.",
"rr5---sn-p5qs7nzk.googlevideo.com.",
"rr5---sn-p5qs7nzr.googlevideo.com.",
"rr5---sn-p5qs7nzy.googlevideo.com.",
"rr5---sn-q4fl6n66.googlevideo.com.",
- "rr5---sn-q4fl6n66.gvt1.com.",
"rr5---sn-q4fl6n6d.googlevideo.com.",
"rr5---sn-q4fl6n6r.googlevideo.com.",
+ "rr5---sn-q4fl6n6r.gvt1.com.",
"rr5---sn-q4fl6n6s.googlevideo.com.",
"rr5---sn-q4fl6n6y.googlevideo.com.",
"rr5---sn-q4fl6n6y.gvt1.com.",
@@ -6431,90 +6124,79 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-q4fl6nde.gvt1.com.",
"rr5---sn-q4fl6ndl.googlevideo.com.",
"rr5---sn-q4fl6nds.googlevideo.com.",
- "rr5---sn-q4fl6nds.gvt1.com.",
"rr5---sn-q4fl6ndz.googlevideo.com.",
"rr5---sn-q4fl6ndz.gvt1.com.",
"rr5---sn-q4fl6nlz.googlevideo.com.",
- "rr5---sn-q4fl6nlz.gvt1.com.",
"rr5---sn-q4fl6ns6.googlevideo.com.",
"rr5---sn-q4fl6ns7.googlevideo.com.",
+ "rr5---sn-q4fl6ns7.gvt1.com.",
"rr5---sn-q4fl6nsd.googlevideo.com.",
"rr5---sn-q4fl6nsk.googlevideo.com.",
"rr5---sn-q4fl6nsl.googlevideo.com.",
"rr5---sn-q4fl6nsl.gvt1.com.",
+ "rr5---sn-q4fl6nsr.googlevideo.com.",
"rr5---sn-q4fl6nss.googlevideo.com.",
"rr5---sn-q4fl6nsy.googlevideo.com.",
"rr5---sn-q4fl6nz6.googlevideo.com.",
- "rr5---sn-q4fl6nz6.gvt1.com.",
"rr5---sn-q4fl6nz7.googlevideo.com.",
"rr5---sn-q4fl6nzy.googlevideo.com.",
- "rr5---sn-q4fl6nzy.gvt1.com.",
+ "rr5---sn-q4flrn7k.googlevideo.com.",
"rr5---sn-q4flrn7r.googlevideo.com.",
- "rr5---sn-q4flrn7r.gvt1.com.",
"rr5---sn-q4flrn7y.googlevideo.com.",
+ "rr5---sn-q4flrn7y.gvt1.com.",
+ "rr5---sn-q4flrne6.googlevideo.com.",
+ "rr5---sn-q4flrne6.gvt1.com.",
"rr5---sn-q4flrne7.googlevideo.com.",
- "rr5---sn-q4flrne7.gvt1.com.",
+ "rr5---sn-q4flrnee.googlevideo.com.",
"rr5---sn-q4flrnek.googlevideo.com.",
- "rr5---sn-q4flrnek.gvt1.com.",
"rr5---sn-q4flrnel.googlevideo.com.",
"rr5---sn-q4flrner.googlevideo.com.",
- "rr5---sn-q4flrner.gvt1.com.",
"rr5---sn-q4flrnes.googlevideo.com.",
"rr5---sn-q4flrney.googlevideo.com.",
- "rr5---sn-q4flrney.gvt1.com.",
"rr5---sn-q4flrnez.googlevideo.com.",
"rr5---sn-q4flrnl6.googlevideo.com.",
"rr5---sn-q4flrnl7.googlevideo.com.",
"rr5---sn-q4flrnld.googlevideo.com.",
+ "rr5---sn-q4flrnld.gvt1.com.",
"rr5---sn-q4flrnle.googlevideo.com.",
"rr5---sn-q4flrnlz.googlevideo.com.",
- "rr5---sn-q4flrnlz.gvt1.com.",
"rr5---sn-q4flrnsd.googlevideo.com.",
"rr5---sn-q4flrnsk.googlevideo.com.",
- "rr5---sn-q4flrnsk.gvt1.com.",
"rr5---sn-q4flrnsl.googlevideo.com.",
"rr5---sn-q4flrnsl.gvt1.com.",
"rr5---sn-q4flrnss.googlevideo.com.",
- "rr5---sn-q4flrnss.gvt1.com.",
"rr5---sn-q4fzen7e.googlevideo.com.",
+ "rr5---sn-q4fzen7e.gvt1.com.",
"rr5---sn-q4fzen7l.googlevideo.com.",
- "rr5---sn-q4fzen7r.googlevideo.com.",
+ "rr5---sn-q4fzen7l.gvt1.com.",
"rr5---sn-q4fzen7s.googlevideo.com.",
- "rr5---sn-q4fzen7s.gvt1.com.",
"rr5---sn-q4fzen7y.googlevideo.com.",
"rr5---sn-q4fzen7y.gvt1.com.",
"rr5---sn-q4fzene7.googlevideo.com.",
- "rr5---sn-q4fzene7.gvt1.com.",
"rr5---sn-q4fzenee.googlevideo.com.",
"rr5---sn-qjp5q5-55.googlevideo.com.",
"rr5---sn-qxo7rn7k.googlevideo.com.",
"rr5---sn-qxo7rn7r.googlevideo.com.",
"rr5---sn-qxo7rn7y.googlevideo.com.",
"rr5---sn-qxoedn7k.googlevideo.com.",
+ "rr5---sn-qxoedne7.googlevideo.com.",
"rr5---sn-qxoednee.googlevideo.com.",
- "rr5---sn-u1hp55-5c.googlevideo.com.",
- "rr5---sn-u1hp55-5c.gvt1.com.",
"rr5---sn-vgqskn66.googlevideo.com.",
"rr5---sn-vgqskn67.googlevideo.com.",
"rr5---sn-vgqskn6d.googlevideo.com.",
"rr5---sn-vgqskn6s.googlevideo.com.",
"rr5---sn-vgqskn6z.googlevideo.com.",
- "rr5---sn-vgqskn6z.gvt1.com.",
"rr5---sn-vgqskne6.googlevideo.com.",
- "rr5---sn-vgqskne6.gvt1.com.",
"rr5---sn-vgqskned.googlevideo.com.",
"rr5---sn-vgqsknek.googlevideo.com.",
"rr5---sn-vgqsknes.googlevideo.com.",
"rr5---sn-vgqsknez.googlevideo.com.",
"rr5---sn-vgqsknld.googlevideo.com.",
- "rr5---sn-vgqsknld.gvt1.com.",
"rr5---sn-vgqsknlk.googlevideo.com.",
"rr5---sn-vgqsknll.googlevideo.com.",
"rr5---sn-vgqsknlr.googlevideo.com.",
- "rr5---sn-vgqsknlr.gvt1.com.",
"rr5---sn-vgqsknls.googlevideo.com.",
"rr5---sn-vgqsknly.googlevideo.com.",
- "rr5---sn-vgqsknly.gvt1.com.",
"rr5---sn-vgqsknlz.googlevideo.com.",
"rr5---sn-vgqskns7.googlevideo.com.",
"rr5---sn-vgqsknse.googlevideo.com.",
@@ -6523,16 +6205,13 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-vgqsknz6.googlevideo.com.",
"rr5---sn-vgqsknz7.googlevideo.com.",
"rr5---sn-vgqsknzd.googlevideo.com.",
- "rr5---sn-vgqsknzd.gvt1.com.",
+ "rr5---sn-vgqsknze.googlevideo.com.",
"rr5---sn-vgqsknzk.googlevideo.com.",
"rr5---sn-vgqsknzl.googlevideo.com.",
"rr5---sn-vgqsknzr.googlevideo.com.",
- "rr5---sn-vgqsknzr.gvt1.com.",
"rr5---sn-vgqsknzs.googlevideo.com.",
"rr5---sn-vgqsknzy.googlevideo.com.",
- "rr5---sn-vgqsknzy.gvt1.com.",
"rr5---sn-vgqsknzz.googlevideo.com.",
- "rr5---sn-vgqsknzz.gvt1.com.",
"rr5---sn-vgqsrn66.googlevideo.com.",
"rr5---sn-vgqsrn67.googlevideo.com.",
"rr5---sn-vgqsrn6e.googlevideo.com.",
@@ -6544,126 +6223,127 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-vgqsrnes.googlevideo.com.",
"rr5---sn-vgqsrnez.googlevideo.com.",
"rr5---sn-vgqsrnl6.googlevideo.com.",
- "rr5---sn-vgqsrnl6.gvt1.com.",
"rr5---sn-vgqsrnld.googlevideo.com.",
- "rr5---sn-vgqsrnld.gvt1.com.",
"rr5---sn-vgqsrnlk.googlevideo.com.",
"rr5---sn-vgqsrnll.googlevideo.com.",
"rr5---sn-vgqsrnls.googlevideo.com.",
"rr5---sn-vgqsrnlz.googlevideo.com.",
"rr5---sn-vgqsrns6.googlevideo.com.",
- "rr5---sn-vgqsrnsd.googlevideo.com.",
"rr5---sn-vgqsrnsr.googlevideo.com.",
"rr5---sn-vgqsrnsy.googlevideo.com.",
"rr5---sn-vgqsrnz6.googlevideo.com.",
"rr5---sn-vgqsrnz7.googlevideo.com.",
"rr5---sn-vgqsrnzd.googlevideo.com.",
- "rr5---sn-vgqsrnzd.gvt1.com.",
"rr5---sn-vgqsrnzk.googlevideo.com.",
"rr5---sn-vgqsrnzr.googlevideo.com.",
"rr5---sn-vgqsrnzs.googlevideo.com.",
"rr5---sn-vgqsrnzy.googlevideo.com.",
"rr5---sn-vgqsrnzz.googlevideo.com.",
- "rr5.sn-4g5ednsy.googlevideo.com.",
- "rr5.sn-hgn7rn7r.googlevideo.com.",
+ "rr5.sn-hp57knds.googlevideo.com.",
+ "rr5.sn-p5qs7nsk.googlevideo.com.",
"rr5.sn-q4fl6n66.googlevideo.com.",
- "rr5.sn-q4fl6nde.googlevideo.com.",
+ "rr5.sn-q4fl6n6d.googlevideo.com.",
"rr5.sn-q4fl6ndl.googlevideo.com.",
- "rr5.sn-q4fl6nsk.googlevideo.com.",
- "rr5.sn-q4flrnss.googlevideo.com.",
- "rr5.sn-q4fzen7s.googlevideo.com.",
+ "rr5.sn-q4flrne6.googlevideo.com.",
+ "rr5.sn-q4flrnl7.googlevideo.com.",
+ "rr5.sn-q4flrnlz.googlevideo.com.",
+ "rr5.sn-q4flrnsd.googlevideo.com.",
+ "rr5.sn-q4fzen7l.googlevideo.com.",
+ "rr5.sn-q4fzenee.googlevideo.com.",
+ "rr5.sn-vgqsrnzz.googlevideo.com.",
+ "rr6---sn-2aqu-hoas7.googlevideo.com.",
+ "rr6---sn-5abxgpxuxaxjvh-9n4e.googlevideo.com.",
+ "rr6---sn-5abxgpxuxaxjvh-9n4l.googlevideo.com.",
+ "rr6---sn-5abxgpxuxaxjvh-j1az.googlevideo.com.",
"rr6---sn-5pgnugx5h-hn2z.googlevideo.com.",
- "rr6---sn-8qj-nbo66.googlevideo.com.",
"rr6---sn-8xgp1vo-ab56.googlevideo.com.",
"rr6---sn-8xgp1vo-ab5d.googlevideo.com.",
"rr6---sn-8xgp1vo-ab5e.googlevideo.com.",
"rr6---sn-8xgp1vo-ab5l.googlevideo.com.",
"rr6---sn-8xgp1vo-ab5s.googlevideo.com.",
"rr6---sn-8xgp1vo-ab5z.googlevideo.com.",
- "rr6---sn-8xgp1vo-p5ie.googlevideo.com.",
+ "rr6---sn-8xgp1vo-p5qe.googlevideo.com.",
+ "rr6---sn-8xgp1vo-p5qe7.googlevideo.com.",
+ "rr6---sn-8xgp1vo-p5qee.googlevideo.com.",
+ "rr6---sn-8xgp1vo-p5ql.googlevideo.com.",
"rr6---sn-8xgp1vo-vgqe.googlevideo.com.",
"rr6---sn-8xgp1vo-xfge.googlevideo.com.",
"rr6---sn-8xgp1vo-xfgl.googlevideo.com.",
"rr6---sn-8xgp1vo-xfgs.googlevideo.com.",
- "rr6---sn-bg5oqxjvh-50nz.googlevideo.com.",
+ "rr6---sn-bvvbaxivnuxqjvhj5nu-hp5e.googlevideo.com.",
+ "rr6---sn-bvvbaxivnuxqjvhj5nu-hp5l.googlevideo.com.",
+ "rr6---sn-bvvbaxivnuxqjvhj5nu-hp5s.googlevideo.com.",
"rr6---sn-jn2pgx4pcxg-w5os.googlevideo.com.",
"rr6---sn-jvhj5nu-2iae.googlevideo.com.",
- "rr6---sn-jvhj5nu-2ial.googlevideo.com.",
- "rr6---sn-jvhj5nu-2ias.googlevideo.com.",
- "rr6---sn-jvhj5nu-nh4e.googlevideo.com.",
- "rr6---sn-jvhj5nu-nh4s.googlevideo.com.",
"rr6---sn-jvhj5nu-qufe.googlevideo.com.",
- "rr6---sn-jvhj5nu-qufl.googlevideo.com.",
- "rr6---sn-jvhj5nu-qufs.googlevideo.com.",
- "rr6---sn-jvhj5nu-qufz.googlevideo.com.",
"rr6---sn-jxopj-n5oe.googlevideo.com.",
"rr6---sn-jxopj-nh4e.googlevideo.com.",
"rr6---sn-jxopj-nh4e.gvt1.com.",
- "rr7---sn-8qj-nbo66.googlevideo.com.",
+ "rr7---sn-2aqu-hoas7.googlevideo.com.",
+ "rr7---sn-2aqu-hoasz.googlevideo.com.",
+ "rr7---sn-5abxgpxuxaxjvh-9n4e.googlevideo.com.",
+ "rr7---sn-5abxgpxuxaxjvh-9n4l.googlevideo.com.",
"rr7---sn-8xgp1vo-ab56.googlevideo.com.",
"rr7---sn-8xgp1vo-ab5d.googlevideo.com.",
"rr7---sn-8xgp1vo-ab5e.googlevideo.com.",
"rr7---sn-8xgp1vo-ab5l.googlevideo.com.",
"rr7---sn-8xgp1vo-ab5s.googlevideo.com.",
"rr7---sn-8xgp1vo-ab5z.googlevideo.com.",
+ "rr7---sn-8xgp1vo-p5qee.googlevideo.com.",
+ "rr7---sn-8xgp1vo-p5qs.googlevideo.com.",
"rr7---sn-8xgp1vo-vgqe.googlevideo.com.",
"rr7---sn-8xgp1vo-xfge.googlevideo.com.",
- "rr7---sn-jvhj5nu-nh4e.googlevideo.com.",
- "rr7---sn-jvhj5nu-nh4s.googlevideo.com.",
- "rr7---sn-jvhj5nu-qufe.googlevideo.com.",
- "rr7---sn-jvhj5nu-qufl.googlevideo.com.",
- "rr7---sn-jvhj5nu-qufs.googlevideo.com.",
- "rr7---sn-jvhj5nu-qufz.googlevideo.com.",
"rr7---sn-jxopj-n5oe.googlevideo.com.",
+ "rr8---sn-2aqu-hoas7.googlevideo.com.",
+ "rr8---sn-5abxgpxuxaxjvh-9n4l.googlevideo.com.",
"rr8---sn-8xgp1vo-ab56.googlevideo.com.",
"rr8---sn-8xgp1vo-ab5d.googlevideo.com.",
"rr8---sn-8xgp1vo-ab5e.googlevideo.com.",
"rr8---sn-8xgp1vo-ab5l.googlevideo.com.",
"rr8---sn-8xgp1vo-ab5z.googlevideo.com.",
- "rr8---sn-8xgp1vo-p5ie.googlevideo.com.",
+ "rr8---sn-8xgp1vo-p5qe.googlevideo.com.",
+ "rr8---sn-8xgp1vo-p5qee.googlevideo.com.",
+ "rr8---sn-8xgp1vo-p5ql.googlevideo.com.",
+ "rr8---sn-8xgp1vo-p5qs.googlevideo.com.",
+ "rr8---sn-8xgp1vo-p5qy.googlevideo.com.",
"rr8---sn-8xgp1vo-vgqe.googlevideo.com.",
"rr8---sn-8xgp1vo-xfge.googlevideo.com.",
"rr8---sn-8xgp1vo-xfgl.googlevideo.com.",
+ "rr9---sn-8xgp1vo-ab56.googlevideo.com.",
+ "rr9---sn-8xgp1vo-ab5d.googlevideo.com.",
+ "rr9---sn-8xgp1vo-p5qee.googlevideo.com.",
"rs-stripe.alm.com.",
- "rs1.qq.com.",
- "rs2.qq.com.",
"rsmjournals.com.",
"rsx.afterpay.com.",
- "rt.applvn.com.",
"rt.teramind.co.",
- "rtb-apac.appocean.media.",
- "rtb-apac.rtbserve.io.",
- "rtb-eu.rtbserve.io.",
- "rtb-useast-v4.qortex.ai.",
+ "rtb-eu-v4.prertbdir.com.",
"rtb-useast.creativedot.net.",
+ "rtb-useast.openrtb.in.",
"rtb-useast.rtbserve.io.",
- "rtb-uswest-v4.qortex.ai.",
- "rtb2-apac.xaprio.net.",
- "rtb2-eu.xaprio.net.",
+ "rtb-uswest-v4.prertbdir.com.",
"rtb2-useast.xaprio.net.",
"rtbsuperhub.com.",
"rtbwave.com.",
"rttf.citrix.com.",
+ "ru-comonrt-stsdk.vivoglobal.com.",
"ru.global.market.xiaomi.com.",
+ "rubiconmd.zoom.us.",
"rubyfish.cn.",
- "rudder.immivision.net.",
"ruijienetworks.com.",
"rule34video.com.",
- "rum.perfops.cdb.cdn.orange.com.",
- "rumt-sg.com.",
"rus-mqtt.transsion-os.com.",
"rutgersconnect-my.sharepoint.com.",
"rutubelist.ru.",
- "rxsafeway-my.sharepoint.com.",
- "s-a.innovid.com.",
"s-cdn.anthropic.com.",
"s-cs.send.microad.jp.",
- "s-pinimg-com-cdn-cloudflare-net.pinimg.com.",
"s.deepl.com.",
"s.seedtag.com.",
+ "s1.listrakbi.com.cdn.cloudflare.net.",
+ "s1001-f104.mp.lura.live.",
+ "s1002-f107.mp.lura.live.",
"s2-a.time.mci1.us.rozint.net.",
"s2-b.time.mci1.us.rozint.net.",
- "s9.gifyu.com.",
+ "s3-design-language-system.cdn4dd.com.",
"sa1.chat.si.riotgames.com.",
"sa2.chat.si.riotgames.com.",
"sa3.chat.si.riotgames.com.",
@@ -6671,10 +6351,13 @@ var FakeECSFQDNs = container.NewMapSet(
"sach.gopsahome.info.",
"saintasaph.remotepc.com.",
"sales.ai.dynamics.com.",
- "salidzini.lv.",
+ "salesbridge.sharepoint.com.",
+ "salesfire.co.uk.",
"saltlakecity.remotepc.com.",
+ "samip.genetec.com.",
"samsclub.quantummetric.com.",
"sanantonio.remotepc.com.",
+ "sandbox.wdesk.com.",
"sandboxclient.retinavue.net.",
"sandboxregister.retinavue.net.",
"sandiego.remotepc.com.",
@@ -6687,12 +6370,10 @@ var FakeECSFQDNs = container.NewMapSet(
"saopaulo1.remotepc.com.",
"sat02pap001.storage.live.com.",
"sat02pap002.storage.live.com.",
- "sat02pap003.storage.live.com.",
- "sat02pap004.storage.live.com.",
+ "sav.cynet.com.",
"sc.zoom.us.",
"schneidercorp.com.",
"sciener.cn.",
- "scm.haplat.net.",
"scontent-ams2-1.cdninstagram.com.",
"scontent-ams2-1.xx.fbcdn.net.",
"scontent-ams4-1.cdninstagram.com.",
@@ -6706,6 +6387,7 @@ var FakeECSFQDNs = container.NewMapSet(
"scontent-atl3-3.cdninstagram.com.",
"scontent-atl3-3.xx.fbcdn.net.",
"scontent-ber1-1.cdninstagram.com.",
+ "scontent-ber1-1.xx.fbcdn.net.",
"scontent-bkk1-1.xx.fbcdn.net.",
"scontent-bkk1-2.xx.fbcdn.net.",
"scontent-bog2-1.cdninstagram.com.",
@@ -6730,6 +6412,8 @@ var FakeECSFQDNs = container.NewMapSet(
"scontent-cgk2-1.xx.fbcdn.net.",
"scontent-den2-1.cdninstagram.com.",
"scontent-den2-1.xx.fbcdn.net.",
+ "scontent-det1-1.cdninstagram.com.",
+ "scontent-det1-1.xx.fbcdn.net.",
"scontent-dfw5-1.cdninstagram.com.",
"scontent-dfw5-1.xx.fbcdn.net.",
"scontent-dfw5-2.cdninstagram.com.",
@@ -6745,8 +6429,8 @@ var FakeECSFQDNs = container.NewMapSet(
"scontent-fra5-1.xx.fbcdn.net.",
"scontent-fra5-2.cdninstagram.com.",
"scontent-fra5-2.xx.fbcdn.net.",
+ "scontent-gmp1-1.cdninstagram.com.",
"scontent-gru1-1.cdninstagram.com.",
- "scontent-gru1-1.xx.fbcdn.net.",
"scontent-gru1-2.cdninstagram.com.",
"scontent-gru1-2.xx.fbcdn.net.",
"scontent-gru2-1.cdninstagram.com.",
@@ -6789,15 +6473,21 @@ var FakeECSFQDNs = container.NewMapSet(
"scontent-lhr8-1.xx.fbcdn.net.",
"scontent-lhr8-2.cdninstagram.com.",
"scontent-lhr8-2.xx.fbcdn.net.",
- "scontent-los2-1.xx.fbcdn.net.",
"scontent-man2-1.cdninstagram.com.",
"scontent-man2-1.xx.fbcdn.net.",
"scontent-mia3-1.cdninstagram.com.",
"scontent-mia3-1.xx.fbcdn.net.",
"scontent-mia3-2.cdninstagram.com.",
"scontent-mia3-2.xx.fbcdn.net.",
+ "scontent-mia3-3.cdninstagram.com.",
+ "scontent-mia3-3.xx.fbcdn.net.",
"scontent-mnl1-1.xx.fbcdn.net.",
"scontent-mnl1-2.xx.fbcdn.net.",
+ "scontent-mnl3-1.xx.fbcdn.net.",
+ "scontent-mnl3-2.xx.fbcdn.net.",
+ "scontent-mrs2-1.xx.fbcdn.net.",
+ "scontent-mrs2-2.xx.fbcdn.net.",
+ "scontent-mrs2-3.xx.fbcdn.net.",
"scontent-msp1-1.cdninstagram.com.",
"scontent-msp1-1.xx.fbcdn.net.",
"scontent-mty2-1.cdninstagram.com.",
@@ -6835,7 +6525,6 @@ var FakeECSFQDNs = container.NewMapSet(
"scontent-sjc3-1.cdninstagram.com.",
"scontent-sjc3-1.xx.fbcdn.net.",
"scontent-ssn1-1.cdninstagram.com.",
- "scontent-tpe1-1.xx.fbcdn.net.",
"scontent-vie1-1.cdninstagram.com.",
"scontent-vie1-1.xx.fbcdn.net.",
"scontent-waw2-1.cdninstagram.com.",
@@ -6844,34 +6533,30 @@ var FakeECSFQDNs = container.NewMapSet(
"scontent-waw2-2.xx.fbcdn.net.",
"scontent-yyz1-1.cdninstagram.com.",
"scontent-yyz1-1.xx.fbcdn.net.",
- "scouccl1.haplat.net.",
- "scouccl2.haplat.net.",
- "scout-cdn.salesloft.com.",
"scraper2.onlineradiobox.com.",
- "scripps.okta.com.",
- "script.crazyegg.com.cdn.cloudflare.net.",
- "sdataprod.ncaa.com.",
"sdk-api-us.maticooads.com.",
- "sdk-cdn.onlineaccess1.com.",
"sdk.beizi.biz.",
"sdk.qcloud.com.",
"sdkgate.pushv3.easebar.com.",
- "sdktmp.hubcloud.com.cn.",
- "seabroadnet.com.",
"seagate.com.",
"seagullscientific.com.",
+ "seal-blue.bbb.org.",
+ "seal-chicago.bbb.org.",
+ "seal-dallas.bbb.org.",
"sealsubscriptions.com.",
"search.dnssearch.org.",
"search.namequery.com.",
+ "search.sunbiz.org.",
"search.us.namequery.com.",
"searchserverapi.com.",
"seattle.remotepc.com.",
"secaucus.remotepc.com.",
"secure.accurint.com.",
- "secure.internetdownloadmanager.com.",
+ "secure.paymentech.com.",
"secure.syndetics.com.",
+ "secure5.arcot.com.",
"securelink.med.usc.edu.",
- "secureprivacy.ai.",
+ "securemail.empower.com.",
"securetheorem.com.",
"securityapi.d3-pr-tm.com.",
"seedtag.com.",
@@ -6883,81 +6568,67 @@ var FakeECSFQDNs = container.NewMapSet(
"sentry.appodeal.com.",
"sentry.archive.org.",
"sentry.radyushin.com.",
+ "sentry.voicemod.net.",
"sentry.wmt.dev.",
"sentry.wrike.com.",
- "seo.apps.avada.io.",
- "serraview.com.",
+ "sephora-live.inside-graph.com.",
"served-by.pixfuture.com.",
- "servedby.flashtalking.com-v1.edgekey.net.",
- "service.maxymiser.net.",
- "service.wemass.com.",
+ "server-time.sybo-aps.workers.dev.",
"service2.ultipro.com.",
- "servicebus102.myconnectsecure.com.",
- "servicebus1021.myconnectsecure.com.",
- "servicebus1022.myconnectsecure.com.",
- "servicebus1023.myconnectsecure.com.",
- "servicebus1024.myconnectsecure.com.",
- "servicebus1041.myconnectsecure.com.",
- "servicebus1042.myconnectsecure.com.",
- "servicebus1043.myconnectsecure.com.",
- "servicebus1044.myconnectsecure.com.",
- "servicebus1051.myconnectsecure.com.",
- "servicebus1052.myconnectsecure.com.",
- "servicebus1053.myconnectsecure.com.",
- "servicebus1054.myconnectsecure.com.",
- "services.docusign.net.",
+ "services-us8.wfs.cloud.",
"services.lego.com.",
"servicetitan.com.",
"servx.opamarketplace.com.",
"servx.playstream.media.",
"settings.luckyorange.com.",
"sevenrooms.com.",
+ "sewjn80htn-3.algolianet.com.",
+ "sex4tokens.com.",
"sg-o-s3.smartcloudcon.com.",
"sg.api.translator.voice.gcloudsdk.com.",
- "sg.business.smartcamera.api.io.mi.com.",
"sg1a-excel-collab.officeapps.live.com.",
"sg1a-powerpoint-collab.officeapps.live.com.",
"sgfp.tongdun.net.",
"sgmbocast.com.",
"sgpcas.ezvizlife.com.",
+ "share.connect.aig.",
+ "share.fcgame.net.",
"sharpschool.com.",
- "shc6.y.qq.com.",
"shopcircle.co.",
"shopify-gtm-suite.getelevar.com.",
- "shopifynetwork.com.",
"shortpixel.ai.",
- "shp.ee.",
- "shredder-eu.osi.office.net.",
"shrinetheme.com.",
"shuzilm.cn.",
+ "sigma-qdata-h72.proxima.nie.netease.com.",
+ "signin.stampsendicia.com.",
"signin.ultipro.com.",
- "sina.com.",
"sinaimg.cn.",
"sip-backup.phonepower.com.",
"sip-primary.phonepower.com.",
"sip.ringcentral.com.",
- "sip112-1121.ringcentral.com.",
- "sip112-1131.ringcentral.com.",
- "sip112-1141.ringcentral.com.",
- "sip113-1121.ringcentral.com.",
- "sip113-1131.ringcentral.com.",
- "sip113-1141.ringcentral.com.",
"sip121-1111.ringcentral.com.",
"sip121-1121.ringcentral.com.",
"sip121-1131.ringcentral.com.",
"sip121-1141.ringcentral.com.",
- "sip121-1221.ringcentral.com.",
+ "sip121-1231.ringcentral.com.",
+ "sip121-1241.ringcentral.com.",
+ "sip123-1111.ringcentral.com.",
"sip123-1121.ringcentral.com.",
"sip123-1131.ringcentral.com.",
"sip123-1141.ringcentral.com.",
"sip123-1211.ringcentral.com.",
"sip123-1221.ringcentral.com.",
+ "sip131-1111.ringcentral.com.",
"sip131-1121.ringcentral.com.",
"sip131-1131.ringcentral.com.",
"sip131-1141.ringcentral.com.",
- "sip131-1241.ringcentral.com.",
+ "sip131-1211.ringcentral.com.",
+ "sip131-1221.ringcentral.com.",
"sip132-1121.ringcentral.com.",
+ "sip132-1131.ringcentral.com.",
"sip132-1141.ringcentral.com.",
+ "sip132-1221.ringcentral.com.",
+ "sip132-1231.ringcentral.com.",
"sip421-121.ringcentral.biz.",
"site-config.com.",
"sjc.zoom.us.",
@@ -6974,9 +6645,20 @@ var FakeECSFQDNs = container.NewMapSet(
"skyward-ocprod.iscorp.com.",
"skyward.iscorp.com.",
"skyward10.iscorp.com.",
+ "slb-p2p.vcloud.ks-live.com.",
+ "slickdealscdn.com.",
+ "slot365ku.info.",
"sm1.selectmedia.asia.",
"smartcloudcon.com.",
"smarthome.ctdevice.ott4china.com.",
+ "smoot-api-safari-ause1a.v.aaplimg.com.",
+ "smoot-api-safari-ause1b.v.aaplimg.com.",
+ "smoot-api-safari-ause1c.v.aaplimg.com.",
+ "smoot-api-safari-ause2a.v.aaplimg.com.",
+ "smoot-api-safari-ause2b.v.aaplimg.com.",
+ "smoot-api-safari-ause2c.v.aaplimg.com.",
+ "smoot-api-safari-ausw2b.v.aaplimg.com.",
+ "smoot-api-safari-ausw2c.v.aaplimg.com.",
"smoot-searchv2-aapse1c.v.aaplimg.com.",
"smoot-searchv2-aeun1a.v.aaplimg.com.",
"smoot-searchv2-aeun1b.v.aaplimg.com.",
@@ -6994,20 +6676,15 @@ var FakeECSFQDNs = container.NewMapSet(
"sms.ads.heytapmobi.com.",
"snap-storage-cdn.l.google.com.",
"snippet.affilimatejs.com.",
- "snippet.tldw.me.",
- "snokido.com.",
- "sns-avatar-qc.xhscdn.com.cdn.cloudflare.net.",
"snz04pap001.storage.live.com.",
"so1506.ci.managedwhitelisting.com.",
"sobot.com.",
"socialchain.app.",
"sockets.stackexchange.com.",
"sofia.remotepc.com.",
- "sogoucdn.com.",
"sohu.com.",
- "solarmanpv.com.",
"solid.preyproject.com.",
- "somplo.com.",
+ "someaitech.com.",
"sonar-akl1-1.xx.fbcdn.net.",
"sonar-ams2-1.xx.fbcdn.net.",
"sonar-ams4-1.xx.fbcdn.net.",
@@ -7019,13 +6696,16 @@ var FakeECSFQDNs = container.NewMapSet(
"sonar-ber1-1.xx.fbcdn.net.",
"sonar-bkk1-1.xx.fbcdn.net.",
"sonar-bkk1-2.xx.fbcdn.net.",
+ "sonar-blr1-1.xx.fbcdn.net.",
+ "sonar-blr1-2.xx.fbcdn.net.",
"sonar-bog2-1.xx.fbcdn.net.",
"sonar-bog2-2.xx.fbcdn.net.",
"sonar-bos5-1.xx.fbcdn.net.",
"sonar-bru2-1.xx.fbcdn.net.",
"sonar-bsb1-1.xx.fbcdn.net.",
- "sonar-ccu1-1.xx.fbcdn.net.",
"sonar-ccu1-2.xx.fbcdn.net.",
+ "sonar-ccu2-1.xx.fbcdn.net.",
+ "sonar-ccu2-2.xx.fbcdn.net.",
"sonar-cdg4-1.xx.fbcdn.net.",
"sonar-cdg4-2.xx.fbcdn.net.",
"sonar-cdg4-3.xx.fbcdn.net.",
@@ -7035,6 +6715,7 @@ var FakeECSFQDNs = container.NewMapSet(
"sonar-cph2-1.xx.fbcdn.net.",
"sonar-cpt1-1.xx.fbcdn.net.",
"sonar-den2-1.xx.fbcdn.net.",
+ "sonar-det1-1.xx.fbcdn.net.",
"sonar-dfw5-1.xx.fbcdn.net.",
"sonar-dfw5-2.xx.fbcdn.net.",
"sonar-doh1-1.xx.fbcdn.net.",
@@ -7100,8 +6781,11 @@ var FakeECSFQDNs = container.NewMapSet(
"sonar-mct1-2.xx.fbcdn.net.",
"sonar-mia3-1.xx.fbcdn.net.",
"sonar-mia3-2.xx.fbcdn.net.",
+ "sonar-mia3-3.xx.fbcdn.net.",
"sonar-mnl1-1.xx.fbcdn.net.",
"sonar-mnl1-2.xx.fbcdn.net.",
+ "sonar-mnl3-1.xx.fbcdn.net.",
+ "sonar-mnl3-2.xx.fbcdn.net.",
"sonar-mrs2-1.xx.fbcdn.net.",
"sonar-mrs2-2.xx.fbcdn.net.",
"sonar-mrs2-3.xx.fbcdn.net.",
@@ -7120,6 +6804,7 @@ var FakeECSFQDNs = container.NewMapSet(
"sonar-pmo1-1.xx.fbcdn.net.",
"sonar-pnq1-1.xx.fbcdn.net.",
"sonar-pnq1-2.xx.fbcdn.net.",
+ "sonar-poa1-1.xx.fbcdn.net.",
"sonar-prg1-1.xx.fbcdn.net.",
"sonar-qro1-1.xx.fbcdn.net.",
"sonar-qro1-2.xx.fbcdn.net.",
@@ -7138,10 +6823,8 @@ var FakeECSFQDNs = container.NewMapSet(
"sonar-sof1-2.xx.fbcdn.net.",
"sonar-ssn1-1.xx.fbcdn.net.",
"sonar-syd2-1.xx.fbcdn.net.",
- "sonar-tir3-1.xx.fbcdn.net.",
"sonar-tir3-2.xx.fbcdn.net.",
"sonar-tir3-3.xx.fbcdn.net.",
- "sonar-tir3-4.xx.fbcdn.net.",
"sonar-tpe1-1.xx.fbcdn.net.",
"sonar-vie1-1.xx.fbcdn.net.",
"sonar-waw2-1.xx.fbcdn.net.",
@@ -7149,36 +6832,57 @@ var FakeECSFQDNs = container.NewMapSet(
"sonar-yyz1-1.xx.fbcdn.net.",
"sonar-zrh1-1.xx.fbcdn.net.",
"sonar.viously.com.",
+ "sonichealthcareusa.com.",
"southafricanorth.api.cognitive.microsoft.com.",
"southcarolina.remotepc.com.",
"southeastasia.api.cognitive.microsoft.com.",
"southindia.api.cognitive.microsoft.com.",
"spadsync.com.",
"sparteo.com.",
+ "special-network.com.",
"speedtest.us-sc.kamatera.com.",
+ "spifc.ssl.fun.",
"spiny.ai.",
"spion.savvy.security.",
- "sq.bls.mdt.qq.com.",
"src.ebay-us.com.",
+ "srv-sg10.traffmonetizer.com.",
+ "srv-sg11.traffmonetizer.com.",
+ "srv-sg12.traffmonetizer.com.",
+ "srv-sg13.traffmonetizer.com.",
+ "srv-sg14.traffmonetizer.com.",
+ "srv-sg7.traffmonetizer.com.",
+ "srv-sg8.traffmonetizer.com.",
+ "srv-sg9.traffmonetizer.com.",
+ "srv.stalker.to.",
"srv2.maxhost.io.",
+ "srv2.traffmonetizer.com.",
+ "srv3.traffmonetizer.com.",
+ "srv4.traffmonetizer.com.",
+ "srv5.traffmonetizer.com.",
+ "srv6.traffmonetizer.com.",
+ "srv7.traffmonetizer.com.",
+ "srv8.traffmonetizer.com.",
+ "srv9.traffmonetizer.com.",
"ssafp.samsclub.com.",
"ssctech.com.",
+ "sse.devcycle.com.",
"ssl.geoplugin.net.",
+ "sso-forms-prod.cdn-tinkoff.ru.",
"sso-stb.jdadelivers.com.",
"sso.services.box.net.",
"ssp.hbrd.io.",
"ssp.hybrid.ai.",
- "sst.puma.com.",
"sstatic.net.",
- "ssxd.mediav.com.",
"st-sysupgrade.vivo.com.cn.",
"stable.dl2.discordapp.net.",
"staffbase.com.",
+ "staples-us.attn.tv.",
+ "stappupgrade.vivo.com.cn.",
"starbucks-wfmr.jdadelivers.com.",
+ "starbucks-wfmrclock.jdadelivers.com.",
"stardustgod.com.",
"starrydyn.com.",
"startssl.com.",
- "stash.webstaurantstore.com.",
"stat.pdfforge.org.",
"statad.ru.",
"static-atl3-1.xx.fbcdn.net.",
@@ -7186,6 +6890,7 @@ var FakeECSFQDNs = container.NewMapSet(
"static-atl3-3.xx.fbcdn.net.",
"static-bos5-1.xx.fbcdn.net.",
"static-den2-1.xx.fbcdn.net.",
+ "static-det1-1.xx.fbcdn.net.",
"static-dfw5-1.xx.fbcdn.net.",
"static-dfw5-2.xx.fbcdn.net.",
"static-hou1-1.xx.fbcdn.net.",
@@ -7195,49 +6900,43 @@ var FakeECSFQDNs = container.NewMapSet(
"static-lax3-2.xx.fbcdn.net.",
"static-lga3-1.xx.fbcdn.net.",
"static-lga3-2.xx.fbcdn.net.",
- "static-lga3-3.xx.fbcdn.net.",
- "static-lhr6-1.xx.fbcdn.net.",
- "static-lhr6-2.xx.fbcdn.net.",
"static-lhr8-1.xx.fbcdn.net.",
"static-lhr8-2.xx.fbcdn.net.",
"static-mia3-1.xx.fbcdn.net.",
"static-mia3-2.xx.fbcdn.net.",
+ "static-mia3-3.xx.fbcdn.net.",
"static-msp1-1.xx.fbcdn.net.",
- "static-na3p1.sabacloud.com.",
"static-ord5-1.xx.fbcdn.net.",
"static-ord5-2.xx.fbcdn.net.",
"static-ord5-3.xx.fbcdn.net.",
- "static-phx1-1.xx.fbcdn.net.",
"static-sea1-1.xx.fbcdn.net.",
"static-sjc3-1.xx.fbcdn.net.",
"static.avito.ru.",
+ "static.ctctcdn.com.",
+ "static.getclicky.com.",
"static.getliner.com.",
- "static.kwcdn.com.",
- "static2.mixi.media.",
- "static6.mixi.media.",
- "static8.mixi.media.",
+ "static.olark.com.",
+ "static.rutubelist.ru.",
+ "static4.mixi.media.",
"stats.aeries.com.",
- "stats.rip.",
+ "stats.senty.com.au.",
"stats.transitapp.com.",
"statsig.anthropic.com.",
- "stevemadden.com.",
+ "stemchristie.rome2rio.com.",
"stg-data-in.ads.heytapmobile.com.",
"stg-data.ads.heytapmobi.com.",
"stockholm.remotepc.com.",
"stocks-analytics-events.apple.com.",
- "storage.procore.com.",
- "store.qq.com.",
"store.vsco.co.",
- "storeedgefd.dsx.mp.microsoft.com.",
- "storymagic.co.",
- "streamhub.tech.",
+ "stp-cdn.inside-graph.com.",
+ "str-vcode-tracker-fenghuang-prd-bj.vivo.com.cn.",
"streetviewpixels-pa.googleapis.com.",
- "stripchat.com.",
+ "stripchats.io.",
"stse02.ultipro.com.",
"stsew02.ultipro.com.",
"stsn02.ultipro.com.",
"student.atitesting.com.",
- "studentaid.gov.",
+ "student.xello.world.",
"studyquicks.com.",
"stun.acrobits.cz.",
"stun.cdnbye.com.",
@@ -7249,52 +6948,44 @@ var FakeECSFQDNs = container.NewMapSet(
"stun2.ringcentral.com.",
"stun3.l.google.com.",
"stun4.l.google.com.",
- "stvinc-my.sharepoint.com.",
"su6786.ci.managedwhitelisting.com.",
+ "subsceness.xyz.",
"sumari-prod-1.srv.jbisumari.org.",
"sumologic.com.",
"sunmi.com.",
"supl.qxwz.com.",
"support.powerschool.com.",
- "sv-ookla.geolinks.com.",
"sv8.cyberhaven.io.",
+ "svideo.ltwebstatic.com.",
"svlive.serraview.com.",
"swag.maxhost.io.",
"swedencentral.api.cognitive.microsoft.com.",
"switch.babybus.com.",
"switzerlandnorth.api.cognitive.microsoft.com.",
- "swoop.com.",
"sydney.remotepc.com.",
- "synacor-match.dotomi.com.",
"sync-1-us-west1-g.sync.services.mozilla.com.",
"sync.bidence.net.",
+ "sync.driftpixel.live.",
"sync.inmobi.com.",
"sync.oraki.io.",
"sync.videowalldirect.com.",
- "syncfusion.com.",
"syndetics.com.",
+ "syndication.diveinthebluesky.biz.",
"systemreportservices.genetec.com.",
+ "t-odx.geo2.op-mobile.opera.com.",
"t-odx.op-mobile.opera.com.",
"t.adcell.com.",
- "t.dailymail.co.uk.",
"t.marketingcloudfx.com.",
"t.mookie1.com.",
- "t.poki.io.",
- "t.wayfair.com.cdn.cloudflare.net.",
- "t1.tiles.virtualearth.net.",
- "t13925.iqzonertb.live.",
"t3.xiaohongshu.com.",
- "t98200.iqzonertb.live.",
"tag.winister.app.",
- "tagcommander.com.",
"tags.natwest.com.",
"tampa.remotepc.com.",
+ "tanjingpaas.com.",
"tapecontent.net.",
+ "taplytics-umami.grubhub.com.",
"tapsell.ir.",
- "tara.ns.cloudflare.com.",
"target.digitalaudience.io.",
- "tasks.office.com.",
- "tasteofhome.com.",
"tatracker-us.rivergame.net.",
"tccprod01.honeywell.com.",
"tccprod01.resideo.com.",
@@ -7303,20 +6994,18 @@ var FakeECSFQDNs = container.NewMapSet(
"tccprod03.honeywell.com.",
"tccprod03.resideo.com.",
"tcdnlive.com.",
- "tch.poe.com.",
"tclclouds.com.",
"tdcservices.tandemdiabetes.com.",
"tdm.qq.com.",
- "teadv.checkpoint.com.",
+ "teamsnap.com.",
"teamviewer.com.",
- "teamviewer.com.cdn.cloudflare.net.",
"teddymobile.cn.",
- "telegraph-sync.quantummetric.com.",
"telemetry-sdk-inmobi-comtm.trafficmanager.net.",
"telemetry.savvy.security.",
"telemetry.sdk.inmobi.com.",
"telephony.goog.",
- "teleport.media.",
+ "tenant-content.apm.appfolio-analytics.com.",
+ "tenant-data.apm.appfolio-analytics.com.",
"tencent-cloud.com.",
"tencent-cloud.net.",
"tencent.com.",
@@ -7325,19 +7014,14 @@ var FakeECSFQDNs = container.NewMapSet(
"tenpay.com.",
"terabox.app.",
"terabox.com.",
- "terabox1024.com.",
"terms3.hicloud.com.",
- "test3.dantri.com.vn.",
"tgp.qq.com.",
"tgpa.qq.com.",
- "thanhnien.vn.",
"theoks.net.",
"thetracker.org.",
"thinkific.com.",
"thm.visa.com.",
"thm12.visa.com.",
- "thumb-v1.xhpingcdn.com.",
- "thumb-v4.xhpingcdn.com.",
"time-a-b.nist.gov.",
"time-a-g.nist.gov.",
"time-a.nist.gov.",
@@ -7352,9 +7036,8 @@ var FakeECSFQDNs = container.NewMapSet(
"time-nw.nist.gov.",
"time.circlevps.net.",
"time.ecansol.net.",
- "time.lmtlabs.com.",
"time.nest.com.",
- "time.tritan.host.",
+ "time.va.lmtlabs.com.",
"time.walb.tech.",
"time1.aliyun.com.",
"time1.google.com.",
@@ -7370,7 +7053,6 @@ var FakeECSFQDNs = container.NewMapSet(
"tk.mosspf.com.",
"tk.mossru.com.",
"tkx.mp.lura.live.",
- "tl.upwork.com.",
"tlivecdn.com.",
"tlivesource.com.",
"tls12.eu01.nr-data.net.cdn.cloudflare.net.",
@@ -7379,12 +7061,10 @@ var FakeECSFQDNs = container.NewMapSet(
"tm.bdc-cdn.com.",
"tm.cybersource.com.",
"tm.regions.com.",
- "tmfp.klarna.com.",
"tmfsdktcp.m.qq.com.",
"tmfsdktcpv4.m.qq.com.",
"tmga.qq.com.",
"tmge.alicdn.com.",
- "tmobile-sync.quantummetric.com.",
"tmx.bestbuy.com.",
"tmx.tdbank.com.",
"tmx.uptodate.com.",
@@ -7399,47 +7079,15 @@ var FakeECSFQDNs = container.NewMapSet(
"tpns.sh.tencent.com.",
"tpns.tencent.com.",
"tpsservice-files-inner.cn-hangzhou.oss-cdn.aliyun-inc.com.",
- "tra-ac-ae.apktorrents.com.",
- "tra-ac-ae.best82.com.",
- "tra-ac-ae2.apktorrents.com.",
- "tra-ac-ae2.best82.com.",
- "tra-ac-id.apktorrents.com.",
- "tra-ac-id.best82.com.",
- "tra-ac-id2.apktorrents.com.",
- "tra-ac-id2.best82.com.",
- "tra-ac-ind.apktorrents.com.",
- "tra-ac-ind.best82.com.",
- "tra-ac-mas.apktorrents.com.",
- "tra-ac-mas.best82.com.",
- "tra-ard-id.apktorrents.com.",
- "tra-ard-id.best82.com.",
- "tra-hd-id.apktorrents.com.",
- "tra-hd-id.best82.com.",
- "tra-ht-id.apktorrents.com.",
- "tra-ht-id.best82.com.",
"tra-hz-de.hyper-torrent.com.",
- "tra-hz-fl.hyper-torrent.com.",
"tra-lwb-sg.best61.com.",
- "tra-s4-us.best61.com.",
- "tra-tc-ind.apktorrents.com.",
- "tra-tc-ind.best82.com.",
- "tra-the-br.apktorrents.com.",
- "tra-the-br.best82.com.",
- "tra-the-tr.apktorrents.com.",
- "tra-the-tr.best82.com.",
- "tra-ved-br.apktorrents.com.",
- "tra-ved-br.best82.com.",
- "tra-ved-in.apktorrents.com.",
- "tra-ved-in.best82.com.",
- "tra-ved-ru.apktorrents.com.",
- "tra-ved-ru.best82.com.",
"trace.qq.com.",
"track-eu1.hubspot.com.",
+ "track-fra04-origin.spectrum.hubspot.com.",
+ "track.rtb-sm.com.",
"track.sendlane.com.",
- "track.yourrtb.com.",
"trackedlink.net.",
"tracker-udp.gbitt.info.",
- "tracker.auctor.tv.",
"tracker.best61.com.",
"tracker.files.fm.",
"tracker.grepler.com.",
@@ -7447,25 +7095,20 @@ var FakeECSFQDNs = container.NewMapSet(
"tracker.hyper-torrent.com.",
"tracker.newtvcdn.com.",
"tracker.nitropay.com.",
- "tracker.nwps.ws.",
- "tracker.tamersunion.org.",
"tracker.theoks.net.",
"tracker1.bt.moack.co.kr.",
- "tracker1.myporn.club.",
- "tracker2.dler.org.",
+ "tracking.carbonatixaudio.com.",
"tracking.eu.flamtyr.com.",
- "tracking.ksztone.com.",
+ "tradovateapi.com.",
"tradplusad.com.",
- "traductor1.spanishdict.com.",
"transaccional.saludtotal.com.co.",
"traversal.syncromsp.com.",
"treas.gov.",
"treasury.gov.",
- "trendyol.com.",
"tribalfusion.com.",
- "trovit.com.",
- "ts1.qq.com.",
- "ts2.qq.com.",
+ "trk-tristique.com.",
+ "truemed.com.",
+ "trustedstack.com.",
"tse1.explicit.bing.net.",
"tsms-dre.security.dbankcloud.com.",
"tt.browser.360.cn.",
@@ -7473,33 +7116,34 @@ var FakeECSFQDNs = container.NewMapSet(
"ttuhscep.cyberhaven.io.",
"tubecup.net.",
"tunnel.googlezip.net.",
- "tuoitre.vn.",
"turn.cloudflare.com.",
"tw.ntp.org.cn.",
- "twcloudgz.ucbj.net.",
- "twcloudgz1.ucbj.net.",
- "twvideogz31.ucbj.net.",
+ "twvideogz32.ucbj.net.",
"twvideohf41.ucbj.net.",
"twvideohf42.ucbj.net.",
"twvideohf43.ucbj.net.",
+ "tx-cfg-u1.ubixioe.com.",
"tydevice.com.",
- "typenetwork.com.",
+ "u-ams.4dex.io.",
+ "u-las.4dex.io.",
"u.4dex.io.",
"uaenorth.api.cognitive.microsoft.com.",
- "uam1.dexcom.com.",
- "uapi.mp.360.cn.",
"uber.zoom.us.",
+ "ubg235.com.",
"uc.cn.",
"ucweb.com.",
"udemycdn.com.",
- "ue.lenovomm.cn.",
- "uhabo.com.",
- "uhf.microsoft.com.",
+ "udms.zoom.us.",
+ "udsp.io.",
+ "uix-pusa01.app.blackbaud.net.",
"uk-api.asm.skype.com.",
"uk-prod.asyncgw.teams.microsoft.com.",
"uk3-excel-collab.officeapps.live.com.",
"uk3-powerpoint-collab.officeapps.live.com.",
+ "uk3-word-collab.officeapps.live.com.",
"uk5-excel-collab.officeapps.live.com.",
+ "uk5-powerpoint-collab.officeapps.live.com.",
+ "uk5-word-collab.officeapps.live.com.",
"ukc-collabrtc-geo.rtc.trafficmanager.net.",
"ukc-collabrtc.officeapps.live.com.",
"ukc-excel-collab.officeapps.live.com.",
@@ -7510,27 +7154,25 @@ var FakeECSFQDNs = container.NewMapSet(
"ultipro.com.",
"ultiprotime.com.",
"ultiproworkplace.com.",
- "umami.is.",
+ "ultrahuman.com.",
"umeng.com.",
- "unicom.shuzilm.cn.",
"unified-inbox-1-gw.ultipro.com.",
"unified-inbox-2-gw.ultipro.com.",
"unipay.qq.com.",
"unisoc.com.",
"united.quantummetric.com.",
"unity.cn.",
- "uodoo.com.",
+ "universityofwieauclaire-my.sharepoint.com.",
+ "unls.mep.go.cr.",
"up.railway.app.",
"update.ee-share.com.",
"update.huorong.cn.",
"update.kingsoftstore.com.",
- "update.pdfforge.org.",
"update.vivaldi.com.",
- "update.yealink.com.",
"updatechannel.sharegate.com.",
"updaterservices.genetec.com.",
+ "updates.dorkbox.com.",
"updatesnl.macrium.com.",
- "upload1.systemmonitor.co.uk.",
"upravel.com.",
"upremium.asia.",
"urekamedia.com.",
@@ -7541,8 +7183,6 @@ var FakeECSFQDNs = container.NewMapSet(
"us-05.ws-api.ringcentral.com.",
"us-06.ws-api.ringcentral.com.",
"us-api.asm.skype.com.",
- "us-atl-anx-r003.router.teamviewer.com.",
- "us-atl-anx-r010.router.teamviewer.com.",
"us-central1-adaptive-growth.cloudfunctions.net.",
"us-central1-adchat-ai.cloudfunctions.net.",
"us-central1-addshoppers-data-production.cloudfunctions.net.",
@@ -7557,52 +7197,54 @@ var FakeECSFQDNs = container.NewMapSet(
"us-central1-fsgenergy-shared.cloudfunctions.net.",
"us-central1-gaggle-staging.cloudfunctions.net.",
"us-central1-justbuild-cdb86.cloudfunctions.net.",
- "us-central1-kube-ownlocal.cloudfunctions.net.",
"us-central1-live-prod-1-1.cloudfunctions.net.",
"us-central1-locket-4252a.cloudfunctions.net.",
- "us-central1-mikmak-microservices.cloudfunctions.net.",
+ "us-central1-marketplace-production-east4.cloudfunctions.net.",
"us-central1-muslim-pro-app.cloudfunctions.net.",
- "us-central1-noteit-4dca3.cloudfunctions.net.",
"us-central1-royal-match-prod-cce6d.cloudfunctions.net.",
"us-central1-shopify-instrumentat-ff788286.cloudfunctions.net.",
+ "us-central1-speechifydev.cloudfunctions.net.",
"us-central1-speechifymobile.cloudfunctions.net.",
+ "us-central1-sq-sgtm-prod.cloudfunctions.net.",
"us-central1-teach-monster.cloudfunctions.net.",
"us-central1-tranquil-petal-272922.cloudfunctions.net.",
"us-central1-webgltest-17af1.cloudfunctions.net.",
"us-central1-wise-arch-107501.cloudfunctions.net.",
- "us-chi-anx-r003.router.teamviewer.com.",
- "us-den-anx-r002.router.teamviewer.com.",
+ "us-cmh-gcp-r002.router.teamviewer.com.",
+ "us-dal-anx-r004.router.teamviewer.com.",
+ "us-dal-anx-r008.router.teamviewer.com.",
+ "us-dal-anx-r010.router.teamviewer.com.",
"us-den-anx-r008.router.teamviewer.com.",
"us-device-scheduler.ymcs.yealink.com.",
"us-device.ymcs.yealink.com.",
"us-east4-chkp-gcp-rnd-threat-hunt-box.cloudfunctions.net.",
- "us-hnl-anx-r001.router.teamviewer.com.",
"us-hnl-anx-r002.router.teamviewer.com.",
- "us-lax-gcp-r005.router.teamviewer.com.",
- "us-mia-anx-r004.router.teamviewer.com.",
- "us-mia-anx-r008.router.teamviewer.com.",
- "us-mia-anx-r010.router.teamviewer.com.",
- "us-mia-anx-r011.router.teamviewer.com.",
- "us-mks-gcp-r002.router.teamviewer.com.",
- "us-mks-gcp-r004.router.teamviewer.com.",
- "us-oma-gcp-r002.router.teamviewer.com.",
- "us-pdx-gcp-r001.router.teamviewer.com.",
+ "us-las-gcp-r005.router.teamviewer.com.",
+ "us-lax-anx-r003.router.teamviewer.com.",
+ "us-lax-anx-r008.router.teamviewer.com.",
+ "us-lax-anx-r011.router.teamviewer.com.",
+ "us-lax-anx-r013.router.teamviewer.com.",
+ "us-lax-anx-r014.router.teamviewer.com.",
+ "us-lax-gcp-r002.router.teamviewer.com.",
+ "us-lax-gcp-r003.router.teamviewer.com.",
+ "us-mia-anx-r009.router.teamviewer.com.",
+ "us-mks-gcp-r003.router.teamviewer.com.",
+ "us-njc-anx-r005.router.teamviewer.com.",
+ "us-njc-anx-r015.router.teamviewer.com.",
"us-prod.asyncgw.teams.microsoft.com.",
- "us-sea-anx-r008.router.teamviewer.com.",
- "us-service.cartsee-from.cartx.cloud.",
"us-spectrum.rcs.telephony.goog.",
- "us-was-anx-r012.router.teamviewer.com.",
+ "us-was-anx-r001.router.teamviewer.com.",
+ "us-was-anx-r003.router.teamviewer.com.",
+ "us-was-anx-r010.router.teamviewer.com.",
"us.att.rcs.telephony.goog.",
"us.evidation.com.",
- "us.hlth.io.mi.com.",
- "us.micardapi.micloud.xiaomi.net.",
- "us.tmobile.rcs.telephony.goog.",
"us.tracfone.rcs.telephony.goog.",
"us.ubianet.com.",
"us.uscc.rcs.telephony.goog.",
"us.xfinity.rcs.telephony.goog.",
"us01docs.zoom.us.",
"us02log.zoom.us.",
+ "us02nws-platform.zoom.us.",
"us02nws.zoom.us.",
"us02polling.zoom.us.",
"us02web.zoom.us.",
@@ -7610,7 +7252,6 @@ var FakeECSFQDNs = container.NewMapSet(
"us04asyncim.zoom.us.",
"us04nws-platform.zoom.us.",
"us04nws.zoom.us.",
- "us04st2.zoom.us.",
"us04web.zoom.us.",
"us04www3.zoom.us.",
"us05nws-platform.zoom.us.",
@@ -7623,22 +7264,29 @@ var FakeECSFQDNs = container.NewMapSet(
"us06polling.zoom.us.",
"us06web.zoom.us.",
"us06www3.zoom.us.",
+ "us2.backdrop.cloud.",
+ "us3a-excel-collab.ocs.trafficmanager.net.",
"us3a-excel-collab.officeapps.live.com.",
+ "us3a-powerpoint-collab.ocs.trafficmanager.net.",
+ "us3a-powerpoint-collab.officeapps.live.com.",
+ "us3a-word-collab.ocs.trafficmanager.net.",
+ "us3a-word-collab.officeapps.live.com.",
"us4b-excel-collab.ocs.trafficmanager.net.",
"us4b-excel-collab.officeapps.live.com.",
"us4b-powerpoint-collab.ocs.trafficmanager.net.",
"us4b-powerpoint-collab.officeapps.live.com.",
"us4b-word-collab.ocs.trafficmanager.net.",
"us4b-word-collab.officeapps.live.com.",
+ "us4n-excel-collab.ocs.trafficmanager.net.",
"us4n-excel-collab.officeapps.live.com.",
"us4n-powerpoint-collab.officeapps.live.com.",
"us4n-word-collab.officeapps.live.com.",
+ "us4s-excel-collab.ocs.trafficmanager.net.",
"us4s-excel-collab.officeapps.live.com.",
"us4s-powerpoint-collab.officeapps.live.com.",
"us4s-word-collab.officeapps.live.com.",
"us7-excel-collab.ocs.trafficmanager.net.",
"us7-excel-collab.officeapps.live.com.",
- "us7-powerpoint-collab.ocs.trafficmanager.net.",
"us7-powerpoint-collab.officeapps.live.com.",
"us7-word-collab.ocs.trafficmanager.net.",
"us7-word-collab.officeapps.live.com.",
@@ -7646,67 +7294,63 @@ var FakeECSFQDNs = container.NewMapSet(
"us8-excel-collab.officeapps.live.com.",
"us8-powerpoint-collab.ocs.trafficmanager.net.",
"us8-powerpoint-collab.officeapps.live.com.",
+ "us8-word-collab.ocs.trafficmanager.net.",
"us8-word-collab.officeapps.live.com.",
"usbank.quantummetric.com.",
- "usc-collabrtc-geo.rtc.trafficmanager.net.",
- "usc-collabrtc.officeapps.live.com.",
"usc-excel-collab-geo.ocs.trafficmanager.net.",
"usc-excel-collab.officeapps.live.com.",
"usc-powerpoint-collab.officeapps.live.com.",
+ "usc-visio.officeapps.live.com.",
"usc-word-collab.officeapps.live.com.",
"usc.edu.",
- "usc.pods.officeapps.live.com.",
"uscis.gov.",
- "useast.quantumdex.io.",
+ "usdoj.gov.",
+ "use4.s.seedtag.com.",
"userlike.com.",
"usgs.gov.",
"ussav.cynet.com.",
"usserver.serverapi.org.",
"usslb.cynet.com.",
+ "usw-aiwit-file-push.oss-us-west-1.aliyuncs.com.",
"usw.stape.io.",
"uswest-beacon.deepintent.com.",
"ut.hzshudian.com.",
- "uu.qq.com.",
+ "ute-tech.com.cn.",
"uuidksinc.net.",
"uxfeedback.ru.",
"v.vivintsky.com.",
- "v2assets.zopim.io.",
- "v31.tiktokcdn.com.",
"v39-as.tiktokcdn.com.",
"v39-ca.tiktokcdn.com.",
"v39-id-telin.tiktokcdn.com.",
- "v39-mx.tiktokcdn.com.",
"v39-row.gts.byteoversea.net.",
"v39-row.tiktokcdn.com.",
"v39-us.gts.byteoversea.net.",
"v39-us.tiktokcdn.com.",
- "v39e-us.tiktokcdn.com.",
"v45-br.tiktokcdn.com.",
"v45-ph-globe.tiktokcdn.com.",
"v6-gdvod.kwaicdn.com.",
- "v8.analytics.pinsightmedia.com.",
- "v8engine.pinsightmedia.com.",
"vador.com.",
"vbw.vivoglobal.com.",
- "veh-dms.na.ultifi.gm.com.",
+ "vc-brain-lf.ndcpp.com.",
"venafi.com.",
"verification.fda.gov.ph.",
+ "verification.repocket.com.",
+ "verizon.quantummetric.com.",
"verticals.wix.com.",
- "vfa.hpplay.cn.",
+ "verval-snpmb.bppp.kemdikbud.go.id.",
"vhx.com.",
"vibe.co.",
"vibeaconstr.onezapp.com.",
"vicoo.tech.",
- "video-ams2-1.xx.fbcdn.net.",
"video-ams4-1.xx.fbcdn.net.",
"video-atl3-1.xx.fbcdn.net.",
"video-atl3-2.xx.fbcdn.net.",
"video-atl3-3.xx.fbcdn.net.",
"video-bos5-1.xx.fbcdn.net.",
"video-den2-1.xx.fbcdn.net.",
+ "video-det1-1.xx.fbcdn.net.",
"video-dfw5-1.xx.fbcdn.net.",
"video-dfw5-2.xx.fbcdn.net.",
- "video-hkg4-1.xx.fbcdn.net.",
"video-hou1-1.xx.fbcdn.net.",
"video-iad3-1.xx.fbcdn.net.",
"video-iad3-2.xx.fbcdn.net.",
@@ -7721,6 +7365,7 @@ var FakeECSFQDNs = container.NewMapSet(
"video-lhr8-2.xx.fbcdn.net.",
"video-mia3-1.xx.fbcdn.net.",
"video-mia3-2.xx.fbcdn.net.",
+ "video-mia3-3.xx.fbcdn.net.",
"video-msp1-1.xx.fbcdn.net.",
"video-ord5-1.xx.fbcdn.net.",
"video-ord5-2.xx.fbcdn.net.",
@@ -7728,16 +7373,13 @@ var FakeECSFQDNs = container.NewMapSet(
"video-phx1-1.xx.fbcdn.net.",
"video-sea1-1.xx.fbcdn.net.",
"video-sjc3-1.xx.fbcdn.net.",
- "video.unrulymedia.com.",
"videocontent-dra.himovie.dbankcloud.com.",
"vidmate.net.",
"vieon.vn.",
- "view.admeking.com.",
"vik-ca.moonactive.net.",
"viki.com.",
"vinted.fr.",
"viously.com.",
- "vipads.live.",
"virgul.com.",
"visaforchina.cn.",
"visitors.live.",
@@ -7745,140 +7387,121 @@ var FakeECSFQDNs = container.NewMapSet(
"vitality.io.",
"vividseats.com.",
"vivoglobal.com.",
- "vntsnotificationservice.visa.com.cdn.cloudflare.net.",
- "vod.ngb.haplat.net.",
- "vod2.ngb.haplat.net.",
- "vod3.ngb.haplat.net.",
- "vod4.ngb.haplat.net.",
- "vod5.ngb.haplat.net.",
- "vod6.ngb.haplat.net.",
+ "vlscppe.microsoft.com.",
"voe.sx.",
"voice.gcloudcs.com.",
"voice.telephony.goog.",
- "voxox.com.",
"voya.com.",
"vpn1.ocso.com.",
"vsco.co.",
- "vx323.com.",
"vzuu.com.",
"w.deepl.com.",
+ "w25.cf.2ksports.com.",
"w3.mp.lura.live.",
"wahapanih.xyz.",
"warsaw.remotepc.com.",
+ "waterfiltersfactory.com.",
"wayfinding-hub-gateway-atl.ultipro.com.",
"wayfinding-hub-gateway-plas1.ultipro.com.",
- "we.footballbros.io.",
+ "wbte.drcedirect.com.",
"weather-analytics-events.apple.com.",
"weather-server-sg.allawnos.com.",
"weather-server.allawntech.com.",
"weather-widget-events.apple.com.",
- "weather.swishapps.ai.",
"weathercn.com.",
+ "web-assets.stylitics.com.cdn.cloudflare.net.",
+ "web-push-sw.useinsider.com.",
"web-static.mindbox.ru.",
"web.voice.telephony.goog.",
"web1.remotepc.com.",
"webapi.teamviewer.com.",
"webmd.com.",
"websocket.app.pdq.com.",
- "webstaurantstore.com.",
"wechatos.net.",
- "weerrhoop.cc.",
- "wegame.com.cn.",
"weibocdn.com.",
"welcome.ultipro.com.",
"wemuslim.com.",
- "wesingapp.com.",
"westcentralus.api.cognitive.microsoft.com.",
"westeurope.api.cognitive.microsoft.com.",
"westpalm.remotepc.com.",
+ "westrockco.sharepoint.com.",
"westus.api.cognitive.microsoft.com.",
"westus2.api.cognitive.microsoft.com.",
"westus3.api.cognitive.microsoft.com.",
- "whatismyipaddress.com.",
"whirlpool.com.",
- "whitefoxboutique.com.",
- "whitingturner-my.sharepoint.com.",
- "whitingturner.sharepoint.com.",
+ "whitehouse.gov.",
+ "whizzco.com.",
+ "whop.com.",
+ "wida-insight.drcedirect.com.",
"widget-api.uxfeedback.ru.",
+ "widgets.risevision.com.",
"widgets.sociablekit.com.",
- "wifispot.io.",
"windows.policies.live.net.",
"winscp.net.",
+ "wireless-social.com.",
"wkhpe.com.",
+ "wl4.loobygameshub.com.",
"wmt.dev.",
"word-collab.officeapps.live.com.",
"wordreference.com.",
"workdaycdn.com.cn.",
- "worldguessr.com.",
"worldnic.com.",
"worldtimeserver.com.",
"wosign.com.",
+ "wpp.lol.",
"wr.moyoung.com.",
"wr.pvp.net.",
"write-free.www.deepl.com.",
"ws.gleap.io.",
- "ws.mybib.com.",
"ws.thales.monumetric.com.",
"ws.tildacdn.com.",
"wsms.haplat.net.",
"wsoversea.com.",
"wss-web.freshchat.com.",
- "wtecdn.net.",
- "wtzw.com.",
+ "wsv3cdn.audioeye.com.",
+ "wsv3cdn.audioeye.com.cdn.cloudflare.net.",
"www.aigconnect.aig.",
- "www.att.com.edgekey.net.",
+ "www.animefox.sbs.",
+ "www.aniwatch.click.",
"www.automizely-analytics.com.",
+ "www.belkin.com.",
"www.breitbart.com.",
- "www.cabelas.com.",
- "www.chrome.com.",
+ "www.britannica.com.",
"www.claudeusercontent.com.",
- "www.cmpassport.com.",
- "www.columbia.com.",
- "www.crazygames.com.",
"www.ctmail.com.",
- "www.geappliances.com.",
+ "www.digitalcardservice.com.",
+ "www.etoro.com.",
"www.geoplugin.net.",
"www.google.org.",
- "www.handmadewithjoann.com.",
- "www.hopkinsmedicine.org.",
- "www.hottopic.com.",
- "www.hpsmart.com.",
"www.in.gov.",
- "www.intel.com.",
+ "www.jhnet.com.",
"www.jimmyjohns.com.",
- "www.jotform.com.",
- "www.justice.gov.",
- "www.kqzyfj.com.",
- "www.linkedin.com.cdn.cloudflare.net.",
"www.maintenanceconnection.com.",
- "www.masterclass.com.",
- "www.nativecos.com.",
+ "www.newegg.com.",
"www.overleaf.com.",
"www.pingler.com.",
- "www.printfriendly.com.",
- "www.rbcroyalbank.com.",
+ "www.qchannel01.cn.",
+ "www.redstream.in.",
"www.regions.com.",
- "www.rentcafe.com.",
- "www.riotgames.com.",
+ "www.routeone.net.",
"www.sevenrooms.com.",
- "www.snokido.com.",
- "www.startssl.com.",
- "www.teacherspayteachers.com.",
+ "www.sobot.com.",
+ "www.syndetics.com.",
"www.terabox.com.",
"www.users.storage.live.com.",
- "www.vipads.live.",
+ "www.ute-tech.com.cn.",
+ "www.viously.com.",
"www.visaforchina.cn.",
- "www.vividseats.com.",
+ "www.waterfiltersfactory.com.",
"www.whitehouse.gov.",
- "www.wifispot.io.",
"www.wix.com.",
"www.worldtimeserver.com.",
"www.zoom.com.",
"www.zoom.us.",
- "www.zoominfo.com.",
- "www.zscaler.com.",
"www1.remotepc.com.",
+ "www19.pointclickcare.com.",
"www2.deepl.com.",
+ "www27.pointclickcare.com.",
"www28.pointclickcare.com.",
"www3.zoom.us.",
"www30.pointclickcare.com.",
@@ -7887,25 +7510,25 @@ var FakeECSFQDNs = container.NewMapSet(
"wxqcloud.qq.com.cn.",
"wyze.com.",
"x-flow.app.",
- "xbox-guide-public.rec.mp.microsoft.com.",
- "xbox.ipv6.microsoft.com.",
+ "x1.xplorebrav.space.",
+ "x20na.appdump.nie.easebar.com.",
"xhpingcdn.com.",
- "xhwear.life.",
+ "xhprograms.site.",
"xiaoyi.com.",
"xinqiucc.com.",
"xintaicz.cn.",
"xmcsrv.net.",
- "xml-eu-v4.ezmob.com.",
- "xml-v4.ezmob.com.",
+ "xml-v4.clickmi.net.",
"xml.acertb.com.",
- "xml.adservtday.com.",
+ "xml.adokutcontextual.com.",
+ "xml.ezmob.com.",
+ "xml.junplatdirect.com.",
"xml.kunvertads.com.",
- "xml.responseservez.com.",
+ "xml.nexrtb.com.",
+ "xml.optumads.com.",
"xml.revrtb.net.",
- "xml.servsserverz.com.",
+ "xml.ripamatic.com.",
"xml.xmlking.com.",
- "xml.zeusadx.com.",
- "xmppapi.zoom.us.",
"xmt.paze.com.",
"xp001.itsupport247.net.",
"xp002.itsupport247.net.",
@@ -7924,23 +7547,23 @@ var FakeECSFQDNs = container.NewMapSet(
"yalla.live.",
"yealink.com.",
"ymmobi.com.",
- "yomedia.vn.",
"yomeno.xyz.",
"yosmart.com.",
"youlesp.com.",
+ "yp.cdnstream1.com.",
"yunxindns.com.",
"yunxinfw.com.",
"z.cdn.adpool.bet.",
"z.cdn.adtarget.market.",
"z.cdn.ftd.agency.",
+ "z.cdn.trafficbass.com.",
"zemanta.com.",
- "zepp.com.",
- "zero.txryan.com.",
"zjcdn.com.yangyi19.com.",
"zog.link.",
"zoom.us.",
"zui.com.",
"zuiqiangyingyu.net.",
"zurich.remotepc.com.",
+ "zybooks.com.",
"zztfly.com.",
)
diff --git a/internal/ecscache/ecscache.go b/internal/ecscache/ecscache.go
index d78f2fc..a706f1b 100644
--- a/internal/ecscache/ecscache.go
+++ b/internal/ecscache/ecscache.go
@@ -13,7 +13,6 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
- "github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/AdGuardDNS/internal/optslog"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
@@ -21,42 +20,12 @@ import (
"github.com/miekg/dns"
)
-// Constants that define cache identifiers for the cache manager.
-const (
- cachePrefix = "dns/"
- cacheIDWithECS = cachePrefix + "ecscache_with_ecs"
- cacheIDNoECS = cachePrefix + "ecscache_no_ecs"
-)
-
-// Middleware is a dnsserver.Middleware with ECS-aware caching.
-type Middleware struct {
- // cloner is the memory-efficient cloner of DNS messages.
- cloner *dnsmsg.Cloner
-
- // cacheReqPool is a pool of cache requests.
- cacheReqPool *syncutil.Pool[cacheRequest]
-
- // logger is used to log the operation of the middleware.
- logger *slog.Logger
-
- // cache is the LRU cache for results indicating no support for ECS.
- cache agdcache.Interface[uint64, *cacheItem]
-
- // ecsCache is the LRU cache for results indicating ECS support.
- ecsCache agdcache.Interface[uint64, *cacheItem]
-
- // geoIP is used to get subnets for countries.
- geoIP geoip.Interface
-
- // cacheMinTTL is the minimum supported TTL for cache items.
- cacheMinTTL time.Duration
-
- // overrideTTL shows if the TTL overrides logic should be used.
- overrideTTL bool
-}
-
// MiddlewareConfig is the configuration structure for [NewMiddleware].
type MiddlewareConfig struct {
+ // Metrics is used for the collection of the ECS cache middleware
+ // statistics. It must not be nil.
+ Metrics Metrics
+
// Cloner is used to clone messages taken from cache. It must not be nil.
Cloner *dnsmsg.Cloner
@@ -86,6 +55,43 @@ type MiddlewareConfig struct {
OverrideTTL bool
}
+// Middleware is a dnsserver.Middleware with ECS-aware caching.
+type Middleware struct {
+ // metrics is used for the collection of the ECS cache statistics.
+ metrics Metrics
+
+ // cloner is the memory-efficient cloner of DNS messages.
+ cloner *dnsmsg.Cloner
+
+ // cacheReqPool is a pool of cache requests.
+ cacheReqPool *syncutil.Pool[cacheRequest]
+
+ // logger is used to log the operation of the middleware.
+ logger *slog.Logger
+
+ // cache is the LRU cache for results indicating no support for ECS.
+ cache agdcache.Interface[uint64, *cacheItem]
+
+ // ecsCache is the LRU cache for results indicating ECS support.
+ ecsCache agdcache.Interface[uint64, *cacheItem]
+
+ // geoIP is used to get subnets for countries.
+ geoIP geoip.Interface
+
+ // cacheMinTTL is the minimum supported TTL for cache items.
+ cacheMinTTL time.Duration
+
+ // overrideTTL shows if the TTL overrides logic should be used.
+ overrideTTL bool
+}
+
+// Constants that define cache identifiers for the cache manager.
+const (
+ cachePrefix = "dns/"
+ cacheIDWithECS = cachePrefix + "ecscache_with_ecs"
+ cacheIDNoECS = cachePrefix + "ecscache_no_ecs"
+)
+
// NewMiddleware initializes a new ECS-aware LRU caching middleware. It also
// adds the caches with IDs [CacheIDNoECS] and [CacheIDWithECS] to the cache
// manager. c must not be nil.
@@ -101,8 +107,9 @@ func NewMiddleware(c *MiddlewareConfig) (m *Middleware) {
c.CacheManager.Add(cacheIDWithECS, ecsCache)
return &Middleware{
- cloner: c.Cloner,
- logger: c.Logger,
+ metrics: c.Metrics,
+ cloner: c.Cloner,
+ logger: c.Logger,
cacheReqPool: syncutil.NewPool(func() (req *cacheRequest) {
return &cacheRequest{}
}),
@@ -134,19 +141,7 @@ func writeCachedResponse(
resp *dns.Msg,
ecs *dnsmsg.ECS,
ecsFam netutil.AddrFamily,
- respIsECSDependent bool,
) (err error) {
- // Increment the hits metrics here, since we already know if the domain name
- // supports ECS or not from the cache data. Increment the misses metrics in
- // writeResponse, where this information is retrieved from the upstream
- metrics.ECSCacheLookupTotalHits.Inc()
-
- metrics.IncrementCond(
- respIsECSDependent,
- metrics.ECSCacheLookupHasSupportHits,
- metrics.ECSCacheLookupNoSupportHits,
- )
-
// If the client query did include the ECS option, the server MUST include
// one in its response.
//
@@ -237,19 +232,19 @@ func (mw *Middleware) writeUpstreamResponse(
reqDO := cr.reqDO
rmHopToHopData(resp, ri.QType, reqDO)
- metrics.ECSCacheLookupTotalMisses.Inc()
-
respIsECS := respIsECSDependent(scope, req.Question[0].Name)
- if respIsECS {
- metrics.ECSCacheLookupHasSupportMisses.Inc()
- metrics.ECSHasSupportCacheSize.Set(float64(mw.ecsCache.Len()))
- } else {
- metrics.ECSCacheLookupNoSupportMisses.Inc()
- metrics.ECSNoSupportCacheSize.Set(float64(mw.cache.Len()))
+ var cache agdcache.Interface[uint64, *cacheItem]
+ if respIsECS {
+ cache = mw.ecsCache
+ } else {
+ cache = mw.cache
cr.subnet = netutil.ZeroPrefix(ecsFam)
}
+ mw.metrics.SetElementsCount(ctx, respIsECS, cache.Len())
+ mw.metrics.IncrementLookups(ctx, respIsECS, false)
+
mw.set(resp, cr, respIsECS)
// Set the AD bit and ECS information here, where it is safe to do so, since
@@ -343,8 +338,14 @@ func (mh *mwHandler) ServeDNS(
if resp != nil {
optslog.Debug1(ctx, mw.logger, "using cached response", "ecs_aware", respIsECS)
+ // Increment the hits metrics here, since we already know if the domain
+ // name supports ECS or not from the cache data. Increment the misses
+ // metrics in writeUpstreamResponse, where this information is retrieved
+ // from the upstream.
+ mw.metrics.IncrementLookups(ctx, respIsECS, true)
+
// Don't wrap the error, because it's informative enough as is.
- return writeCachedResponse(ctx, rw, req, resp, ri.ECS, ecsFam, respIsECS)
+ return writeCachedResponse(ctx, rw, req, resp, ri.ECS, ecsFam)
}
mw.logger.DebugContext(ctx, "no cached response")
diff --git a/internal/ecscache/ecscache_test.go b/internal/ecscache/ecscache_test.go
index ef1e221..154bc34 100644
--- a/internal/ecscache/ecscache_test.go
+++ b/internal/ecscache/ecscache_test.go
@@ -680,6 +680,7 @@ func newWithCache(
return dnsserver.WithMiddlewares(
h,
ecscache.NewMiddleware(&ecscache.MiddlewareConfig{
+ Metrics: ecscache.EmptyMetrics{},
Cloner: agdtest.NewCloner(),
Logger: slogutil.NewDiscardLogger(),
CacheManager: agdcache.EmptyManager{},
diff --git a/internal/ecscache/metrics.go b/internal/ecscache/metrics.go
new file mode 100644
index 0000000..b8ec588
--- /dev/null
+++ b/internal/ecscache/metrics.go
@@ -0,0 +1,28 @@
+package ecscache
+
+import "context"
+
+// Metrics is an interface that is used for the collection of the ECS cache
+// statistics.
+type Metrics interface {
+ // SetElementsCount sets the total number of items in the cache for domain
+ // names that support or do not support ECS.
+ SetElementsCount(ctx context.Context, supportsECS bool, count int)
+
+ // IncrementLookups increments the number of ECS cache lookups for hosts
+ // that does or doesn't support ECS.
+ IncrementLookups(ctx context.Context, supportsECS, hit bool)
+}
+
+// EmptyMetrics is the implementation of the [Metrics] interface that does
+// nothing.
+type EmptyMetrics struct{}
+
+// type check
+var _ Metrics = EmptyMetrics{}
+
+// SetElementsCount implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) SetElementsCount(_ context.Context, _ bool, _ int) {}
+
+// IncrementLookups implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) IncrementLookups(_ context.Context, _, _ bool) {}
diff --git a/internal/errcoll/errcoll.go b/internal/errcoll/errcoll.go
index 0644f39..2284324 100644
--- a/internal/errcoll/errcoll.go
+++ b/internal/errcoll/errcoll.go
@@ -7,8 +7,8 @@ import (
"fmt"
"log/slog"
- "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/logutil/slogutil"
+ "github.com/AdguardTeam/golibs/service"
)
// Interface is the interface for error collectors that process information
@@ -17,14 +17,6 @@ type Interface interface {
Collect(ctx context.Context, err error)
}
-// Collectf is a helper method for reporting non-critical errors. It writes the
-// resulting error into the log and also into errColl.
-func Collectf(ctx context.Context, errColl Interface, format string, args ...any) {
- err := fmt.Errorf(format, args...)
- log.Error("%s", err)
- errColl.Collect(ctx, err)
-}
-
// Collect is a helper method for reporting non-critical errors. It writes the
// resulting error into the log and also into errColl.
//
@@ -33,3 +25,28 @@ func Collect(ctx context.Context, errColl Interface, l *slog.Logger, msg string,
l.ErrorContext(ctx, msg, slogutil.KeyError, err)
errColl.Collect(ctx, fmt.Errorf("%s: %w", msg, err))
}
+
+// RefreshErrorHandler is a [service.ErrorHandler] that can be used whenever a
+// [service.Refresher] cannot report its own errors for some reason.
+type RefreshErrorHandler struct {
+ logger *slog.Logger
+ errColl Interface
+}
+
+// NewRefreshErrorHandler returns a properly initialized *RefreshErrorHandler.
+// All arguments must not be nil.
+func NewRefreshErrorHandler(logger *slog.Logger, errColl Interface) (h *RefreshErrorHandler) {
+ return &RefreshErrorHandler{
+ logger: logger,
+ errColl: errColl,
+ }
+}
+
+// type check
+var _ service.ErrorHandler = (*RefreshErrorHandler)(nil)
+
+// Handle implements the [service.ErrorHandler] interface for
+// *RefreshErrorHandler.
+func (h *RefreshErrorHandler) Handle(ctx context.Context, err error) {
+ Collect(ctx, h.errColl, h.logger, "refreshing", err)
+}
diff --git a/internal/errcoll/sentry.go b/internal/errcoll/sentry.go
index ae75bc8..4210bca 100644
--- a/internal/errcoll/sentry.go
+++ b/internal/errcoll/sentry.go
@@ -4,6 +4,7 @@ import (
"cmp"
"context"
"io"
+ "log/slog"
"net"
"os"
"strconv"
@@ -15,7 +16,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/forward"
"github.com/AdguardTeam/AdGuardDNS/internal/version"
"github.com/AdguardTeam/golibs/errors"
- "github.com/AdguardTeam/golibs/log"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/getsentry/sentry-go"
"golang.org/x/sys/unix"
)
@@ -23,13 +24,15 @@ import (
// SentryErrorCollector is an [Interface] implementation that sends errors to a
// Sentry-like HTTP API.
type SentryErrorCollector struct {
+ logger *slog.Logger
sentry *sentry.Client
}
-// NewSentryErrorCollector returns a new SentryErrorCollector. cli must be
-// non-nil.
-func NewSentryErrorCollector(cli *sentry.Client) (c *SentryErrorCollector) {
+// NewSentryErrorCollector returns a new SentryErrorCollector. All arguments
+// must not be nil.
+func NewSentryErrorCollector(cli *sentry.Client, l *slog.Logger) (c *SentryErrorCollector) {
return &SentryErrorCollector{
+ logger: l,
sentry: cli,
}
}
@@ -40,7 +43,7 @@ var _ Interface = (*SentryErrorCollector)(nil)
// Collect implements the [Interface] interface for *SentryErrorCollector.
func (c *SentryErrorCollector) Collect(ctx context.Context, err error) {
if !isReportable(err) {
- log.Debug("errcoll: sentry: non-reportable error: %s", err)
+ c.logger.DebugContext(ctx, "non-reportable error", slogutil.KeyError, err)
return
}
diff --git a/internal/errcoll/sentry_test.go b/internal/errcoll/sentry_test.go
index 8f95b77..02d8995 100644
--- a/internal/errcoll/sentry_test.go
+++ b/internal/errcoll/sentry_test.go
@@ -12,46 +12,26 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/version"
"github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
+ "github.com/AdguardTeam/golibs/testutil/sentrytest"
"github.com/getsentry/sentry-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
-// testSentryTransport is a sentry.Transport for tests.
-type testSentryTransport struct {
- onConfigure func(opts sentry.ClientOptions)
- onFlush func(timeout time.Duration) (ok bool)
- onSend func(e *sentry.Event)
-}
-
-// type check
-var _ sentry.Transport = (*testSentryTransport)(nil)
-
-// Configure implements the sentry.Transport interface for *testSentryTransport.
-func (t *testSentryTransport) Configure(ops sentry.ClientOptions) {
- t.onConfigure(ops)
-}
-
-// Flush implements the sentry.Transport interface for *testSentryTransport.
-func (t *testSentryTransport) Flush(timeout time.Duration) (ok bool) {
- return t.onFlush(timeout)
-}
-
-// Send implements the sentry.Transport interface for *testSentryTransport.
-func (t *testSentryTransport) SendEvent(e *sentry.Event) {
- t.onSend(e)
-}
-
func TestSentryErrorCollector(t *testing.T) {
gotEventCh := make(chan *sentry.Event, 1)
- tr := &testSentryTransport{
- onConfigure: func(_ sentry.ClientOptions) {
+ tr := &sentrytest.Transport{
+ OnClose: func() {
// Do nothing.
},
- onFlush: func(_ time.Duration) (ok bool) {
+ OnConfigure: func(_ sentry.ClientOptions) {
+ // Do nothing.
+ },
+ OnFlush: func(_ time.Duration) (ok bool) {
return true
},
- onSend: func(e *sentry.Event) {
+ OnSendEvent: func(e *sentry.Event) {
gotEventCh <- e
},
}
@@ -63,7 +43,7 @@ func TestSentryErrorCollector(t *testing.T) {
})
require.NoError(t, err)
- c := errcoll.NewSentryErrorCollector(sentryClient)
+ c := errcoll.NewSentryErrorCollector(sentryClient, slogutil.NewDiscardLogger())
const devID = "dev1234"
const fltGrpID = "fg1234"
diff --git a/internal/experiment/experiment.go b/internal/experiment/experiment.go
index 71b8753..8ab0990 100644
--- a/internal/experiment/experiment.go
+++ b/internal/experiment/experiment.go
@@ -11,19 +11,18 @@
// - Some errors may be logged or ignored.
// - Tests may be lacking.
// - The environment may be read here as opposed to package cmd.
-// - init() is allowed.
package experiment
import (
+ "log/slog"
"os"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
- "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/prometheus/client_golang/prometheus"
)
-func init() {
+func Init(l *slog.Logger) {
expStr := os.Getenv("EXPERIMENTS")
if expStr == "" {
return
@@ -36,7 +35,7 @@ func init() {
// case idMyExp:
// enableMyExp()
default:
- log.Error("experiment: no experiment with id %q", id)
+ l.Error("no such experiment", "id", id)
}
}
diff --git a/internal/filter/config.go b/internal/filter/config.go
index b71aad6..f62b2fe 100644
--- a/internal/filter/config.go
+++ b/internal/filter/config.go
@@ -1,6 +1,12 @@
package filter
-import "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
+import (
+ "context"
+ "net/netip"
+
+ "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
+ "github.com/AdguardTeam/urlfilter"
+)
// Config is the sum type of [Storage.ForConfig] configurations.
//
@@ -39,7 +45,32 @@ func (*ConfigClient) isConfig() {}
// ConfigCustom is the configuration for identification or construction of a
// custom filter for a client.
-type ConfigCustom = internal.ConfigCustom
+type ConfigCustom struct {
+ // Filter is the custom filter to use for this client, if any. If
+ // [ConfigCustom.Enabled] is true, Filter must not be nil.
+ Filter Custom
+
+ // Enabled shows whether the custom filters are applied at all.
+ Enabled bool
+}
+
+// Custom is a custom filter for a client.
+type Custom interface {
+ // DNSResult returns the result of applying the urlfilter DNS filtering
+ // engine. If the request is not filtered, DNSResult returns nil.
+ DNSResult(
+ ctx context.Context,
+ clientIP netip.Addr,
+ clientName string,
+ host string,
+ rrType dnsmsg.RRType,
+ isAns bool,
+ ) (res *urlfilter.DNSResult)
+
+ // Rules returns the rules used to create the filter. rules must not be
+ // modified.
+ Rules() (rules []RuleText)
+}
// ConfigParental is the configuration for parental-control filtering.
type ConfigParental struct {
diff --git a/internal/filter/custom/custom.go b/internal/filter/custom/custom.go
new file mode 100644
index 0000000..bceaddd
--- /dev/null
+++ b/internal/filter/custom/custom.go
@@ -0,0 +1,91 @@
+// Package custom contains filters made from custom filtering rules of clients.
+package custom
+
+import (
+ "context"
+ "log/slog"
+ "net/netip"
+ "strings"
+ "sync"
+
+ "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist"
+ "github.com/AdguardTeam/golibs/stringutil"
+ "github.com/AdguardTeam/urlfilter"
+)
+
+// Filter is a custom filter for a client.
+type Filter struct {
+ logger *slog.Logger
+ initOnce *sync.Once
+ immutable *rulelist.Immutable
+ rules []filter.RuleText
+}
+
+// Config is the configuration for a custom filter.
+type Config struct {
+ // Logger is used for logging the compilation of the engine. It must not be
+ // nil.
+ Logger *slog.Logger
+
+ // Rules are the rules for this custom filter. They must not be modified
+ // after calling New.
+ Rules []filter.RuleText
+}
+
+// New creates a new custom filter. c must not be nil and must be valid.
+func New(c *Config) (f *Filter) {
+ return &Filter{
+ logger: c.Logger,
+ initOnce: &sync.Once{},
+ rules: c.Rules,
+ }
+}
+
+// init initializes f.immutable.
+func (f *Filter) init(ctx context.Context) {
+ // TODO(a.garipov): Consider making a copy of [strings.Join] for
+ // [filter.RuleText].
+ textLen := 0
+ for _, r := range f.rules {
+ textLen += len(r) + len("\n")
+ }
+
+ b := &strings.Builder{}
+ b.Grow(textLen)
+
+ for _, r := range f.rules {
+ stringutil.WriteToBuilder(b, string(r), "\n")
+ }
+
+ // Don't use cache for users' custom filters, because [rulelist.ResultCache]
+ // doesn't take $client rules into account.
+ //
+ // TODO(a.garipov): Consider adding client names to the result-cache keys.
+ cache := rulelist.EmptyResultCache{}
+
+ f.immutable = rulelist.NewImmutable(b.String(), filter.IDCustom, "", cache)
+
+ f.logger.DebugContext(ctx, "engine compiled", "num_rules", f.immutable.RulesCount())
+}
+
+// DNSResult returns the result of applying the custom filter to the query with
+// the given parameters.
+func (f *Filter) DNSResult(
+ ctx context.Context,
+ clientIP netip.Addr,
+ clientName string,
+ host string,
+ rrType dnsmsg.RRType,
+ isAns bool,
+) (r *urlfilter.DNSResult) {
+ f.initOnce.Do(func() {
+ f.init(ctx)
+ })
+
+ return f.immutable.DNSResult(clientIP, clientName, host, rrType, isAns)
+}
+
+// Rules implements the [filter.Custom] interface for *Filter.
+func (f *Filter) Rules() (rules []filter.RuleText) { return f.rules }
diff --git a/internal/filter/custom/custom_test.go b/internal/filter/custom/custom_test.go
new file mode 100644
index 0000000..415e33c
--- /dev/null
+++ b/internal/filter/custom/custom_test.go
@@ -0,0 +1,68 @@
+package custom_test
+
+import (
+ "context"
+ "testing"
+
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter/custom"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/filtertest"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
+ "github.com/miekg/dns"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestFilter(t *testing.T) {
+ t.Parallel()
+
+ rules := []filter.RuleText{
+ filtertest.RuleBlock,
+ filtertest.RuleBlockForClientIP,
+ filtertest.RuleBlockForClientName,
+ }
+
+ f := custom.New(&custom.Config{
+ Logger: slogutil.NewDiscardLogger(),
+ Rules: rules,
+ })
+ require.NotNil(t, f)
+ require.Equal(t, rules, f.Rules())
+
+ ip := filtertest.IPv4Client
+
+ testCases := []struct {
+ name string
+ cliName string
+ host string
+ wantRuleStr string
+ }{{
+ name: "simple",
+ cliName: "",
+ host: filtertest.HostBlocked,
+ wantRuleStr: filtertest.RuleBlockStr,
+ }, {
+ name: "client_ip",
+ cliName: "",
+ host: filtertest.HostBlockedForClientIP,
+ wantRuleStr: filtertest.RuleBlockForClientIPStr,
+ }, {
+ name: "client_name",
+ cliName: filtertest.ClientName,
+ host: filtertest.HostBlockedForClientName,
+ wantRuleStr: filtertest.RuleBlockForClientNameStr,
+ }}
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+
+ dr := f.DNSResult(context.Background(), ip, tc.cliName, tc.host, dns.TypeA, false)
+
+ require.NotNil(t, dr)
+ require.NotNil(t, dr.NetworkRule)
+
+ assert.Equal(t, tc.wantRuleStr, dr.NetworkRule.RuleText)
+ })
+ }
+}
diff --git a/internal/filter/filter.go b/internal/filter/filter.go
index 1e1848d..dc384ad 100644
--- a/internal/filter/filter.go
+++ b/internal/filter/filter.go
@@ -5,93 +5,77 @@ package filter
import (
"context"
+ "net/netip"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
+ "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
+ "github.com/miekg/dns"
)
// Interface is the DNS request and response filter interface.
-type Interface = internal.Interface
+type Interface interface {
+ // FilterRequest filters a DNS request based on the information provided
+ // about the request. req must be valid.
+ FilterRequest(ctx context.Context, req *Request) (r Result, err error)
-// Empty is an [Interface] implementation that always returns nil.
-type Empty = internal.Empty
+ // FilterResponse filters a DNS response based on the information provided
+ // about the response. resp must be valid.
+ FilterResponse(ctx context.Context, resp *Response) (r Result, err error)
+}
// Request contains information about a request being filtered.
-type Request = internal.Request
+type Request struct {
+ // DNS is the original DNS request used to create filtered responses. It
+ // must not be nil and must have exactly one question.
+ DNS *dns.Msg
+
+ // Messages is used to create filtered responses for this request. It must
+ // not be nil.
+ Messages *dnsmsg.Constructor
+
+ // RemoteIP is the remote IP address of the client.
+ RemoteIP netip.Addr
+
+ // ClientName is the client name for rule-list filtering.
+ ClientName string
+
+ // Host is the lowercased, non-FQDN version of the hostname from the
+ // question of the request.
+ Host string
+
+ // QType is the type of question for this request.
+ QType dnsmsg.RRType
+
+ // QClass is the class of question for this request.
+ QClass dnsmsg.Class
+}
// Response contains information about a response being filtered.
-type Response = internal.Response
+type Response struct {
+ // DNS is the original DNS response used to create filtered responses. It
+ // must not be nil and must have exactly one question.
+ DNS *dns.Msg
-// Result is a sum type of all possible filtering actions. See the following
-// types as implementations:
-//
-// - [*ResultAllowed]
-// - [*ResultBlocked]
-// - [*ResultModifiedResponse]
-// - [*ResultModifiedRequest]
-type Result = internal.Result
+ // RemoteIP is the remote IP address of the client.
+ RemoteIP netip.Addr
-// ResultAllowed means that this request or response was allowed by an allowlist
-// rule within the given filter list.
-type ResultAllowed = internal.ResultAllowed
+ // ClientName is the client name for rule-list filtering.
+ ClientName string
+}
-// ResultBlocked means that this request or response was blocked by a blocklist
-// rule within the given filter list.
-type ResultBlocked = internal.ResultBlocked
+// Empty is an [Interface] implementation that always returns nil.
+type Empty struct{}
-// ResultModifiedResponse means that this response was rewritten or modified by
-// a rewrite rule within the given filter list.
-type ResultModifiedResponse = internal.ResultModifiedResponse
+// type check
+var _ Interface = Empty{}
-// ResultModifiedRequest means that this request was modified by a rewrite rule
-// within the given filter list.
-type ResultModifiedRequest = internal.ResultModifiedRequest
+// FilterRequest implements the [Interface] interface for Empty.
+func (Empty) FilterRequest(_ context.Context, _ *Request) (r Result, err error) {
+ return nil, nil
+}
-// ID is the ID of a filter list. It is an opaque string.
-type ID = internal.ID
-
-// Special ID values shared across the AdGuard DNS system.
-//
-// NOTE: DO NOT change these as other parts of the system depend on these
-// values.
-//
-// TODO(a.garipov): Consider removing those that aren't used outside of the
-// filter subpackages.
-const (
- IDNone = internal.IDNone
-
- IDAdGuardDNS = internal.IDAdGuardDNS
- IDAdultBlocking = internal.IDAdultBlocking
- IDBlockedService = internal.IDBlockedService
- IDCustom = internal.IDCustom
- IDGeneralSafeSearch = internal.IDGeneralSafeSearch
- IDNewRegDomains = internal.IDNewRegDomains
- IDSafeBrowsing = internal.IDSafeBrowsing
- IDYoutubeSafeSearch = internal.IDYoutubeSafeSearch
-)
-
-// NewID converts a simple string into an ID and makes sure that it's valid.
-// This should be preferred to a simple type conversion.
-func NewID(s string) (id ID, err error) { return internal.NewID(s) }
-
-// RuleText is the text of a single rule within a rule-list filter.
-type RuleText = internal.RuleText
-
-// NewRuleText converts a simple string into an RuleText and makes sure that
-// it's valid. This should be preferred to a simple type conversion.
-func NewRuleText(s string) (id RuleText, err error) { return internal.NewRuleText(s) }
-
-// 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 = internal.BlockedServiceID
-
-// 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) {
- return internal.NewBlockedServiceID(s)
+// FilterResponse implements the [Interface] interface for Empty.
+func (Empty) FilterResponse(_ context.Context, _ *Response) (r Result, err error) {
+ return nil, nil
}
// HashMatcher is the interface for a safe-browsing and adult-blocking hash
@@ -105,10 +89,3 @@ const (
GeneralTXTSuffix = ".sb.dns.adguard.com"
AdultBlockingTXTSuffix = ".pc.dns.adguard.com"
)
-
-// Metrics is the interface for metrics of filters.
-type Metrics = internal.Metrics
-
-// EmptyMetrics is the implementation of the [Metrics] interface that does
-// nothing.
-type EmptyMetrics = internal.EmptyMetrics
diff --git a/internal/filter/internal/internal_test.go b/internal/filter/filter_test.go
similarity index 55%
rename from internal/filter/internal/internal_test.go
rename to internal/filter/filter_test.go
index a79895c..a6963b5 100644
--- a/internal/filter/internal/internal_test.go
+++ b/internal/filter/filter_test.go
@@ -1,10 +1,8 @@
-package internal_test
+package filter_test
import "strings"
// Common long strings for tests.
-//
-// TODO(a.garipov): Move to a new validation package.
var (
testLongStr = strings.Repeat("a", 200)
)
diff --git a/internal/filter/filterstorage/config.go b/internal/filter/filterstorage/config.go
index 72173ba..cff4bc1 100644
--- a/internal/filter/filterstorage/config.go
+++ b/internal/filter/filterstorage/config.go
@@ -6,10 +6,10 @@ import (
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix"
+ "github.com/AdguardTeam/golibs/timeutil"
"github.com/c2h5oh/datasize"
)
@@ -52,7 +52,7 @@ type Config struct {
// Clock is used for time-related operations, such as schedule checking.
// It must not be nil.
- Clock agdtime.Clock
+ Clock timeutil.Clock
// ErrColl is used to collect non-critical and rare errors as well as
// refresh errors. It must not be nil.
diff --git a/internal/filter/filterstorage/default.go b/internal/filter/filterstorage/default.go
index e7a5acc..30b4cc8 100644
--- a/internal/filter/filterstorage/default.go
+++ b/internal/filter/filterstorage/default.go
@@ -10,32 +10,29 @@ import (
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/composite"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/custom"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/refreshable"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/safesearch"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/serviceblock"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
+ "github.com/AdguardTeam/golibs/timeutil"
"github.com/c2h5oh/datasize"
)
// Default is the default filter storage that assembles filters based on rule
-// lists, custom filters of profiles, safe browsing, and safe search ones. It
-// should be initially refreshed with [Default.RefreshInitial].
+// lists, safe browsing, and safe search ones. It should be initially refreshed
+// with [Default.RefreshInitial].
type Default struct {
baseLogger *slog.Logger
logger *slog.Logger
services *serviceblock.Filter
- custom *custom.Filters
-
adult *hashprefix.Filter
dangerous *hashprefix.Filter
newlyRegistered *hashprefix.Filter
@@ -50,7 +47,7 @@ type Default struct {
ruleListIdxRefr *refreshable.Refreshable
cacheManager agdcache.Manager
- clock agdtime.Clock
+ clock timeutil.Clock
errColl errcoll.Interface
metrics filter.Metrics
@@ -83,9 +80,6 @@ func New(c *Config) (s *Default, err error) {
// Initialized in [Default.initBlockedServices].
services: nil,
- // Initialized in [Default.initCustom].
- custom: nil,
-
adult: c.HashPrefix.Adult,
dangerous: c.HashPrefix.Dangerous,
newlyRegistered: c.HashPrefix.NewlyRegistered,
@@ -132,8 +126,6 @@ func New(c *Config) (s *Default, err error) {
// init finishes the initialization of a storage. c must not be nil.
func (s *Default) init(c *Config) (err error) {
- s.initCustom(c.Custom)
-
var errs []error
err = s.initBlockedServices(c.BlockedServices)
if err != nil {
@@ -156,21 +148,6 @@ func (s *Default) init(c *Config) (err error) {
return errors.Join(errs...)
}
-// initCustom initializes the custom-filter storage in s. c must not be nil.
-func (s *Default) initCustom(c *ConfigCustom) {
- s.custom = custom.New(&custom.Config{
- Logger: s.baseLogger.With(
- slogutil.KeyPrefix,
- path.Join("filters", string(filter.IDCustom)),
- ),
- ErrColl: s.errColl,
- CacheConf: &agdcache.LRUConfig{
- Count: c.CacheCount,
- },
- CacheManager: s.cacheManager,
- })
-}
-
// initBlockedServices initializes the blocked-service filter in s. c must not
// be nil.
func (s *Default) initBlockedServices(c *ConfigBlockedServices) (err error) {
@@ -298,13 +275,15 @@ func (s *Default) forClient(ctx context.Context, c *filter.ConfigClient) (f filt
s.setRuleLists(compConf, c.RuleList)
s.setSafeBrowsing(compConf, c.SafeBrowsing)
- compConf.Custom = s.custom.Get(ctx, c.Custom)
+ if c.Custom.Enabled {
+ compConf.Custom = c.Custom.Filter
+ }
return composite.New(compConf)
}
-// setParental sets the parental-control filters in compConf from c. c must not
-// be nil.
+// setParental checks if the parental-control filters are enabled and, if they
+// are, sets them in compConf from c. c must not be nil.
func (s *Default) setParental(
ctx context.Context,
compConf *composite.Config,
@@ -319,15 +298,27 @@ func (s *Default) setParental(
return
}
- if c.AdultBlockingEnabled {
+ s.setEnabledParental(ctx, compConf, c)
+}
+
+// setEnabledParental sets the parental-control filters in compConf from c. c
+// must not be nil.
+func (s *Default) setEnabledParental(
+ ctx context.Context,
+ compConf *composite.Config,
+ c *filter.ConfigParental,
+) {
+ // NOTE: Here and below always check the pointer for nil to avoid non-nil
+ // interface values containing nil pointers.
+ if c.AdultBlockingEnabled && s.adult != nil {
compConf.AdultBlocking = s.adult
}
- if c.SafeSearchGeneralEnabled {
+ if c.SafeSearchGeneralEnabled && s.safeSearchGeneral != nil {
compConf.GeneralSafeSearch = s.safeSearchGeneral
}
- if c.SafeSearchYouTubeEnabled {
+ if c.SafeSearchYouTubeEnabled && s.safeSearchYouTube != nil {
compConf.YouTubeSafeSearch = s.safeSearchYouTube
}
@@ -361,11 +352,11 @@ func (s *Default) setSafeBrowsing(compConf *composite.Config, c *filter.ConfigSa
return
}
- if c.DangerousDomainsEnabled {
+ if c.DangerousDomainsEnabled && s.dangerous != nil {
compConf.SafeBrowsing = s.dangerous
}
- if c.NewlyRegisteredDomainsEnabled {
+ if c.NewlyRegisteredDomainsEnabled && s.newlyRegistered != nil {
compConf.NewRegisteredDomains = s.newlyRegistered
}
}
diff --git a/internal/filter/filterstorage/default_test.go b/internal/filter/filterstorage/default_test.go
index 384e06b..3cb5216 100644
--- a/internal/filter/filterstorage/default_test.go
+++ b/internal/filter/filterstorage/default_test.go
@@ -7,9 +7,11 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter/custom"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/filterstorage"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/filtertest"
"github.com/AdguardTeam/golibs/container"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
@@ -105,12 +107,11 @@ func TestDefault_ForConfig_client(t *testing.T) {
newFltConfigSafeBrowsing(false, false),
)
- conf.Custom.ID = "1234"
- conf.Custom.UpdateTime = time.Now()
- conf.Custom.Rules = []filter.RuleText{
- filtertest.RuleBlock,
- }
conf.Custom.Enabled = true
+ conf.Custom.Filter = custom.New(&custom.Config{
+ Logger: slogutil.NewDiscardLogger(),
+ Rules: []filter.RuleText{filtertest.RuleBlock},
+ })
ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
f := s.ForConfig(ctx, conf)
diff --git a/internal/filter/filterstorage/filterstorage_test.go b/internal/filter/filterstorage/filterstorage_test.go
index b204ded..1632aa5 100644
--- a/internal/filter/filterstorage/filterstorage_test.go
+++ b/internal/filter/filterstorage/filterstorage_test.go
@@ -7,13 +7,13 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/filterstorage"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/filtertest"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
+ "github.com/AdguardTeam/golibs/timeutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/require"
)
@@ -162,7 +162,7 @@ func newDisabledConfig(
Enabled: false,
},
CacheManager: agdcache.EmptyManager{},
- Clock: agdtime.SystemClock{},
+ Clock: timeutil.SystemClock{},
ErrColl: agdtest.NewErrorCollector(),
Metrics: filter.EmptyMetrics{},
CacheDir: tb.TempDir(),
diff --git a/internal/filter/filterstorage/refresh.go b/internal/filter/filterstorage/refresh.go
index 322dbd9..e0ec7fe 100644
--- a/internal/filter/filterstorage/refresh.go
+++ b/internal/filter/filterstorage/refresh.go
@@ -9,19 +9,19 @@ import (
"slices"
"strings"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/refreshable"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
+ "github.com/AdguardTeam/golibs/service"
)
// type check
-var _ agdservice.Refresher = (*Default)(nil)
+var _ service.Refresher = (*Default)(nil)
-// Refresh implements the [agdservice.Refresher] interface for *Default.
+// Refresh implements the [service.Refresher] interface for *Default.
func (s *Default) Refresh(ctx context.Context) (err error) {
s.logger.InfoContext(ctx, "refresh started")
defer s.logger.InfoContext(ctx, "refresh finished")
diff --git a/internal/filter/hashprefix/filter.go b/internal/filter/hashprefix/filter.go
index dfc28b4..0d41696 100644
--- a/internal/filter/hashprefix/filter.go
+++ b/internal/filter/hashprefix/filter.go
@@ -11,16 +11,15 @@ import (
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/refreshable"
- "github.com/AdguardTeam/AdGuardDNS/internal/metrics"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist"
"github.com/AdguardTeam/golibs/netutil"
+ "github.com/AdguardTeam/golibs/service"
"github.com/c2h5oh/datasize"
"github.com/miekg/dns"
- "github.com/prometheus/client_golang/prometheus"
"golang.org/x/net/publicsuffix"
)
@@ -44,14 +43,14 @@ type FilterConfig struct {
// ErrColl is used to collect non-critical and rare errors.
ErrColl errcoll.Interface
+ // HashPrefixMtcs are the specific metrics for the hashprefix filter.
+ HashPrefixMtcs Metrics
+
// Metrics are the metrics for the hashprefix filter.
- //
- // TODO(a.garipov): Create a separate interface to also handle the
- // hashprefix-specific metrics.
- Metrics internal.Metrics
+ Metrics filter.Metrics
// ID is the ID of this hash storage for logging and error reporting.
- ID internal.ID
+ ID filter.ID
// CachePath is the path to the file containing the cached filtered
// hostnames, one per line.
@@ -85,7 +84,7 @@ type FilterConfig struct {
// cacheItem represents an item that we will store in the cache.
type cacheItem struct {
// res is the filtering result.
- res internal.Result
+ res filter.Result
// host is the cached normalized hostname for later cache key collision
// checks.
@@ -95,16 +94,17 @@ type cacheItem struct {
// Filter is a filter that matches hosts by their hashes based on a hash-prefix
// table. It should be initially refreshed with [Filter.RefreshInitial].
type Filter struct {
- logger *slog.Logger
- cloner *dnsmsg.Cloner
- hashes *Storage
- refr *refreshable.Refreshable
- errColl errcoll.Interface
- metrics internal.Metrics
- resCache agdcache.Interface[internal.CacheKey, *cacheItem]
- id internal.ID
- repIP netip.Addr
- repFQDN string
+ logger *slog.Logger
+ cloner *dnsmsg.Cloner
+ hashes *Storage
+ refr *refreshable.Refreshable
+ errColl errcoll.Interface
+ hashprefixMtcs Metrics
+ metrics filter.Metrics
+ resCache agdcache.Interface[rulelist.CacheKey, *cacheItem]
+ id filter.ID
+ repIP netip.Addr
+ repFQDN string
}
// IDPrefix is a common prefix for cache IDs, logging, and refreshes of
@@ -119,20 +119,21 @@ const IDPrefix = "filters/hashprefix"
func NewFilter(c *FilterConfig) (f *Filter, err error) {
id := c.ID
- resCache := agdcache.NewLRU[internal.CacheKey, *cacheItem](&agdcache.LRUConfig{
+ resCache := agdcache.NewLRU[rulelist.CacheKey, *cacheItem](&agdcache.LRUConfig{
Count: c.CacheCount,
})
c.CacheManager.Add(path.Join(IDPrefix, string(id)), resCache)
f = &Filter{
- logger: c.Logger,
- cloner: c.Cloner,
- hashes: c.Hashes,
- errColl: c.ErrColl,
- metrics: c.Metrics,
- resCache: resCache,
- id: id,
+ logger: c.Logger,
+ cloner: c.Cloner,
+ hashes: c.Hashes,
+ errColl: c.ErrColl,
+ hashprefixMtcs: c.HashPrefixMtcs,
+ metrics: c.Metrics,
+ resCache: resCache,
+ id: id,
}
repHost := c.ReplacementHost
@@ -164,20 +165,17 @@ func NewFilter(c *FilterConfig) (f *Filter, err error) {
return f, nil
}
-// type check
-var _ internal.RequestFilter = (*Filter)(nil)
-
-// FilterRequest implements the [internal.RequestFilter] interface for *Filter.
+// FilterRequest implements the [composite.RequestFilter] interface for *Filter.
// It modifies the request or response if host matches f.
func (f *Filter) FilterRequest(
ctx context.Context,
- req *internal.Request,
-) (r internal.Result, err error) {
+ req *filter.Request,
+) (r filter.Result, err error) {
host, qt, cl := req.Host, req.QType, req.QClass
- cacheKey := internal.NewCacheKey(host, qt, cl, false)
+ cacheKey := rulelist.NewCacheKey(host, qt, cl, false)
item, ok := f.itemFromCache(ctx, cacheKey, host)
- f.updateCacheLookupsMetrics(ok)
+ f.hashprefixMtcs.IncrementLookups(ctx, ok)
if ok {
return f.clonedResult(req.DNS, item.res), nil
}
@@ -214,7 +212,7 @@ func (f *Filter) FilterRequest(
f.setInCache(cacheKey, r, host)
- f.updateCacheSizeMetrics(f.resCache.Len())
+ f.hashprefixMtcs.UpdateCacheSize(ctx, f.resCache.Len())
return r, nil
}
@@ -224,7 +222,7 @@ func (f *Filter) FilterRequest(
// false.
func (f *Filter) itemFromCache(
ctx context.Context,
- key internal.CacheKey,
+ key rulelist.CacheKey,
host string,
) (item *cacheItem, ok bool) {
item, ok = f.resCache.Get(key)
@@ -241,11 +239,6 @@ func (f *Filter) itemFromCache(
return item, true
}
-// ID implements the [internal.RequestFilter] interface for *Filter.
-func (f *Filter) ID() (id internal.ID) {
- return f.id
-}
-
// isFilterable returns true if the question type is filterable. If the type is
// filterable with a blocked page, fam is the address family for the IP
// addresses of the blocked page; otherwise fam is [netutil.AddrFamilyNone].
@@ -260,14 +253,14 @@ func isFilterable(qt dnsmsg.RRType) (fam netutil.AddrFamily, ok bool) {
}
// clonedResult returns a clone of the result based on its type. r must be nil,
-// [*internal.ResultModifiedRequest], or [*internal.ResultModifiedResponse].
-func (f *Filter) clonedResult(req *dns.Msg, r internal.Result) (clone internal.Result) {
+// [*filter.ResultModifiedRequest], or [*filter.ResultModifiedResponse].
+func (f *Filter) clonedResult(req *dns.Msg, r filter.Result) (clone filter.Result) {
switch r := r.(type) {
case nil:
return nil
- case *internal.ResultModifiedRequest:
+ case *filter.ResultModifiedRequest:
return r.Clone(f.cloner)
- case *internal.ResultModifiedResponse:
+ case *filter.ResultModifiedResponse:
return r.CloneForReq(f.cloner, req)
default:
panic(fmt.Errorf("hashprefix: unexpected type for result: %T(%[1]v)", r))
@@ -276,19 +269,19 @@ func (f *Filter) clonedResult(req *dns.Msg, r internal.Result) (clone internal.R
// filteredResult returns a filtered request or response.
func (f *Filter) filteredResult(
- req *internal.Request,
+ req *filter.Request,
matched string,
fam netutil.AddrFamily,
-) (r internal.Result, err error) {
+) (r filter.Result, err error) {
if f.repFQDN != "" {
// Assume that the repFQDN is a valid domain name then.
modReq := f.cloner.Clone(req.DNS)
modReq.Question[0].Name = dns.Fqdn(f.repFQDN)
- return &internal.ResultModifiedRequest{
+ return &filter.ResultModifiedRequest{
Msg: modReq,
List: f.id,
- Rule: internal.RuleText(matched),
+ Rule: filter.RuleText(matched),
}, nil
}
@@ -297,17 +290,17 @@ func (f *Filter) filteredResult(
return nil, fmt.Errorf("filter %s: creating modified result: %w", f.id, err)
}
- return &internal.ResultModifiedResponse{
+ return &filter.ResultModifiedResponse{
Msg: resp,
List: f.id,
- Rule: internal.RuleText(matched),
+ Rule: filter.RuleText(matched),
}, nil
}
// respForFamily returns a filtered response in accordance with the protocol
// family and question type.
func (f *Filter) respForFamily(
- req *internal.Request,
+ req *filter.Request,
fam netutil.AddrFamily,
) (resp *dns.Msg, err error) {
if fam == netutil.AddrFamilyNone {
@@ -338,18 +331,18 @@ func (f *Filter) respForFamily(
// setInCache sets r in cache. It clones the result to make sure that
// modifications to the result message down the pipeline don't interfere with
-// the cached value. r must be either [*internal.ResultModifiedRequest] or
-// [*internal.ResultModifiedResponse].
+// the cached value. r must be either [*filter.ResultModifiedRequest] or
+// [*filter.ResultModifiedResponse].
//
// See AGDNS-359.
-func (f *Filter) setInCache(k internal.CacheKey, r internal.Result, host string) {
+func (f *Filter) setInCache(k rulelist.CacheKey, r filter.Result, host string) {
switch r := r.(type) {
- case *internal.ResultModifiedRequest:
+ case *filter.ResultModifiedRequest:
f.resCache.Set(k, &cacheItem{
res: r.Clone(f.cloner),
host: host,
})
- case *internal.ResultModifiedResponse:
+ case *filter.ResultModifiedResponse:
f.resCache.Set(k, &cacheItem{
res: r.Clone(f.cloner),
host: host,
@@ -359,44 +352,10 @@ func (f *Filter) setInCache(k internal.CacheKey, r internal.Result, host string)
}
}
-// updateCacheSizeMetrics updates cache size metrics.
-func (f *Filter) updateCacheSizeMetrics(size int) {
- switch id := f.id; id {
- case internal.IDSafeBrowsing:
- metrics.HashPrefixFilterSafeBrowsingCacheSize.Set(float64(size))
- case internal.IDAdultBlocking:
- metrics.HashPrefixFilterAdultBlockingCacheSize.Set(float64(size))
- case internal.IDNewRegDomains:
- metrics.HashPrefixFilterNewRegDomainsCacheSize.Set(float64(size))
- default:
- panic(fmt.Errorf("unsupported FilterListID %s", id))
- }
-}
-
-// updateCacheLookupsMetrics updates cache lookups metrics.
-func (f *Filter) updateCacheLookupsMetrics(hit bool) {
- var hitsMetric, missesMetric prometheus.Counter
- switch id := f.id; id {
- case internal.IDSafeBrowsing:
- hitsMetric = metrics.HashPrefixFilterCacheSafeBrowsingHits
- missesMetric = metrics.HashPrefixFilterCacheSafeBrowsingMisses
- case internal.IDAdultBlocking:
- hitsMetric = metrics.HashPrefixFilterCacheAdultBlockingHits
- missesMetric = metrics.HashPrefixFilterCacheAdultBlockingMisses
- case internal.IDNewRegDomains:
- hitsMetric = metrics.HashPrefixFilterCacheNewRegDomainsHits
- missesMetric = metrics.HashPrefixFilterCacheNewRegDomainsMisses
- default:
- panic(fmt.Errorf("unsupported filter list id %s", id))
- }
-
- metrics.IncrementCond(hit, hitsMetric, missesMetric)
-}
-
// type check
-var _ agdservice.Refresher = (*Filter)(nil)
+var _ service.Refresher = (*Filter)(nil)
-// Refresh implements the [agdservice.Refresher] interface for *Filter.
+// Refresh implements the [service.Refresher] interface for *Filter.
func (f *Filter) Refresh(ctx context.Context) (err error) {
f.logger.InfoContext(ctx, "refresh started")
defer f.logger.InfoContext(ctx, "refresh finished")
diff --git a/internal/filter/hashprefix/filter_test.go b/internal/filter/hashprefix/filter_test.go
index 1b4e6e5..10bb274 100644
--- a/internal/filter/hashprefix/filter_test.go
+++ b/internal/filter/hashprefix/filter_test.go
@@ -13,7 +13,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/composite"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/filtertest"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
@@ -22,6 +22,9 @@ import (
"github.com/stretchr/testify/require"
)
+// type check
+var _ composite.RequestFilter = (*hashprefix.Filter)(nil)
+
func TestFilter_FilterRequest_host(t *testing.T) {
t.Parallel()
@@ -31,7 +34,7 @@ func TestFilter_FilterRequest_host(t *testing.T) {
name string
host string
replHost string
- wantRule internal.RuleText
+ wantRule filter.RuleText
qType dnsmsg.RRType
wantResult bool
}{{
@@ -101,7 +104,7 @@ func TestFilter_FilterRequest_host(t *testing.T) {
ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
req := dnsservertest.NewReq(dns.Fqdn(tc.host), tc.qType, dns.ClassINET)
- r, err := f.FilterRequest(ctx, &internal.Request{
+ r, err := f.FilterRequest(ctx, &filter.Request{
DNS: req,
Messages: msgs,
Host: tc.host,
@@ -109,7 +112,7 @@ func TestFilter_FilterRequest_host(t *testing.T) {
})
require.NoError(t, err)
- var wantRes internal.Result
+ var wantRes filter.Result
if tc.wantResult {
if tc.replHost == filtertest.HostAdultContentRepl {
wantRes = newModReqResult(req, tc.wantRule)
@@ -123,7 +126,7 @@ func TestFilter_FilterRequest_host(t *testing.T) {
}
require.True(t, t.Run("cached_success", func(t *testing.T) {
- f := filtertest.NewHashprefixFilter(t, internal.IDAdultBlocking)
+ f := filtertest.NewHashprefixFilter(t, filter.IDAdultBlocking)
req := filtertest.NewARequest(t, filtertest.HostAdultContent)
@@ -138,7 +141,7 @@ func TestFilter_FilterRequest_host(t *testing.T) {
}))
require.True(t, t.Run("cached_no_match", func(t *testing.T) {
- f := filtertest.NewHashprefixFilter(t, internal.IDAdultBlocking)
+ f := filtertest.NewHashprefixFilter(t, filter.IDAdultBlocking)
req := filtertest.NewARequest(t, filtertest.Host)
@@ -153,7 +156,7 @@ func TestFilter_FilterRequest_host(t *testing.T) {
}))
require.True(t, t.Run("https", func(t *testing.T) {
- f := filtertest.NewHashprefixFilter(t, internal.IDAdultBlocking)
+ f := filtertest.NewHashprefixFilter(t, filter.IDAdultBlocking)
req := filtertest.NewRequest(
t,
@@ -175,7 +178,7 @@ func TestFilter_FilterRequest_host(t *testing.T) {
require.True(t, t.Run("https_ip", func(t *testing.T) {
f := filtertest.NewHashprefixFilterWithRepl(
t,
- internal.IDAdultBlocking,
+ filter.IDAdultBlocking,
filtertest.IPv4AdultContentReplStr,
)
@@ -192,7 +195,7 @@ func TestFilter_FilterRequest_host(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, r)
- m := testutil.RequireTypeAssert[*internal.ResultModifiedResponse](t, r)
+ m := testutil.RequireTypeAssert[*filter.ResultModifiedResponse](t, r)
require.NotNil(t, m.Msg)
require.Len(t, m.Msg.Question, 1)
@@ -207,13 +210,13 @@ func newModRespResult(
req *dns.Msg,
messages *dnsmsg.Constructor,
replIP netip.Addr,
-) (r *internal.ResultModifiedResponse) {
+) (r *filter.ResultModifiedResponse) {
tb.Helper()
resp, err := messages.NewRespIP(req, replIP)
require.NoError(tb, err)
- return &internal.ResultModifiedResponse{
+ return &filter.ResultModifiedResponse{
Msg: resp,
List: filter.IDAdultBlocking,
Rule: filtertest.HostAdultContent,
@@ -221,11 +224,11 @@ func newModRespResult(
}
// newModReqResult is a helper for creating modified results for tests.
-func newModReqResult(req *dns.Msg, rule internal.RuleText) (r *internal.ResultModifiedRequest) {
+func newModReqResult(req *dns.Msg, rule filter.RuleText) (r *filter.ResultModifiedRequest) {
req = dnsmsg.Clone(req)
req.Question[0].Name = filtertest.FQDNAdultContentRepl
- return &internal.ResultModifiedRequest{
+ return &filter.ResultModifiedRequest{
Msg: req,
List: filter.IDAdultBlocking,
Rule: rule,
@@ -248,8 +251,9 @@ func TestFilter_Refresh(t *testing.T) {
Hashes: strg,
URL: srvURL,
ErrColl: agdtest.NewErrorCollector(),
+ HashPrefixMtcs: hashprefix.EmptyMetrics{},
Metrics: filter.EmptyMetrics{},
- ID: internal.IDAdultBlocking,
+ ID: filter.IDAdultBlocking,
CachePath: cachePath,
ReplacementHost: filtertest.HostAdultContentRepl,
Staleness: filtertest.Staleness,
@@ -300,8 +304,9 @@ func TestFilter_FilterRequest_staleCache(t *testing.T) {
Hashes: strg,
URL: srvURL,
ErrColl: agdtest.NewErrorCollector(),
+ HashPrefixMtcs: hashprefix.EmptyMetrics{},
Metrics: filter.EmptyMetrics{},
- ID: internal.IDAdultBlocking,
+ ID: filter.IDAdultBlocking,
CachePath: cachePath,
ReplacementHost: filtertest.HostAdultContentRepl,
Staleness: filtertest.Staleness,
@@ -330,7 +335,7 @@ func TestFilter_FilterRequest_staleCache(t *testing.T) {
require.True(t, t.Run("hit_cached_host", func(t *testing.T) {
ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
- var r internal.Result
+ var r filter.Result
r, err = f.FilterRequest(ctx, otherHostReq)
require.NoError(t, err)
@@ -355,7 +360,7 @@ func TestFilter_FilterRequest_staleCache(t *testing.T) {
require.True(t, t.Run("previously_cached", func(t *testing.T) {
ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
- var r internal.Result
+ var r filter.Result
r, err = f.FilterRequest(ctx, otherHostReq)
require.NoError(t, err)
@@ -365,7 +370,7 @@ func TestFilter_FilterRequest_staleCache(t *testing.T) {
require.True(t, t.Run("new_host", func(t *testing.T) {
ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
- var r internal.Result
+ var r filter.Result
r, err = f.FilterRequest(ctx, hostReq)
require.NoError(t, err)
diff --git a/internal/filter/hashprefix/metrics.go b/internal/filter/hashprefix/metrics.go
new file mode 100644
index 0000000..fca1183
--- /dev/null
+++ b/internal/filter/hashprefix/metrics.go
@@ -0,0 +1,28 @@
+package hashprefix
+
+import (
+ "context"
+)
+
+// Metrics is an interface used for collection if the hashprefix filter
+// statistics.
+type Metrics interface {
+ // IncrementLookups increments the number of lookups. hit is true if the
+ // lookup returned a value.
+ IncrementLookups(ctx context.Context, hit bool)
+
+ // UpdateCacheSize is called when the cache size is updated.
+ UpdateCacheSize(ctx context.Context, cacheLen int)
+}
+
+// EmptyMetrics is the implementation of the [Metrics] interface that does nothing.
+type EmptyMetrics struct{}
+
+// type check
+var _ Metrics = EmptyMetrics{}
+
+// IncrementLookups implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) IncrementLookups(_ context.Context, _ bool) {}
+
+// UpdateCacheSize implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) UpdateCacheSize(_ context.Context, _ int) {}
diff --git a/internal/filter/internal/id.go b/internal/filter/id.go
similarity index 70%
rename from internal/filter/internal/id.go
rename to internal/filter/id.go
index 9f6cdee..d28a1e0 100644
--- a/internal/filter/internal/id.go
+++ b/internal/filter/id.go
@@ -1,13 +1,14 @@
-package internal
+package filter
import (
"fmt"
"unicode/utf8"
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdvalidate"
"github.com/AdguardTeam/golibs/errors"
)
-// ID is the identifier of a filter. It is an opaque string.
+// ID is the ID of a filter list. It is an opaque string.
type ID string
// The maximum and minimum lengths of a filter ID.
@@ -21,7 +22,7 @@ const (
func NewID(s string) (id ID, err error) {
defer func() { err = errors.Annotate(err, "bad filter id %q: %w", s) }()
- err = validateInclusion(len(s), MaxIDLen, MinIDLen, unitByte)
+ err = agdvalidate.Inclusion(len(s), MinIDLen, MaxIDLen, agdvalidate.UnitByte)
if err != nil {
return IDNone, err
}
@@ -29,65 +30,32 @@ func NewID(s string) (id ID, err error) {
// 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 {
+ if i, r := agdvalidate.FirstNonIDRune(s, true); i != -1 {
return IDNone, fmt.Errorf("bad rune %q at index %d", r, i)
}
return ID(s), nil
}
-// firstNonIDRune returns the first non-printable or non-ASCII rune and its
-// index. If slashes is true, it also looks for slashes. If there are no such
-// runes, i is -1.
-//
-// TODO(a.garipov): Merge with the one in package agd once the refactoring is
-// over.
-func firstNonIDRune(s string, slashes bool) (i int, r rune) {
- for i, r = range s {
- if r < '!' || r > '~' || (slashes && r == '/') {
- return i, r
- }
- }
-
- return -1, 0
-}
-
-// unit name constants.
-//
-// TODO(a.garipov): Merge with the one in package agd once the refactoring is
-// over.
-const (
- unitByte = "bytes"
- unitRune = "runes"
-)
-
-// validateInclusion returns an error if n is greater than max or less than min.
-// unitName is used for error messages, see unitFoo constants.
-//
-// TODO(a.garipov): Consider switching min and max; the current order seems
-// confusing.
-//
-// TODO(a.garipov): Merge with the one in package agd once the refactoring is
-// over.
-func validateInclusion(n, max, min int, unitName string) (err error) {
- switch {
- case n > max:
- return fmt.Errorf("too long: got %d %s, max %d", n, unitName, max)
- case n < min:
- return fmt.Errorf("too short: got %d %s, min %d", n, unitName, min)
- default:
- return nil
- }
-}
-
// Special ID values shared across the AdGuard DNS system.
//
// NOTE: DO NOT change these as other parts of the system depend on these
// values.
+//
+// TODO(a.garipov): Consider removing those that aren't used outside of the
+// filter subpackages.
const (
// IDNone means that no filter were applied at all.
IDNone ID = ""
+ // IDAdGuardDNS is the special filter ID of the main AdGuard DNS
+ // filtering-rule list. For this list, rule statistics are collected.
+ IDAdGuardDNS ID = "adguard_dns_filter"
+
+ // IDAdultBlocking is the special shared filter ID used when a request was
+ // filtered by the adult content blocking filter.
+ IDAdultBlocking ID = "adult_blocking"
+
// IDBlockedService is the shared filter ID used when a request was blocked
// by the service blocker.
IDBlockedService ID = "blocked_service"
@@ -96,29 +64,21 @@ const (
// by a custom profile rule.
IDCustom ID = "custom"
- // IDAdultBlocking is the special shared filter ID used when a request was
- // filtered by the adult content blocking filter.
- IDAdultBlocking ID = "adult_blocking"
-
- // IDSafeBrowsing is the special shared filter ID used when a request was
- // filtered by the safe browsing filter.
- IDSafeBrowsing ID = "safe_browsing"
+ // IDGeneralSafeSearch is the shared filter ID used when a request was
+ // modified by the general safe search filter.
+ IDGeneralSafeSearch ID = "general_safe_search"
// IDNewRegDomains is the special shared filter ID used when a request was
// filtered by the newly registered domains filter.
IDNewRegDomains ID = "newly_registered_domains"
- // IDGeneralSafeSearch is the shared filter ID used when a request was
- // modified by the general safe search filter.
- IDGeneralSafeSearch ID = "general_safe_search"
+ // IDSafeBrowsing is the special shared filter ID used when a request was
+ // filtered by the safe browsing filter.
+ IDSafeBrowsing ID = "safe_browsing"
// IDYoutubeSafeSearch is the special shared filter ID used when a request
// was modified by the YouTube safe search filter.
IDYoutubeSafeSearch ID = "youtube_safe_search"
-
- // IDAdGuardDNS is the special filter ID of the main AdGuard DNS
- // filtering-rule list. For this list, rule statistics are collected.
- IDAdGuardDNS ID = "adguard_dns_filter"
)
// RuleText is the text of a single rule within a rule-list filter.
@@ -132,7 +92,12 @@ const MaxRuleTextRuneLen = 1024
func NewRuleText(s string) (t RuleText, err error) {
defer func() { err = errors.Annotate(err, "bad filter rule text %q: %w", s) }()
- err = validateInclusion(utf8.RuneCountInString(s), MaxRuleTextRuneLen, 0, unitRune)
+ err = agdvalidate.Inclusion(
+ utf8.RuneCountInString(s),
+ 0,
+ MaxRuleTextRuneLen,
+ agdvalidate.UnitRune,
+ )
if err != nil {
return "", err
}
@@ -159,7 +124,12 @@ const (
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)
+ err = agdvalidate.Inclusion(
+ len(s),
+ MinBlockedServiceIDLen,
+ MaxBlockedServiceIDLen,
+ agdvalidate.UnitByte,
+ )
if err != nil {
return "", err
}
@@ -167,7 +137,7 @@ func NewBlockedServiceID(s string) (id BlockedServiceID, err error) {
// 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 {
+ if i, r := agdvalidate.FirstNonIDRune(s, true); i != -1 {
return "", fmt.Errorf("bad char %q at index %d", r, i)
}
diff --git a/internal/filter/internal/id_test.go b/internal/filter/id_test.go
similarity index 86%
rename from internal/filter/internal/id_test.go
rename to internal/filter/id_test.go
index 2db5f85..465b774 100644
--- a/internal/filter/internal/id_test.go
+++ b/internal/filter/id_test.go
@@ -1,10 +1,10 @@
-package internal_test
+package filter_test
import (
"strings"
"testing"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
)
@@ -38,7 +38,7 @@ func TestNewID(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
- id, err := internal.NewID(tc.in)
+ id, err := filter.NewID(tc.in)
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
if tc.wantErrMsg == "" && tc.in != "" {
assert.NotEmpty(t, id)
@@ -52,8 +52,8 @@ func TestNewID(t *testing.T) {
func TestNewRuleText(t *testing.T) {
t.Parallel()
- tooLong := strings.Repeat("a", internal.MaxRuleTextRuneLen+1)
- tooLongUnicode := strings.Repeat("ы", internal.MaxRuleTextRuneLen+1)
+ tooLong := strings.Repeat("a", filter.MaxRuleTextRuneLen+1)
+ tooLongUnicode := strings.Repeat("ы", filter.MaxRuleTextRuneLen+1)
testCases := []struct {
name string
@@ -86,7 +86,7 @@ func TestNewRuleText(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
- txt, err := internal.NewRuleText(tc.in)
+ txt, err := filter.NewRuleText(tc.in)
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
if tc.wantErrMsg == "" && tc.in != "" {
assert.NotEmpty(t, txt)
diff --git a/internal/filter/internal/cachekey.go b/internal/filter/internal/cachekey.go
deleted file mode 100644
index 9394a1a..0000000
--- a/internal/filter/internal/cachekey.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package internal
-
-import (
- "encoding/binary"
- "hash/maphash"
-
- "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
- "github.com/AdguardTeam/golibs/mathutil"
-)
-
-// CacheKey is the cache key type for [NewCacheKey].
-type CacheKey uint64
-
-// hashSeed is the seed used by all hashes to create hash keys.
-var hashSeed = maphash.MakeSeed()
-
-// NewCacheKey produces a cache key based on host, qt, and isAns using the
-// default algorithm.
-func NewCacheKey(host string, qt dnsmsg.RRType, cl dnsmsg.Class, isAns bool) (k CacheKey) {
- // Use maphash explicitly instead of using a key structure to reduce
- // allocations and optimize interface conversion up the stack.
- h := &maphash.Hash{}
- h.SetSeed(hashSeed)
-
- _, _ = h.WriteString(host)
-
- // Save on allocations by reusing a buffer.
- var buf [5]byte
- binary.LittleEndian.PutUint16(buf[:2], qt)
- binary.LittleEndian.PutUint16(buf[2:4], cl)
- buf[4] = mathutil.BoolToNumber[byte](isAns)
-
- _, _ = h.Write(buf[:])
-
- return CacheKey(h.Sum64())
-}
diff --git a/internal/filter/internal/composite/composite.go b/internal/filter/internal/composite/composite.go
index bf96949..0b5be03 100644
--- a/internal/filter/internal/composite/composite.go
+++ b/internal/filter/internal/composite/composite.go
@@ -4,15 +4,11 @@ package composite
import (
"context"
- "fmt"
"strings"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/safesearch"
"github.com/miekg/dns"
)
@@ -20,7 +16,7 @@ import (
// rule-list filters.
type Filter struct {
// custom is the custom rule-list filter of the profile, if any.
- custom *rulelist.Immutable
+ custom filter.Custom
// ruleLists are the enabled rule-list filters of the profile or filtering
// group.
@@ -32,29 +28,29 @@ type Filter struct {
// reqFilters are the safe-browsing and safe-search request filters in the
// composite filter.
- reqFilters []internal.RequestFilter
+ reqFilters []RequestFilter
}
// Config is the configuration structure for the composite filter.
type Config struct {
// SafeBrowsing is the safe-browsing filter to apply, if any.
- SafeBrowsing *hashprefix.Filter
+ SafeBrowsing RequestFilter
// AdultBlocking is the adult-content filter to apply, if any.
- AdultBlocking *hashprefix.Filter
+ AdultBlocking RequestFilter
// NewRegisteredDomains is the newly registered domains filter to apply, if
// any.
- NewRegisteredDomains *hashprefix.Filter
+ NewRegisteredDomains RequestFilter
// GeneralSafeSearch is the general safe-search filter to apply, if any.
- GeneralSafeSearch *safesearch.Filter
+ GeneralSafeSearch RequestFilter
// YouTubeSafeSearch is the youtube safe-search filter to apply, if any.
- YouTubeSafeSearch *safesearch.Filter
+ YouTubeSafeSearch RequestFilter
// Custom is the custom rule-list filter of the profile, if any.
- Custom *rulelist.Immutable
+ Custom filter.Custom
// RuleLists are the enabled rule-list filters of the profile or filtering
// group, if any. All items must not be nil.
@@ -65,6 +61,13 @@ type Config struct {
ServiceLists []*rulelist.Immutable
}
+// RequestFilter can filter a request based on the request info.
+type RequestFilter interface {
+ // FilterRequest filters a DNS request based on the information provided
+ // about the request. req must be valid.
+ FilterRequest(ctx context.Context, req *filter.Request) (r filter.Result, err error)
+}
+
// New returns a new composite filter. c must not be nil.
func New(c *Config) (f *Filter) {
f = &Filter{
@@ -74,22 +77,19 @@ func New(c *Config) (f *Filter) {
}
// DO NOT change the order of request filters without necessity.
- f.reqFilters = appendReqFilter(f.reqFilters, c.SafeBrowsing)
- f.reqFilters = appendReqFilter(f.reqFilters, c.AdultBlocking)
- f.reqFilters = appendReqFilter(f.reqFilters, c.GeneralSafeSearch)
- f.reqFilters = appendReqFilter(f.reqFilters, c.YouTubeSafeSearch)
- f.reqFilters = appendReqFilter(f.reqFilters, c.NewRegisteredDomains)
+ f.reqFilters = appendIfNotNil(f.reqFilters, c.SafeBrowsing)
+ f.reqFilters = appendIfNotNil(f.reqFilters, c.AdultBlocking)
+ f.reqFilters = appendIfNotNil(f.reqFilters, c.GeneralSafeSearch)
+ f.reqFilters = appendIfNotNil(f.reqFilters, c.YouTubeSafeSearch)
+ f.reqFilters = appendIfNotNil(f.reqFilters, c.NewRegisteredDomains)
return f
}
-// appendReqFilter appends flt to flts if flt is not nil.
-func appendReqFilter[T *hashprefix.Filter | *safesearch.Filter](
- flts []internal.RequestFilter,
- flt T,
-) (res []internal.RequestFilter) {
+// appendIfNotNil appends flt to flts if flt is not nil.
+func appendIfNotNil(flts []RequestFilter, flt RequestFilter) (res []RequestFilter) {
if flt != nil {
- flts = append(flts, internal.RequestFilter(flt))
+ flts = append(flts, flt)
}
return flts
@@ -98,7 +98,7 @@ func appendReqFilter[T *hashprefix.Filter | *safesearch.Filter](
// type check
var _ filter.Interface = (*Filter)(nil)
-// FilterRequest implements the [internal.Interface] interface for *Filter. The
+// FilterRequest implements the [filter.Interface] interface for *Filter. The
// order in which the filters are applied is the following:
//
// 1. Custom filter.
@@ -113,23 +113,23 @@ var _ filter.Interface = (*Filter)(nil)
// If f is empty, it returns nil with no error.
func (f *Filter) FilterRequest(
ctx context.Context,
- req *internal.Request,
-) (r internal.Result, err error) {
+ req *filter.Request,
+) (r filter.Result, err error) {
// Prepare common data for filters. Firstly, check the profile's rule-list
// filtering, the custom rules, and the rules from blocked services
// settings.
- rlRes := f.filterReqWithRuleLists(req)
+ rlRes := f.filterReqWithRuleLists(ctx, req)
switch flRes := rlRes.(type) {
- case *internal.ResultAllowed:
+ case *filter.ResultAllowed:
// Skip any additional filtering if the domain is explicitly allowed by
// user's custom rule.
- if flRes.List == internal.IDCustom {
+ if flRes.List == filter.IDCustom {
return flRes, nil
}
case
- *internal.ResultBlocked,
- *internal.ResultModifiedRequest,
- *internal.ResultModifiedResponse:
+ *filter.ResultBlocked,
+ *filter.ResultModifiedRequest,
+ *filter.ResultModifiedResponse:
// Skip any additional filtering if the query is already blocked or
// modified.
return flRes, nil
@@ -152,15 +152,20 @@ func (f *Filter) FilterRequest(
// filterReqWithRuleLists filters one question's information through all rule
// list filters of the composite filter. req must not be nil.
-func (f *Filter) filterReqWithRuleLists(req *internal.Request) (r internal.Result) {
+func (f *Filter) filterReqWithRuleLists(
+ ctx context.Context,
+ req *filter.Request,
+) (r filter.Result) {
ip, host, qt := req.RemoteIP, req.Host, req.QType
- ufRes := &rulelist.URLFilterResult{}
+ // TODO(a.garipov): Consider adding a pool of results to the default
+ // storage and use it here.
+ ufRes := newURLFilterResult()
if f.custom != nil {
- id := internal.IDCustom
+ id := filter.IDCustom
// Only use the device name for custom filters of profiles with devices.
- dr := f.custom.DNSResult(ip, req.ClientName, host, qt, false)
+ dr := f.custom.DNSResult(ctx, ip, req.ClientName, host, qt, false)
mod := rulelist.ProcessDNSRewrites(req, dr.DNSRewrites(), id)
if mod != nil {
// Process the DNS rewrites of the custom list and return them
@@ -168,7 +173,7 @@ func (f *Filter) filterReqWithRuleLists(req *internal.Request) (r internal.Resul
return mod
}
- ufRes.Add(dr)
+ ufRes.add(id, "", dr)
}
for _, rl := range f.ruleLists {
@@ -181,26 +186,27 @@ func (f *Filter) filterReqWithRuleLists(req *internal.Request) (r internal.Resul
return mod
}
- ufRes.Add(dr)
+ ufRes.add(id, "", dr)
}
for _, rl := range f.svcLists {
- ufRes.Add(rl.DNSResult(ip, "", host, qt, false))
+ id, svcID := rl.ID()
+ ufRes.add(id, svcID, rl.DNSResult(ip, "", host, qt, false))
}
- return ufRes.ToInternal(f, qt)
+ return ufRes.toInternal(qt)
}
-// FilterResponse implements the [internal.Interface] interface for *Filter. It
+// FilterResponse implements the [filter.Interface] interface for *Filter. It
// returns the action created from the filter list network rule with the highest
// priority. If f is empty, it returns nil with no error. Note that rewrite
// results are not applied to responses.
func (f *Filter) FilterResponse(
- _ context.Context,
- resp *internal.Response,
-) (r internal.Result, err error) {
+ ctx context.Context,
+ resp *filter.Response,
+) (r filter.Result, err error) {
for _, ans := range resp.DNS.Answer {
- r = f.filterAnswer(resp, ans)
+ r = f.filterAnswer(ctx, resp, ans)
if r != nil {
break
}
@@ -211,9 +217,13 @@ func (f *Filter) FilterResponse(
// filterAnswer filters a single answer of a response. r is not nil if the
// response is filtered.
-func (f *Filter) filterAnswer(resp *internal.Response, ans dns.RR) (r internal.Result) {
+func (f *Filter) filterAnswer(
+ ctx context.Context,
+ resp *filter.Response,
+ ans dns.RR,
+) (r filter.Result) {
if rr, ok := ans.(*dns.HTTPS); ok {
- return f.filterHTTPSAnswer(resp, rr)
+ return f.filterHTTPSAnswer(ctx, resp, rr)
}
host, rrType, ok := parseRespAnswer(ans)
@@ -221,39 +231,47 @@ func (f *Filter) filterAnswer(resp *internal.Response, ans dns.RR) (r internal.R
return nil
}
- return f.filterRespWithRuleLists(resp, host, rrType)
+ return f.filterRespWithRuleLists(ctx, resp, host, rrType)
}
// filterRespWithRuleLists filters one answer's information through all
// rule-list filters of the composite filter.
func (f *Filter) filterRespWithRuleLists(
- resp *internal.Response,
+ ctx context.Context,
+ resp *filter.Response,
host string,
rrType dnsmsg.RRType,
-) (r internal.Result) {
- ufRes := &rulelist.URLFilterResult{}
+) (r filter.Result) {
+ ufRes := newURLFilterResult()
for _, rl := range f.ruleLists {
- ufRes.Add(rl.DNSResult(resp.RemoteIP, "", host, rrType, true))
+ id, _ := rl.ID()
+ ufRes.add(id, "", rl.DNSResult(resp.RemoteIP, "", host, rrType, true))
}
if f.custom != nil {
- ufRes.Add(f.custom.DNSResult(resp.RemoteIP, resp.ClientName, host, rrType, true))
+ dr := f.custom.DNSResult(ctx, resp.RemoteIP, resp.ClientName, host, rrType, true)
+ ufRes.add(filter.IDCustom, "", dr)
}
for _, rl := range f.svcLists {
- ufRes.Add(rl.DNSResult(resp.RemoteIP, "", host, rrType, true))
+ id, svcID := rl.ID()
+ ufRes.add(id, svcID, rl.DNSResult(resp.RemoteIP, "", host, rrType, true))
}
- return ufRes.ToInternal(f, rrType)
+ return ufRes.toInternal(rrType)
}
// filterHTTPSAnswer filters HTTPS answers information through all rule list
// filters of the composite filter.
-func (f *Filter) filterHTTPSAnswer(resp *internal.Response, rr *dns.HTTPS) (r internal.Result) {
+func (f *Filter) filterHTTPSAnswer(
+ ctx context.Context,
+ resp *filter.Response,
+ rr *dns.HTTPS,
+) (r filter.Result) {
for _, kv := range rr.Value {
switch kv.Key() {
case dns.SVCB_IPV4HINT, dns.SVCB_IPV6HINT:
- r = f.filterSVCBHint(kv.String(), resp)
+ r = f.filterSVCBHint(ctx, kv.String(), resp)
if r != nil {
return r
}
@@ -267,9 +285,13 @@ func (f *Filter) filterHTTPSAnswer(resp *internal.Response, rr *dns.HTTPS) (r in
// filterSVCBHint filters SVCB hint information through all rule list filters of
// the composite filter.
-func (f *Filter) filterSVCBHint(hint string, resp *internal.Response) (r internal.Result) {
+func (f *Filter) filterSVCBHint(
+ ctx context.Context,
+ hint string,
+ resp *filter.Response,
+) (r filter.Result) {
for _, s := range strings.Split(hint, ",") {
- r = f.filterRespWithRuleLists(resp, s, dns.TypeHTTPS)
+ r = f.filterRespWithRuleLists(ctx, resp, s, dns.TypeHTTPS)
if r != nil {
return r
}
@@ -293,31 +315,3 @@ func parseRespAnswer(ans dns.RR) (hostname string, rrType dnsmsg.RRType, ok bool
return "", dns.TypeNone, false
}
}
-
-// type check
-var _ rulelist.IDMapper = (*Filter)(nil)
-
-// Map implements the [rulelist.IDMapper] interface for *Filter. It returns the
-// rule list data by its synthetic integer ID in the urlfilter engine. It
-// panics if id is not found.
-func (f *Filter) Map(id int) (fltID internal.ID, svcID internal.BlockedServiceID) {
- for _, rl := range f.ruleLists {
- if rl.URLFilterID() == id {
- return rl.ID()
- }
- }
-
- if rl := f.custom; rl != nil && rl.URLFilterID() == id {
- return rl.ID()
- }
-
- for _, rl := range f.svcLists {
- if rl.URLFilterID() == id {
- return rl.ID()
- }
- }
-
- // Technically shouldn't happen, since id is supposed to be among the rule
- // list filters in the composite filter.
- panic(fmt.Errorf("filter: synthetic id %d not found", id))
-}
diff --git a/internal/filter/internal/composite/composite_internal_test.go b/internal/filter/internal/composite/composite_internal_test.go
index 715f58f..a018298 100644
--- a/internal/filter/internal/composite/composite_internal_test.go
+++ b/internal/filter/internal/composite/composite_internal_test.go
@@ -1,40 +1,40 @@
package composite
import (
+ "context"
"testing"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/filtertest"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist"
"github.com/miekg/dns"
- "github.com/stretchr/testify/require"
)
// Sinks for benchmarks.
var (
- resultSink internal.Result
+ resultSink filter.Result
)
func BenchmarkFilter_FilterReqWithRuleLists(b *testing.B) {
- blockingRL, err := rulelist.NewFromString(
+ blockingRL := rulelist.NewFromString(
filtertest.RuleBlockStr+"\n",
"test",
"",
- rulelist.ResultCacheEmpty{},
+ rulelist.EmptyResultCache{},
)
- require.NoError(b, err)
f := New(&Config{
RuleLists: []*rulelist.Refreshable{blockingRL},
})
+ ctx := context.Background()
req := filtertest.NewRequest(b, "", filtertest.HostBlocked, filtertest.IPv4Client, dns.TypeA)
b.ReportAllocs()
b.ResetTimer()
for range b.N {
- resultSink = f.filterReqWithRuleLists(req)
+ resultSink = f.filterReqWithRuleLists(ctx, req)
}
// Most recent results:
diff --git a/internal/filter/internal/composite/composite_test.go b/internal/filter/internal/composite/composite_test.go
index 129c113..da57a31 100644
--- a/internal/filter/internal/composite/composite_test.go
+++ b/internal/filter/internal/composite/composite_test.go
@@ -7,10 +7,12 @@ import (
"testing"
"time"
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter/custom"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/composite"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/filtertest"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/refreshable"
@@ -23,41 +25,26 @@ import (
"github.com/stretchr/testify/require"
)
-// newFromStr is a helper to create a rule-list filter from a rule text and a
-// filtering-list ID.
-func newFromStr(tb testing.TB, text string, id internal.ID) (rl *rulelist.Refreshable) {
- tb.Helper()
-
- rl, err := rulelist.NewFromString(text, id, "", rulelist.ResultCacheEmpty{})
- require.NoError(tb, err)
-
- return rl
-}
-
-// newImmutable is a helper to create an immutable rule-list filter from a rule
-// text and a filtering-list ID.
-func newImmutable(tb testing.TB, text string, id internal.ID) (rl *rulelist.Immutable) {
- tb.Helper()
-
- rl, err := rulelist.NewImmutable(text, id, "", rulelist.ResultCacheEmpty{})
- require.NoError(tb, err)
-
- return rl
-}
-
// newReqData returns data for calling FilterRequest. The context uses
// [filtertest.Timeout] and [tb.Cleanup] is used for its cancelation. req uses
// [filtertest.FQDNBlocked], [dns.TypeA], and [dns.ClassINET] for the request
// data.
-func newReqData(tb testing.TB) (ctx context.Context, req *internal.Request) {
+func newReqData(tb testing.TB) (ctx context.Context, req *filter.Request) {
+ tb.Helper()
+
+ return newReqDataWithFQDN(tb, filtertest.FQDNBlocked)
+}
+
+// newReqDataWithFQDN is like [newReqData] but allows setting the FQDN.
+func newReqDataWithFQDN(tb testing.TB, fqdn string) (ctx context.Context, req *filter.Request) {
tb.Helper()
ctx = testutil.ContextWithTimeout(tb, filtertest.Timeout)
- req = &internal.Request{
- DNS: dnsservertest.NewReq(filtertest.FQDNBlocked, dns.TypeA, dns.ClassINET),
+ req = &filter.Request{
+ DNS: dnsservertest.NewReq(fqdn, dns.TypeA, dns.ClassINET),
Messages: agdtest.NewConstructor(tb),
RemoteIP: filtertest.IPv4Client,
- Host: filtertest.HostBlocked,
+ Host: agdnet.NormalizeDomain(fqdn),
QType: dns.TypeA,
QClass: dns.ClassINET,
}
@@ -66,29 +53,29 @@ func newReqData(tb testing.TB) (ctx context.Context, req *internal.Request) {
}
func TestFilter_FilterRequest_customWithClientName(t *testing.T) {
- const (
- devName = "MyDevice"
- blockRule = filtertest.RuleBlockStr + "$client=" + devName
- )
-
f := composite.New(&composite.Config{
- Custom: newImmutable(t, blockRule, internal.IDCustom),
+ Custom: custom.New(&custom.Config{
+ Logger: slogutil.NewDiscardLogger(),
+ Rules: []filter.RuleText{
+ filtertest.RuleBlockForClientName,
+ },
+ }),
})
- ctx, req := newReqData(t)
+ ctx, req := newReqDataWithFQDN(t, filtertest.FQDNBlockedForClientName)
res, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
assert.Nil(t, res)
- req.ClientName = devName
+ req.ClientName = filtertest.ClientName
res, err = f.FilterRequest(ctx, req)
require.NoError(t, err)
- wantRes := &internal.ResultBlocked{
- List: internal.IDCustom,
- Rule: blockRule,
+ wantRes := &filter.ResultBlocked{
+ List: filter.IDCustom,
+ Rule: filtertest.RuleBlockForClientName,
}
assert.Equal(t, wantRes, res)
@@ -105,11 +92,11 @@ func TestFilter_FilterRequest_badfilter(t *testing.T) {
testCases := []struct {
name string
- wantRes internal.Result
+ wantRes filter.Result
ruleLists []*rulelist.Refreshable
}{{
name: "block",
- wantRes: &internal.ResultBlocked{
+ wantRes: &filter.ResultBlocked{
List: filtertest.RuleListID1,
Rule: blockRule,
},
@@ -139,11 +126,22 @@ func TestFilter_FilterRequest_badfilter(t *testing.T) {
}
}
+// newFromStr is a helper to create a rule-list filter from a rule text and a
+// filtering-list ID.
+func newFromStr(tb testing.TB, text string, id filter.ID) (rl *rulelist.Refreshable) {
+ tb.Helper()
+
+ return rulelist.NewFromString(text, id, "", rulelist.EmptyResultCache{})
+}
+
func TestFilter_FilterRequest_customAllow(t *testing.T) {
- const allowRule = "@@" + filtertest.RuleBlockStr
+ const allowRule = "@@" + filtertest.RuleBlock
blockingRL := newFromStr(t, filtertest.RuleBlockStr, filtertest.RuleListID1)
- customRL := newImmutable(t, allowRule, internal.IDCustom)
+ customRL := custom.New(&custom.Config{
+ Logger: slogutil.NewDiscardLogger(),
+ Rules: []filter.RuleText{allowRule},
+ })
f := composite.New(&composite.Config{
Custom: customRL,
@@ -154,8 +152,8 @@ func TestFilter_FilterRequest_customAllow(t *testing.T) {
res, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
- want := &internal.ResultAllowed{
- List: internal.IDCustom,
+ want := &filter.ResultAllowed{
+ List: filter.IDCustom,
Rule: allowRule,
}
assert.Equal(t, want, res)
@@ -177,10 +175,10 @@ func TestFilter_FilterRequest_dnsrewrite(t *testing.T) {
var (
rlNonRewrite = newFromStr(t, blockRule, filtertest.RuleListID1)
- rlCustomRefused = newImmutable(t, dnsRewriteRuleRefused, internal.IDCustom)
- rlCustomCname = newImmutable(t, dnsRewriteRuleCname, internal.IDCustom)
- rlCustom2Rules = newImmutable(t, dnsRewrite2Rules, internal.IDCustom)
- rlCustomTyped = newImmutable(t, dnsRewriteTypedRules, internal.IDCustom)
+ rlCustomRefused = newCustom(t, dnsRewriteRuleRefused)
+ rlCustomCname = newCustom(t, dnsRewriteRuleCname)
+ rlCustom2Rules = newCustom(t, dnsRewrite2Rules)
+ rlCustomTyped = newCustom(t, dnsRewriteTypedRules)
)
req := dnsservertest.NewReq(filtertest.FQDNBlocked, dns.TypeA, dns.ClassINET)
@@ -196,15 +194,15 @@ func TestFilter_FilterRequest_dnsrewrite(t *testing.T) {
soaReq.Question[0].Qtype = dns.TypeSOA
testCases := []struct {
- custom *rulelist.Immutable
+ custom filter.Custom
req *dns.Msg
- wantRes internal.Result
+ wantRes filter.Result
name string
ruleLists []*rulelist.Refreshable
}{{
custom: nil,
req: req,
- wantRes: &internal.ResultBlocked{
+ wantRes: &filter.ResultBlocked{
List: filtertest.RuleListID1,
Rule: blockRule,
},
@@ -213,7 +211,7 @@ func TestFilter_FilterRequest_dnsrewrite(t *testing.T) {
}, {
custom: nil,
req: req,
- wantRes: &internal.ResultBlocked{
+ wantRes: &filter.ResultBlocked{
List: filtertest.RuleListID1,
Rule: blockRule,
},
@@ -222,9 +220,9 @@ func TestFilter_FilterRequest_dnsrewrite(t *testing.T) {
}, {
custom: rlCustomRefused,
req: req,
- wantRes: &internal.ResultModifiedResponse{
+ wantRes: &filter.ResultModifiedResponse{
Msg: dnsservertest.NewResp(dns.RcodeRefused, req),
- List: internal.IDCustom,
+ List: filter.IDCustom,
Rule: dnsRewriteRuleRefused,
},
name: "dnsrewrite_block",
@@ -232,9 +230,9 @@ func TestFilter_FilterRequest_dnsrewrite(t *testing.T) {
}, {
custom: rlCustomCname,
req: req,
- wantRes: &internal.ResultModifiedRequest{
+ wantRes: &filter.ResultModifiedRequest{
Msg: modifiedReq,
- List: internal.IDCustom,
+ List: filter.IDCustom,
Rule: dnsRewriteRuleCname,
},
name: "dnsrewrite_cname",
@@ -242,7 +240,7 @@ func TestFilter_FilterRequest_dnsrewrite(t *testing.T) {
}, {
custom: rlCustom2Rules,
req: req,
- wantRes: &internal.ResultModifiedResponse{
+ wantRes: &filter.ResultModifiedResponse{
Msg: dnsservertest.NewResp(dns.RcodeSuccess, req, dnsservertest.SectionAnswer{
dnsservertest.NewA(
filtertest.FQDNBlocked,
@@ -255,7 +253,7 @@ func TestFilter_FilterRequest_dnsrewrite(t *testing.T) {
netip.MustParseAddr("1.2.3.5"),
),
}),
- List: internal.IDCustom,
+ List: filter.IDCustom,
Rule: "",
},
name: "dnsrewrite_answers",
@@ -263,7 +261,7 @@ func TestFilter_FilterRequest_dnsrewrite(t *testing.T) {
}, {
custom: rlCustomTyped,
req: txtReq,
- wantRes: &internal.ResultModifiedResponse{
+ wantRes: &filter.ResultModifiedResponse{
Msg: dnsservertest.NewResp(dns.RcodeSuccess, txtReq, dnsservertest.SectionAnswer{
dnsservertest.NewTXT(
filtertest.FQDNBlocked,
@@ -271,7 +269,7 @@ func TestFilter_FilterRequest_dnsrewrite(t *testing.T) {
"abcdefg",
),
}),
- List: internal.IDCustom,
+ List: filter.IDCustom,
Rule: "",
},
name: "dnsrewrite_txt",
@@ -279,9 +277,9 @@ func TestFilter_FilterRequest_dnsrewrite(t *testing.T) {
}, {
custom: rlCustomTyped,
req: soaReq,
- wantRes: &internal.ResultModifiedResponse{
+ wantRes: &filter.ResultModifiedResponse{
Msg: dnsservertest.NewResp(dns.RcodeSuccess, soaReq),
- List: internal.IDCustom,
+ List: filter.IDCustom,
},
name: "dnsrewrite_soa",
ruleLists: []*rulelist.Refreshable{},
@@ -289,13 +287,15 @@ func TestFilter_FilterRequest_dnsrewrite(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- f := composite.New(&composite.Config{
+ c := &composite.Config{
Custom: tc.custom,
RuleLists: tc.ruleLists,
- })
+ }
+
+ f := composite.New(c)
ctx := context.Background()
- res, fltErr := f.FilterRequest(ctx, &internal.Request{
+ res, fltErr := f.FilterRequest(ctx, &filter.Request{
DNS: tc.req,
Messages: agdtest.NewConstructor(t),
Host: filtertest.HostBlocked,
@@ -309,6 +309,18 @@ func TestFilter_FilterRequest_dnsrewrite(t *testing.T) {
}
}
+// newCustom is a helper to create a cusotm filter from a rule text.
+func newCustom(tb testing.TB, text string) (f *custom.Filter) {
+ tb.Helper()
+
+ return custom.New(&custom.Config{
+ Logger: slogutil.NewDiscardLogger(),
+ Rules: []filter.RuleText{
+ filter.RuleText(text),
+ },
+ })
+}
+
func TestFilter_FilterRequest_hostsRules(t *testing.T) {
const (
reqHost4 = "www.example.com"
@@ -326,18 +338,18 @@ func TestFilter_FilterRequest_hostsRules(t *testing.T) {
RuleLists: []*rulelist.Refreshable{rl},
})
- resBlocked4 := &internal.ResultBlocked{
+ resBlocked4 := &filter.ResultBlocked{
List: filtertest.RuleListID1,
Rule: blockRule4,
}
- resBlocked6 := &internal.ResultBlocked{
+ resBlocked6 := &filter.ResultBlocked{
List: filtertest.RuleListID1,
Rule: blockRule6,
}
testCases := []struct {
- wantRes internal.Result
+ wantRes filter.Result
name string
reqHost string
reqType dnsmsg.RRType
@@ -379,7 +391,7 @@ func TestFilter_FilterRequest_hostsRules(t *testing.T) {
}
ctx := context.Background()
- fltReq := &internal.Request{
+ fltReq := &filter.Request{
DNS: req,
Messages: agdtest.NewConstructor(t),
Host: tc.reqHost,
@@ -399,7 +411,7 @@ func TestFilter_FilterRequest_safeSearch(t *testing.T) {
const rewriteRule = filtertest.RuleSafeSearchGeneralIPv4Str + "\n"
cachePath, srvURL := filtertest.PrepareRefreshable(t, nil, rewriteRule, http.StatusOK)
- const fltListID = internal.IDGeneralSafeSearch
+ const fltListID = filter.IDGeneralSafeSearch
gen, err := safesearch.New(
&safesearch.Config{
@@ -425,10 +437,7 @@ func TestFilter_FilterRequest_safeSearch(t *testing.T) {
GeneralSafeSearch: gen,
})
- ctx, req := newReqData(t)
- req.DNS.Question[0].Name = filtertest.FQDNSafeSearchGeneralIPv4
- req.Host = filtertest.HostSafeSearchGeneralIPv4
-
+ ctx, req := newReqDataWithFQDN(t, filtertest.FQDNSafeSearchGeneralIPv4)
res, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
@@ -439,7 +448,7 @@ func TestFilter_FilterRequest_safeSearch(t *testing.T) {
filtertest.IPv4SafeSearchRepl,
),
})
- want := &internal.ResultModifiedResponse{
+ want := &filter.ResultModifiedResponse{
Msg: wantResp,
List: fltListID,
Rule: filtertest.HostSafeSearchGeneralIPv4,
@@ -448,13 +457,12 @@ func TestFilter_FilterRequest_safeSearch(t *testing.T) {
}
func TestFilter_FilterRequest_services(t *testing.T) {
- svcRL, err := rulelist.NewImmutable(
+ svcRL := rulelist.NewImmutable(
filtertest.RuleBlockStr,
- internal.IDBlockedService,
+ filter.IDBlockedService,
filtertest.BlockedServiceID1,
- rulelist.ResultCacheEmpty{},
+ rulelist.EmptyResultCache{},
)
- require.NoError(t, err)
f := composite.New(&composite.Config{
ServiceLists: []*rulelist.Immutable{svcRL},
@@ -464,9 +472,9 @@ func TestFilter_FilterRequest_services(t *testing.T) {
res, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
- want := &internal.ResultBlocked{
- List: internal.IDBlockedService,
- Rule: internal.RuleText(filtertest.BlockedServiceID1),
+ want := &filter.ResultBlocked{
+ List: filter.IDBlockedService,
+ Rule: filter.RuleText(filtertest.BlockedServiceID1),
}
assert.Equal(t, want, res)
}
@@ -499,7 +507,7 @@ func TestFilter_FilterResponse(t *testing.T) {
testCases := []struct {
name string
reqFQDN string
- wantRule internal.RuleText
+ wantRule filter.RuleText
respAns dnsservertest.SectionAnswer
qType dnsmsg.RRType
}{{
@@ -583,11 +591,10 @@ func TestFilter_FilterResponse(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- ctx, req := newReqData(t)
- req.DNS.Question[0].Name = tc.reqFQDN
+ ctx, req := newReqDataWithFQDN(t, tc.reqFQDN)
req.DNS.Question[0].Qtype = tc.qType
- res, err := f.FilterResponse(ctx, &internal.Response{
+ res, err := f.FilterResponse(ctx, &filter.Response{
DNS: dnsservertest.NewResp(dns.RcodeSuccess, req.DNS, tc.respAns),
RemoteIP: filtertest.IPv4Client,
})
@@ -599,7 +606,7 @@ func TestFilter_FilterResponse(t *testing.T) {
return
}
- want := &internal.ResultBlocked{
+ want := &filter.ResultBlocked{
List: filtertest.RuleListID1,
Rule: tc.wantRule,
}
diff --git a/internal/filter/internal/composite/result.go b/internal/filter/internal/composite/result.go
new file mode 100644
index 0000000..72e185f
--- /dev/null
+++ b/internal/filter/internal/composite/result.go
@@ -0,0 +1,189 @@
+package composite
+
+import (
+ "fmt"
+
+ "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
+ "github.com/AdguardTeam/urlfilter"
+ "github.com/AdguardTeam/urlfilter/rules"
+ "github.com/miekg/dns"
+)
+
+// urlFilterResult is an entity simplifying the collection and compilation of
+// urlfilter results. It contains per-pointer indexes of the IDs of filters
+// producing network and host rules.
+type urlFilterResult struct {
+ netRuleIDs map[*rules.NetworkRule]filter.ID
+ hostRuleIDs map[*rules.HostRule]filter.ID
+
+ netRuleSvcIDs map[*rules.NetworkRule]filter.BlockedServiceID
+ hostRuleSvcIDs map[*rules.HostRule]filter.BlockedServiceID
+
+ networkRules []*rules.NetworkRule
+ hostRules4 []*rules.HostRule
+ hostRules6 []*rules.HostRule
+}
+
+// newURLFilterResult returns a properly initialized *urlFilterResult.
+func newURLFilterResult() (r *urlFilterResult) {
+ return &urlFilterResult{
+ netRuleIDs: map[*rules.NetworkRule]filter.ID{},
+ hostRuleIDs: map[*rules.HostRule]filter.ID{},
+
+ netRuleSvcIDs: map[*rules.NetworkRule]filter.BlockedServiceID{},
+ hostRuleSvcIDs: map[*rules.HostRule]filter.BlockedServiceID{},
+ }
+}
+
+// add appends the rules from dr to the slices within r. If dr is nil, add does
+// nothing.
+func (r *urlFilterResult) add(
+ id filter.ID,
+ svcID filter.BlockedServiceID,
+ dr *urlfilter.DNSResult,
+) {
+ if dr == nil {
+ return
+ }
+
+ for _, nr := range dr.NetworkRules {
+ r.networkRules = append(r.networkRules, nr)
+ r.netRuleIDs[nr] = id
+ if svcID != "" {
+ r.netRuleSvcIDs[nr] = svcID
+ }
+ }
+
+ r.addHostRules(id, svcID, dr.HostRulesV4, dr.HostRulesV6)
+}
+
+// addHostRules adds the host rules to the result.
+func (r *urlFilterResult) addHostRules(
+ id filter.ID,
+ svcID filter.BlockedServiceID,
+ hostRules4 []*rules.HostRule,
+ hostRules6 []*rules.HostRule,
+) {
+ for _, hr4 := range hostRules4 {
+ r.hostRules4 = append(r.hostRules4, hr4)
+ r.hostRuleIDs[hr4] = id
+ if svcID != "" {
+ r.hostRuleSvcIDs[hr4] = svcID
+ }
+ }
+
+ for _, hr6 := range hostRules6 {
+ r.hostRules6 = append(r.hostRules6, hr6)
+ r.hostRuleIDs[hr6] = id
+ if svcID != "" {
+ r.hostRuleSvcIDs[hr6] = svcID
+ }
+ }
+}
+
+// toInternal converts a result of using several urlfilter rulelists into a
+// filter.Result.
+func (r *urlFilterResult) toInternal(rrType dnsmsg.RRType) (res filter.Result) {
+ if nr := rules.GetDNSBasicRule(r.networkRules); nr != nil {
+ return r.netRuleDataToResult(nr)
+ }
+
+ return r.hostsRulesToResult(rrType)
+}
+
+// netRuleDataToResult converts a urlfilter network rule into a filtering
+// result.
+func (r *urlFilterResult) netRuleDataToResult(nr *rules.NetworkRule) (res filter.Result) {
+ fltID, ok := r.netRuleIDs[nr]
+ if !ok {
+ // Shouldn't happen, since fltID is supposed to be among the filters
+ // added to the result.
+ panic(fmt.Errorf("composite: filter id %q not found", fltID))
+ }
+
+ var rule filter.RuleText
+ if fltID == filter.IDBlockedService {
+ var svcID filter.BlockedServiceID
+ svcID, ok = r.netRuleSvcIDs[nr]
+ if !ok {
+ // Shouldn't happen, since svcID is supposed to be among the filters
+ // added to the result.
+ panic(fmt.Errorf("composite: service id %q not found", svcID))
+ }
+
+ rule = filter.RuleText(svcID)
+ } else {
+ rule = filter.RuleText(nr.RuleText)
+ }
+
+ if nr.Whitelist {
+ return &filter.ResultAllowed{
+ List: fltID,
+ Rule: rule,
+ }
+ }
+
+ return &filter.ResultBlocked{
+ List: fltID,
+ Rule: rule,
+ }
+}
+
+// hostsRulesToResult converts /etc/hosts-style rules into a filtering result.
+func (r *urlFilterResult) hostsRulesToResult(rrType dnsmsg.RRType) (res filter.Result) {
+ if len(r.hostRules4) == 0 && len(r.hostRules6) == 0 {
+ return nil
+ }
+
+ // Only use the first matched rule, since we currently don't care about the
+ // IP addresses in the rule. If the request is neither an A one nor an AAAA
+ // one, or if there are no matching rules of the requested type, then use
+ // whatever rule isn't empty.
+ //
+ // See also AGDNS-591.
+ var resHostRule *rules.HostRule
+ if rrType == dns.TypeA && len(r.hostRules4) > 0 {
+ resHostRule = r.hostRules4[0]
+ } else if rrType == dns.TypeAAAA && len(r.hostRules6) > 0 {
+ resHostRule = r.hostRules6[0]
+ } else {
+ if len(r.hostRules4) > 0 {
+ resHostRule = r.hostRules4[0]
+ } else {
+ resHostRule = r.hostRules6[0]
+ }
+ }
+
+ return r.hostRuleDataToResult(resHostRule)
+}
+
+// hostRuleDataToResult converts a urlfilter host rule into a filtering result.
+func (r *urlFilterResult) hostRuleDataToResult(hr *rules.HostRule) (res filter.Result) {
+ fltID, ok := r.hostRuleIDs[hr]
+ if !ok {
+ // Shouldn't happen, since fltID is supposed to be among the filters
+ // added to the result.
+ panic(fmt.Errorf("composite: filter id %q not found", fltID))
+ }
+
+ var rule filter.RuleText
+ if fltID == filter.IDBlockedService {
+ var svcID filter.BlockedServiceID
+ svcID, ok = r.hostRuleSvcIDs[hr]
+ if !ok {
+ // Shouldn't happen, since svcID is supposed to be among the filters
+ // added to the result.
+ panic(fmt.Errorf("composite: service id %q not found", svcID))
+ }
+
+ rule = filter.RuleText(svcID)
+ } else {
+ rule = filter.RuleText(hr.RuleText)
+ }
+
+ return &filter.ResultBlocked{
+ List: fltID,
+ Rule: rule,
+ }
+}
diff --git a/internal/filter/internal/custom/custom.go b/internal/filter/internal/custom/custom.go
deleted file mode 100644
index 1703789..0000000
--- a/internal/filter/internal/custom/custom.go
+++ /dev/null
@@ -1,161 +0,0 @@
-// Package custom contains the caching storage of filters made from custom
-// filtering rules of clients.
-package custom
-
-import (
- "context"
- "fmt"
- "log/slog"
- "strings"
- "time"
-
- "github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
- "github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist"
- "github.com/AdguardTeam/AdGuardDNS/internal/metrics"
- "github.com/AdguardTeam/golibs/stringutil"
-)
-
-// Filters contains custom filters made from custom filtering rules of clients.
-type Filters struct {
- logger *slog.Logger
- cache agdcache.Interface[string, *cacheItem]
- errColl errcoll.Interface
-}
-
-// cacheItem is an item of the custom filter cache.
-type cacheItem struct {
- updTime time.Time
- ruleList *rulelist.Immutable
-}
-
-// CacheID is a cache identifier for clients' custom filters.
-const CacheID = "filters/" + string(internal.IDCustom)
-
-// Config is the configuration structure for the custom-filter storage. All
-// fields must not be nil.
-type Config struct {
- // Logger is used to log the operation of the storage.
- Logger *slog.Logger
-
- // ErrColl is used to collect errors arising during engine compilation.
- ErrColl errcoll.Interface
-
- // CacheConf is used as the configuration for the cache.
- CacheConf *agdcache.LRUConfig
-
- // CacheManager is used to create the cache for the storage.
- CacheManager agdcache.Manager
-}
-
-// New returns a new custom filter storage. It also adds the cache with ID
-// [CacheID] to the cache manager. c must not be nil.
-func New(c *Config) (f *Filters) {
- cache := agdcache.NewLRU[string, *cacheItem](c.CacheConf)
- c.CacheManager.Add(CacheID, cache)
-
- return &Filters{
- logger: c.Logger,
- cache: cache,
- errColl: c.ErrColl,
- }
-}
-
-// ClientConfig is the configuration for identification or construction of a
-// custom filter for a client.
-type ClientConfig = internal.ConfigCustom
-
-// Get returns the custom rule-list filter made from the client configuration.
-// c must not be nil.
-func (f *Filters) Get(ctx context.Context, c *ClientConfig) (rl *rulelist.Immutable) {
- if !c.Enabled || len(c.Rules) == 0 {
- // Technically, there could be an old filter left in the cache, but it
- // will eventually be evicted, so don't do anything about it.
- return nil
- }
-
- // Report the custom filters cache lookup to prometheus so that we could
- // keep track of whether the cache size is enough.
- defer func() {
- // TODO(a.garipov): Add a Metrics interface.
- metrics.IncrementCond(
- rl == nil,
- metrics.FilterCustomCacheLookupsMisses,
- metrics.FilterCustomCacheLookupsHits,
- )
- }()
-
- rl = f.get(c)
- if rl != nil {
- return rl
- }
-
- // TODO(a.garipov): Consider making a copy of [strings.Join] for
- // [internal.RuleText].
- textLen := 0
- for _, r := range c.Rules {
- textLen += len(r) + len("\n")
- }
-
- b := &strings.Builder{}
- b.Grow(textLen)
-
- for _, r := range c.Rules {
- stringutil.WriteToBuilder(b, string(r), "\n")
- }
-
- rl, err := rulelist.NewImmutable(
- b.String(),
- internal.IDCustom,
- "",
- // Don't use cache for users' custom filters, because resultcache
- // doesn't take $client rules into account.
- //
- // TODO(a.garipov): Consider enabling caching if necessary.
- rulelist.ResultCacheEmpty{},
- )
- if err != nil {
- // In a rare situation where the custom rules are so badly formed that
- // we cannot even create a filtering engine, consider that there is no
- // custom filter, but signal this to the error collector.
- err = fmt.Errorf("compiling custom filter for client with id %s: %w", c.ID, err)
- f.errColl.Collect(ctx, err)
-
- return nil
- }
-
- f.logger.DebugContext(
- ctx,
- "got rules for client",
- "client_id", c.ID,
- "num_rules", rl.RulesCount(),
- )
-
- f.set(c, rl)
-
- return rl
-}
-
-// get returns the cached custom rule-list filter, if there is one and the
-// client configuration hasn't changed since the filter was cached.
-func (f *Filters) get(c *ClientConfig) (rl *rulelist.Immutable) {
- item, ok := f.cache.Get(c.ID)
- if !ok {
- return nil
- }
-
- if item.updTime.Before(c.UpdateTime) {
- return nil
- }
-
- return item.ruleList
-}
-
-// set caches the custom rule-list filter.
-func (f *Filters) set(c *ClientConfig, rl *rulelist.Immutable) {
- f.cache.Set(c.ID, &cacheItem{
- updTime: c.UpdateTime,
- ruleList: rl,
- })
-}
diff --git a/internal/filter/internal/custom/custom_test.go b/internal/filter/internal/custom/custom_test.go
deleted file mode 100644
index 59d6849..0000000
--- a/internal/filter/internal/custom/custom_test.go
+++ /dev/null
@@ -1,103 +0,0 @@
-package custom_test
-
-import (
- "context"
- "testing"
- "time"
-
- "github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/custom"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist"
- "github.com/AdguardTeam/golibs/logutil/slogutil"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-// testClientConfID is the client configuration ID for tests.
-const testClientConfID = "cli1234"
-
-func TestFilters_Get(t *testing.T) {
- f := custom.New(&custom.Config{
- Logger: slogutil.NewDiscardLogger(),
- ErrColl: agdtest.NewErrorCollector(),
- CacheConf: &agdcache.LRUConfig{
- Count: 1,
- },
- CacheManager: agdcache.EmptyManager{},
- })
-
- c := &custom.ClientConfig{
- ID: testClientConfID,
- UpdateTime: time.Now(),
- Rules: []internal.RuleText{
- "||first.example",
- },
- Enabled: true,
- }
-
- ctx := context.Background()
-
- rl := f.Get(ctx, c)
- require.NotNil(t, rl)
-
- // Recheck cached.
- cachedRL := f.Get(ctx, c)
- require.NotNil(t, cachedRL)
-
- assert.Same(t, rl, cachedRL)
-}
-
-var ruleListSink *rulelist.Immutable
-
-func BenchmarkFilters_Get(b *testing.B) {
- f := custom.New(&custom.Config{
- Logger: slogutil.NewDiscardLogger(),
- ErrColl: agdtest.NewErrorCollector(),
- CacheConf: &agdcache.LRUConfig{
- Count: 1,
- },
- CacheManager: agdcache.EmptyManager{},
- })
-
- c := &custom.ClientConfig{
- ID: testClientConfID,
- UpdateTime: time.Now(),
- Rules: []internal.RuleText{
- "||first.example",
- "||second.example",
- "||third.example",
- },
- Enabled: true,
- }
-
- ctx := context.Background()
-
- b.Run("cache", func(b *testing.B) {
- b.ReportAllocs()
- b.ResetTimer()
- for range b.N {
- ruleListSink = f.Get(ctx, c)
- }
- })
-
- b.Run("no_cache", func(b *testing.B) {
- b.ReportAllocs()
- b.ResetTimer()
- for range b.N {
- // Update the time on each iteration to make sure that the cache is
- // never used.
- c.UpdateTime = c.UpdateTime.Add(1 * time.Millisecond)
- ruleListSink = f.Get(ctx, c)
- }
- })
-
- // Most recent results:
- // goos: linux
- // goarch: amd64
- // pkg: github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/custom
- // cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
- // BenchmarkFilters_Get/cache-16 5702966 186.7 ns/op 16 B/op 1 allocs/op
- // BenchmarkFilters_Get/no_cache-16 61044 18373 ns/op 14488 B/op 89 allocs/op
-}
diff --git a/internal/filter/internal/filtertest/filtertest.go b/internal/filter/internal/filtertest/filtertest.go
index 12de8c0..fe69689 100644
--- a/internal/filter/internal/filtertest/filtertest.go
+++ b/internal/filter/internal/filtertest/filtertest.go
@@ -7,7 +7,7 @@ import (
"net/netip"
"time"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/golibs/errors"
"github.com/c2h5oh/datasize"
)
@@ -15,6 +15,8 @@ import (
// Common rules for tests.
const (
RuleBlockStr = "|" + HostBlocked + "^"
+ RuleBlockForClientIPStr = "|" + HostBlockedForClientIP + "^$client=" + IPv4ClientStr
+ RuleBlockForClientNameStr = "|" + HostBlockedForClientName + "^$client=" + ClientName
RuleSafeSearchGeneralHostStr = "|" + HostSafeSearchGeneral + "^$dnsrewrite=NOERROR;CNAME;" +
HostSafeSearchGeneralRepl
RuleSafeSearchGeneralIPv4Str = "|" + HostSafeSearchGeneralIPv4 + "^$dnsrewrite=NOERROR;A;" +
@@ -24,7 +26,9 @@ const (
RuleSafeSearchYouTubeStr = "|" + HostSafeSearchYouTube + "^$dnsrewrite=NOERROR;CNAME;" +
HostSafeSearchYouTubeRepl
- RuleBlock internal.RuleText = RuleBlockStr
+ RuleBlock filter.RuleText = RuleBlockStr
+ RuleBlockForClientIP filter.RuleText = RuleBlockForClientIPStr
+ RuleBlockForClientName filter.RuleText = RuleBlockForClientNameStr
)
// Common string representations of IP addresses.
@@ -50,6 +54,8 @@ const (
HostAdultContentSub = "a.b.c." + HostAdultContent
HostAdultContentRepl = "adult-content-repl.example"
HostBlocked = "blocked.example"
+ HostBlockedForClientIP = "blocked-for-client-ip.example"
+ HostBlockedForClientName = "blocked-for-client-name.example"
HostBlockedService1 = "service-1.example"
HostDangerous = "dangerous-domain.example"
HostDangerousRepl = "dangerous-domain-repl.example"
@@ -66,6 +72,7 @@ const (
FQDNAdultContent = HostAdultContent + "."
FQDNAdultContentRepl = HostAdultContentRepl + "."
FQDNBlocked = HostBlocked + "."
+ FQDNBlockedForClientName = HostBlockedForClientName + "."
FQDNDangerous = HostDangerous + "."
FQDNDangerousRepl = HostDangerousRepl + "."
FQDNNewlyRegistered = HostNewlyRegistered + "."
@@ -83,9 +90,9 @@ const (
BlockedServiceID2Str = "blocked_service_2"
BlockedServiceIDDoesNotExistStr = "blocked_service_none"
- BlockedServiceID1 internal.BlockedServiceID = BlockedServiceID1Str
- BlockedServiceID2 internal.BlockedServiceID = BlockedServiceID2Str
- BlockedServiceIDDoesNotExist internal.BlockedServiceID = BlockedServiceIDDoesNotExistStr
+ BlockedServiceID1 filter.BlockedServiceID = BlockedServiceID1Str
+ BlockedServiceID2 filter.BlockedServiceID = BlockedServiceID2Str
+ BlockedServiceIDDoesNotExist filter.BlockedServiceID = BlockedServiceIDDoesNotExistStr
)
// BlockedServiceIndex is a service-index response for tests.
@@ -116,8 +123,8 @@ const (
RuleListID1Str = "rule_list_1"
RuleListID2Str = "rule_list_2"
- RuleListID1 internal.ID = RuleListID1Str
- RuleListID2 internal.ID = RuleListID2Str
+ RuleListID1 filter.ID = RuleListID1Str
+ RuleListID2 filter.ID = RuleListID2Str
)
// NewRuleListIndex returns a rule-list index containing a record for a filter
@@ -137,6 +144,9 @@ const CacheTTL = 1 * time.Hour
// CacheCount is the common count of cache items for filtering tests.
const CacheCount = 100
+// ClientName is the common client name for tests.
+const ClientName = "MyDevice1"
+
// FilterMaxSize is the maximum size of the downloadable rule-list for filtering
// tests.
const FilterMaxSize = 640 * datasize.KB
diff --git a/internal/filter/internal/filtertest/hashprefix.go b/internal/filter/internal/filtertest/hashprefix.go
index fba9ad5..0ee1052 100644
--- a/internal/filter/internal/filtertest/hashprefix.go
+++ b/internal/filter/internal/filtertest/hashprefix.go
@@ -6,10 +6,10 @@ import (
"testing"
"github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
- "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/require"
@@ -17,16 +17,16 @@ import (
// NewHashprefixFilter is like [NewHashprefixFilterWithRepl], but the
// replacement host is also set in accordance with id.
-func NewHashprefixFilter(tb testing.TB, id internal.ID) (f *hashprefix.Filter) {
+func NewHashprefixFilter(tb testing.TB, id filter.ID) (f *hashprefix.Filter) {
tb.Helper()
var replHost string
switch id {
- case internal.IDAdultBlocking:
+ case filter.IDAdultBlocking:
replHost = HostAdultContentRepl
- case internal.IDNewRegDomains:
+ case filter.IDNewRegDomains:
replHost = HostNewlyRegisteredRepl
- case internal.IDSafeBrowsing:
+ case filter.IDSafeBrowsing:
replHost = HostDangerousRepl
default:
tb.Fatalf("bad id: %q", id)
@@ -39,18 +39,18 @@ func NewHashprefixFilter(tb testing.TB, id internal.ID) (f *hashprefix.Filter) {
// tests. The hash data is set in accordance with id.
func NewHashprefixFilterWithRepl(
tb testing.TB,
- id internal.ID,
+ id filter.ID,
replHost string,
) (f *hashprefix.Filter) {
tb.Helper()
var data string
switch id {
- case internal.IDAdultBlocking:
+ case filter.IDAdultBlocking:
data = HostAdultContent + "\n"
- case internal.IDNewRegDomains:
+ case filter.IDNewRegDomains:
data = HostNewlyRegistered + "\n"
- case internal.IDSafeBrowsing:
+ case filter.IDSafeBrowsing:
data = HostDangerous + "\n"
default:
tb.Fatalf("bad id: %q", id)
@@ -62,17 +62,14 @@ func NewHashprefixFilterWithRepl(
require.NoError(tb, err)
f, err = hashprefix.NewFilter(&hashprefix.FilterConfig{
- Logger: slogutil.NewDiscardLogger(),
- // TODO(a.garipov): Use [agdtest.NewCloner] when the import cycle is
- // resolved.
- Cloner: dnsmsg.NewCloner(dnsmsg.EmptyClonerStat{}),
- CacheManager: agdcache.EmptyManager{},
- Hashes: strg,
- URL: srvURL,
- // TODO(a.garipov): Use [agdtest.NewErrorCollector] when the import
- // cycle is resolved.
- ErrColl: errColl{},
- Metrics: internal.EmptyMetrics{},
+ Logger: slogutil.NewDiscardLogger(),
+ Cloner: agdtest.NewCloner(),
+ CacheManager: agdcache.EmptyManager{},
+ Hashes: strg,
+ URL: srvURL,
+ ErrColl: agdtest.NewErrorCollector(),
+ HashPrefixMtcs: hashprefix.EmptyMetrics{},
+ Metrics: filter.EmptyMetrics{},
ID: id,
CachePath: cachePath,
ReplacementHost: replHost,
diff --git a/internal/filter/internal/filtertest/result.go b/internal/filter/internal/filtertest/result.go
index c04c294..b123cb9 100644
--- a/internal/filter/internal/filtertest/result.go
+++ b/internal/filter/internal/filtertest/result.go
@@ -3,27 +3,26 @@ package filtertest
import (
"net/netip"
"testing"
- "time"
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
)
// AssertEqualResult is a test helper that compares two results taking
-// [internal.ResultModifiedRequest] and its difference in IDs into account.
-func AssertEqualResult(tb testing.TB, want, got internal.Result) (ok bool) {
+// [filter.ResultModifiedRequest] and its difference in IDs into account.
+func AssertEqualResult(tb testing.TB, want, got filter.Result) (ok bool) {
tb.Helper()
- wantRM, ok := want.(*internal.ResultModifiedRequest)
+ wantRM, ok := want.(*filter.ResultModifiedRequest)
if !ok {
return assert.Equal(tb, want, got)
}
- gotRM := testutil.RequireTypeAssert[*internal.ResultModifiedRequest](tb, got)
+ gotRM := testutil.RequireTypeAssert[*filter.ResultModifiedRequest](tb, got)
return assert.Equal(tb, wantRM.List, gotRM.List) &&
assert.Equal(tb, wantRM.Rule, gotRM.Rule) &&
@@ -56,7 +55,7 @@ func NewRequest(
host string,
ip netip.Addr,
qt dnsmsg.RRType,
-) (req *internal.Request) {
+) (req *filter.Request) {
tb.Helper()
dnsReq := &dns.Msg{
@@ -67,22 +66,9 @@ func NewRequest(
}},
}
- // TODO(a.garipov): Use [agdtest.NewConstructor] when the import cycle is
- // resolved.
- msgs, err := dnsmsg.NewConstructor(&dnsmsg.ConstructorConfig{
- Cloner: dnsmsg.NewCloner(dnsmsg.EmptyClonerStat{}),
- BlockingMode: &dnsmsg.BlockingModeNullIP{},
- StructuredErrors: &dnsmsg.StructuredDNSErrorsConfig{
- Enabled: false,
- },
- FilteredResponseTTL: 10 * time.Second,
- EDEEnabled: false,
- })
- require.NoError(tb, err)
-
- return &internal.Request{
+ return &filter.Request{
DNS: dnsReq,
- Messages: msgs,
+ Messages: agdtest.NewConstructor(tb),
RemoteIP: ip,
ClientName: cliName,
Host: host,
@@ -93,7 +79,7 @@ func NewRequest(
// NewARequest is like [NewRequest] but cliName is always empty, ip is always
// [IPv4Client], and qt is always [dns.TypeA].
-func NewARequest(tb testing.TB, host string) (req *internal.Request) {
+func NewARequest(tb testing.TB, host string) (req *filter.Request) {
tb.Helper()
return NewRequest(tb, "", host, IPv4Client, dns.TypeA)
diff --git a/internal/filter/internal/internal.go b/internal/filter/internal/internal.go
deleted file mode 100644
index 5f168c0..0000000
--- a/internal/filter/internal/internal.go
+++ /dev/null
@@ -1,109 +0,0 @@
-// Package internal contains common constants, types, and utilities shared by
-// other subpackages of package filter/.
-//
-// TODO(a.garipov): Merge into package filter.
-package internal
-
-import (
- "context"
- "net/netip"
- "time"
-
- "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
- "github.com/miekg/dns"
-)
-
-// Interface is the DNS request and response filter interface.
-type Interface interface {
- // FilterRequest filters a DNS request based on the information provided
- // about the request. req must be valid.
- FilterRequest(ctx context.Context, req *Request) (r Result, err error)
-
- // FilterResponse filters a DNS response based on the information provided
- // about the response. resp must be valid.
- FilterResponse(ctx context.Context, resp *Response) (r Result, err error)
-}
-
-// Request contains information about a request being filtered.
-type Request struct {
- // DNS is the original DNS request used to create filtered responses. It
- // must not be nil and must have exactly one question.
- DNS *dns.Msg
-
- // Messages is used to create filtered responses for this request. It must
- // not be nil.
- Messages *dnsmsg.Constructor
-
- // RemoteIP is the remote IP address of the client.
- RemoteIP netip.Addr
-
- // ClientName is the client name for rule-list filtering.
- ClientName string
-
- // Host is the lowercased, non-FQDN version of the hostname from the
- // question of the request.
- Host string
-
- // QType is the type of question for this request.
- QType dnsmsg.RRType
-
- // QClass is the class of question for this request.
- QClass dnsmsg.Class
-}
-
-// Response contains information about a response being filtered.
-type Response struct {
- // DNS is the original DNS response used to create filtered responses. It
- // must not be nil and must have exactly one question.
- DNS *dns.Msg
-
- // RemoteIP is the remote IP address of the client.
- RemoteIP netip.Addr
-
- // ClientName is the client name for rule-list filtering.
- ClientName string
-}
-
-// Empty is an [Interface] implementation that always returns nil.
-type Empty struct{}
-
-// type check
-var _ Interface = Empty{}
-
-// FilterRequest implements the [Interface] interface for Empty.
-func (Empty) FilterRequest(_ context.Context, _ *Request) (r Result, err error) {
- return nil, nil
-}
-
-// FilterResponse implements the [Interface] interface for Empty.
-func (Empty) FilterResponse(_ context.Context, _ *Response) (r Result, err error) {
- return nil, nil
-}
-
-// DefaultResolveTimeout is the default timeout for resolving hosts for
-// safe-search and safe-browsing filters.
-//
-// TODO(ameshkov): Consider making configurable.
-const DefaultResolveTimeout = 1 * time.Second
-
-// RequestFilter can filter a request based on the request info.
-type RequestFilter interface {
- FilterRequest(ctx context.Context, req *Request) (r Result, err error)
- ID() (id ID)
-}
-
-// ConfigCustom is the configuration for identification or construction of a
-// custom filter for a client.
-type ConfigCustom struct {
- // ID is the unique ID for this custom filter.
- ID string
-
- // UpdateTime is the last time this configuration has been updated.
- UpdateTime time.Time
-
- // Rules are the filtering rules for this configuration.
- Rules []RuleText
-
- // Enabled shows whether the custom filters are applied at all.
- Enabled bool
-}
diff --git a/internal/filter/internal/refreshable/refreshable.go b/internal/filter/internal/refreshable/refreshable.go
index 34a619f..a728630 100644
--- a/internal/filter/internal/refreshable/refreshable.go
+++ b/internal/filter/internal/refreshable/refreshable.go
@@ -64,10 +64,10 @@ type Config struct {
// New returns a new refreshable. c must not be nil.
func New(c *Config) (f *Refreshable, err error) {
if c.URL == nil {
- return nil, fmt.Errorf("internal.NewRefreshable: nil url for refreshable with ID %q", c.ID)
+ return nil, fmt.Errorf("refreshable.New: nil url for refreshable with ID %q", c.ID)
} else if s := c.URL.Scheme; !strings.EqualFold(s, urlutil.SchemeFile) &&
!urlutil.IsValidHTTPURLScheme(s) {
- return nil, fmt.Errorf("internal.NewRefreshable: bad url scheme %q", s)
+ return nil, fmt.Errorf("refreshable.New: bad url scheme %q", s)
}
return &Refreshable{
diff --git a/internal/filter/internal/rulelist/cache.go b/internal/filter/internal/rulelist/cache.go
new file mode 100644
index 0000000..55acc4c
--- /dev/null
+++ b/internal/filter/internal/rulelist/cache.go
@@ -0,0 +1,100 @@
+package rulelist
+
+import (
+ "encoding/binary"
+ "hash/maphash"
+
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
+ "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
+ "github.com/AdguardTeam/golibs/mathutil"
+ "github.com/AdguardTeam/urlfilter"
+)
+
+// CacheKey is the cache key type for [NewCacheKey].
+type CacheKey uint64
+
+// hashSeed is the seed used by all hashes to create hash keys.
+var hashSeed = maphash.MakeSeed()
+
+// NewCacheKey produces a cache key based on the arguments using default
+// algorithm.
+func NewCacheKey(host string, qt dnsmsg.RRType, cl dnsmsg.Class, isAns bool) (k CacheKey) {
+ // Use maphash explicitly instead of using a key structure to reduce
+ // allocations and optimize interface conversion up the stack.
+ h := &maphash.Hash{}
+ h.SetSeed(hashSeed)
+
+ _, _ = h.WriteString(host)
+
+ // Save on allocations by reusing a buffer.
+ var buf [5]byte
+ binary.LittleEndian.PutUint16(buf[:2], qt)
+ binary.LittleEndian.PutUint16(buf[2:4], cl)
+ buf[4] = mathutil.BoolToNumber[byte](isAns)
+
+ _, _ = h.Write(buf[:])
+
+ return CacheKey(h.Sum64())
+}
+
+type (
+ // ResultCache is a convenient alias for cache to keep types in check.
+ ResultCache = agdcache.Interface[CacheKey, *CacheItem]
+
+ // EmptyResultCache is a convenient alias for empty cache to keep types in
+ // check. See [filter.DNSResult].
+ EmptyResultCache = agdcache.Empty[CacheKey, *CacheItem]
+)
+
+// NewResultCache returns a new initialized cache with the given element count.
+// If useCache is false, it returns a cache implementation that does nothing.
+func NewResultCache(count int, useCache bool) (cache ResultCache) {
+ if !useCache {
+ return EmptyResultCache{}
+ }
+
+ return agdcache.NewLRU[CacheKey, *CacheItem](&agdcache.LRUConfig{
+ Count: count,
+ })
+}
+
+// NewManagedResultCache is like [NewResultCache] but it also adds a newly
+// created cache to the cache manager by id.
+func NewManagedResultCache(
+ m agdcache.Manager,
+ id string,
+ count int,
+ useCache bool,
+) (cache ResultCache) {
+ cache = NewResultCache(count, useCache)
+ m.Add(id, cache)
+
+ return cache
+}
+
+// CacheItem is an item stored in a [ResultCache].
+type CacheItem struct {
+ // res is the DNS filtering result.
+ res *urlfilter.DNSResult
+
+ // host is the cached normalized hostname for later cache key collision
+ // checks.
+ host string
+}
+
+// itemFromCache retrieves a cache item for the given key. host is used to
+// detect key collisions. If there is a key collision, it returns nil and
+// false.
+func itemFromCache(cache ResultCache, key CacheKey, host string) (item *CacheItem, ok bool) {
+ item, ok = cache.Get(key)
+ if !ok {
+ return nil, false
+ }
+
+ if item.host != host {
+ // Cache collision.
+ return nil, false
+ }
+
+ return item, true
+}
diff --git a/internal/filter/internal/rulelist/dnsrewrite.go b/internal/filter/internal/rulelist/dnsrewrite.go
index 933cbf7..6b5acfe 100644
--- a/internal/filter/internal/rulelist/dnsrewrite.go
+++ b/internal/filter/internal/rulelist/dnsrewrite.go
@@ -6,7 +6,7 @@ import (
"strings"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns"
@@ -15,10 +15,10 @@ import (
// ProcessDNSRewrites processes $dnsrewrite rules dnsr and creates a filtering
// result, if id allows it. res.List, if any, is set to id.
func ProcessDNSRewrites(
- req *internal.Request,
+ req *filter.Request,
dnsr []*rules.NetworkRule,
- id internal.ID,
-) (res internal.Result) {
+ id filter.ID,
+) (res filter.Result) {
if len(dnsr) == 0 {
return nil
}
@@ -35,7 +35,7 @@ func ProcessDNSRewrites(
modReq := dnsmsg.Clone(req.DNS)
modReq.Question[0].Name = dns.Fqdn(resCanonName)
- return &internal.ResultModifiedRequest{
+ return &filter.ResultModifiedRequest{
Msg: modReq,
List: id,
Rule: dnsRewriteResult.ResRuleText,
@@ -48,7 +48,7 @@ func ProcessDNSRewrites(
// defined statically.
resp := req.Messages.NewBlockedRespRCode(req.DNS, dnsmsg.RCode(dnsRewriteResult.RCode))
- return &internal.ResultModifiedResponse{
+ return &filter.ResultModifiedResponse{
Msg: resp,
List: id,
Rule: dnsRewriteResult.ResRuleText,
@@ -60,7 +60,7 @@ func ProcessDNSRewrites(
return nil
}
- return &internal.ResultModifiedResponse{
+ return &filter.ResultModifiedResponse{
Msg: resp,
List: id,
}
@@ -70,7 +70,7 @@ func ProcessDNSRewrites(
type dnsRewriteResult struct {
Response dnsRewriteResultResponse
CanonName string
- ResRuleText internal.RuleText
+ ResRuleText filter.RuleText
Rules []*rules.NetworkRule
RCode rules.RCode
}
@@ -91,7 +91,7 @@ func processDNSRewriteRules(dnsr []*rules.NetworkRule) (res *dnsRewriteResult) {
if dr.NewCNAME != "" {
// NewCNAME rules have a higher priority than other rules.
return &dnsRewriteResult{
- ResRuleText: internal.RuleText(rule.RuleText),
+ ResRuleText: filter.RuleText(rule.RuleText),
CanonName: dr.NewCNAME,
}
}
@@ -105,7 +105,7 @@ func processDNSRewriteRules(dnsr []*rules.NetworkRule) (res *dnsRewriteResult) {
// RcodeRefused and other such codes have higher priority. Return
// immediately.
return &dnsRewriteResult{
- ResRuleText: internal.RuleText(rule.RuleText),
+ ResRuleText: filter.RuleText(rule.RuleText),
RCode: dr.RCode,
}
}
@@ -117,7 +117,7 @@ func processDNSRewriteRules(dnsr []*rules.NetworkRule) (res *dnsRewriteResult) {
// filterDNSRewrite handles dnsrewrite filters. It constructs a DNS response
// and returns it. dnsrr.RCode should be [dns.RcodeSuccess] and contain a
// non-empty dnsrr.Response.
-func filterDNSRewrite(req *internal.Request, dnsrr *dnsRewriteResult) (resp *dns.Msg, err error) {
+func filterDNSRewrite(req *filter.Request, dnsrr *dnsRewriteResult) (resp *dns.Msg, err error) {
if dnsrr.Response == nil {
return nil, errors.Error("no dns rewrite rule responses")
}
@@ -149,7 +149,7 @@ func filterDNSRewrite(req *internal.Request, dnsrr *dnsRewriteResult) (resp *dns
// filterDNSRewriteResponse handles a single DNS rewrite response entry.
// It returns the properly constructed answer resource record.
func filterDNSRewriteResponse(
- req *internal.Request,
+ req *filter.Request,
rr rules.RRType,
v rules.RRValue,
) (ans dns.RR, err error) {
@@ -174,7 +174,7 @@ func filterDNSRewriteResponse(
}
// newAnswerSRV returns a new resource record created from DNSSRV rules value.
-func newAnswerSRV(req *internal.Request, v rules.RRValue, rr rules.RRType) (ans dns.RR, err error) {
+func newAnswerSRV(req *filter.Request, v rules.RRValue, rr rules.RRType) (ans dns.RR, err error) {
srv, ok := v.(*rules.DNSSRV)
if !ok {
return nil, fmt.Errorf("value for rr type %d has type %T, not *rules.DNSSRV", rr, v)
@@ -184,11 +184,7 @@ func newAnswerSRV(req *internal.Request, v rules.RRValue, rr rules.RRType) (ans
}
// newAnsFromSVCB returns a new resource record created from DNSSVCB rules value.
-func newAnsFromSVCB(
- req *internal.Request,
- v rules.RRValue,
- rr rules.RRType,
-) (ans dns.RR, err error) {
+func newAnsFromSVCB(req *filter.Request, v rules.RRValue, rr rules.RRType) (ans dns.RR, err error) {
svcb, ok := v.(*rules.DNSSVCB)
if !ok {
return nil, fmt.Errorf("value for rr type %d has type %T, not *rules.DNSSVCB", rr, v)
@@ -203,7 +199,7 @@ func newAnsFromSVCB(
// newAnsFromString returns a new resource record created from string value.
func newAnsFromString(
- req *internal.Request,
+ req *filter.Request,
v rules.RRValue,
rr rules.RRType,
) (ans dns.RR, err error) {
@@ -221,7 +217,7 @@ func newAnsFromString(
// newAnsFromIP returns a new resource record with an IP address. ip must be an
// IPv4 or IPv6 address.
-func newAnsFromIP(req *internal.Request, v rules.RRValue, rr rules.RRType) (ans dns.RR, err error) {
+func newAnsFromIP(req *filter.Request, v rules.RRValue, rr rules.RRType) (ans dns.RR, err error) {
ip, ok := v.(netip.Addr)
if !ok {
return nil, fmt.Errorf("value for rr type %d has type %T, not net.IP", rr, v)
@@ -237,7 +233,7 @@ func newAnsFromIP(req *internal.Request, v rules.RRValue, rr rules.RRType) (ans
}
// newAnswerMX returns a new resource record created from DNSMX rules value.
-func newAnswerMX(req *internal.Request, v, rr rules.RRValue) (ans dns.RR, err error) {
+func newAnswerMX(req *filter.Request, v, rr rules.RRValue) (ans dns.RR, err error) {
mx, ok := v.(*rules.DNSMX)
if !ok {
return nil, fmt.Errorf("value for rr type %d has type %T, not *rules.DNSMX", rr, v)
diff --git a/internal/filter/internal/rulelist/immutable.go b/internal/filter/internal/rulelist/immutable.go
index 38644bf..f8c3f1b 100644
--- a/internal/filter/internal/rulelist/immutable.go
+++ b/internal/filter/internal/rulelist/immutable.go
@@ -1,8 +1,6 @@
package rulelist
-import (
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
-)
+import "github.com/AdguardTeam/AdGuardDNS/internal/filter"
// Immutable is a rule-list filter that doesn't refresh or change. It is used
// for users' custom rule-lists as well as in service blocking.
@@ -17,23 +15,18 @@ import (
type Immutable struct {
// TODO(a.garipov): Find ways to embed it in a way that shows the methods,
// doesn't result in double dereferences, and doesn't cause naming issues.
- *filter
+ *baseFilter
}
// NewImmutable returns a new immutable DNS request and response filter using
-// the provided rule text and ID.
+// the provided rule text and IDs.
func NewImmutable(
text string,
- id internal.ID,
- svcID internal.BlockedServiceID,
+ id filter.ID,
+ svcID filter.BlockedServiceID,
cache ResultCache,
-) (f *Immutable, err error) {
- f = &Immutable{}
- f.filter, err = newFilter(text, id, svcID, cache)
- if err != nil {
- // Don't wrap the error, because it's informative enough as is.
- return nil, err
+) (f *Immutable) {
+ return &Immutable{
+ baseFilter: newBaseFilter(text, id, svcID, cache),
}
-
- return f, nil
}
diff --git a/internal/filter/internal/rulelist/refreshable.go b/internal/filter/internal/rulelist/refreshable.go
index 9f48500..c7768d6 100644
--- a/internal/filter/internal/rulelist/refreshable.go
+++ b/internal/filter/internal/rulelist/refreshable.go
@@ -9,7 +9,7 @@ import (
"sync"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/refreshable"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/AdguardTeam/urlfilter"
@@ -23,7 +23,7 @@ import (
// for multiple rule lists and using it to optimize the filtering using default
// filtering groups.
type Refreshable struct {
- *filter
+ *baseFilter
logger *slog.Logger
@@ -42,8 +42,9 @@ type Refreshable struct {
// HTTP(S) URL. The initial refresh should be called explicitly if necessary.
func NewRefreshable(c *refreshable.Config, cache ResultCache) (f *Refreshable, err error) {
f = &Refreshable{
- logger: c.Logger,
- mu: &sync.RWMutex{},
+ baseFilter: newBaseFilter("", c.ID, "", cache),
+ logger: c.Logger,
+ mu: &sync.RWMutex{},
}
if strings.EqualFold(c.URL.Scheme, urlutil.SchemeFile) {
@@ -63,35 +64,23 @@ func NewRefreshable(c *refreshable.Config, cache ResultCache) (f *Refreshable, e
return nil, fmt.Errorf("creating refreshable: %w", err)
}
- f.filter, err = newFilter("", c.ID, "", cache)
- if err != nil {
- // Should never happen, since text is empty.
- panic(fmt.Errorf("unexpected filter error: %w", err))
- }
-
return f, nil
}
// NewFromString returns a new DNS request and response filter using the
-// provided rule text and ID.
+// provided rule text and IDs.
//
-// TODO(a.garipov): Only used in tests. Consider removing later.
+// TODO(a.garipov): Only used in tests. Consider removing later.
func NewFromString(
text string,
- id internal.ID,
- svcID internal.BlockedServiceID,
+ id filter.ID,
+ svcID filter.BlockedServiceID,
cache ResultCache,
-) (f *Refreshable, err error) {
- filter, err := newFilter(text, id, svcID, cache)
- if err != nil {
- // Don't wrap the error, because it's informative enough as is.
- return nil, err
- }
-
+) (f *Refreshable) {
return &Refreshable{
- mu: &sync.RWMutex{},
- filter: filter,
- }, nil
+ mu: &sync.RWMutex{},
+ baseFilter: newBaseFilter(text, id, svcID, cache),
+ }
}
// DNSResult returns the result of applying the urlfilter DNS filtering engine.
@@ -106,7 +95,7 @@ func (f *Refreshable) DNSResult(
f.mu.RLock()
defer f.mu.RUnlock()
- return f.filter.DNSResult(clientIP, clientName, host, rrType, isAns)
+ return f.baseFilter.DNSResult(clientIP, clientName, host, rrType, isAns)
}
// Refresh reloads the rule list data. If acceptStale is true, do not try to
@@ -121,7 +110,6 @@ func (f *Refreshable) Refresh(ctx context.Context, acceptStale bool) (err error)
// TODO(a.garipov): Add filterlist.BytesRuleList.
strList := &filterlist.StringRuleList{
- ID: f.urlFilterID,
RulesText: text,
IgnoreCosmetic: true,
}
@@ -148,5 +136,5 @@ func (f *Refreshable) RulesCount() (n int) {
f.mu.RLock()
defer f.mu.RUnlock()
- return f.filter.RulesCount()
+ return f.baseFilter.RulesCount()
}
diff --git a/internal/filter/internal/rulelist/refreshable_test.go b/internal/filter/internal/rulelist/refreshable_test.go
index d871dfa..4f80c83 100644
--- a/internal/filter/internal/rulelist/refreshable_test.go
+++ b/internal/filter/internal/rulelist/refreshable_test.go
@@ -5,7 +5,7 @@ import (
"net/netip"
"testing"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/filtertest"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/refreshable"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist"
@@ -23,27 +23,20 @@ const testReqHost = "blocked.example"
var testRemoteIP = netip.MustParseAddr("1.2.3.4")
// testFltListID is the common filter list IDs for tests.
-const testFltListID internal.ID = "fl1"
+const testFltListID filter.ID = "fl1"
// testBlockRule is the common blocking rule for tests.
const testBlockRule = "||" + testReqHost + "\n"
func TestRefreshable_RulesCount(t *testing.T) {
- rl, err := rulelist.NewFromString(
- testBlockRule,
- testFltListID,
- "",
- rulelist.ResultCacheEmpty{},
- )
- require.NoError(t, err)
+ rl := rulelist.NewFromString(testBlockRule, testFltListID, "", rulelist.EmptyResultCache{})
assert.Equal(t, 1, rl.RulesCount())
}
func TestRefreshable_DNSResult_cache(t *testing.T) {
cache := rulelist.NewResultCache(filtertest.CacheCount, true)
- rl, err := rulelist.NewFromString(testBlockRule, testFltListID, "", cache)
- require.NoError(t, err)
+ rl := rulelist.NewFromString(testBlockRule, testFltListID, "", cache)
const qt = dns.TypeA
@@ -71,14 +64,8 @@ func TestRefreshable_DNSResult_cache(t *testing.T) {
}
func TestRefreshable_ID(t *testing.T) {
- const svcID = internal.BlockedServiceID("test_service")
- rl, err := rulelist.NewFromString(
- testBlockRule,
- testFltListID,
- svcID,
- rulelist.ResultCacheEmpty{},
- )
- require.NoError(t, err)
+ const svcID = filter.BlockedServiceID("test_service")
+ rl := rulelist.NewFromString(testBlockRule, testFltListID, svcID, rulelist.EmptyResultCache{})
gotID, gotSvcID := rl.ID()
assert.Equal(t, testFltListID, gotID)
diff --git a/internal/filter/internal/rulelist/result.go b/internal/filter/internal/rulelist/result.go
deleted file mode 100644
index b1ccf1b..0000000
--- a/internal/filter/internal/rulelist/result.go
+++ /dev/null
@@ -1,94 +0,0 @@
-package rulelist
-
-import (
- "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
- "github.com/AdguardTeam/urlfilter"
- "github.com/AdguardTeam/urlfilter/rules"
- "github.com/miekg/dns"
-)
-
-// URLFilterResult is an entity simplifying the collection and compilation of
-// urlfilter results.
-type URLFilterResult struct {
- networkRules []*rules.NetworkRule
- hostRules4 []*rules.HostRule
- hostRules6 []*rules.HostRule
-}
-
-// Add appends the rules from dr to the slices within r. If dr is nil, Add does
-// nothing.
-func (r *URLFilterResult) Add(dr *urlfilter.DNSResult) {
- if dr != nil {
- r.networkRules = append(r.networkRules, dr.NetworkRules...)
- r.hostRules4 = append(r.hostRules4, dr.HostRulesV4...)
- r.hostRules6 = append(r.hostRules6, dr.HostRulesV6...)
- }
-}
-
-// ToInternal converts a result of using several urlfilter rulelists into an
-// internal.Result.
-func (r *URLFilterResult) ToInternal(m IDMapper, rrType dnsmsg.RRType) (res internal.Result) {
- if nr := rules.GetDNSBasicRule(r.networkRules); nr != nil {
- return ruleDataToResult(m, nr.FilterListID, nr.RuleText, nr.Whitelist)
- }
-
- return r.hostsRulesToResult(m, rrType)
-}
-
-// IDMapper maps an internal urlfilter ID to AdGuard DNS IDs.
-type IDMapper interface {
- Map(ufID int) (id internal.ID, svcID internal.BlockedServiceID)
-}
-
-// hostsRulesToResult converts /etc/hosts-style rules into a filtering result.
-func (r *URLFilterResult) hostsRulesToResult(m IDMapper, rrType dnsmsg.RRType) (res internal.Result) {
- if len(r.hostRules4) == 0 && len(r.hostRules6) == 0 {
- return nil
- }
-
- // Only use the first matched rule, since we currently don't care about the
- // IP addresses in the rule. If the request is neither an A one nor an AAAA
- // one, or if there are no matching rules of the requested type, then use
- // whatever rule isn't empty.
- //
- // See also AGDNS-591.
- var resHostRule *rules.HostRule
- if rrType == dns.TypeA && len(r.hostRules4) > 0 {
- resHostRule = r.hostRules4[0]
- } else if rrType == dns.TypeAAAA && len(r.hostRules6) > 0 {
- resHostRule = r.hostRules6[0]
- } else {
- if len(r.hostRules4) > 0 {
- resHostRule = r.hostRules4[0]
- } else {
- resHostRule = r.hostRules6[0]
- }
- }
-
- return ruleDataToResult(m, resHostRule.FilterListID, resHostRule.RuleText, false)
-}
-
-// ruleDataToResult converts a urlfilter rule data into a filtering result.
-func ruleDataToResult(m IDMapper, ufID int, ruleText string, isAllowlist bool) (r internal.Result) {
- fltID, svcID := m.Map(ufID)
-
- var rule internal.RuleText
- if fltID == internal.IDBlockedService {
- rule = internal.RuleText(svcID)
- } else {
- rule = internal.RuleText(ruleText)
- }
-
- if isAllowlist {
- return &internal.ResultAllowed{
- List: fltID,
- Rule: rule,
- }
- }
-
- return &internal.ResultBlocked{
- List: fltID,
- Rule: rule,
- }
-}
diff --git a/internal/filter/internal/rulelist/rulelist.go b/internal/filter/internal/rulelist/rulelist.go
index 0849f5d..009fa97 100644
--- a/internal/filter/internal/rulelist/rulelist.go
+++ b/internal/filter/internal/rulelist/rulelist.go
@@ -4,99 +4,18 @@ package rulelist
import (
"fmt"
- "math/rand"
"net/netip"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/urlfilter"
"github.com/AdguardTeam/urlfilter/filterlist"
"github.com/miekg/dns"
)
-// newURLFilterID returns a new random ID for the urlfilter DNS engine to use.
-func newURLFilterID() (id int) {
- // #nosec G404 -- Do not use cryptographically random ID generation, since
- // these are only used in ../composite/Filter.mustRuleListDataByURLFilterID
- // and are not used in any security-sensitive context.
- //
- // Despite the fact that the type of integer filter list IDs in module
- // urlfilter is int, the module actually assumes that the ID is a
- // non-negative integer, or at least not a largely negative one. Otherwise,
- // some of its low-level optimizations seem to break.
- return int(rand.Int31())
-}
-
-type (
- // ResultCache is a convenient alias for cache to keep types in check.
- ResultCache = agdcache.Interface[internal.CacheKey, *CacheItem]
-
- // ResultCacheEmpty is a convenient alias for empty cache to keep types in
- // check. See [filter.DNSResult].
- ResultCacheEmpty = agdcache.Empty[internal.CacheKey, *CacheItem]
-)
-
-// NewResultCache returns a new initialized cache with the given element count.
-// If useCache is false, it returns a cache implementation that does nothing.
-func NewResultCache(count int, useCache bool) (cache ResultCache) {
- if !useCache {
- return ResultCacheEmpty{}
- }
-
- return agdcache.NewLRU[internal.CacheKey, *CacheItem](&agdcache.LRUConfig{
- Count: count,
- })
-}
-
-// NewManagedResultCache is like [NewResultCache] but it also adds a newly
-// created cache to the cache manager by id.
-func NewManagedResultCache(
- m agdcache.Manager,
- id string,
- count int,
- useCache bool,
-) (cache ResultCache) {
- cache = NewResultCache(count, useCache)
- m.Add(id, cache)
-
- return cache
-}
-
-// CacheItem represents an item that we will store in the cache.
-type CacheItem struct {
- // res is the DNS filtering result.
- res *urlfilter.DNSResult
-
- // host is the cached normalized hostname for later cache key collision
- // checks.
- host string
-}
-
-// itemFromCache retrieves a cache item for the given key. host is used to
-// detect key collisions. If there is a key collision, it returns nil and
-// false.
-func itemFromCache(
- cache ResultCache,
- key internal.CacheKey,
- host string,
-) (item *CacheItem, ok bool) {
- item, ok = cache.Get(key)
- if !ok {
- return nil, false
- }
-
- if item.host != host {
- // Cache collision.
- return nil, false
- }
-
- return item, true
-}
-
-// filter is the basic rule-list filter that doesn't refresh or change in any
+// baseFilter is the vase rule-list filter that doesn't refresh or change in any
// other way.
-type filter struct {
+type baseFilter struct {
// engine is the DNS filtering engine.
//
// NOTE: Do not save the [filterlist.RuleList] used to create the engine to
@@ -110,53 +29,53 @@ type filter struct {
cache ResultCache
// id is the filter list ID, if any.
- id internal.ID
+ id filter.ID
// svcID is the additional identifier for blocked service lists. If id is
- svcID internal.BlockedServiceID
-
- // urlFilterID is the synthetic integer identifier for the urlfilter engine.
- //
- // TODO(a.garipov): Change the type to a string in module urlfilter and
- // remove this crutch.
- urlFilterID int
+ svcID filter.BlockedServiceID
}
-// newFilter returns a new basic DNS request and response filter using the
-// provided rule text and ID.
-func newFilter(
+// newBaseFilter returns a new base DNS request and response filter using the
+// provided rule text and IDs.
+func newBaseFilter(
text string,
- id internal.ID,
- svcID internal.BlockedServiceID,
- cache agdcache.Interface[internal.CacheKey, *CacheItem],
-) (f *filter, err error) {
- f = &filter{
- cache: cache,
- id: id,
- svcID: svcID,
- urlFilterID: newURLFilterID(),
+ id filter.ID,
+ svcID filter.BlockedServiceID,
+ cache ResultCache,
+) (f *baseFilter) {
+ f = &baseFilter{
+ cache: cache,
+ id: id,
+ svcID: svcID,
}
// TODO(a.garipov): Add filterlist.BytesRuleList.
strList := &filterlist.StringRuleList{
- ID: f.urlFilterID,
RulesText: text,
IgnoreCosmetic: true,
}
s, err := filterlist.NewRuleStorage([]filterlist.RuleList{strList})
if err != nil {
- return nil, fmt.Errorf("creating rule storage: %w", err)
+ // Should never happen, there is only one filter list, and the only
+ // error that is currently returned from [filterlist.NewRuleStorage] is
+ // about duplicated IDs.
+ panic(fmt.Errorf(
+ "rulelist: compiling storage for filter id %q and svc id %q: %w",
+ id,
+ svcID,
+ err,
+ ))
}
f.engine = urlfilter.NewDNSEngine(s)
- return f, nil
+ return f
}
// DNSResult returns the result of applying the urlfilter DNS filtering engine.
// If the request is not filtered, DNSResult returns nil.
-func (f *filter) DNSResult(
+func (f *baseFilter) DNSResult(
clientIP netip.Addr,
clientName string,
host string,
@@ -164,15 +83,15 @@ func (f *filter) DNSResult(
isAns bool,
) (res *urlfilter.DNSResult) {
var ok bool
- var cacheKey internal.CacheKey
+ var cacheKey CacheKey
var item *CacheItem
// Don't waste resources on computing the cache key if the cache is not
// enabled.
- _, emptyCache := f.cache.(ResultCacheEmpty)
+ _, emptyCache := f.cache.(EmptyResultCache)
if !emptyCache {
// TODO(a.garipov): Add real class here.
- cacheKey = internal.NewCacheKey(host, rrType, dns.ClassINET, isAns)
+ cacheKey = NewCacheKey(host, rrType, dns.ClassINET, isAns)
item, ok = itemFromCache(f.cache, cacheKey, host)
if ok {
return item.res
@@ -202,16 +121,11 @@ func (f *filter) DNSResult(
// ID returns the filter list ID of this rule list filter, as well as the ID of
// the blocked service, if any.
-func (f *filter) ID() (id internal.ID, svcID internal.BlockedServiceID) {
+func (f *baseFilter) ID() (id filter.ID, svcID filter.BlockedServiceID) {
return f.id, f.svcID
}
// RulesCount returns the number of rules in the filter's engine.
-func (f *filter) RulesCount() (n int) {
+func (f *baseFilter) RulesCount() (n int) {
return f.engine.RulesCount
}
-
-// URLFilterID returns the synthetic ID used for the urlfilter module.
-func (f *filter) URLFilterID() (n int) {
- return f.urlFilterID
-}
diff --git a/internal/filter/internal/safesearch/safesearch.go b/internal/filter/internal/safesearch/safesearch.go
index ade529a..7d211be 100644
--- a/internal/filter/internal/safesearch/safesearch.go
+++ b/internal/filter/internal/safesearch/safesearch.go
@@ -7,7 +7,8 @@ import (
"fmt"
"time"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/composite"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/refreshable"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist"
"github.com/miekg/dns"
@@ -45,14 +46,14 @@ func New(c *Config, cache rulelist.ResultCache) (f *Filter, err error) {
}
// type check
-var _ internal.RequestFilter = (*Filter)(nil)
+var _ composite.RequestFilter = (*Filter)(nil)
-// FilterRequest implements the [internal.RequestFilter] interface for *Filter.
+// FilterRequest implements the [composite.RequestFilter] interface for *Filter.
// It modifies the response if host matches f.
func (f *Filter) FilterRequest(
ctx context.Context,
- req *internal.Request,
-) (r internal.Result, err error) {
+ req *filter.Request,
+) (r filter.Result, err error) {
qt := req.QType
switch qt {
case dns.TypeA, dns.TypeAAAA, dns.TypeHTTPS:
@@ -73,28 +74,21 @@ func (f *Filter) FilterRequest(
}
// replaceRule replaces the r.Rule with host if r is not nil. r must be nil,
-// [*internal.ResultModifiedRequest], or [*internal.ResultModifiedResponse].
-func replaceRule(r internal.Result, host string) {
- rule := internal.RuleText(host)
+// [*filter.ResultModifiedRequest], or [*filter.ResultModifiedResponse].
+func replaceRule(r filter.Result, host string) {
+ rule := filter.RuleText(host)
switch r := r.(type) {
case nil:
// Do nothing.
- case *internal.ResultModifiedRequest:
+ case *filter.ResultModifiedRequest:
r.Rule = rule
- case *internal.ResultModifiedResponse:
+ case *filter.ResultModifiedResponse:
r.Rule = rule
default:
panic(fmt.Errorf("safesearch: unexpected type for result: %T(%[1]v)", r))
}
}
-// ID implements the [internal.RequestFilter] interface for *Filter.
-func (f *Filter) ID() (id internal.ID) {
- id, _ = f.flt.ID()
-
- return id
-}
-
// Refresh reloads the rule list data. If acceptStale is true, and the cache
// file exists, the data is read from there regardless of its staleness.
func (f *Filter) Refresh(ctx context.Context, acceptStale bool) (err error) {
diff --git a/internal/filter/internal/safesearch/safesearch_test.go b/internal/filter/internal/safesearch/safesearch_test.go
index eabafb2..a341a1c 100644
--- a/internal/filter/internal/safesearch/safesearch_test.go
+++ b/internal/filter/internal/safesearch/safesearch_test.go
@@ -10,7 +10,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/filtertest"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/refreshable"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist"
@@ -50,7 +50,7 @@ func TestFilter(t *testing.T) {
&safesearch.Config{
Refreshable: &refreshable.Config{
Logger: slogutil.NewDiscardLogger(),
- ID: internal.IDGeneralSafeSearch,
+ ID: filter.IDGeneralSafeSearch,
URL: srvURL,
CachePath: cachePath,
Staleness: filtertest.Staleness,
@@ -100,10 +100,10 @@ func TestFilter(t *testing.T) {
res, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
- rm := testutil.RequireTypeAssert[*internal.ResultModifiedResponse](t, res)
+ rm := testutil.RequireTypeAssert[*filter.ResultModifiedResponse](t, res)
require.Len(t, rm.Msg.Answer, 1)
- assert.Equal(t, rm.Rule, internal.RuleText(testEngineWithIP))
+ assert.Equal(t, rm.Rule, filter.RuleText(testEngineWithIP))
a := testutil.RequireTypeAssert[*dns.A](t, rm.Msg.Answer[0])
assert.Equal(t, net.IP(testIPOfEngineWithIP.AsSlice()), a.A)
@@ -111,7 +111,7 @@ func TestFilter(t *testing.T) {
t.Run("cached", func(t *testing.T) {
newReq := newReq(t, testEngineWithIP, dns.TypeA)
- var cachedRes internal.Result
+ var cachedRes filter.Result
cachedRes, err = f.FilterRequest(ctx, newReq)
require.NoError(t, err)
@@ -119,7 +119,7 @@ func TestFilter(t *testing.T) {
// result of a safe search is always cloned. But assert that the
// non-clonable fields are equal and that the message has reply
// fields set properly.
- cachedMR := testutil.RequireTypeAssert[*internal.ResultModifiedResponse](t, cachedRes)
+ cachedMR := testutil.RequireTypeAssert[*filter.ResultModifiedResponse](t, cachedRes)
assert.NotSame(t, cachedMR, rm)
assert.Equal(t, cachedMR.Msg.Id, newReq.DNS.Id)
assert.Equal(t, cachedMR.List, rm.List)
@@ -133,12 +133,12 @@ func TestFilter(t *testing.T) {
res, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
- rm := testutil.RequireTypeAssert[*internal.ResultModifiedRequest](t, res)
+ rm := testutil.RequireTypeAssert[*filter.ResultModifiedRequest](t, res)
require.NotNil(t, rm.Msg)
require.Len(t, rm.Msg.Question, 1)
assert.False(t, rm.Msg.Response)
- assert.Equal(t, rm.Rule, internal.RuleText(testEngineWithDomain))
+ assert.Equal(t, rm.Rule, filter.RuleText(testEngineWithDomain))
q := rm.Msg.Question[0]
assert.Equal(t, dns.TypeA, q.Qtype)
@@ -151,12 +151,12 @@ func TestFilter(t *testing.T) {
res, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
- rm := testutil.RequireTypeAssert[*internal.ResultModifiedRequest](t, res)
+ rm := testutil.RequireTypeAssert[*filter.ResultModifiedRequest](t, res)
require.NotNil(t, rm.Msg)
require.Len(t, rm.Msg.Question, 1)
assert.False(t, rm.Msg.Response)
- assert.Equal(t, rm.Rule, internal.RuleText(testEngineWithDomain))
+ assert.Equal(t, rm.Rule, filter.RuleText(testEngineWithDomain))
q := rm.Msg.Question[0]
assert.Equal(t, dns.TypeHTTPS, q.Qtype)
@@ -166,10 +166,10 @@ func TestFilter(t *testing.T) {
// newReq is a test helper that returns the filtering request with the given
// data.
-func newReq(tb testing.TB, host string, qt dnsmsg.RRType) (req *internal.Request) {
+func newReq(tb testing.TB, host string, qt dnsmsg.RRType) (req *filter.Request) {
tb.Helper()
- return &internal.Request{
+ return &filter.Request{
DNS: dnsservertest.NewReq(host, qt, dns.ClassINET),
Messages: agdtest.NewConstructor(tb),
Host: host,
diff --git a/internal/filter/internal/serviceblock/index.go b/internal/filter/internal/serviceblock/index.go
index c6b8a69..040964a 100644
--- a/internal/filter/internal/serviceblock/index.go
+++ b/internal/filter/internal/serviceblock/index.go
@@ -9,7 +9,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist"
"github.com/AdguardTeam/golibs/errors"
)
@@ -38,7 +38,7 @@ func (r *indexResp) toInternal(
errs := make([]error, len(r.BlockedServices))
for i, svc := range r.BlockedServices {
var (
- svcID internal.BlockedServiceID
+ svcID filter.BlockedServiceID
rl *rulelist.Immutable
)
@@ -71,7 +71,7 @@ type indexRespService struct {
const cachePrefix = "filters"
// toInternal converts the service from the index to a rule-list filter. It
-// also adds the cache with ID "[internal.IDBlockedService]/[svc.ID]" to
+// also adds the cache with ID "[filter.IDBlockedService]/[svc.ID]" to
// the cache manager.
func (svc *indexRespService) toInternal(
ctx context.Context,
@@ -80,8 +80,8 @@ func (svc *indexRespService) toInternal(
cacheManager agdcache.Manager,
cacheCount int,
useCache bool,
-) (svcID internal.BlockedServiceID, rl *rulelist.Immutable, err error) {
- svcID, err = internal.NewBlockedServiceID(svc.ID)
+) (svcID filter.BlockedServiceID, rl *rulelist.Immutable, err error) {
+ svcID, err = filter.NewBlockedServiceID(svc.ID)
if err != nil {
return "", nil, fmt.Errorf("validating id: %w", err)
}
@@ -91,17 +91,9 @@ func (svc *indexRespService) toInternal(
logger.WarnContext(ctx, "service has no rules", "svc_id", svcID)
}
- fltIDStr := path.Join(cachePrefix, string(internal.IDBlockedService), string(svcID))
+ fltIDStr := path.Join(cachePrefix, string(filter.IDBlockedService), string(svcID))
cache := rulelist.NewManagedResultCache(cacheManager, fltIDStr, cacheCount, useCache)
- rl, err = rulelist.NewImmutable(
- strings.Join(svc.Rules, "\n"),
- internal.IDBlockedService,
- svcID,
- cache,
- )
- if err != nil {
- return "", nil, fmt.Errorf("compiling %s: %w", svc.ID, err)
- }
+ rl = rulelist.NewImmutable(strings.Join(svc.Rules, "\n"), filter.IDBlockedService, svcID, cache)
logger.InfoContext(ctx, "converted service", "svc_id", svcID, "num_rules", rl.RulesCount())
diff --git a/internal/filter/internal/serviceblock/serviceblock.go b/internal/filter/internal/serviceblock/serviceblock.go
index d2d5001..c99b760 100644
--- a/internal/filter/internal/serviceblock/serviceblock.go
+++ b/internal/filter/internal/serviceblock/serviceblock.go
@@ -14,7 +14,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/refreshable"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist"
)
@@ -30,11 +30,11 @@ type Filter struct {
services serviceRuleLists
errColl errcoll.Interface
- metrics internal.Metrics
+ metrics filter.Metrics
}
// serviceRuleLists is convenient alias for an ID to filter mapping.
-type serviceRuleLists = map[internal.BlockedServiceID]*rulelist.Immutable
+type serviceRuleLists = map[filter.BlockedServiceID]*rulelist.Immutable
// Config is the configuration for the service-blocking filter.
type Config struct {
@@ -48,7 +48,7 @@ type Config struct {
// Metrics are the metrics for the service-blocking filter. It must not be
// nil.
- Metrics internal.Metrics
+ Metrics filter.Metrics
}
// New returns a fully initialized service blocker. c must not be nil and must
@@ -73,7 +73,7 @@ func New(c *Config) (f *Filter, err error) {
// The order of the elements in rls is undefined.
func (f *Filter) RuleLists(
ctx context.Context,
- ids []internal.BlockedServiceID,
+ ids []filter.BlockedServiceID,
) (rls []*rulelist.Immutable) {
if len(ids) == 0 {
return nil
@@ -105,7 +105,7 @@ func (f *Filter) Refresh(
var count int
defer func() {
// TODO(a.garipov): Consider using [agdtime.Clock].
- f.metrics.SetFilterStatus(ctx, string(internal.IDBlockedService), time.Now(), count, err)
+ f.metrics.SetFilterStatus(ctx, string(filter.IDBlockedService), time.Now(), count, err)
}()
resp, err := f.loadIndex(ctx, acceptStale)
diff --git a/internal/filter/internal/serviceblock/serviceblock_test.go b/internal/filter/internal/serviceblock/serviceblock_test.go
index 3ed5f46..e174b99 100644
--- a/internal/filter/internal/serviceblock/serviceblock_test.go
+++ b/internal/filter/internal/serviceblock/serviceblock_test.go
@@ -8,7 +8,6 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/filtertest"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/refreshable"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/serviceblock"
@@ -31,7 +30,7 @@ func TestFilter(t *testing.T) {
Refreshable: &refreshable.Config{
Logger: slogutil.NewDiscardLogger(),
URL: srvURL,
- ID: internal.IDBlockedService,
+ ID: filter.IDBlockedService,
CachePath: cachePath,
Staleness: filtertest.Staleness,
Timeout: filtertest.Timeout,
@@ -49,23 +48,23 @@ func TestFilter(t *testing.T) {
testutil.RequireReceive(t, reqCh, filtertest.Timeout)
- rls := f.RuleLists(ctx, []internal.BlockedServiceID{
+ rls := f.RuleLists(ctx, []filter.BlockedServiceID{
filtertest.BlockedServiceID1,
filtertest.BlockedServiceID2,
filtertest.BlockedServiceIDDoesNotExist,
})
require.Len(t, rls, 2)
- wantSvcIDs := []internal.BlockedServiceID{
+ wantSvcIDs := []filter.BlockedServiceID{
filtertest.BlockedServiceID1,
filtertest.BlockedServiceID2,
}
- gotFltIDs := make([]internal.ID, 2)
- gotSvcIDs := make([]internal.BlockedServiceID, 2)
+ gotFltIDs := make([]filter.ID, 2)
+ gotSvcIDs := make([]filter.BlockedServiceID, 2)
gotFltIDs[0], gotSvcIDs[0] = rls[0].ID()
gotFltIDs[1], gotSvcIDs[1] = rls[1].ID()
- assert.Equal(t, internal.IDBlockedService, gotFltIDs[0])
- assert.Equal(t, internal.IDBlockedService, gotFltIDs[1])
+ assert.Equal(t, filter.IDBlockedService, gotFltIDs[0])
+ assert.Equal(t, filter.IDBlockedService, gotFltIDs[1])
assert.ElementsMatch(t, wantSvcIDs, gotSvcIDs)
}
diff --git a/internal/filter/internal/metrics.go b/internal/filter/metrics.go
similarity index 85%
rename from internal/filter/internal/metrics.go
rename to internal/filter/metrics.go
index 130ceb5..f75353e 100644
--- a/internal/filter/internal/metrics.go
+++ b/internal/filter/metrics.go
@@ -1,10 +1,13 @@
-package internal
+package filter
import (
"context"
"time"
)
+// TODO(a.garipov): Consider re-adding some metrics for custom filters after
+// AGDNS-1519.
+
// Metrics is the interface for metrics of filters.
type Metrics interface {
// SetFilterStatus sets the status of a filter by its id. If err is not
diff --git a/internal/filter/internal/result.go b/internal/filter/result.go
similarity index 99%
rename from internal/filter/internal/result.go
rename to internal/filter/result.go
index d926a2c..1d5fcff 100644
--- a/internal/filter/internal/result.go
+++ b/internal/filter/result.go
@@ -1,4 +1,4 @@
-package internal
+package filter
import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
diff --git a/internal/geoip/asntops.go b/internal/geoip/asntops.go
index 4e0b562..b9b3b85 100644
--- a/internal/geoip/asntops.go
+++ b/internal/geoip/asntops.go
@@ -6,6 +6,8 @@ import "github.com/AdguardTeam/golibs/container"
// DefaultTopASNs contains all specially handled ASNs.
var DefaultTopASNs = container.NewMapSet[ASN](
+ 3,
+ 6,
137,
174,
209,
@@ -16,14 +18,13 @@ var DefaultTopASNs = container.NewMapSet[ASN](
680,
701,
719,
- 760,
766,
786,
+ 802,
803,
812,
852,
855,
- 906,
967,
984,
1103,
@@ -39,6 +40,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
1653,
1659,
1680,
+ 1756,
1759,
1764,
1835,
@@ -53,7 +55,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
2110,
2116,
2119,
- 2200,
2497,
2514,
2516,
@@ -69,12 +70,10 @@ var DefaultTopASNs = container.NewMapSet[ASN](
2614,
2740,
2764,
- 2843,
2847,
2852,
2856,
2860,
- 3132,
3209,
3212,
3215,
@@ -89,11 +88,9 @@ var DefaultTopASNs = container.NewMapSet[ASN](
3269,
3292,
3301,
- 3302,
3303,
3308,
3320,
- 3326,
3329,
3342,
3352,
@@ -103,6 +100,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
3549,
3559,
3605,
+ 3661,
3695,
3741,
3758,
@@ -114,6 +112,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
4134,
4181,
4230,
+ 4515,
4538,
4609,
4638,
@@ -123,7 +122,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
4713,
4721,
4725,
- 4739,
4760,
4761,
4764,
@@ -145,18 +143,16 @@ var DefaultTopASNs = container.NewMapSet[ASN](
5089,
5377,
5378,
- 5379,
5384,
- 5390,
5391,
5408,
5410,
5413,
5416,
5432,
+ 5438,
5466,
- 5470,
- 5479,
+ 5482,
5483,
5518,
5532,
@@ -186,17 +182,17 @@ var DefaultTopASNs = container.NewMapSet[ASN](
6639,
6661,
6677,
- 6682,
6697,
6700,
6703,
6713,
6730,
6739,
- 6747,
6752,
6758,
+ 6769,
6772,
+ 6774,
6799,
6802,
6805,
@@ -213,16 +209,17 @@ var DefaultTopASNs = container.NewMapSet[ASN](
6939,
7018,
7029,
+ 7054,
7057,
7122,
7131,
7303,
+ 7311,
7315,
7418,
7438,
7470,
7482,
- 7497,
7522,
7524,
7545,
@@ -234,7 +231,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
7738,
7794,
7922,
- 7979,
7992,
8014,
8048,
@@ -244,12 +240,11 @@ var DefaultTopASNs = container.NewMapSet[ASN](
8167,
8193,
8200,
- 8220,
+ 8240,
8248,
8251,
8257,
8273,
- 8285,
8290,
8301,
8339,
@@ -260,6 +255,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
8374,
8376,
8386,
+ 8393,
8400,
8402,
8412,
@@ -270,9 +266,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
8449,
8452,
8462,
- 8468,
8473,
- 8517,
8542,
8544,
8551,
@@ -292,6 +286,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
8767,
8771,
8772,
+ 8778,
8781,
8814,
8818,
@@ -300,6 +295,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
8849,
8866,
8881,
+ 8899,
8926,
8948,
8953,
@@ -316,18 +312,20 @@ var DefaultTopASNs = container.NewMapSet[ASN](
9051,
9063,
9070,
+ 9080,
9105,
9119,
9121,
9123,
9125,
9129,
+ 9141,
9145,
9146,
9155,
9158,
+ 9186,
9198,
- 9199,
9208,
9231,
9245,
@@ -335,9 +333,10 @@ var DefaultTopASNs = container.NewMapSet[ASN](
9249,
9260,
9269,
+ 9293,
9299,
- 9303,
9304,
+ 9313,
9316,
9318,
9329,
@@ -362,6 +361,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
9674,
9694,
9751,
+ 9770,
9790,
9808,
9824,
@@ -371,13 +371,13 @@ var DefaultTopASNs = container.NewMapSet[ASN](
9873,
9902,
9908,
+ 9919,
9922,
9924,
9930,
9931,
9934,
- 9946,
- 9976,
+ 9988,
9997,
10010,
10013,
@@ -385,24 +385,23 @@ var DefaultTopASNs = container.NewMapSet[ASN](
10036,
10066,
10075,
- 10076,
10094,
10099,
10101,
+ 10103,
10118,
10131,
10139,
10143,
10214,
10219,
+ 10222,
10226,
10269,
10292,
10299,
10396,
- 10412,
10429,
- 10474,
10617,
10620,
10796,
@@ -415,7 +414,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
11259,
11260,
11290,
- 11315,
11351,
11367,
11426,
@@ -426,15 +424,12 @@ var DefaultTopASNs = container.NewMapSet[ASN](
11580,
11594,
11664,
- 11721,
11776,
11814,
- 11815,
11816,
11830,
11845,
11888,
- 11992,
12046,
12066,
12083,
@@ -444,6 +439,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
12297,
12301,
12302,
+ 12312,
12322,
12334,
12338,
@@ -452,24 +448,30 @@ var DefaultTopASNs = container.NewMapSet[ASN](
12361,
12365,
12389,
- 12390,
12392,
12400,
12406,
12414,
+ 12426,
12430,
12436,
12444,
+ 12453,
12455,
12479,
+ 12483,
12491,
+ 12496,
12508,
12539,
12552,
+ 12556,
+ 12570,
12576,
12578,
12597,
12605,
+ 12620,
12668,
12684,
12709,
@@ -479,13 +481,11 @@ var DefaultTopASNs = container.NewMapSet[ASN](
12741,
12754,
12764,
- 12796,
12810,
12829,
12849,
12874,
12876,
- 12905,
12912,
12929,
12946,
@@ -493,15 +493,18 @@ var DefaultTopASNs = container.NewMapSet[ASN](
12969,
12975,
12978,
+ 12993,
12997,
13000,
13030,
13036,
13037,
+ 13044,
13045,
13046,
13092,
13099,
+ 13101,
13110,
13122,
13124,
@@ -514,20 +517,20 @@ var DefaultTopASNs = container.NewMapSet[ASN](
13213,
13280,
13285,
- 13306,
13335,
13489,
+ 13682,
13771,
13999,
14061,
14080,
14117,
+ 14232,
14234,
14259,
14434,
14522,
14593,
- 14618,
14638,
14709,
14754,
@@ -536,6 +539,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
14956,
14979,
14988,
+ 15128,
15146,
15169,
15311,
@@ -548,7 +552,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
15397,
15399,
15404,
- 15425,
15433,
15435,
15440,
@@ -562,30 +565,28 @@ var DefaultTopASNs = container.NewMapSet[ASN](
15557,
15600,
15614,
- 15623,
15659,
15704,
15706,
15723,
15735,
15751,
- 15765,
15766,
15774,
15796,
15802,
15805,
+ 15806,
15808,
15836,
15895,
15897,
- 15899,
15924,
15943,
+ 15955,
15958,
15962,
15964,
- 15975,
15994,
16010,
16019,
@@ -594,6 +595,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
16082,
16086,
16116,
+ 16117,
16125,
16135,
16178,
@@ -628,7 +630,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
17421,
17451,
17458,
- 17465,
17470,
17480,
17488,
@@ -642,13 +643,13 @@ var DefaultTopASNs = container.NewMapSet[ASN](
17557,
17621,
17622,
- 17623,
+ 17638,
17639,
17665,
17676,
17698,
17705,
- 17716,
+ 17726,
17809,
17816,
17828,
@@ -663,6 +664,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
18001,
18004,
18013,
+ 18021,
18024,
18049,
18053,
@@ -674,18 +676,18 @@ var DefaultTopASNs = container.NewMapSet[ASN](
18199,
18200,
18209,
- 18278,
18371,
18390,
18403,
18412,
18419,
+ 18429,
18734,
- 18747,
18809,
18822,
18840,
18881,
+ 18895,
19108,
19114,
19180,
@@ -693,6 +695,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
19246,
19422,
19429,
+ 19447,
19624,
19711,
19863,
@@ -711,12 +714,14 @@ var DefaultTopASNs = container.NewMapSet[ASN](
20473,
20485,
20545,
+ 20626,
20634,
20661,
+ 20676,
20771,
20776,
- 20804,
20845,
+ 20846,
20860,
20875,
20879,
@@ -725,20 +730,20 @@ var DefaultTopASNs = container.NewMapSet[ASN](
20960,
20963,
20978,
+ 21001,
21003,
21021,
21040,
21050,
21056,
- 21100,
21107,
- 21171,
21183,
+ 21193,
21211,
- 21221,
21230,
21232,
21246,
+ 21263,
21271,
21277,
21283,
@@ -765,7 +770,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
22047,
22069,
22085,
- 22313,
22423,
22581,
22652,
@@ -781,9 +785,9 @@ var DefaultTopASNs = container.NewMapSet[ASN](
23201,
23243,
23383,
- 23470,
23487,
23520,
+ 23563,
23655,
23657,
23673,
@@ -791,19 +795,18 @@ var DefaultTopASNs = container.NewMapSet[ASN](
23688,
23693,
23700,
+ 23750,
23752,
- 23764,
- 23783,
23860,
23889,
23900,
+ 23917,
23923,
23944,
23955,
23956,
23969,
24016,
- 24020,
24033,
24086,
24157,
@@ -811,11 +814,9 @@ var DefaultTopASNs = container.NewMapSet[ASN](
24163,
24164,
24165,
- 24183,
24186,
24203,
24309,
- 24324,
24337,
24378,
24389,
@@ -833,7 +834,8 @@ var DefaultTopASNs = container.NewMapSet[ASN](
24560,
24589,
24608,
- 24631,
+ 24634,
+ 24641,
24645,
24651,
24691,
@@ -849,12 +851,10 @@ var DefaultTopASNs = container.NewMapSet[ASN](
24889,
24921,
24940,
- 24953,
24955,
- 24971,
25019,
25106,
- 25117,
+ 25129,
25133,
25135,
25139,
@@ -862,38 +862,38 @@ var DefaultTopASNs = container.NewMapSet[ASN](
25159,
25184,
25190,
- 25198,
+ 25211,
25229,
25248,
25250,
25255,
25274,
+ 25311,
25369,
25374,
- 25375,
25400,
25406,
25424,
25429,
25441,
- 25447,
25454,
25467,
25471,
25472,
- 25490,
25491,
- 25509,
25512,
25513,
+ 25528,
25543,
25607,
25620,
25668,
25695,
+ 25820,
26130,
26210,
26383,
+ 26548,
26599,
26611,
26615,
@@ -904,13 +904,12 @@ var DefaultTopASNs = container.NewMapSet[ASN](
27660,
27665,
27668,
+ 27669,
27672,
27680,
27694,
- 27695,
27696,
27699,
- 27708,
27717,
27725,
27729,
@@ -928,7 +927,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
27789,
27796,
27800,
- 27806,
27813,
27831,
27833,
@@ -951,7 +949,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
27932,
27947,
27951,
- 27953,
27983,
27984,
27988,
@@ -964,9 +961,8 @@ var DefaultTopASNs = container.NewMapSet[ASN](
28036,
28048,
28049,
- 28061,
- 28067,
28075,
+ 28080,
28094,
28103,
28104,
@@ -975,7 +971,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
28146,
28182,
28186,
- 28191,
+ 28198,
28201,
28210,
28220,
@@ -1015,23 +1011,27 @@ var DefaultTopASNs = container.NewMapSet[ASN](
28787,
28812,
28840,
+ 28849,
+ 28854,
28878,
28884,
28885,
- 28919,
28952,
28964,
28972,
29027,
29030,
+ 29032,
+ 29039,
29049,
29061,
29070,
29084,
29091,
29119,
+ 29124,
+ 29148,
29170,
- 29182,
29208,
29222,
29238,
@@ -1039,16 +1039,14 @@ var DefaultTopASNs = container.NewMapSet[ASN](
29247,
29256,
29286,
+ 29287,
29310,
29314,
- 29340,
- 29348,
29355,
29357,
29405,
29447,
29465,
- 29467,
29485,
29492,
29518,
@@ -1058,15 +1056,14 @@ var DefaultTopASNs = container.NewMapSet[ASN](
29571,
29580,
29582,
- 29584,
- 29600,
29614,
+ 29672,
29687,
- 29689,
29695,
29975,
30036,
30058,
+ 30272,
30344,
30526,
30600,
@@ -1077,12 +1074,11 @@ var DefaultTopASNs = container.NewMapSet[ASN](
30844,
30848,
30873,
- 30896,
- 30929,
30982,
30983,
30985,
30986,
+ 30987,
30990,
30992,
30999,
@@ -1099,37 +1095,41 @@ var DefaultTopASNs = container.NewMapSet[ASN](
31143,
31148,
31163,
- 31169,
31200,
31204,
31205,
+ 31208,
31213,
31224,
31242,
31245,
31246,
31252,
- 31263,
31272,
31287,
- 31390,
31404,
31423,
31435,
31452,
31499,
+ 31510,
+ 31520,
31543,
31549,
31615,
+ 31638,
31655,
+ 31679,
31689,
31721,
31726,
+ 31736,
31856,
31898,
31960,
32020,
32098,
+ 32167,
32398,
32860,
33363,
@@ -1150,49 +1150,51 @@ var DefaultTopASNs = container.NewMapSet[ASN](
33885,
33915,
33922,
+ 33947,
33983,
34001,
- 34032,
34058,
- 34080,
34087,
+ 34120,
34170,
+ 34187,
34224,
34244,
34245,
- 34295,
34296,
- 34318,
34362,
+ 34368,
34447,
34458,
34471,
- 34488,
34515,
+ 34525,
34533,
+ 34547,
34557,
34577,
34594,
34661,
34666,
- 34683,
34700,
34702,
+ 34705,
34718,
- 34724,
34743,
34772,
34779,
34797,
34803,
- 34820,
+ 34841,
34857,
34876,
+ 34918,
34977,
34984,
34989,
35046,
35047,
+ 35063,
35091,
35104,
35107,
@@ -1210,35 +1212,42 @@ var DefaultTopASNs = container.NewMapSet[ASN](
35328,
35346,
35362,
+ 35370,
35432,
35444,
35457,
+ 35506,
35518,
35549,
35566,
35567,
35568,
35612,
+ 35613,
35699,
+ 35706,
35725,
35729,
35753,
35790,
35805,
35807,
+ 35816,
35819,
+ 35892,
35900,
+ 35911,
36290,
- 36384,
- 36511,
+ 36352,
+ 36492,
36549,
- 36599,
36864,
36865,
36866,
36873,
36874,
36877,
+ 36881,
36884,
36890,
36892,
@@ -1251,11 +1260,11 @@ var DefaultTopASNs = container.NewMapSet[ASN](
36912,
36913,
36914,
+ 36916,
36920,
36924,
36925,
36926,
- 36930,
36935,
36937,
36939,
@@ -1266,6 +1275,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
36962,
36963,
36965,
+ 36969,
36972,
36974,
36977,
@@ -1278,10 +1288,10 @@ var DefaultTopASNs = container.NewMapSet[ASN](
37002,
37006,
37009,
+ 37012,
37014,
37020,
37027,
- 37028,
37030,
37035,
37037,
@@ -1296,7 +1306,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
37074,
37075,
37076,
- 37081,
37084,
37090,
37094,
@@ -1307,12 +1316,13 @@ var DefaultTopASNs = container.NewMapSet[ASN](
37113,
37119,
37123,
- 37124,
+ 37126,
37129,
37133,
37136,
37141,
37148,
+ 37150,
37154,
37163,
37164,
@@ -1331,6 +1341,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
37208,
37211,
37215,
+ 37218,
37219,
37223,
37228,
@@ -1343,6 +1354,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
37282,
37284,
37287,
+ 37292,
37294,
37303,
37305,
@@ -1359,8 +1371,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
37342,
37343,
37349,
- 37350,
- 37353,
37358,
37371,
37376,
@@ -1405,14 +1415,13 @@ var DefaultTopASNs = container.NewMapSet[ASN](
37552,
37559,
37563,
+ 37568,
37575,
37577,
- 37580,
37582,
37584,
37594,
37604,
- 37608,
37611,
37612,
37614,
@@ -1420,25 +1429,29 @@ var DefaultTopASNs = container.NewMapSet[ASN](
37622,
37642,
37645,
- 37647,
37649,
37654,
+ 37662,
37665,
37671,
+ 37677,
37678,
37680,
37682,
37693,
37697,
37705,
- 37713,
+ 37711,
37721,
+ 37723,
37963,
38009,
38019,
38067,
38077,
38136,
+ 38172,
+ 38176,
38195,
38198,
38201,
@@ -1449,7 +1462,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
38264,
38266,
38322,
- 38333,
38442,
38466,
38511,
@@ -1460,7 +1472,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
38623,
38742,
38800,
- 38805,
38819,
38841,
38875,
@@ -1468,44 +1479,47 @@ var DefaultTopASNs = container.NewMapSet[ASN](
38999,
39007,
39010,
+ 39024,
39032,
39067,
+ 39120,
+ 39122,
39184,
- 39199,
39216,
39232,
+ 39251,
39273,
- 39280,
+ 39279,
+ 39308,
39344,
39351,
39374,
39397,
+ 39401,
39402,
39440,
- 39544,
+ 39507,
+ 39574,
39603,
39608,
39611,
- 39615,
39642,
39647,
39686,
- 39791,
- 39823,
+ 39699,
+ 39766,
39824,
39826,
39891,
- 39906,
- 39912,
- 40021,
+ 39927,
40029,
+ 40065,
40786,
40788,
+ 40934,
40945,
- 41007,
- 41019,
+ 41046,
41068,
- 41096,
41124,
41164,
41202,
@@ -1514,25 +1528,25 @@ var DefaultTopASNs = container.NewMapSet[ASN](
41313,
41329,
41330,
- 41378,
- 41454,
- 41496,
+ 41371,
41549,
41557,
41563,
41564,
+ 41627,
41676,
41704,
- 41714,
+ 41712,
41733,
41738,
41745,
41750,
41798,
+ 41820,
41833,
+ 41881,
41897,
41956,
- 41997,
42003,
42013,
42082,
@@ -1540,8 +1554,10 @@ var DefaultTopASNs = container.NewMapSet[ASN](
42109,
42162,
42183,
+ 42201,
42232,
42235,
+ 42248,
42298,
42306,
42313,
@@ -1558,18 +1574,18 @@ var DefaultTopASNs = container.NewMapSet[ASN](
42580,
42581,
42610,
+ 42621,
42652,
42689,
42708,
42713,
- 42714,
42772,
42779,
42828,
42837,
42841,
+ 42852,
42863,
- 42864,
42905,
42908,
42912,
@@ -1578,55 +1594,51 @@ var DefaultTopASNs = container.NewMapSet[ASN](
42991,
43019,
43060,
- 43118,
+ 43061,
+ 43104,
43139,
+ 43192,
43197,
43205,
+ 43227,
43242,
43248,
43256,
- 43284,
43350,
43375,
+ 43444,
43451,
43452,
43513,
43529,
- 43533,
43568,
- 43581,
43612,
43627,
43633,
- 43656,
+ 43653,
43700,
- 43708,
- 43709,
43733,
43754,
43766,
43769,
- 43791,
+ 43811,
43824,
- 43873,
- 43892,
- 43922,
+ 43870,
43925,
43939,
43940,
- 43957,
+ 43994,
+ 44021,
44027,
44034,
+ 44066,
44086,
44087,
- 44118,
44134,
44143,
44213,
44217,
- 44234,
44244,
- 44285,
44313,
44327,
44377,
@@ -1636,20 +1648,26 @@ var DefaultTopASNs = container.NewMapSet[ASN](
44483,
44489,
44515,
- 44549,
44558,
44566,
- 44575,
+ 44631,
+ 44692,
44702,
44708,
+ 44709,
+ 44725,
44735,
+ 44803,
44869,
+ 44901,
44925,
+ 45007,
45090,
45102,
45143,
45177,
45178,
+ 45193,
45245,
45267,
45271,
@@ -1664,9 +1682,9 @@ var DefaultTopASNs = container.NewMapSet[ASN](
45543,
45588,
45609,
- 45637,
45650,
45669,
+ 45671,
45700,
45754,
45758,
@@ -1678,155 +1696,133 @@ var DefaultTopASNs = container.NewMapSet[ASN](
45905,
45916,
45925,
- 45935,
45960,
46198,
46408,
- 46573,
46650,
- 46868,
- 46941,
47139,
+ 47159,
47169,
- 47204,
- 47217,
47232,
47237,
47253,
+ 47292,
47331,
+ 47376,
47377,
47394,
- 47474,
+ 47402,
47485,
- 47493,
47524,
- 47583,
+ 47556,
47588,
47589,
+ 47782,
47790,
- 47794,
47881,
47883,
47887,
- 47898,
+ 47890,
47943,
47956,
- 47959,
47962,
- 48014,
48092,
- 48133,
- 48147,
48161,
48190,
48206,
- 48239,
48252,
48260,
- 48266,
48288,
- 48289,
- 48418,
- 48431,
+ 48408,
48437,
- 48480,
48492,
48503,
48506,
- 48584,
48629,
+ 48642,
48675,
+ 48695,
+ 48715,
48728,
- 48747,
- 48781,
48830,
48832,
48847,
48887,
48917,
- 48944,
48966,
49020,
- 49040,
49044,
49056,
49100,
49101,
49110,
- 49115,
49117,
49129,
49223,
49242,
49273,
- 49282,
- 49362,
- 49453,
- 49460,
+ 49455,
49561,
49628,
+ 49711,
49724,
49760,
49798,
49800,
+ 49801,
49808,
+ 49840,
49889,
49902,
49911,
49914,
49981,
+ 49985,
50010,
50181,
- 50195,
50223,
- 50231,
50251,
50261,
50266,
50274,
50294,
- 50304,
- 50318,
- 50334,
50349,
50436,
50463,
50467,
50482,
50500,
- 50506,
- 50558,
50581,
50613,
50616,
50635,
50648,
+ 50666,
50670,
50685,
+ 50698,
50749,
50767,
50770,
50810,
50821,
50825,
- 50917,
+ 50925,
+ 50953,
50959,
50973,
50979,
51018,
51020,
- 51026,
+ 51069,
51104,
51110,
- 51132,
- 51142,
51167,
51175,
51184,
51207,
51265,
51336,
- 51342,
51346,
51375,
51395,
@@ -1835,7 +1831,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
51495,
51504,
51582,
- 51604,
51645,
51653,
51684,
@@ -1844,13 +1839,14 @@ var DefaultTopASNs = container.NewMapSet[ASN](
51809,
51825,
51847,
- 51873,
+ 51852,
51878,
51896,
- 51947,
52075,
+ 52116,
52173,
52228,
+ 52232,
52233,
52238,
52242,
@@ -1872,7 +1868,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
52433,
52436,
52444,
- 52455,
52465,
52468,
52471,
@@ -1881,14 +1876,12 @@ var DefaultTopASNs = container.NewMapSet[ASN](
53006,
53667,
53764,
- 53813,
- 53926,
+ 54115,
54198,
54614,
- 54801,
55081,
- 55286,
55330,
+ 55387,
55391,
55392,
55415,
@@ -1896,12 +1889,9 @@ var DefaultTopASNs = container.NewMapSet[ASN](
55427,
55430,
55501,
- 55523,
- 55561,
55577,
55685,
55699,
- 55720,
55769,
55805,
55821,
@@ -1910,6 +1900,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
55853,
55900,
55915,
+ 55933,
55943,
55944,
55990,
@@ -1923,6 +1914,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
56047,
56048,
56055,
+ 56068,
56089,
56099,
56120,
@@ -1934,23 +1926,24 @@ var DefaultTopASNs = container.NewMapSet[ASN](
56300,
56309,
56329,
- 56354,
56369,
+ 56400,
56410,
- 56465,
56468,
56478,
- 56494,
+ 56491,
56497,
56568,
+ 56630,
56653,
56655,
+ 56656,
56665,
56696,
+ 56704,
56709,
- 56882,
+ 56821,
56902,
- 56933,
56971,
56995,
57000,
@@ -1963,21 +1956,21 @@ var DefaultTopASNs = container.NewMapSet[ASN](
57218,
57223,
57248,
+ 57256,
57269,
- 57279,
57293,
57344,
- 57367,
- 57370,
57374,
57388,
57389,
+ 57391,
57443,
57513,
57564,
- 57566,
57630,
57634,
+ 57722,
+ 57728,
57743,
57760,
57761,
@@ -1985,22 +1978,25 @@ var DefaultTopASNs = container.NewMapSet[ASN](
57778,
57794,
57852,
+ 57858,
57869,
57888,
+ 57961,
58061,
58065,
- 58087,
58098,
- 58118,
+ 58121,
58130,
58224,
58309,
+ 58321,
58322,
58328,
58453,
58460,
58461,
58485,
+ 58495,
58504,
58507,
58524,
@@ -2014,68 +2010,67 @@ var DefaultTopASNs = container.NewMapSet[ASN](
58731,
58821,
58895,
- 58945,
58952,
58955,
59108,
59125,
59126,
+ 59253,
59257,
- 59355,
+ 59317,
59362,
59381,
- 59396,
59497,
+ 59573,
59588,
59625,
59668,
+ 59702,
59729,
59847,
59861,
59890,
59989,
60068,
+ 60111,
60171,
60258,
60294,
- 60296,
60304,
60352,
60367,
60372,
60377,
+ 60398,
+ 60404,
60415,
60471,
- 60587,
+ 60515,
+ 60588,
60602,
- 60631,
- 60656,
- 60690,
60725,
60754,
60757,
60781,
- 60791,
60806,
+ 60886,
+ 60895,
60999,
61006,
- 61010,
61071,
61079,
61112,
- 61135,
- 61138,
61143,
+ 61174,
61189,
+ 61208,
61211,
61272,
61275,
61287,
61307,
- 61317,
61367,
61424,
- 61449,
61461,
61466,
61468,
@@ -2084,81 +2079,82 @@ var DefaultTopASNs = container.NewMapSet[ASN](
61512,
62005,
62013,
+ 62059,
62161,
62179,
62211,
62212,
- 62214,
62240,
62250,
- 62281,
62282,
+ 62336,
62419,
62563,
+ 62627,
62651,
63023,
63199,
+ 63393,
63526,
+ 63852,
63859,
- 63888,
63949,
63969,
63991,
- 64037,
+ 63996,
64043,
64073,
- 64098,
64105,
+ 64126,
64134,
- 64443,
+ 64139,
64466,
131090,
131111,
131178,
+ 131198,
131207,
131267,
131284,
131285,
- 131316,
131429,
131445,
131464,
131471,
- 131584,
131591,
131596,
131602,
131627,
131706,
131769,
+ 131965,
132061,
- 132077,
+ 132080,
132148,
132165,
132173,
132199,
132203,
132204,
+ 132222,
+ 132280,
132298,
132429,
132447,
- 132449,
132462,
132468,
132471,
132525,
+ 132608,
132618,
- 132686,
- 132815,
- 133012,
133076,
- 133199,
133200,
133210,
133334,
- 133384,
+ 133385,
+ 133398,
133433,
- 133480,
+ 133440,
133481,
133524,
133533,
@@ -2168,18 +2164,17 @@ var DefaultTopASNs = container.NewMapSet[ASN](
133623,
133661,
133752,
- 133875,
+ 133798,
133894,
133897,
133982,
134090,
134113,
134134,
- 134139,
134204,
134356,
- 134359,
134489,
+ 134525,
134562,
134651,
134674,
@@ -2188,11 +2183,12 @@ var DefaultTopASNs = container.NewMapSet[ASN](
134715,
134732,
134783,
+ 134806,
134810,
134840,
134995,
135043,
- 135097,
+ 135059,
135126,
135298,
135341,
@@ -2210,87 +2206,83 @@ var DefaultTopASNs = container.NewMapSet[ASN](
135607,
135887,
136000,
+ 136030,
136039,
136093,
136119,
136141,
136167,
- 136210,
136238,
- 136239,
136255,
- 136379,
136380,
136384,
- 136400,
136442,
136454,
- 136474,
+ 136461,
136479,
+ 136480,
136515,
136525,
136557,
- 136617,
+ 136780,
136787,
136873,
136907,
- 136919,
+ 136929,
136969,
136972,
136986,
136994,
- 137040,
137047,
137080,
- 137226,
+ 137266,
137409,
137412,
137424,
- 137443,
137449,
137453,
137503,
137526,
137561,
+ 137580,
137853,
137872,
137895,
137959,
137967,
+ 137989,
138089,
+ 138109,
+ 138123,
138134,
- 138167,
+ 138152,
138179,
138197,
- 138345,
138346,
138368,
138384,
138423,
+ 138500,
138529,
138558,
138590,
- 138606,
138640,
138655,
138754,
138886,
138915,
- 138926,
138964,
138965,
- 138997,
139009,
139029,
139043,
- 139058,
139224,
139238,
+ 139285,
139325,
139628,
- 139692,
- 139704,
- 139741,
+ 139651,
+ 139659,
139759,
139820,
139831,
@@ -2300,214 +2292,209 @@ var DefaultTopASNs = container.NewMapSet[ASN](
139898,
139922,
139952,
+ 139967,
139981,
139994,
140045,
140061,
140072,
- 140091,
140220,
+ 140243,
140292,
- 140345,
+ 140401,
+ 140457,
140485,
140499,
140504,
140594,
- 140659,
+ 140638,
140726,
- 140867,
140900,
141015,
141024,
141031,
141039,
141047,
+ 141077,
141127,
141140,
141145,
- 141216,
141342,
141421,
141607,
141680,
141681,
141711,
+ 141739,
141767,
141778,
141995,
- 142032,
142065,
142139,
142271,
142295,
+ 142319,
142386,
142647,
- 146961,
- 147023,
147049,
+ 147184,
147302,
+ 147314,
149034,
149173,
149359,
149360,
149419,
149456,
- 149487,
149707,
149771,
- 150222,
- 150331,
+ 149866,
150371,
- 150407,
+ 150381,
150452,
150683,
150692,
- 150748,
150750,
150774,
- 151080,
151396,
- 151491,
- 151636,
- 151965,
151983,
152178,
152317,
- 152329,
- 152337,
+ 152448,
152605,
152677,
+ 152918,
+ 153323,
196640,
- 196838,
+ 196735,
+ 196854,
196874,
+ 196906,
196925,
196961,
+ 197013,
+ 197119,
197207,
197225,
197248,
197296,
197301,
+ 197307,
197350,
197398,
197423,
- 197470,
197540,
197556,
+ 197623,
197706,
197716,
197830,
197862,
197882,
- 197897,
- 198023,
+ 198004,
198068,
- 198256,
198265,
198279,
- 198288,
- 198440,
198441,
198471,
- 198499,
198504,
198589,
198605,
- 198632,
198668,
198820,
198890,
- 198930,
198961,
198966,
- 199081,
+ 199128,
199140,
+ 199152,
199155,
199173,
+ 199239,
+ 199274,
199276,
- 199326,
199469,
199490,
199493,
199524,
199620,
199636,
+ 199698,
199707,
199731,
199736,
199739,
- 199785,
- 200019,
- 200076,
+ 199995,
200134,
200154,
200200,
200313,
200446,
+ 200475,
200590,
- 200665,
+ 200640,
+ 200683,
200724,
200736,
+ 200740,
200742,
200845,
200865,
- 201004,
201019,
201073,
201089,
- 201107,
+ 201150,
201167,
201241,
201249,
201411,
- 201476,
201505,
201540,
201577,
201596,
- 201601,
201603,
- 201746,
201749,
201767,
201776,
+ 201814,
201838,
201884,
- 201973,
+ 201890,
201986,
- 201997,
202023,
+ 202053,
202087,
202098,
202204,
+ 202246,
202254,
202293,
202316,
+ 202376,
+ 202391,
202422,
- 202433,
+ 202425,
202441,
- 202468,
202498,
202561,
202613,
202616,
202632,
+ 202635,
202651,
- 202652,
202662,
+ 202672,
202710,
202759,
202870,
- 202876,
- 202921,
+ 202895,
202940,
- 202960,
202987,
203020,
203136,
203143,
203206,
203214,
- 203217,
203257,
203424,
203451,
@@ -2515,129 +2502,129 @@ var DefaultTopASNs = container.NewMapSet[ASN](
203622,
203675,
203680,
- 203715,
- 203811,
+ 203744,
+ 203827,
203877,
- 203912,
203916,
- 203917,
203936,
203953,
203964,
203971,
203995,
- 203999,
- 204020,
204106,
- 204108,
204151,
204170,
- 204267,
204274,
204279,
204317,
204342,
+ 204356,
+ 204383,
+ 204390,
+ 204403,
+ 204429,
204457,
- 204467,
- 204566,
204592,
204595,
204649,
+ 204666,
+ 204716,
204793,
- 204816,
+ 204802,
+ 204894,
204918,
+ 204926,
204957,
204986,
- 205015,
+ 204996,
+ 205007,
205110,
- 205168,
205244,
205254,
205278,
- 205367,
205368,
- 205371,
205547,
205638,
205647,
205714,
205800,
- 205814,
+ 205832,
205889,
- 206013,
206026,
206065,
206067,
+ 206088,
+ 206092,
206119,
+ 206170,
206206,
206238,
206260,
206262,
- 206283,
206358,
206375,
206406,
- 206429,
206446,
+ 206471,
206478,
206519,
- 206557,
- 206575,
206610,
206666,
206774,
206783,
- 206784,
+ 206804,
206892,
206920,
206977,
+ 207044,
207097,
207137,
+ 207154,
207192,
207251,
+ 207348,
207369,
207375,
- 207408,
- 207502,
207569,
207589,
+ 207766,
207782,
207810,
207876,
+ 207980,
207990,
207991,
+ 208031,
+ 208077,
+ 208161,
208286,
208320,
208324,
- 208339,
- 208343,
- 208448,
208570,
208592,
+ 208666,
+ 208668,
208671,
208730,
208734,
208859,
+ 208864,
208905,
208972,
208997,
- 209012,
209046,
- 209049,
+ 209181,
209193,
209196,
209240,
209262,
- 209273,
209277,
- 209302,
209360,
209424,
- 209429,
209442,
- 209491,
209531,
- 209778,
+ 209641,
+ 209816,
209835,
209839,
209854,
@@ -2646,12 +2633,10 @@ var DefaultTopASNs = container.NewMapSet[ASN](
210016,
210021,
210022,
- 210061,
- 210070,
+ 210116,
210125,
210147,
210218,
- 210273,
210278,
210315,
210402,
@@ -2660,30 +2645,30 @@ var DefaultTopASNs = container.NewMapSet[ASN](
210616,
210625,
210644,
+ 210693,
210740,
- 210747,
210808,
- 210865,
211028,
211057,
+ 211098,
211211,
- 211235,
+ 211250,
211309,
211356,
211450,
- 211458,
211468,
+ 211531,
211555,
211559,
- 211759,
+ 211841,
211908,
211995,
+ 212046,
212183,
212238,
- 212271,
212330,
- 212444,
212449,
+ 212477,
212531,
212572,
212616,
@@ -2691,54 +2676,66 @@ var DefaultTopASNs = container.NewMapSet[ASN](
212645,
212655,
212661,
- 212766,
- 212851,
- 212865,
212898,
+ 212910,
212999,
+ 213139,
213155,
213207,
+ 213261,
213295,
213320,
213398,
213402,
- 213957,
+ 213946,
+ 214185,
+ 214359,
+ 214515,
+ 214576,
+ 214668,
214739,
214790,
+ 214808,
214996,
+ 215025,
215052,
215284,
- 215304,
+ 215287,
+ 215311,
215346,
215355,
+ 215416,
215421,
215423,
215501,
215540,
215597,
+ 215733,
215746,
+ 215859,
215886,
215910,
215968,
216046,
216071,
+ 216086,
216139,
- 216165,
+ 216154,
216183,
216200,
+ 216312,
216374,
216463,
262145,
262146,
262159,
+ 262179,
262181,
262186,
- 262188,
262191,
262197,
262202,
262210,
- 262215,
262223,
262234,
262239,
@@ -2746,22 +2743,21 @@ var DefaultTopASNs = container.NewMapSet[ASN](
262248,
262253,
262262,
- 262287,
- 262354,
262378,
262468,
262481,
- 262494,
262589,
262663,
262753,
262773,
262916,
+ 262932,
263073,
263175,
+ 263189,
263210,
- 263216,
263222,
+ 263223,
263224,
263238,
263242,
@@ -2769,7 +2765,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
263248,
263292,
263327,
- 263684,
263686,
263689,
263694,
@@ -2777,7 +2772,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
263703,
263717,
263725,
- 263732,
263749,
263750,
263751,
@@ -2785,18 +2779,19 @@ var DefaultTopASNs = container.NewMapSet[ASN](
263762,
263763,
263765,
+ 263767,
263783,
- 263785,
263791,
263792,
- 263793,
263805,
263824,
263980,
264221,
+ 264344,
264605,
264609,
264628,
+ 264631,
264635,
264637,
264640,
@@ -2807,33 +2802,35 @@ var DefaultTopASNs = container.NewMapSet[ASN](
264685,
264686,
264694,
+ 264696,
264731,
264733,
264738,
- 264744,
264750,
- 264754,
264756,
264758,
+ 264764,
264770,
264778,
264779,
264780,
264783,
+ 264796,
264814,
- 264821,
264825,
264837,
+ 264838,
264847,
264984,
265509,
265540,
265561,
265594,
+ 265605,
265606,
265608,
265629,
- 265631,
+ 265630,
265632,
265636,
265675,
@@ -2845,105 +2842,107 @@ var DefaultTopASNs = container.NewMapSet[ASN](
265721,
265724,
265727,
- 265762,
+ 265735,
+ 265749,
265767,
265780,
265798,
265799,
- 265818,
265822,
- 265825,
265855,
+ 265862,
265867,
265880,
266445,
266668,
266673,
- 266694,
+ 266695,
+ 266710,
266725,
- 266742,
+ 266734,
+ 266755,
266757,
+ 266762,
266792,
- 266802,
+ 266812,
266814,
266815,
+ 266828,
266831,
- 266832,
266841,
266853,
- 266858,
266860,
+ 266870,
+ 266880,
266893,
266894,
266904,
267684,
267685,
267699,
- 267705,
267708,
267713,
267749,
267761,
267765,
- 267767,
+ 267790,
267797,
267803,
267809,
+ 267818,
267828,
267845,
267846,
+ 267869,
267882,
267883,
- 267900,
267904,
- 267905,
- 267916,
- 267920,
- 268323,
268976,
269036,
+ 269194,
+ 269725,
269729,
269730,
269733,
269734,
+ 269748,
269749,
- 269750,
- 269769,
+ 269763,
269783,
- 269788,
269797,
269804,
269806,
269816,
269820,
269822,
+ 269829,
269831,
269832,
+ 269838,
269840,
- 269843,
269846,
269853,
269862,
- 269894,
+ 269868,
+ 269898,
269901,
269908,
+ 269909,
+ 269918,
269919,
269921,
269927,
269931,
269934,
+ 269936,
269940,
269946,
- 269950,
- 269953,
- 269964,
269965,
269973,
269976,
- 269981,
- 269983,
269984,
269989,
+ 270006,
270007,
270026,
270029,
@@ -2954,146 +2953,156 @@ var DefaultTopASNs = container.NewMapSet[ASN](
270058,
270068,
270071,
- 270075,
- 270081,
270096,
- 270098,
+ 270108,
+ 270158,
270161,
- 270186,
271773,
271781,
271791,
+ 271793,
271795,
- 271806,
271808,
271814,
+ 271819,
271835,
271837,
+ 271868,
271874,
+ 271880,
271899,
271907,
271909,
271911,
- 271916,
271929,
- 271930,
271933,
271935,
- 271942,
- 271943,
271951,
- 271956,
271965,
+ 271968,
271971,
- 272018,
+ 271978,
+ 271979,
+ 271988,
+ 272025,
272026,
272027,
- 272062,
- 272066,
+ 272057,
+ 272065,
+ 272075,
272083,
- 272094,
272099,
272102,
- 272104,
272106,
272110,
272112,
- 272120,
272122,
272132,
272134,
- 272138,
272809,
- 272814,
272818,
272827,
272836,
- 272838,
272851,
- 272869,
+ 272854,
272882,
272886,
272906,
272916,
272921,
+ 272928,
+ 272943,
+ 272946,
272953,
272955,
272962,
272991,
+ 273009,
273019,
- 273024,
+ 273023,
+ 273034,
273054,
- 273063,
273067,
- 273093,
- 273098,
273113,
+ 273123,
273133,
273139,
+ 273171,
273172,
273173,
- 273180,
+ 273189,
273203,
+ 273205,
+ 273221,
+ 273309,
+ 273683,
273824,
+ 273832,
273872,
+ 273960,
+ 273961,
+ 273966,
+ 274035,
327687,
327693,
327697,
327707,
327712,
+ 327714,
327716,
327724,
327725,
327728,
+ 327733,
327738,
327750,
- 327754,
327756,
+ 327760,
327765,
327769,
- 327771,
327776,
327782,
327786,
+ 327794,
327795,
327798,
327799,
327802,
+ 327804,
+ 327809,
327819,
327820,
327828,
327829,
327830,
327862,
- 327863,
327864,
327871,
+ 327876,
+ 327879,
327885,
327900,
327901,
- 327903,
- 327921,
327931,
- 327932,
327934,
+ 327941,
327947,
327972,
327975,
- 327987,
327990,
- 327991,
- 328001,
328061,
328073,
328075,
328079,
328088,
+ 328098,
328111,
328136,
328140,
328146,
328169,
328181,
+ 328184,
328191,
328196,
328198,
@@ -3101,28 +3110,28 @@ var DefaultTopASNs = container.NewMapSet[ASN](
328207,
328223,
328228,
- 328230,
- 328249,
+ 328244,
328250,
328253,
328258,
- 328282,
+ 328269,
+ 328271,
328284,
328286,
+ 328293,
328297,
- 328304,
+ 328309,
328319,
- 328320,
328331,
328341,
+ 328351,
328358,
328411,
- 328432,
- 328436,
+ 328427,
328442,
+ 328460,
328469,
328471,
- 328473,
328475,
328479,
328480,
@@ -3130,7 +3139,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
328490,
328491,
328494,
- 328509,
328510,
328514,
328535,
@@ -3139,46 +3147,47 @@ var DefaultTopASNs = container.NewMapSet[ASN](
328549,
328567,
328570,
- 328576,
328581,
328586,
328590,
328594,
+ 328599,
328605,
328610,
328611,
- 328614,
328619,
328628,
+ 328636,
328638,
- 328652,
328659,
328676,
328697,
328702,
328708,
- 328716,
328717,
328727,
- 328753,
+ 328743,
328755,
328770,
328777,
328817,
328844,
+ 328850,
328856,
328857,
328858,
328880,
- 328895,
328899,
+ 328907,
328923,
+ 328935,
+ 328939,
328943,
- 328950,
328954,
328959,
328961,
328965,
+ 328975,
328977,
328987,
328988,
@@ -3188,69 +3197,71 @@ var DefaultTopASNs = container.NewMapSet[ASN](
329014,
329027,
329029,
+ 329041,
329044,
+ 329067,
329078,
329082,
- 329095,
329101,
329103,
- 329110,
+ 329109,
329126,
329129,
+ 329135,
329140,
- 329155,
329167,
- 329170,
329174,
329179,
329183,
329192,
+ 329198,
329205,
+ 329207,
329211,
329219,
329220,
- 329223,
329253,
+ 329255,
329261,
329274,
329286,
- 329288,
329301,
- 329322,
- 329373,
+ 329341,
329387,
329390,
329411,
329415,
329422,
- 329425,
- 329426,
+ 329435,
329437,
329472,
+ 329475,
329504,
+ 393275,
+ 393559,
+ 393573,
393629,
393894,
394311,
394381,
394684,
- 395092,
395561,
- 395916,
395965,
+ 396082,
396304,
396338,
+ 396356,
396357,
- 396420,
396982,
- 397735,
397961,
398228,
+ 398326,
398721,
398901,
- 399077,
399498,
399724,
- 400099,
+ 400304,
+ 400741,
)
// DefaultCountryTopASNs is a mapping of a country to their top ASNs.
@@ -3263,9 +3274,9 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryAL: 50973,
CountryAM: 43733,
CountryAO: 37119,
- CountryAQ: 199707,
+ CountryAQ: 214808,
CountryAR: 7303,
- CountryAS: 9751,
+ CountryAS: 12684,
CountryAT: 8412,
CountryAU: 1221,
CountryAW: 11816,
@@ -3279,29 +3290,30 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryBG: 8866,
CountryBH: 5416,
CountryBI: 327799,
- CountryBJ: 37424,
- CountryBM: 32020,
+ CountryBJ: 328228,
+ CountryBL: 14593,
+ CountryBM: 3855,
CountryBN: 10094,
CountryBO: 6568,
- CountryBQ: 28104,
+ CountryBQ: 27745,
CountryBR: 26599,
CountryBS: 15146,
CountryBT: 18024,
- CountryBW: 37014,
+ CountryBW: 14988,
CountryBY: 25106,
CountryBZ: 10269,
- CountryCA: 812,
- CountryCD: 37020,
+ CountryCA: 577,
+ CountryCD: 37447,
CountryCF: 37460,
CountryCG: 36924,
- CountryCH: 6730,
+ CountryCH: 3303,
CountryCI: 29571,
CountryCK: 10131,
CountryCL: 27651,
CountryCM: 30992,
CountryCN: 4134,
CountryCO: 10620,
- CountryCR: 52263,
+ CountryCR: 11830,
CountryCU: 27725,
CountryCV: 37517,
CountryCW: 52233,
@@ -3314,14 +3326,14 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryDO: 6400,
CountryDZ: 36947,
CountryEC: 27947,
- CountryEE: 3249,
+ CountryEE: 2586,
CountryEG: 8452,
CountryER: 24757,
CountryES: 3352,
CountryET: 24757,
CountryFI: 51765,
CountryFJ: 38442,
- CountryFK: 198605,
+ CountryFK: 204649,
CountryFM: 139759,
CountryFO: 15389,
CountryFR: 3215,
@@ -3332,11 +3344,11 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryGF: 3215,
CountryGG: 8680,
CountryGH: 30986,
- CountryGI: 8301,
+ CountryGI: 44477,
CountryGL: 8818,
CountryGM: 37552,
CountryGN: 37461,
- CountryGP: 3215,
+ CountryGP: 16028,
CountryGQ: 37173,
CountryGR: 6799,
CountryGT: 14754,
@@ -3355,7 +3367,7 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryIN: 55836,
CountryIO: 17458,
CountryIQ: 203214,
- CountryIR: 197207,
+ CountryIR: 58224,
CountryIS: 44735,
CountryIT: 1267,
CountryJE: 8681,
@@ -3367,28 +3379,27 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryKH: 38623,
CountryKI: 134783,
CountryKM: 328061,
- CountryKN: 11139,
- CountryKP: 199707,
+ CountryKN: 36290,
CountryKR: 4766,
CountryKW: 29357,
CountryKY: 6639,
- CountryKZ: 206026,
- CountryLA: 9873,
- CountryLB: 57513,
+ CountryKZ: 48503,
+ CountryLA: 131267,
+ CountryLB: 42003,
CountryLC: 15344,
- CountryLI: 136787,
+ CountryLI: 20634,
CountryLK: 18001,
- CountryLR: 37094,
- CountryLS: 37057,
+ CountryLR: 37410,
+ CountryLS: 33567,
CountryLT: 8764,
CountryLU: 6661,
- CountryLV: 24921,
+ CountryLV: 1257,
CountryLY: 328286,
CountryMA: 36903,
CountryMC: 6758,
CountryMD: 8926,
CountryME: 43940,
- CountryMF: 33392,
+ CountryMF: 393894,
CountryMG: 37054,
CountryMH: 24439,
CountryMK: 6821,
@@ -3397,7 +3408,7 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryMN: 17882,
CountryMO: 4609,
CountryMP: 7131,
- CountryMQ: 16028,
+ CountryMQ: 20776,
CountryMR: 29544,
CountryMS: 396304,
CountryMT: 12709,
@@ -3410,12 +3421,14 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryNA: 36996,
CountryNC: 18200,
CountryNE: 37531,
+ CountryNF: 198605,
CountryNG: 29465,
CountryNI: 14754,
CountryNL: 1136,
CountryNO: 9009,
CountryNP: 17501,
- CountryNR: 140504,
+ CountryNR: 14593,
+ CountryNU: 198605,
CountryNZ: 9790,
CountryOM: 28885,
CountryPA: 11556,
@@ -3426,13 +3439,13 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryPK: 45669,
CountryPL: 5617,
CountryPM: 3695,
- CountryPR: 14638,
+ CountryPR: 21928,
CountryPS: 12975,
- CountryPT: 3243,
+ CountryPT: 12353,
CountryPW: 17893,
CountryPY: 23201,
CountryQA: 8781,
- CountryRE: 3215,
+ CountryRE: 199140,
CountryRO: 8708,
CountryRS: 8400,
CountryRU: 8359,
@@ -3441,17 +3454,16 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountrySB: 45891,
CountrySC: 36958,
CountrySD: 15706,
- CountrySE: 60068,
+ CountrySE: 1257,
CountrySG: 4773,
- CountrySH: 17400,
CountrySI: 21283,
CountrySK: 6855,
- CountrySL: 36988,
+ CountrySL: 37164,
CountrySM: 15433,
CountrySN: 8346,
CountrySO: 37371,
CountrySR: 27775,
- CountrySS: 37594,
+ CountrySS: 14593,
CountryST: 328191,
CountrySV: 14754,
CountrySX: 27781,
@@ -3462,13 +3474,13 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryTG: 36924,
CountryTH: 131445,
CountryTJ: 43197,
- CountryTK: 55523,
CountryTL: 58731,
CountryTM: 20661,
CountryTN: 37693,
- CountryTO: 38201,
+ CountryTO: 38198,
CountryTR: 47331,
CountryTT: 27800,
+ CountryTV: 23917,
CountryTW: 3462,
CountryTZ: 36908,
CountryUA: 15895,
@@ -3479,16 +3491,16 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryVA: 8978,
CountryVC: 46408,
CountryVE: 8048,
- CountryVG: 11139,
+ CountryVG: 14813,
CountryVI: 14434,
CountryVN: 7552,
- CountryVU: 9249,
+ CountryVU: 132429,
CountryWF: 45879,
CountryWS: 38800,
CountryXK: 21246,
CountryYE: 30873,
- CountryYT: 3215,
+ CountryYT: 49902,
CountryZA: 37457,
CountryZM: 37287,
- CountryZW: 56696,
+ CountryZW: 328088,
}
diff --git a/internal/geoip/file.go b/internal/geoip/file.go
index b9d15d6..1c47978 100644
--- a/internal/geoip/file.go
+++ b/internal/geoip/file.go
@@ -10,10 +10,10 @@ import (
"sync"
"github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
- "github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
+ "github.com/AdguardTeam/golibs/service"
"github.com/oschwald/maxminddb-golang"
)
@@ -34,6 +34,9 @@ type FileConfig struct {
// CacheManager is the global cache manager. CacheManager must not be nil.
CacheManager agdcache.Manager
+ // Metrics is used for the collection of the Geo IP database statistics.
+ Metrics Metrics
+
// AllTopASNs contains all subnets from CountryTopASNs. While scanning the
// statistics data file this set is used to check if the current ASN
// included in CountryTopASNs.
@@ -72,6 +75,8 @@ type File struct {
asn *maxminddb.Reader
country *maxminddb.Reader
+ metrics Metrics
+
// TODO(a.garipov): Consider switching fully to the country ASN method and
// removing these.
//
@@ -144,7 +149,8 @@ func NewFile(c *FileConfig) (f *File) {
c.CacheManager.Add(cacheIDIP, ipCache)
return &File{
- logger: c.Logger,
+ logger: c.Logger,
+ metrics: c.Metrics,
mu: &sync.RWMutex{},
@@ -242,8 +248,11 @@ func (f *File) SubnetByLocation(l *Location, fam netutil.AddrFamily) (n netip.Pr
// Data implements the Interface interface for *File. If ip is netip.Addr{},
// Data tries to lookup and return the data based on host, unless it's empty.
func (f *File) Data(host string, ip netip.Addr) (l *Location, err error) {
+ // TODO(e.burkov): Add context to the [Interface] methods.
+ ctx := context.TODO()
+
if ip == (netip.Addr{}) {
- return f.dataByHost(host), nil
+ return f.dataByHost(ctx, host), nil
} else if ip.Is4In6() {
// This can really only happen when querying data for ECS addresses,
// since the remote IP address is normalized in dnssvc.ipFromNetAddr.
@@ -254,12 +263,12 @@ func (f *File) Data(host string, ip netip.Addr) (l *Location, err error) {
cacheKey := ipToCacheKey(ip)
item, ok := f.ipCache.Get(cacheKey)
if ok {
- metrics.GeoIPCacheLookupsHits.Inc()
+ f.metrics.IncrementIPCacheLookups(ctx, true)
return item, nil
}
- metrics.GeoIPCacheLookupsMisses.Inc()
+ f.metrics.IncrementIPCacheLookups(ctx, false)
f.mu.RLock()
defer f.mu.RUnlock()
@@ -285,16 +294,17 @@ func (f *File) Data(host string, ip netip.Addr) (l *Location, err error) {
}
// dataByHost returns GeoIP data that has been cached previously.
-func (f *File) dataByHost(host string) (l *Location) {
+func (f *File) dataByHost(ctx context.Context, host string) (l *Location) {
item, ok := f.hostCache.Get(host)
+ if ok {
+ f.metrics.IncrementHostCacheLookups(ctx, true)
- metrics.IncrementCond(
- ok,
- metrics.GeoIPHostCacheLookupsHits,
- metrics.GeoIPHostCacheLookupsMisses,
- )
+ return item
+ }
- return item
+ f.metrics.IncrementHostCacheLookups(ctx, false)
+
+ return nil
}
// asnResult is used to retrieve autonomous system number data from a GeoIP
@@ -368,24 +378,29 @@ func (f *File) setCaches(host string, ipCacheKey any, l *Location) {
f.hostCache.Set(host, l)
}
-// Refresh implements the [agdservice.Refresher] interface for *File. It
-// reopens the GeoIP database files.
+// type check
+var _ service.Refresher = (*File)(nil)
+
+// Refresh implements the [service.Refresher] interface for *File. It reopens
+// the GeoIP database files.
func (f *File) Refresh(ctx context.Context) (err error) {
f.logger.InfoContext(ctx, "refresh started")
defer f.logger.InfoContext(ctx, "refresh finished")
- asn, err := geoIPFromFile(f.asnPath)
- if err != nil {
- metrics.GeoIPUpdateStatus.WithLabelValues(f.asnPath).Set(0)
+ var asnErr, ctryErr error
+ defer func() {
+ f.metrics.HandleASNUpdateStatus(ctx, asnErr)
+ f.metrics.HandleCountryUpdateStatus(ctx, ctryErr)
+ }()
- return fmt.Errorf("reading asn geoip: %w", err)
- }
+ asn, asnErr := geoIPFromFile(f.asnPath)
+ country, ctryErr := geoIPFromFile(f.countryPath)
- country, err := geoIPFromFile(f.countryPath)
- if err != nil {
- metrics.GeoIPUpdateStatus.WithLabelValues(f.countryPath).Set(0)
-
- return fmt.Errorf("reading country geoip: %w", err)
+ if asnErr != nil || ctryErr != nil {
+ return errors.Join(
+ errors.Annotate(asnErr, "reading asn geoip: %w"),
+ errors.Annotate(ctryErr, "reading country geoip: %w"),
+ )
}
err = f.resetSubnetMappings(ctx, asn, country)
@@ -393,11 +408,6 @@ func (f *File) Refresh(ctx context.Context) (err error) {
return fmt.Errorf("resetting geoip: %w", err)
}
- metrics.GeoIPUpdateTime.WithLabelValues(f.asnPath).SetToCurrentTime()
- metrics.GeoIPUpdateStatus.WithLabelValues(f.asnPath).Set(1)
- metrics.GeoIPUpdateTime.WithLabelValues(f.countryPath).SetToCurrentTime()
- metrics.GeoIPUpdateStatus.WithLabelValues(f.countryPath).Set(1)
-
f.mu.Lock()
defer f.mu.Unlock()
@@ -427,7 +437,9 @@ func (f *File) resetSubnetMappings(
ipv4, ipv6, locErr = f.resetLocationSubnets(ctx, asn, country)
if locErr != nil {
- metrics.GeoIPUpdateStatus.WithLabelValues(f.countryPath).Set(0)
+ // TODO(a.garipov): Clarify which metrics should be updated in case
+ // of location.
+ f.metrics.HandleCountryUpdateStatus(ctx, locErr)
locErr = fmt.Errorf("location subnet data: %w", locErr)
}
@@ -445,7 +457,7 @@ func (f *File) resetSubnetMappings(
ipv4, ipv6, ctryErr = resetCountrySubnets(ctx, f.logger, country)
if ctryErr != nil {
- metrics.GeoIPUpdateStatus.WithLabelValues(f.countryPath).Set(0)
+ f.metrics.HandleCountryUpdateStatus(ctx, ctryErr)
ctryErr = fmt.Errorf("country subnet data: %w", ctryErr)
}
diff --git a/internal/geoip/file_test.go b/internal/geoip/file_test.go
index 7fd3c5b..e3370f1 100644
--- a/internal/geoip/file_test.go
+++ b/internal/geoip/file_test.go
@@ -7,7 +7,6 @@ import (
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
@@ -16,9 +15,6 @@ import (
"github.com/stretchr/testify/require"
)
-// type check
-var _ agdservice.Refresher = (*geoip.File)(nil)
-
// testTimeout is the common timeout for tests and contexts.
const testTimeout = 1 * time.Second
@@ -36,6 +32,7 @@ func newFile(tb testing.TB, conf *geoip.FileConfig) (g *geoip.File) {
func TestFile_Data_cityDB(t *testing.T) {
conf := &geoip.FileConfig{
Logger: slogutil.NewDiscardLogger(),
+ Metrics: geoip.EmptyMetrics{},
CacheManager: agdcache.EmptyManager{},
ASNPath: asnPath,
CountryPath: cityPath,
@@ -63,6 +60,7 @@ func TestFile_Data_cityDB(t *testing.T) {
func TestFile_Data_countryDB(t *testing.T) {
conf := &geoip.FileConfig{
Logger: slogutil.NewDiscardLogger(),
+ Metrics: geoip.EmptyMetrics{},
CacheManager: agdcache.EmptyManager{},
ASNPath: asnPath,
CountryPath: countryPath,
@@ -90,6 +88,7 @@ func TestFile_Data_countryDB(t *testing.T) {
func TestFile_Data_hostCache(t *testing.T) {
conf := &geoip.FileConfig{
Logger: slogutil.NewDiscardLogger(),
+ Metrics: geoip.EmptyMetrics{},
CacheManager: agdcache.EmptyManager{},
ASNPath: asnPath,
CountryPath: cityPath,
@@ -120,6 +119,7 @@ func TestFile_Data_hostCache(t *testing.T) {
func TestFile_SubnetByLocation(t *testing.T) {
conf := &geoip.FileConfig{
Logger: slogutil.NewDiscardLogger(),
+ Metrics: geoip.EmptyMetrics{},
CacheManager: agdcache.EmptyManager{},
ASNPath: asnPath,
CountryPath: cityPath,
@@ -199,6 +199,7 @@ var (
func BenchmarkFile_Data(b *testing.B) {
conf := &geoip.FileConfig{
Logger: slogutil.NewDiscardLogger(),
+ Metrics: geoip.EmptyMetrics{},
CacheManager: agdcache.EmptyManager{},
ASNPath: asnPath,
CountryPath: cityPath,
@@ -251,6 +252,7 @@ func BenchmarkFile_Data(b *testing.B) {
func BenchmarkFile_Refresh(b *testing.B) {
conf := &geoip.FileConfig{
Logger: slogutil.NewDiscardLogger(),
+ Metrics: geoip.EmptyMetrics{},
CacheManager: agdcache.EmptyManager{},
ASNPath: asnPath,
CountryPath: cityPath,
diff --git a/internal/geoip/metrics.go b/internal/geoip/metrics.go
new file mode 100644
index 0000000..15a0d1c
--- /dev/null
+++ b/internal/geoip/metrics.go
@@ -0,0 +1,43 @@
+package geoip
+
+import "context"
+
+// Metrics is an interface that is used for the collection of the GeoIP database
+// statistics.
+type Metrics interface {
+ // HandleASNUpdateStatus updates the GeoIP ASN database update status.
+ HandleASNUpdateStatus(ctx context.Context, err error)
+
+ // HandleCountryUpdateStatus updates the GeoIP countries database update
+ // status.
+ HandleCountryUpdateStatus(ctx context.Context, err error)
+
+ // IncrementHostCacheLookups increments the number of GeoIP cache lookups
+ // for hostnames.
+ IncrementHostCacheLookups(ctx context.Context, hit bool)
+
+ // IncrementIPCacheLookups increments the number of GeoIP cache lookups for
+ // IP addresses.
+ IncrementIPCacheLookups(ctx context.Context, hit bool)
+}
+
+// EmptyMetrics is the implementation of the [Metrics] interface that does
+// nothing.
+type EmptyMetrics struct{}
+
+// type check
+var _ Metrics = EmptyMetrics{}
+
+// HandleASNUpdateStatus implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) HandleASNUpdateStatus(_ context.Context, _ error) {}
+
+// HandleCountryUpdateStatus implements the [Metrics] interface for
+// EmptyMetrics.
+func (EmptyMetrics) HandleCountryUpdateStatus(_ context.Context, _ error) {}
+
+// IncrementHostCacheLookups implements the [Metrics] interface for
+// EmptyMetrics.
+func (EmptyMetrics) IncrementHostCacheLookups(_ context.Context, _ bool) {}
+
+// IncrementIPCacheLookups implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) IncrementIPCacheLookups(_ context.Context, _ bool) {}
diff --git a/internal/metrics/billstat.go b/internal/metrics/billstat.go
index 55381be..620534c 100644
--- a/internal/metrics/billstat.go
+++ b/internal/metrics/billstat.go
@@ -12,9 +12,9 @@ import (
// Billstat is the Prometheus-based implementation of the [billstat.Metrics]
// interface.
type Billstat struct {
- // bufSize is a gauge with the total count of records in the local billing
- // statistics database.
- bufSize prometheus.Gauge
+ // recordCount is a gauge with the total count of records in the local
+ // billing statistics database.
+ recordCount prometheus.Gauge
// uploadStatus is a gauge with the status of the last billing statistics
// upload.
@@ -33,15 +33,15 @@ type Billstat struct {
// properly initialized [Billstat].
func NewBillstat(namespace string, reg prometheus.Registerer) (m *Billstat, err error) {
const (
- bufSize = "buf_size"
+ recordCount = "buf_size"
uploadStatus = "bill_stat_upload_status"
uploadTimestamp = "bill_stat_upload_timestamp"
uploadDuration = "bill_stat_upload_duration"
)
m = &Billstat{
- bufSize: prometheus.NewGauge(prometheus.GaugeOpts{
- Name: bufSize,
+ recordCount: prometheus.NewGauge(prometheus.GaugeOpts{
+ Name: recordCount,
Namespace: namespace,
Subsystem: subsystemBillStat,
Help: "Count of records in the local billstat DB.",
@@ -69,8 +69,8 @@ func NewBillstat(namespace string, reg prometheus.Registerer) (m *Billstat, err
var errs []error
collectors := container.KeyValues[string, prometheus.Collector]{{
- Key: bufSize,
- Value: m.bufSize,
+ Key: recordCount,
+ Value: m.recordCount,
}, {
Key: uploadStatus,
Value: m.uploadStatus,
@@ -96,20 +96,22 @@ func NewBillstat(namespace string, reg prometheus.Registerer) (m *Billstat, err
return m, nil
}
-// BufferSizeSet implements the [billstat.Metrics] interface for *Billstat.
-func (m *Billstat) BufferSizeSet(_ context.Context, n float64) {
- m.bufSize.Set(n)
+// SetRecordCount implements the [billstat.Metrics] interface for *Billstat.
+func (m *Billstat) SetRecordCount(_ context.Context, count int) {
+ m.recordCount.Set(float64(count))
}
// HandleUploadDuration implements the [billstat.Metrics] interface for
// *Billstat.
-func (m *Billstat) HandleUploadDuration(_ context.Context, dur float64, isSuccess bool) {
+func (m *Billstat) HandleUploadDuration(_ context.Context, dur float64, err error) {
m.uploadDuration.Observe(dur)
- if isSuccess {
- m.uploadTimestamp.SetToCurrentTime()
- m.uploadStatus.Set(1)
- } else {
+ if err != nil {
m.uploadStatus.Set(0)
+
+ return
}
+
+ m.uploadStatus.Set(1)
+ m.uploadTimestamp.SetToCurrentTime()
}
diff --git a/internal/metrics/connlimiter.go b/internal/metrics/connlimiter.go
index 154d8d1..1bea677 100644
--- a/internal/metrics/connlimiter.go
+++ b/internal/metrics/connlimiter.go
@@ -1,45 +1,152 @@
package metrics
import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/AdguardTeam/golibs/container"
+ "github.com/AdguardTeam/golibs/errors"
"github.com/prometheus/client_golang/prometheus"
- "github.com/prometheus/client_golang/prometheus/promauto"
)
-// ConnLimiterLimits is the gauge vector for showing the configured limits of
-// the number of active stream-connections.
-var ConnLimiterLimits = promauto.NewGaugeVec(prometheus.GaugeOpts{
- Name: "limits",
- Namespace: namespace,
- Subsystem: subsystemConnLimiter,
- Help: `The current limits of the number of active stream-connections: ` +
- `kind="stop" for the stopping limit and kind="resume" for the resuming one.`,
-}, []string{"kind"})
+// ConnLimiterConnMetricsData is an alias for a structure that contains
+// information about a stream-connection. All fields must not be empty.
+//
+// See [connlimiter.ConnMetricsData].
+type ConnLimiterConnMetricsData = struct {
+ // Addr is the address that the server is configured to listen on.
+ Addr string
-// ConnLimiterActiveStreamConns is the gauge vector for the number of active
-// stream-connections.
-var ConnLimiterActiveStreamConns = promauto.NewGaugeVec(prometheus.GaugeOpts{
- Name: "active_stream_conns",
- Namespace: namespace,
- Subsystem: subsystemConnLimiter,
- Help: `The number of currently active stream-connections.`,
-}, []string{"name", "proto", "addr"})
+ // Name is the name of the server.
+ Name string
-// StreamConnWaitDuration is a histogram with the duration of waiting times for
-// accepting stream connections.
-var StreamConnWaitDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{
- Name: "stream_conn_wait_duration_seconds",
- Subsystem: subsystemConnLimiter,
- Namespace: namespace,
- Help: "How long a stream connection waits for an accept, in seconds.",
- Buckets: []float64{0.00001, 0.01, 0.1, 1, 10, 30, 60},
-}, []string{"name", "proto", "addr"})
+ // Proto is the protocol of the server.
+ Proto string
+}
-// StreamConnLifeDuration is a histogram with the duration of lives of stream
-// connections.
-var StreamConnLifeDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{
- Name: "stream_conn_life_duration_seconds",
- Subsystem: subsystemConnLimiter,
- Namespace: namespace,
- Help: "How long a stream connection lives, in seconds.",
- Buckets: []float64{0.1, 1, 5, 10, 30, 60},
-}, []string{"name", "proto", "addr"})
+// ConnLimiter is a Prometheus-based implementation of the [connlimiter.Metrics]
+// interface.
+type ConnLimiter struct {
+ // activeConnections is the metrics gauge of currently active stream
+ // connections.
+ activeConnections *prometheus.GaugeVec
+
+ // lifeDuration is a histogram with the duration of stream connection lives.
+ lifeDuration *prometheus.HistogramVec
+
+ // limits is the gauge vector for showing the configured limits for active
+ // stream connections.
+ limits *prometheus.GaugeVec
+
+ // waitingDuration is a histogram with the duration of waiting times for
+ // accepting stream connections.
+ waitingDuration *prometheus.HistogramVec
+}
+
+// NewConnLimiter registers the stream-connections metrics in reg and returns a
+// properly initialized [*ConnLimiter].
+func NewConnLimiter(namespace string, reg prometheus.Registerer) (m *ConnLimiter, err error) {
+ const (
+ activeConnections = "active_stream_conns"
+ lifeDuration = "stream_conn_life_duration_seconds"
+ limits = "limits"
+ waitingDuration = "stream_conn_wait_duration_seconds"
+ )
+
+ m = &ConnLimiter{
+ activeConnections: prometheus.NewGaugeVec(prometheus.GaugeOpts{
+ Name: activeConnections,
+ Subsystem: subsystemConnLimiter,
+ Namespace: namespace,
+ Help: `The number of currently active stream-connections.`,
+ }, []string{"name", "proto", "addr"}),
+ lifeDuration: prometheus.NewHistogramVec(prometheus.HistogramOpts{
+ Name: lifeDuration,
+ Subsystem: subsystemConnLimiter,
+ Namespace: namespace,
+ Help: "How long a stream connection lives, in seconds.",
+ Buckets: []float64{0.1, 1, 5, 10, 30, 60},
+ }, []string{"name", "proto", "addr"}),
+ limits: prometheus.NewGaugeVec(prometheus.GaugeOpts{
+ Name: limits,
+ Namespace: namespace,
+ Subsystem: subsystemConnLimiter,
+ Help: `The current limits of the number of active stream-connections: ` +
+ `kind="stop" for the stopping limit and kind="resume" for the resuming one.`,
+ }, []string{"kind"}),
+ waitingDuration: prometheus.NewHistogramVec(prometheus.HistogramOpts{
+ Name: waitingDuration,
+ Subsystem: subsystemConnLimiter,
+ Namespace: namespace,
+ Help: "How long a stream connection waits for an accept, in seconds.",
+ Buckets: []float64{0.00001, 0.01, 0.1, 1, 10, 30, 60},
+ }, []string{"name", "proto", "addr"}),
+ }
+
+ var errs []error
+ collectors := container.KeyValues[string, prometheus.Collector]{{
+ Key: activeConnections,
+ Value: m.activeConnections,
+ }, {
+ Key: lifeDuration,
+ Value: m.lifeDuration,
+ }, {
+ Key: limits,
+ Value: m.limits,
+ }, {
+ Key: waitingDuration,
+ Value: m.waitingDuration,
+ }}
+
+ for _, c := range collectors {
+ err = reg.Register(c.Value)
+ if err != nil {
+ errs = append(errs, fmt.Errorf("registering metrics %q: %w", c.Key, err))
+ }
+ }
+
+ if err = errors.Join(errs...); err != nil {
+ return nil, err
+ }
+
+ return m, nil
+}
+
+// IncrementActive implements the [Metrics] interface for *ConnLimiter.
+func (c *ConnLimiter) IncrementActive(_ context.Context, m *ConnLimiterConnMetricsData) {
+ c.activeConnections.WithLabelValues(m.Name, m.Proto, m.Addr).Inc()
+}
+
+// DecrementActive implements the [Metrics] interface for *ConnLimiter.
+func (c *ConnLimiter) DecrementActive(_ context.Context, m *ConnLimiterConnMetricsData) {
+ c.activeConnections.WithLabelValues(m.Name, m.Proto, m.Addr).Dec()
+}
+
+// ObserveLifeDuration implements the [Metrics] interface for *ConnLimiter.
+func (c *ConnLimiter) ObserveLifeDuration(
+ _ context.Context,
+ m *ConnLimiterConnMetricsData,
+ dur time.Duration,
+) {
+ c.lifeDuration.WithLabelValues(m.Name, m.Proto, m.Addr).Observe(dur.Seconds())
+}
+
+// ObserveWaitingDuration implements the [Metrics] interface for *ConnLimiter.
+func (c *ConnLimiter) ObserveWaitingDuration(
+ _ context.Context,
+ m *ConnLimiterConnMetricsData,
+ dur time.Duration,
+) {
+ c.waitingDuration.WithLabelValues(m.Name, m.Proto, m.Addr).Observe(dur.Seconds())
+}
+
+// SetStopLimit implements the [Metrics] interface for *ConnLimiter.
+func (c *ConnLimiter) SetStopLimit(_ context.Context, n uint64) {
+ c.limits.WithLabelValues("stop").Set(float64(n))
+}
+
+// SetResumeLimit implements the [Metrics] interface for *ConnLimiter.
+func (c *ConnLimiter) SetResumeLimit(_ context.Context, n uint64) {
+ c.limits.WithLabelValues("resume").Set(float64(n))
+}
diff --git a/internal/metrics/dnscheck.go b/internal/metrics/dnscheck.go
index ddd2938..91405b1 100644
--- a/internal/metrics/dnscheck.go
+++ b/internal/metrics/dnscheck.go
@@ -1,25 +1,85 @@
package metrics
import (
+ "context"
+ "fmt"
+
+ "github.com/AdguardTeam/golibs/container"
+ "github.com/AdguardTeam/golibs/errors"
"github.com/prometheus/client_golang/prometheus"
- "github.com/prometheus/client_golang/prometheus/promauto"
)
-// DNSCheckRequestTotal is a counter with the total number of dnscheck requests.
-// "type" can be "dns" or "http". "valid" can be "1" or "0".
-var DNSCheckRequestTotal = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "request_total",
- Namespace: namespace,
- Subsystem: subsystemDNSCheck,
- Help: "The number of requests to the DNSCheck service.",
-}, []string{"type", "valid"})
+// DNSCheck is the Prometheus-based implementation of the [dnscheck.Metrics]
+// interface.
+type DNSCheck struct {
+ // requestTotal is a counter with the total number of dnscheck requests
+ // labeled by type and validity.
+ requestTotal *prometheus.CounterVec
-// DNSCheckErrorTotal is a gauge with the total number of errors occurred with
-// dnscheck requests. "source" can be "dns" or "http". "type" is either
-// "timeout", "ratelimit" or "other".
-var DNSCheckErrorTotal = promauto.NewGaugeVec(prometheus.GaugeOpts{
- Name: "error_total",
- Namespace: namespace,
- Subsystem: subsystemDNSCheck,
- Help: "The total number of errors with requests to the DNSCheck service.",
-}, []string{"source", "type"})
+ // errorTotal is a gauge with the total number of errors occurred with
+ // dnscheck requests labeled by request and error types.
+ errorTotal *prometheus.GaugeVec
+}
+
+// NewDNSCheck registers the DNS checker metrics in reg and returns a properly
+// initialized [*DNSCheck].
+func NewDNSCheck(namespace string, reg prometheus.Registerer) (m *DNSCheck, err error) {
+ const (
+ reqTotal = "request_total"
+ errTotal = "error_total"
+ )
+
+ m = &DNSCheck{
+ requestTotal: prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: reqTotal,
+ Namespace: namespace,
+ Subsystem: subsystemDNSCheck,
+ Help: "The number of requests to the DNSCheck service.",
+ }, []string{"type", "valid"}),
+ errorTotal: prometheus.NewGaugeVec(prometheus.GaugeOpts{
+ Name: errTotal,
+ Namespace: namespace,
+ Subsystem: subsystemDNSCheck,
+ Help: "The total number of errors with requests to the DNSCheck service.",
+ }, []string{"source", "type"}),
+ }
+
+ var errs []error
+ collectors := container.KeyValues[string, prometheus.Collector]{{
+ Key: reqTotal,
+ Value: m.requestTotal,
+ }, {
+ Key: errTotal,
+ Value: m.errorTotal,
+ }}
+
+ for _, c := range collectors {
+ err = reg.Register(c.Value)
+ if err != nil {
+ errs = append(errs, fmt.Errorf("registering metrics %q: %w", c.Key, err))
+ }
+ }
+
+ if err = errors.Join(errs...); err != nil {
+ return nil, err
+ }
+
+ return m, nil
+}
+
+// HandleError implements the [dnscheck.Metrics] interface for *DNSCheck.
+// reqType must be "dns" or "http". errType must be either "timeout",
+// "ratelimit", "other" or an empty string.
+func (m *DNSCheck) HandleError(_ context.Context, reqType, errType string) {
+ if errType == "" {
+ return
+ }
+
+ m.errorTotal.WithLabelValues(reqType, errType).Inc()
+}
+
+// HandleRequest implements the [dnscheck.Metrics] interface for *DNSCheck.
+// reqType must be "dns" or "http".
+func (m *DNSCheck) HandleRequest(_ context.Context, reqType string, isValid bool) {
+ m.requestTotal.WithLabelValues(reqType, BoolString(isValid)).Inc()
+}
diff --git a/internal/metrics/dnsdb.go b/internal/metrics/dnsdb.go
index 1d7ad39..9f15508 100644
--- a/internal/metrics/dnsdb.go
+++ b/internal/metrics/dnsdb.go
@@ -1,34 +1,94 @@
package metrics
import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/AdguardTeam/golibs/container"
+ "github.com/AdguardTeam/golibs/errors"
"github.com/prometheus/client_golang/prometheus"
- "github.com/prometheus/client_golang/prometheus/promauto"
)
-var (
- // DNSDBBufferSize is a gauge with the total count of records in the
- // in-memory temporary buffer.
- DNSDBBufferSize = promauto.NewGauge(prometheus.GaugeOpts{
- Name: "buffer_size",
- Namespace: namespace,
- Subsystem: subsystemDNSDB,
- Help: "Count of records in the in-memory buffer.",
- })
+// DNSDB is the Prometheus-based implementation of the [dnsdb.Metrics]
+// interface.
+type DNSDB struct {
+ // recordCount is a gauge with the total count of records in the in-memory
+ // temporary buffer.
+ recordCount prometheus.Gauge
- // DNSDBRotateTime is a gauge with the time when the DNSDB was rotated.
- DNSDBRotateTime = promauto.NewGauge(prometheus.GaugeOpts{
- Name: "rotate_time",
- Namespace: namespace,
- Subsystem: subsystemDNSDB,
- Help: "Last time when the database was rotated.",
- })
+ // rotateTime is a gauge with the time at which the DNS database was
+ // rotated.
+ rotateTime prometheus.Gauge
- // DNSDBSaveDuration is a histogram with the time elapsed on rotating the
- // buffer for sending over HTTP.
- DNSDBSaveDuration = promauto.NewHistogram(prometheus.HistogramOpts{
- Name: "save_duration",
- Namespace: namespace,
- Subsystem: subsystemDNSDB,
- Help: "Time elapsed on rotating the buffer for sending over HTTP.",
- })
-)
+ // rotateDuration is a histogram that stores the time elapsed during the
+ // rotation of the DNS database.
+ rotateDuration prometheus.Histogram
+}
+
+// NewDNSDB registers the filtering rule metrics in reg and returns a properly
+// initialized [*DNSDB].
+func NewDNSDB(namespace string, reg prometheus.Registerer) (m *DNSDB, err error) {
+ const (
+ recordCount = "buffer_size"
+ rotateTime = "rotate_time"
+ rotateDuration = "save_duration"
+ )
+
+ m = &DNSDB{
+ recordCount: prometheus.NewGauge(prometheus.GaugeOpts{
+ Name: recordCount,
+ Namespace: namespace,
+ Subsystem: subsystemDNSDB,
+ Help: "Count of records in the in-memory buffer.",
+ }),
+ rotateTime: prometheus.NewGauge(prometheus.GaugeOpts{
+ Name: rotateTime,
+ Namespace: namespace,
+ Subsystem: subsystemDNSDB,
+ Help: "Last time when the database was rotated.",
+ }),
+ rotateDuration: prometheus.NewHistogram(prometheus.HistogramOpts{
+ Name: rotateDuration,
+ Namespace: namespace,
+ Subsystem: subsystemDNSDB,
+ Help: "Time elapsed on rotating the buffer for sending over HTTP.",
+ }),
+ }
+
+ var errs []error
+ collectors := container.KeyValues[string, prometheus.Collector]{{
+ Key: recordCount,
+ Value: m.recordCount,
+ }, {
+ Key: rotateTime,
+ Value: m.rotateTime,
+ }, {
+ Key: rotateDuration,
+ Value: m.rotateDuration,
+ }}
+
+ for _, c := range collectors {
+ err = reg.Register(c.Value)
+ if err != nil {
+ errs = append(errs, fmt.Errorf("registering metrics %q: %w", c.Key, err))
+ }
+ }
+
+ if err = errors.Join(errs...); err != nil {
+ return nil, err
+ }
+
+ return m, nil
+}
+
+// SetRecordCount implements the [dnsdb.Metrics] interface for *DNSDB.
+func (m *DNSDB) SetRecordCount(_ context.Context, count int) {
+ m.recordCount.Set(float64(count))
+}
+
+// ObserveRotation implements the [dnsdb.Metrics] interface for *DNSDB.
+func (m *DNSDB) ObserveRotation(_ context.Context, dur time.Duration) {
+ m.rotateTime.SetToCurrentTime()
+ m.rotateDuration.Observe(dur.Seconds())
+}
diff --git a/internal/metrics/ecscache.go b/internal/metrics/ecscache.go
index 9608e9e..fa2641f 100644
--- a/internal/metrics/ecscache.go
+++ b/internal/metrics/ecscache.go
@@ -1,89 +1,122 @@
package metrics
import (
+ "context"
+ "fmt"
+
+ "github.com/AdguardTeam/golibs/container"
+ "github.com/AdguardTeam/golibs/errors"
"github.com/prometheus/client_golang/prometheus"
- "github.com/prometheus/client_golang/prometheus/promauto"
)
-// Cache size metrics.
-var (
- // ecsCacheSize is the gauge with the total number of items in a cache.
- // "supports" is either "yes" (the metric is for hostnames that support ECS)
- // or "no" (the metric is for hostnames that don't support ECS).
- ecsCacheSize = promauto.NewGaugeVec(prometheus.GaugeOpts{
- Name: "size",
+// ECSCache is a Prometheus-based implementation of the [ecscache.Metrics]
+// interface.
+type ECSCache struct {
+ // supportedCount is a gauge with the total number of items in the cache for
+ // domain names that support ECS.
+ supportedCount prometheus.Gauge
+
+ // unsupportedCount is a gauge with the total number of items in the cache
+ // for domain names that do not support ECS.
+ unsupportedCount prometheus.Gauge
+
+ // hitsTotal is a counter with the total number of ECS cache hits.
+ hitsTotal prometheus.Counter
+
+ // missesTotal is a counter with the total number of ECS cache misses.
+ missesTotal prometheus.Counter
+
+ // supportedHitsTotal is a counter with the total number of ECS cache hits
+ // for hosts that support ECS.
+ supportedHitsTotal prometheus.Counter
+
+ // supportedMissesTotal is a counter with the total number of ECS cache
+ // misses for hosts that support ECS.
+ supportedMissesTotal prometheus.Counter
+
+ // unsupportedHitsTotal is a counter with the total number of ECS cache hits
+ // for hosts that don't support ECS.
+ unsupportedHitsTotal prometheus.Counter
+
+ // unsupportedMissesTotal is a counter with the total number of ECS cache
+ // misses for hosts that don't support ECS.
+ unsupportedMissesTotal prometheus.Counter
+}
+
+// NewECSCache registers the ECS cache metrics in reg and returns a properly
+// initialized [*ECSCache].
+func NewECSCache(namespace string, reg prometheus.Registerer) (m *ECSCache, err error) {
+ const (
+ size = "size"
+ cacheLookupTotal = "total_cache_lookups"
+ )
+
+ ecsCacheSize := prometheus.NewGaugeVec(prometheus.GaugeOpts{
+ Name: size,
Namespace: namespace,
Subsystem: subsystemECSCache,
Help: "The total number of items in the ECS cache.",
}, []string{"supports"})
-
- // ECSNoSupportCacheSize is the gauge with the total number of items in
- // the cache for domain names that do not support ECS.
- ECSNoSupportCacheSize = ecsCacheSize.With(prometheus.Labels{
- "supports": "no",
- })
-
- // ECSHasSupportCacheSize is the gauge with the total number of items in
- // the cache for domain names that support ECS.
- ECSHasSupportCacheSize = ecsCacheSize.With(prometheus.Labels{
- "supports": "yes",
- })
-)
-
-// Lookup metrics.
-var (
- // ecsCacheLookups is a counter with the total number of the ECS cache
- // lookups. "hit" is either "1" (item found) or "0" (item not found).
- // "supports" is either "yes" (the metric is for hostnames that support
- // ECS), "no" (the metric is for hostnames that don't support ECS), or "all"
- // (the metric is for all hosts).
- ecsCacheLookups = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "total_cache_lookups",
+ ecsCacheLookups := prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: cacheLookupTotal,
Subsystem: subsystemECSCache,
Namespace: namespace,
- Help: "The total number of ECS cache lookups. " +
- "hit=1 means that a cached item was found.",
+ Help: "The total number of ECS cache lookups. hit=1 means that a " +
+ "cached item was found.",
}, []string{"supports", "hit"})
- // ECSCacheLookupTotalHits is a counter with the total number of ECS cache
- // hits.
- ECSCacheLookupTotalHits = ecsCacheLookups.With(prometheus.Labels{
- "hit": "1",
- "supports": "all",
- })
+ m = &ECSCache{
+ supportedCount: ecsCacheSize.WithLabelValues("yes"),
+ unsupportedCount: ecsCacheSize.WithLabelValues("no"),
- // ECSCacheLookupHasSupportHits is a counter with the number of ECS cache
- // hits for hosts that support ECS.
- ECSCacheLookupHasSupportHits = ecsCacheLookups.With(prometheus.Labels{
- "hit": "1",
- "supports": "yes",
- })
+ hitsTotal: ecsCacheLookups.WithLabelValues("all", "1"),
+ missesTotal: ecsCacheLookups.WithLabelValues("all", "0"),
- // ECSCacheLookupNoSupportHits is a counter with the number of ECS cache
- // hits for hosts that don't support ECS.
- ECSCacheLookupNoSupportHits = ecsCacheLookups.With(prometheus.Labels{
- "hit": "1",
- "supports": "no",
- })
+ supportedHitsTotal: ecsCacheLookups.WithLabelValues("yes", "1"),
+ supportedMissesTotal: ecsCacheLookups.WithLabelValues("yes", "0"),
- // ECSCacheLookupTotalMisses is a counter with the total number of ECS cache
- // misses.
- ECSCacheLookupTotalMisses = ecsCacheLookups.With(prometheus.Labels{
- "hit": "0",
- "supports": "all",
- })
+ unsupportedHitsTotal: ecsCacheLookups.WithLabelValues("no", "1"),
+ unsupportedMissesTotal: ecsCacheLookups.WithLabelValues("no", "0"),
+ }
- // ECSCacheLookupHasSupportMisses is a counter with the number of ECS cache
- // misses for hosts that support ECS.
- ECSCacheLookupHasSupportMisses = ecsCacheLookups.With(prometheus.Labels{
- "hit": "0",
- "supports": "yes",
- })
+ var errs []error
+ collectors := container.KeyValues[string, prometheus.Collector]{{
+ Key: size,
+ Value: ecsCacheSize,
+ }, {
+ Key: cacheLookupTotal,
+ Value: ecsCacheLookups,
+ }}
- // ECSCacheLookupNoSupportMisses is a counter with the number of ECS cache
- // misses for hosts that don't support ECS.
- ECSCacheLookupNoSupportMisses = ecsCacheLookups.With(prometheus.Labels{
- "hit": "0",
- "supports": "no",
- })
-)
+ for _, c := range collectors {
+ err = reg.Register(c.Value)
+ if err != nil {
+ errs = append(errs, fmt.Errorf("registering metrics %q: %w", c.Key, err))
+ }
+ }
+
+ if err = errors.Join(errs...); err != nil {
+ return nil, err
+ }
+
+ return m, nil
+}
+
+// SetElementsCount implements the [ecscache.Metrics] interface for *ECSCache.
+func (m *ECSCache) SetElementsCount(_ context.Context, supportsECS bool, count int) {
+ if supportsECS {
+ m.supportedCount.Set(float64(count))
+ } else {
+ m.unsupportedCount.Set(float64(count))
+ }
+}
+
+// IncrementLookups implements the [ecscache.Metrics] interface for *ECSCache.
+func (m *ECSCache) IncrementLookups(_ context.Context, supportsECS, hit bool) {
+ IncrementCond(hit, m.hitsTotal, m.missesTotal)
+ if hit {
+ IncrementCond(supportsECS, m.supportedHitsTotal, m.unsupportedHitsTotal)
+ } else {
+ IncrementCond(supportsECS, m.supportedMissesTotal, m.unsupportedMissesTotal)
+ }
+}
diff --git a/internal/metrics/filter.go b/internal/metrics/filter.go
index 2945932..be3d29b 100644
--- a/internal/metrics/filter.go
+++ b/internal/metrics/filter.go
@@ -8,109 +8,6 @@ import (
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/prometheus/client_golang/prometheus"
- "github.com/prometheus/client_golang/prometheus/promauto"
-)
-
-var (
- // filterCustomCacheLookups is a counter with the total number of lookups to
- // the custom filtering rules cache. "hit" is "1" if the filter was found
- // in the cache, otherwise it is "0".
- filterCustomCacheLookups = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "custom_cache_lookups",
- Subsystem: subsystemFilter,
- Namespace: namespace,
- Help: "Total number of custom filters cache lookups.",
- }, []string{"hit"})
-
- // FilterCustomCacheLookupsHits is a counter with the total number of the
- // custom filter cache hits.
- FilterCustomCacheLookupsHits = filterCustomCacheLookups.With(prometheus.Labels{"hit": "1"})
-
- // FilterCustomCacheLookupsMisses is a counter with the total number of the
- // custom filter cache misses.
- FilterCustomCacheLookupsMisses = filterCustomCacheLookups.With(prometheus.Labels{"hit": "0"})
-)
-
-var (
- // hashPrefixFilterCacheSize is a gauge with the total count of records in
- // the HashStorage cache.
- hashPrefixFilterCacheSize = promauto.NewGaugeVec(prometheus.GaugeOpts{
- Name: "hash_prefix_cache_size",
- Subsystem: subsystemFilter,
- Namespace: namespace,
- Help: "The total number of items in the HashPrefixFilter cache.",
- }, []string{"filter"})
-
- // HashPrefixFilterSafeBrowsingCacheSize is the gauge with the total number
- // of items in the cache for domain names for safe browsing filter.
- HashPrefixFilterSafeBrowsingCacheSize = hashPrefixFilterCacheSize.With(prometheus.Labels{
- "filter": "safe_browsing",
- })
-
- // HashPrefixFilterAdultBlockingCacheSize is the gauge with the total number
- // of items in the cache for domain names for adult blocking filter.
- HashPrefixFilterAdultBlockingCacheSize = hashPrefixFilterCacheSize.With(prometheus.Labels{
- "filter": "adult_blocking",
- })
-
- // HashPrefixFilterNewRegDomainsCacheSize is the gauge with the total number
- // of items in the cache for domain names for safe browsing newly registered
- // domains filter.
- HashPrefixFilterNewRegDomainsCacheSize = hashPrefixFilterCacheSize.With(prometheus.Labels{
- "filter": "newly_registered_domains",
- })
-
- // hashPrefixFilterCacheLookups is a counter with the total number of host
- // cache lookups. "hit" is either "1" (item found) or "0". (item not found).
- hashPrefixFilterCacheLookups = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "hash_prefix_cache_lookups",
- Subsystem: subsystemFilter,
- Namespace: namespace,
- Help: "The number of HashPrefixFilter host cache lookups. " +
- "hit=1 means that a cached item was found.",
- }, []string{"hit", "filter"})
-
- // HashPrefixFilterCacheSafeBrowsingHits is a counter with the total number
- // of safe browsing filter cache hits.
- HashPrefixFilterCacheSafeBrowsingHits = hashPrefixFilterCacheLookups.With(prometheus.Labels{
- "hit": "1",
- "filter": "safe_browsing",
- })
-
- // HashPrefixFilterCacheSafeBrowsingMisses is a counter with the total number
- // of safe browsing filter cache misses.
- HashPrefixFilterCacheSafeBrowsingMisses = hashPrefixFilterCacheLookups.With(prometheus.Labels{
- "hit": "0",
- "filter": "safe_browsing",
- })
-
- // HashPrefixFilterCacheAdultBlockingHits is a counter with the total number
- // of adult blocking filter cache hits.
- HashPrefixFilterCacheAdultBlockingHits = hashPrefixFilterCacheLookups.With(prometheus.Labels{
- "hit": "1",
- "filter": "adult_blocking",
- })
-
- // HashPrefixFilterCacheAdultBlockingMisses is a counter with the total number
- // of adult blocking filter cache misses.
- HashPrefixFilterCacheAdultBlockingMisses = hashPrefixFilterCacheLookups.With(prometheus.Labels{
- "hit": "0",
- "filter": "adult_blocking",
- })
-
- // HashPrefixFilterCacheNewRegDomainsHits is a counter with the total number
- // of newly registered domains filter cache hits.
- HashPrefixFilterCacheNewRegDomainsHits = hashPrefixFilterCacheLookups.With(prometheus.Labels{
- "hit": "1",
- "filter": "newly_registered_domains",
- })
-
- // HashPrefixFilterCacheNewRegDomainsMisses is a counter with the total
- // number of newly registered domains filter cache misses.
- HashPrefixFilterCacheNewRegDomainsMisses = hashPrefixFilterCacheLookups.With(prometheus.Labels{
- "hit": "0",
- "filter": "newly_registered_domains",
- })
)
// Filter is the Prometheus-based implementation of the [Filter]
@@ -189,7 +86,7 @@ func NewFilter(namespace string, reg prometheus.Registerer) (m *Filter, err erro
// SetFilterStatus implements the [filter.Metrics] interface for *Filter.
func (m *Filter) SetFilterStatus(
- ctx context.Context,
+ _ context.Context,
id string,
updTime time.Time,
ruleCount int,
diff --git a/internal/metrics/geoip.go b/internal/metrics/geoip.go
index cec1c63..11abb08 100644
--- a/internal/metrics/geoip.go
+++ b/internal/metrics/geoip.go
@@ -1,67 +1,148 @@
package metrics
import (
+ "context"
+ "fmt"
+
+ "github.com/AdguardTeam/golibs/container"
+ "github.com/AdguardTeam/golibs/errors"
"github.com/prometheus/client_golang/prometheus"
- "github.com/prometheus/client_golang/prometheus/promauto"
)
-var (
- // GeoIPUpdateTime is a gauge with the timestamp of the last GeoIP database
- // update.
- GeoIPUpdateTime = promauto.NewGaugeVec(prometheus.GaugeOpts{
- Name: "update_time",
- Subsystem: subsystemGeoIP,
- Namespace: namespace,
- Help: "The time when the GeoIP was loaded last time.",
- }, []string{"path"})
+// GeoIP is the Prometheus-based implementation of the [geoip.Metrics]
+// interface.
+type GeoIP struct {
+ updateASNTimestamp prometheus.Gauge
+ updateASNStatus prometheus.Gauge
- // GeoIPUpdateStatus is a gauge with the last GeoIP database update status.
- // 1 means success, 0 means an error occurred.
- GeoIPUpdateStatus = promauto.NewGaugeVec(prometheus.GaugeOpts{
- Name: "update_status",
- Subsystem: subsystemGeoIP,
- Namespace: namespace,
- Help: "Status of the last GeoIP update. 1 is okay, 0 means that something went wrong.",
- }, []string{"path"})
-)
+ updateCountryTimestamp prometheus.Gauge
+ updateCountryStatus prometheus.Gauge
-var (
- // geoIPCacheLookups is a counter with the total number of the GeoIP IP
- // cache lookups. "hit" is either "1" (item found) or "0" (item not found).
- geoIPCacheLookups = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "cache_lookups",
+ hostHits prometheus.Counter
+ hostMisses prometheus.Counter
+
+ ipHits prometheus.Counter
+ ipMisses prometheus.Counter
+}
+
+// NewGeoIP registers the GeoIP metrics in reg and returns a properly
+// initialized GeoIP.
+func NewGeoIP(
+ namespace string,
+ reg prometheus.Registerer,
+ asnPath string,
+ ctryPath string,
+) (m *GeoIP, err error) {
+ const (
+ updateStatus = "update_status"
+ updateTime = "update_time"
+ ipCacheLookups = "cache_lookups"
+ hostCacheLookups = "host_cache_lookups"
+ )
+
+ ipCacheLookupsCount := prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: ipCacheLookups,
Subsystem: subsystemGeoIP,
Namespace: namespace,
Help: "The number of GeoIP IP cache lookups. " +
"hit=1 means that a cached item was found.",
}, []string{"hit"})
-
- // GeoIPCacheLookupsHits is a counter with the total number of the GeoIP IP
- // cache hits.
- GeoIPCacheLookupsHits = geoIPCacheLookups.With(prometheus.Labels{"hit": "1"})
-
- // GeoIPCacheLookupsMisses is a counter with the total number of the GeoIP
- // IP cache misses.
- GeoIPCacheLookupsMisses = geoIPCacheLookups.With(prometheus.Labels{"hit": "0"})
-)
-
-var (
- // geoIPHostCacheLookups is a counter with the total number of the GeoIP
- // hostname cache lookups. "hit" is either "1" (item found) or "0" (item
- // not found).
- geoIPHostCacheLookups = promauto.NewCounterVec(prometheus.CounterOpts{
- Name: "host_cache_lookups",
+ hostCacheLookupsCount := prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: hostCacheLookups,
Subsystem: subsystemGeoIP,
Namespace: namespace,
Help: "The number of GeoIP hostname cache lookups. " +
"hit=1 means that a cached item was found.",
}, []string{"hit"})
+ updateTimestampGauge := prometheus.NewGaugeVec(prometheus.GaugeOpts{
+ Name: updateTime,
+ Subsystem: subsystemGeoIP,
+ Namespace: namespace,
+ Help: "The time when the GeoIP was loaded last time.",
+ }, []string{"path"})
+ updateStatusGauge := prometheus.NewGaugeVec(prometheus.GaugeOpts{
+ Name: updateStatus,
+ Subsystem: subsystemGeoIP,
+ Namespace: namespace,
+ Help: "Status of the last GeoIP update. " +
+ "1 is okay, 0 means that something went wrong.",
+ }, []string{"path"})
- // GeoIPHostCacheLookupsHits is a counter with the total number of the GeoIP
- // hostname cache hits.
- GeoIPHostCacheLookupsHits = geoIPHostCacheLookups.With(prometheus.Labels{"hit": "1"})
+ m = &GeoIP{
+ updateASNTimestamp: updateTimestampGauge.WithLabelValues(asnPath),
+ updateASNStatus: updateStatusGauge.WithLabelValues(asnPath),
- // GeoIPHostCacheLookupsMisses is a counter with the total number of the
- // GeoIP hostname cache misses.
- GeoIPHostCacheLookupsMisses = geoIPHostCacheLookups.With(prometheus.Labels{"hit": "0"})
-)
+ updateCountryTimestamp: updateTimestampGauge.WithLabelValues(ctryPath),
+ updateCountryStatus: updateStatusGauge.WithLabelValues(ctryPath),
+
+ hostHits: hostCacheLookupsCount.WithLabelValues("1"),
+ hostMisses: hostCacheLookupsCount.WithLabelValues("0"),
+
+ ipHits: ipCacheLookupsCount.WithLabelValues("1"),
+ ipMisses: ipCacheLookupsCount.WithLabelValues("0"),
+ }
+
+ var errs []error
+ collectors := container.KeyValues[string, prometheus.Collector]{{
+ Key: updateStatus,
+ Value: updateStatusGauge,
+ }, {
+ Key: updateTime,
+ Value: updateTimestampGauge,
+ }, {
+ Key: ipCacheLookups,
+ Value: ipCacheLookupsCount,
+ }, {
+ Key: hostCacheLookups,
+ Value: hostCacheLookupsCount,
+ }}
+
+ for _, c := range collectors {
+ err = reg.Register(c.Value)
+ if err != nil {
+ errs = append(errs, fmt.Errorf("registering metrics %q: %w", c.Key, err))
+ }
+ }
+
+ if err = errors.Join(errs...); err != nil {
+ return nil, err
+ }
+
+ return m, nil
+}
+
+// HandleASNUpdateStatus implements the [geoip.Metrics] interface for *GeoIP.
+func (m *GeoIP) HandleASNUpdateStatus(_ context.Context, err error) {
+ if err != nil {
+ m.updateASNStatus.Set(0)
+
+ return
+ }
+
+ m.updateASNStatus.Set(1)
+ m.updateASNTimestamp.SetToCurrentTime()
+}
+
+// HandleCountryUpdateStatus implements the [geoip.Metrics] interface for
+// *GeoIP.
+func (m *GeoIP) HandleCountryUpdateStatus(_ context.Context, err error) {
+ if err != nil {
+ m.updateCountryStatus.Set(0)
+
+ return
+ }
+
+ m.updateCountryStatus.Set(1)
+ m.updateCountryTimestamp.SetToCurrentTime()
+}
+
+// IncrementHostCacheLookups implements the [geoip.Metrics] interface for
+// *GeoIP.
+func (m *GeoIP) IncrementHostCacheLookups(_ context.Context, hit bool) {
+ IncrementCond(hit, m.hostHits, m.hostMisses)
+}
+
+// IncrementIPCacheLookups implements the [geoip.Metrics] interface for *GeoIP.
+func (m *GeoIP) IncrementIPCacheLookups(_ context.Context, hit bool) {
+ IncrementCond(hit, m.ipHits, m.ipMisses)
+}
diff --git a/internal/metrics/hashprefix.go b/internal/metrics/hashprefix.go
new file mode 100644
index 0000000..0e938e2
--- /dev/null
+++ b/internal/metrics/hashprefix.go
@@ -0,0 +1,97 @@
+package metrics
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/AdguardTeam/golibs/container"
+ "github.com/AdguardTeam/golibs/errors"
+ "github.com/prometheus/client_golang/prometheus"
+)
+
+// HashPrefixFilter is the Prometheus-based implementation of the
+// [hashprefix.Metrics] interface.
+type HashPrefixFilter struct {
+ // cacheSize is a gauge with the total count of records in the HashStorage
+ // cache.
+ cacheSize prometheus.Gauge
+
+ // hits is a counter of the total number of lookups to the HashStorage
+ // cache that succeeded.
+ hits prometheus.Counter
+
+ // misses is a counter of the total number of lookups to the HashStorage
+ // cache that resulted in a miss.
+ misses prometheus.Counter
+}
+
+// NewHashPrefixFilter registers the filtering metrics in reg and returns a
+// properly initialized *HashPrefixFilter. filterName must be a valid label
+// name.
+func NewHashPrefixFilter(
+ namespace string,
+ filterName string,
+ reg prometheus.Registerer,
+) (m *HashPrefixFilter, err error) {
+ const (
+ cacheLookups = "hash_prefix_cache_lookups"
+ cacheSize = "hash_prefix_cache_size"
+ )
+
+ labels := prometheus.Labels{"filter": filterName}
+
+ lookups := prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: cacheLookups,
+ Subsystem: subsystemFilter,
+ Namespace: namespace,
+ Help: "Total number of lookups to HashPrefixFilter host cache lookups. " +
+ "Label hit is the lookup result, either 1 for hit or 0 for miss.",
+ ConstLabels: labels,
+ }, []string{"hit"})
+
+ m = &HashPrefixFilter{
+ cacheSize: prometheus.NewGauge(prometheus.GaugeOpts{
+ Name: cacheSize,
+ Subsystem: subsystemFilter,
+ Namespace: namespace,
+ Help: "The total number of items in the HashPrefixFilter cache.",
+ ConstLabels: labels,
+ }),
+ hits: lookups.WithLabelValues("1"),
+ misses: lookups.WithLabelValues("0"),
+ }
+
+ var errs []error
+ collectors := container.KeyValues[string, prometheus.Collector]{{
+ Key: cacheSize,
+ Value: m.cacheSize,
+ }, {
+ Key: cacheLookups,
+ Value: lookups,
+ }}
+
+ for _, c := range collectors {
+ err = reg.Register(c.Value)
+ if err != nil {
+ errs = append(errs, fmt.Errorf("registering metrics %q: %w", c.Key, err))
+ }
+ }
+
+ if err = errors.Join(errs...); err != nil {
+ return nil, err
+ }
+
+ return m, nil
+}
+
+// IncrementLookups implements the [hashprefix.Metrics] interface for
+// *HashPrefixFilter.
+func (m *HashPrefixFilter) IncrementLookups(_ context.Context, hit bool) {
+ IncrementCond(hit, m.hits, m.misses)
+}
+
+// UpdateCacheSize implements the [hashprefix.Metrics] interface for
+// *HashPrefixFilter.
+func (m *HashPrefixFilter) UpdateCacheSize(_ context.Context, size int) {
+ m.cacheSize.Set(float64(size))
+}
diff --git a/internal/metrics/mainmw.go b/internal/metrics/mainmw.go
index 3b25f95..254b267 100644
--- a/internal/metrics/mainmw.go
+++ b/internal/metrics/mainmw.go
@@ -3,6 +3,7 @@ package metrics
import (
"context"
"fmt"
+ "log/slog"
"net/netip"
"strconv"
"time"
@@ -63,8 +64,10 @@ type DefaultMainMiddleware struct {
}
// NewDefaultMainMiddleware registers the filtering-middleware metrics in reg
-// and returns a properly initialized *DefaultMainMiddleware.
+// and returns a properly initialized *DefaultMainMiddleware. All arguments
+// must be set.
func NewDefaultMainMiddleware(
+ logger *slog.Logger,
namespace string,
reg prometheus.Registerer,
) (m *DefaultMainMiddleware, err error) {
@@ -144,7 +147,7 @@ func NewDefaultMainMiddleware(
Help: "The approximate number of DNS users for the last 1 hour.",
})
- m.userCounter = NewUserCounter(ipsLastHour, ipsLastDay)
+ m.userCounter = NewUserCounter(logger, ipsLastHour, ipsLastDay)
var errs []error
collectors := container.KeyValues[string, prometheus.Collector]{{
@@ -182,7 +185,7 @@ func NewDefaultMainMiddleware(
}
// OnRequest implements the [Metrics] interface for *DefaultMainMiddleware.
-func (m *DefaultMainMiddleware) OnRequest(_ context.Context, rm *MainMiddlewareRequestMetrics) {
+func (m *DefaultMainMiddleware) OnRequest(ctx context.Context, rm *MainMiddlewareRequestMetrics) {
m.filteringDuration.Observe(rm.FilteringDuration.Seconds())
asnStr := strconv.FormatUint(uint64(rm.ASN), 10)
@@ -197,5 +200,5 @@ func (m *DefaultMainMiddleware) OnRequest(_ context.Context, rm *MainMiddlewareR
// Assume that ip is the remote IP address, which has already been unmapped
// by [netutil.NetAddrToAddrPort].
ipArr := rm.RemoteIP.As16()
- m.userCounter.Record(time.Now(), ipArr[:], false)
+ m.userCounter.Record(ctx, time.Now(), ipArr[:], false)
}
diff --git a/internal/metrics/metrics_test.go b/internal/metrics/metrics_test.go
index 73c2259..4b0ac86 100644
--- a/internal/metrics/metrics_test.go
+++ b/internal/metrics/metrics_test.go
@@ -3,13 +3,20 @@ package metrics_test
import (
"github.com/AdguardTeam/AdGuardDNS/internal/backendpb"
"github.com/AdguardTeam/AdGuardDNS/internal/billstat"
+ "github.com/AdguardTeam/AdGuardDNS/internal/connlimiter"
"github.com/AdguardTeam/AdGuardDNS/internal/consul"
+ "github.com/AdguardTeam/AdGuardDNS/internal/dnscheck"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/dnssvc"
+ "github.com/AdguardTeam/AdGuardDNS/internal/ecscache"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix"
+ "github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb"
+ "github.com/AdguardTeam/AdGuardDNS/internal/querylog"
"github.com/AdguardTeam/AdGuardDNS/internal/remotekv/rediskv"
+ "github.com/AdguardTeam/AdGuardDNS/internal/rulestat"
"github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig"
)
@@ -21,15 +28,22 @@ var (
_ backendpb.GRPCMetrics = (*metrics.BackendGRPC)(nil)
_ backendpb.ProfileDBMetrics = (*metrics.BackendProfileDB)(nil)
_ backendpb.RemoteKVMetrics = (*metrics.BackendRemoteKV)(nil)
+ _ connlimiter.Metrics = (*metrics.ConnLimiter)(nil)
_ billstat.Metrics = (*metrics.Billstat)(nil)
_ consul.Metrics = (*metrics.Allowlist)(nil)
+ _ dnscheck.Metrics = (*metrics.DNSCheck)(nil)
_ dnsmsg.ClonerStat = metrics.ClonerStat{}
_ dnssvc.MainMiddlewareMetrics = (*metrics.DefaultMainMiddleware)(nil)
_ dnssvc.MainMiddlewareMetrics = metrics.MainMiddleware(nil)
_ dnssvc.RatelimitMiddlewareMetrics = (*metrics.DefaultRatelimitMiddleware)(nil)
_ dnssvc.RatelimitMiddlewareMetrics = metrics.RatelimitMiddleware(nil)
+ _ ecscache.Metrics = (*metrics.ECSCache)(nil)
_ filter.Metrics = (*metrics.Filter)(nil)
+ _ geoip.Metrics = (*metrics.GeoIP)(nil)
+ _ hashprefix.Metrics = (*metrics.HashPrefixFilter)(nil)
_ profiledb.Metrics = (*metrics.ProfileDB)(nil)
+ _ querylog.Metrics = (*metrics.QueryLog)(nil)
_ rediskv.Metrics = (*metrics.RedisKV)(nil)
+ _ rulestat.Metrics = (*metrics.RuleStat)(nil)
_ tlsconfig.Metrics = (*metrics.TLSConfig)(nil)
)
diff --git a/internal/metrics/querylog.go b/internal/metrics/querylog.go
index ef9d82a..deaf1ab 100644
--- a/internal/metrics/querylog.go
+++ b/internal/metrics/querylog.go
@@ -1,39 +1,100 @@
package metrics
import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/AdguardTeam/golibs/container"
+ "github.com/AdguardTeam/golibs/errors"
+ "github.com/c2h5oh/datasize"
"github.com/prometheus/client_golang/prometheus"
- "github.com/prometheus/client_golang/prometheus/promauto"
)
-// QueryLogItemsCount is a counter with the total number of query log items
-// written to the file.
-var QueryLogItemsCount = promauto.NewCounter(prometheus.CounterOpts{
- Name: "items_total",
- Subsystem: subsystemQueryLog,
- Namespace: namespace,
- Help: "The total number of query log items written.",
-})
+// QueryLog is the Prometheus-based implementation of the [querylog.Metrics]
+// interface.
+type QueryLog struct {
+ itemsTotal prometheus.Counter
+ itemSize prometheus.Histogram
+ writeDuration prometheus.Histogram
+}
-// QueryLogItemSize is a histogram with the query log items size.
-var QueryLogItemSize = promauto.NewHistogram(prometheus.HistogramOpts{
- Name: "items_size_bytes",
- Subsystem: subsystemQueryLog,
- Namespace: namespace,
- Help: "A histogram with the query log items size.",
- // Query log items are measured in bytes. Most of the space might be taken
- // by domain names and filtering rules which might in theory be pretty long,
- // therefore buckets are up to 2000 bytes.
- Buckets: []float64{50, 100, 200, 300, 400, 600, 800, 1000, 2000},
-})
+// NewQueryLog creates a new Prometheus-based query log metrics collector.
+func NewQueryLog(namespace string, reg prometheus.Registerer) (m *QueryLog, err error) {
+ const (
+ itemsTotal = "items_total"
+ itemSize = "items_size_bytes"
+ writeDuration = "write_duration_seconds"
+ )
-// QueryLogWriteDuration is a histogram with the time spent writing a query log
-// item to the file.
-var QueryLogWriteDuration = promauto.NewHistogram(prometheus.HistogramOpts{
- Name: "write_duration_seconds",
- Subsystem: subsystemQueryLog,
- Namespace: namespace,
- Help: "A histogram with the query log items size.",
- // We chose buckets considering that writing to a file is a fast operation.
- // If for some reason it takes over 1ms, something went terribly wrong.
- Buckets: []float64{0.00001, 0.0001, 0.001, 0.01, 0.1, 1},
-})
+ m = &QueryLog{
+ itemsTotal: prometheus.NewCounter(prometheus.CounterOpts{
+ Name: itemsTotal,
+ Subsystem: subsystemQueryLog,
+ Namespace: namespace,
+ Help: "The total number of query log items written.",
+ }),
+ itemSize: prometheus.NewHistogram(prometheus.HistogramOpts{
+ Name: itemSize,
+ Subsystem: subsystemQueryLog,
+ Namespace: namespace,
+ Help: "A histogram with the query log items size.",
+ // Query log items are measured in bytes. Most of the space might be
+ // taken by domain names and filtering rules which might in theory
+ // be pretty long, therefore buckets are up to 2000 bytes.
+ Buckets: []float64{50, 100, 200, 300, 400, 600, 800, 1000, 2000},
+ }),
+ writeDuration: prometheus.NewHistogram(prometheus.HistogramOpts{
+ Name: writeDuration,
+ Subsystem: subsystemQueryLog,
+ Namespace: namespace,
+ Help: "A histogram with the query log items size.",
+ // We chose buckets considering that writing to a file is a fast
+ // operation. If for some reason it takes over 1ms, something went
+ // terribly wrong.
+ Buckets: []float64{0.00001, 0.0001, 0.001, 0.01, 0.1, 1},
+ }),
+ }
+
+ var errs []error
+ collectors := container.KeyValues[string, prometheus.Collector]{{
+ Key: itemsTotal,
+ Value: m.itemsTotal,
+ }, {
+ Key: itemSize,
+ Value: m.itemSize,
+ }, {
+ Key: writeDuration,
+ Value: m.writeDuration,
+ }}
+
+ for _, c := range collectors {
+ err = reg.Register(c.Value)
+ if err != nil {
+ errs = append(errs, fmt.Errorf("registering metrics %q: %w", c.Key, err))
+ }
+ }
+
+ if err = errors.Join(errs...); err != nil {
+ return nil, err
+ }
+
+ return m, nil
+}
+
+// IncrementItemsCount implements the [querylog.Metrics] interface for
+// *QueryLog.
+func (m *QueryLog) IncrementItemsCount(_ context.Context) {
+ m.itemsTotal.Inc()
+}
+
+// ObserveItemSize implements the [querylog.Metrics] interface for *QueryLog.
+func (m *QueryLog) ObserveItemSize(_ context.Context, size datasize.ByteSize) {
+ m.itemSize.Observe(float64(size))
+}
+
+// ObserveWriteDuration implements the [querylog.Metrics] interface for
+// *QueryLog.
+func (m *QueryLog) ObserveWriteDuration(_ context.Context, dur time.Duration) {
+ m.writeDuration.Observe(dur.Seconds())
+}
diff --git a/internal/metrics/remotekv.go b/internal/metrics/remotekv.go
index e7a09d3..3f988ca 100644
--- a/internal/metrics/remotekv.go
+++ b/internal/metrics/remotekv.go
@@ -69,10 +69,10 @@ func NewRedisKV(namespace string, reg prometheus.Registerer) (m *RedisKV, err er
}
// UpdateMetrics implements the [rediskv.Metrics] interface for *RedisKV.
-func (m *RedisKV) UpdateMetrics(_ context.Context, val uint, isSuccess bool) {
+func (m *RedisKV) UpdateMetrics(_ context.Context, val uint, err error) {
m.activeConnections.Set(float64(val))
- if !isSuccess {
+ if err != nil {
m.errors.Inc()
}
}
diff --git a/internal/metrics/rulestat.go b/internal/metrics/rulestat.go
index 0cd4a1f..5d5712d 100644
--- a/internal/metrics/rulestat.go
+++ b/internal/metrics/rulestat.go
@@ -1,32 +1,99 @@
package metrics
import (
+ "context"
+ "fmt"
+
+ "github.com/AdguardTeam/golibs/container"
+ "github.com/AdguardTeam/golibs/errors"
"github.com/prometheus/client_golang/prometheus"
- "github.com/prometheus/client_golang/prometheus/promauto"
)
-var (
- // RuleStatCacheSize is a gauge with the count of recorded rule hits not
- // yet uploaded.
- RuleStatCacheSize = promauto.NewGauge(prometheus.GaugeOpts{
- Name: "stats_cache_size",
- Namespace: namespace,
- Subsystem: subsystemRuleStat,
- Help: "Count of recorded rule hits not yet dumped.",
- })
- // RuleStatUploadStatus is a gauge with the status of the last stats upload.
- RuleStatUploadStatus = promauto.NewGauge(prometheus.GaugeOpts{
- Name: "stats_upload_status",
- Namespace: namespace,
- Subsystem: subsystemRuleStat,
- Help: "Status of the last stats upload.",
- })
- // RuleStatUploadTimestamp is a gauge with the timestamp of the last stats
- // upload.
- RuleStatUploadTimestamp = promauto.NewGauge(prometheus.GaugeOpts{
- Name: "stats_upload_timestamp",
- Namespace: namespace,
- Subsystem: subsystemRuleStat,
- Help: "Time when stats were uploaded last time.",
- })
-)
+// RuleStat is the Prometheus-based implementation of the [rulestat.Metrics]
+// interface.
+type RuleStat struct {
+ // hitCount is a gauge with the count of recorded rule hits that have not
+ // yet been uploaded.
+ hitCount prometheus.Gauge
+
+ // uploadStatus is a gauge with the status of the last stats upload.
+ uploadStatus prometheus.Gauge
+
+ // uploadTimestamp is a gauge with the timestamp of the last successful
+ // stats upload.
+ uploadTimestamp prometheus.Gauge
+}
+
+// NewRuleStat registers the filtering rule metrics in reg and returns a
+// properly initialized [*RuleStat].
+func NewRuleStat(namespace string, reg prometheus.Registerer) (m *RuleStat, err error) {
+ const (
+ // TODO(s.chzhen): Check if this is correct.
+ hitCount = "stats_cache_size"
+ uploadStatus = "stats_upload_status"
+ uploadTimestamp = "stats_upload_timestamp"
+ )
+
+ m = &RuleStat{
+ hitCount: prometheus.NewGauge(prometheus.GaugeOpts{
+ Name: hitCount,
+ Namespace: namespace,
+ Subsystem: subsystemRuleStat,
+ Help: "Count of recorded rule hits not yet dumped.",
+ }),
+ uploadStatus: prometheus.NewGauge(prometheus.GaugeOpts{
+ Name: uploadStatus,
+ Namespace: namespace,
+ Subsystem: subsystemRuleStat,
+ Help: "Status of the last stats upload.",
+ }),
+ uploadTimestamp: prometheus.NewGauge(prometheus.GaugeOpts{
+ Name: uploadTimestamp,
+ Namespace: namespace,
+ Subsystem: subsystemRuleStat,
+ Help: "Time when stats were uploaded last time.",
+ }),
+ }
+
+ var errs []error
+ collectors := container.KeyValues[string, prometheus.Collector]{{
+ Key: hitCount,
+ Value: m.hitCount,
+ }, {
+ Key: uploadStatus,
+ Value: m.uploadStatus,
+ }, {
+ Key: uploadTimestamp,
+ Value: m.uploadTimestamp,
+ }}
+
+ for _, c := range collectors {
+ err = reg.Register(c.Value)
+ if err != nil {
+ errs = append(errs, fmt.Errorf("registering metrics %q: %w", c.Key, err))
+ }
+ }
+
+ if err = errors.Join(errs...); err != nil {
+ return nil, err
+ }
+
+ return m, nil
+}
+
+// SetHitCount implements the [rulestat.Metrics] interface for *RuleStat.
+func (m *RuleStat) SetHitCount(_ context.Context, count int64) {
+ m.hitCount.Set(float64(count))
+}
+
+// HandleUploadStatus implements the [rulestat.Metrics] interface for *RuleStat.
+func (m *RuleStat) HandleUploadStatus(_ context.Context, err error) {
+ if err != nil {
+ m.uploadStatus.Set(0)
+
+ return
+ }
+
+ m.uploadStatus.Set(1)
+ m.uploadTimestamp.SetToCurrentTime()
+}
diff --git a/internal/metrics/tls.go b/internal/metrics/tls.go
index 5aed2e3..7c7edcf 100644
--- a/internal/metrics/tls.go
+++ b/internal/metrics/tls.go
@@ -210,8 +210,8 @@ func (m *TLSConfig) SetCertificateInfo(_ context.Context, algo, subj string, not
// SetSessionTicketRotationStatus implements the [tlsconfig.Metrics] interface
// for *TLSConfig.
-func (m *TLSConfig) SetSessionTicketRotationStatus(_ context.Context, enabled bool) {
- if !enabled {
+func (m *TLSConfig) SetSessionTicketRotationStatus(_ context.Context, err error) {
+ if err != nil {
m.sessionTicketsRotateStatus.Set(0)
return
diff --git a/internal/metrics/usercount.go b/internal/metrics/usercount.go
index 49ace3b..0e304bd 100644
--- a/internal/metrics/usercount.go
+++ b/internal/metrics/usercount.go
@@ -1,11 +1,13 @@
package metrics
import (
+ "context"
"fmt"
+ "log/slog"
"sync"
"time"
- "github.com/AdguardTeam/golibs/log"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/timeutil"
"github.com/axiomhq/hyperloglog"
"github.com/prometheus/client_golang/prometheus"
@@ -24,6 +26,9 @@ const (
//
// TODO(a.garipov): Improve and move to golibs.
type UserCounter struct {
+ // logger is used to report errors.
+ logger *slog.Logger
+
// lastHour is a gauge with an approximate number of DNS users for the
// last 1 hour.
lastHour prometheus.Gauge
@@ -55,9 +60,11 @@ type UserCounter struct {
}
// NewUserCounter initializes and returns a properly initialized *UserCounter
-// that uses the given gauges to estimate the user count.
-func NewUserCounter(lastHour, lastDay prometheus.Gauge) (c *UserCounter) {
+// that uses the given gauges to estimate the user count. All arguments must
+// not be nil.
+func NewUserCounter(logger *slog.Logger, lastHour, lastDay prometheus.Gauge) (c *UserCounter) {
return &UserCounter{
+ logger: logger,
lastHour: lastHour,
lastDay: lastDay,
currentMu: &sync.Mutex{},
@@ -80,7 +87,7 @@ func NewUserCounter(lastHour, lastDay prometheus.Gauge) (c *UserCounter) {
// synchronously. It is currently only used in tests.
//
// It currently assumes that it will be called at least once per day.
-func (c *UserCounter) Record(now time.Time, userData []byte, syncUpdate bool) {
+func (c *UserCounter) Record(ctx context.Context, now time.Time, userData []byte, syncUpdate bool) {
hour, minute, _ := now.Clock()
minuteOfDay := hour*minutesPerHour + minute
@@ -98,9 +105,9 @@ func (c *UserCounter) Record(now time.Time, userData []byte, syncUpdate bool) {
// counters, since there are none.
if prevMinute != -1 {
if syncUpdate {
- c.updateCounters(prevMinute, hour, prevMinuteCounter)
+ c.updateCounters(ctx, prevMinute, hour, prevMinuteCounter)
} else {
- go c.updateCounters(prevMinute, hour, prevMinuteCounter)
+ go c.updateCounters(ctx, prevMinute, hour, prevMinuteCounter)
}
}
}
@@ -112,11 +119,12 @@ func (c *UserCounter) Record(now time.Time, userData []byte, syncUpdate bool) {
// the metrics. It also clears all the stale hourly counters from the previous
// day.
func (c *UserCounter) updateCounters(
+ ctx context.Context,
prevMinute int,
currentHour int,
prevMinuteCounter *hyperloglog.Sketch,
) {
- defer log.OnPanic("metrics.userCounter.updateCounters")
+ defer slogutil.RecoverAndLog(ctx, c.logger)
prevMinuteOfHour := prevMinute % minutesPerHour
hourOfPrevMinute := prevMinute / minutesPerHour
diff --git a/internal/metrics/usercount_test.go b/internal/metrics/usercount_test.go
index d29c610..3788c81 100644
--- a/internal/metrics/usercount_test.go
+++ b/internal/metrics/usercount_test.go
@@ -1,6 +1,8 @@
package metrics_test
import (
+ "context"
+ "math/rand/v2"
"net"
"net/netip"
"strconv"
@@ -8,15 +10,15 @@ import (
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
- "golang.org/x/exp/rand"
)
-// Use a constant seed to make the test reproducible.
-const randSeed = 1234
+// Use the same seed to make the test reproducible.
+var randSeed = [32]byte([]byte("01234567890123456789012345678901"))
// Gauges for tests.
var (
@@ -24,10 +26,13 @@ var (
testLastDay = prometheus.NewGauge(prometheus.GaugeOpts{})
)
+// testLogger is the common logger for tests.
+var testLogger = slogutil.NewDiscardLogger()
+
// randIPBytes is a test helper that returns a pseudorandomly generated
// IP-address bytes. fam must be either [netutil.AddrFamilyIPv4] or
// [netutil.AddrFamilyIPv6].
-func randIPBytes(t testing.TB, r *rand.Rand, fam netutil.AddrFamily) (ipBytes []byte) {
+func randIPBytes(t testing.TB, src *rand.ChaCha8, fam netutil.AddrFamily) (ipBytes []byte) {
t.Helper()
switch fam {
@@ -39,7 +44,7 @@ func randIPBytes(t testing.TB, r *rand.Rand, fam netutil.AddrFamily) (ipBytes []
t.Fatalf("unexpected address family %q", fam)
}
- n, err := r.Read(ipBytes)
+ n, err := src.Read(ipBytes)
require.NoError(t, err)
require.Equal(t, len(ipBytes), n)
@@ -166,11 +171,12 @@ func TestUserCounter_Estimate(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- r := rand.New(rand.NewSource(randSeed))
- c := metrics.NewUserCounter(testLastHour, testLastDay)
+ src := rand.NewChaCha8(randSeed)
+ c := metrics.NewUserCounter(testLogger, testLastHour, testLastDay)
+ ctx := context.Background()
for _, now := range tc.nows {
- c.Record(now, randIPBytes(t, r, netutil.AddrFamilyIPv6), true)
+ c.Record(ctx, now, randIPBytes(t, src, netutil.AddrFamilyIPv6), true)
}
hourly, daily := c.Estimate()
@@ -183,17 +189,17 @@ func TestUserCounter_Estimate(t *testing.T) {
func TestUserCounter_simple(t *testing.T) {
const ipsPerMinute = 2
- src := rand.NewSource(randSeed)
- r := rand.New(src)
+ src := rand.NewChaCha8(randSeed)
- c := metrics.NewUserCounter(testLastHour, testLastDay)
+ c := metrics.NewUserCounter(testLogger, testLastHour, testLastDay)
now := time.Unix(0, 0).UTC()
for d, h := now.Day(), now.Hour(); now.Day() == d; h = now.Hour() {
t.Run(strconv.Itoa(now.Hour()), func(t *testing.T) {
+ ctx := context.Background()
for ; now.Hour() == h; now = now.Add(1 * time.Minute) {
for range ipsPerMinute {
- c.Record(now, randIPBytes(t, r, netutil.AddrFamilyIPv4), true)
+ c.Record(ctx, now, randIPBytes(t, src, netutil.AddrFamilyIPv4), true)
}
}
@@ -212,23 +218,24 @@ var uint64Sink uint64
func BenchmarkUserCounter_Estimate(b *testing.B) {
const n = 100
+ ctx := context.Background()
zeroTime := time.Unix(0, 0).UTC()
- sparseCounter := metrics.NewUserCounter(testLastHour, testLastDay)
+ sparseCounter := metrics.NewUserCounter(testLogger, testLastHour, testLastDay)
for d, now := zeroTime.Day(), zeroTime; d == now.Day(); now = now.Add(time.Minute) {
- r := rand.New(rand.NewSource(randSeed))
+ src := rand.NewChaCha8(randSeed)
for range n {
- sparseCounter.Record(now, randIPBytes(b, r, netutil.AddrFamilyIPv6), true)
+ sparseCounter.Record(ctx, now, randIPBytes(b, src, netutil.AddrFamilyIPv6), true)
}
}
- seqCounter := metrics.NewUserCounter(testLastHour, testLastDay)
+ seqCounter := metrics.NewUserCounter(testLogger, testLastHour, testLastDay)
for d, now := zeroTime.Day(), zeroTime; d == now.Day(); now = now.Add(time.Minute) {
addr := netip.AddrFrom16([16]byte{})
for range n {
addr = addr.Next()
addrArr := addr.As16()
- seqCounter.Record(now, addrArr[:], true)
+ seqCounter.Record(ctx, now, addrArr[:], true)
}
}
diff --git a/internal/optlog/optlog.go b/internal/optlog/optlog.go
deleted file mode 100644
index 8f03e90..0000000
--- a/internal/optlog/optlog.go
+++ /dev/null
@@ -1,15 +0,0 @@
-// Package optlog contains ugly hacks to make debug logs allocate less when
-// debug mode is not enabled. Add all such hacks here to make sure that we keep
-// track of them.
-package optlog
-
-import (
- "github.com/AdguardTeam/golibs/log"
-)
-
-// Debug3 is an ugly hack to prevent [log.Debug] from allocating.
-func Debug3[T1, T2, T3 any](msg string, arg1 T1, arg2 T2, arg3 T3) {
- if log.GetLevel() >= log.DEBUG {
- log.Debug(msg, arg1, arg2, arg3)
- }
-}
diff --git a/internal/profiledb/internal/filecachepb/filecache.pb.go b/internal/profiledb/internal/filecachepb/filecache.pb.go
index e926633..d29155d 100644
--- a/internal/profiledb/internal/filecachepb/filecache.pb.go
+++ b/internal/profiledb/internal/filecachepb/filecache.pb.go
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
-// protoc-gen-go v1.35.2
-// protoc v5.28.3
+// protoc-gen-go v1.36.5
+// protoc v5.29.1
// source: filecache.proto
package filecachepb
@@ -13,6 +13,7 @@ import (
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
+ unsafe "unsafe"
)
const (
@@ -23,14 +24,13 @@ const (
)
type FileCache struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ SyncTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=sync_time,json=syncTime,proto3" json:"sync_time,omitempty"`
+ Profiles []*Profile `protobuf:"bytes,2,rep,name=profiles,proto3" json:"profiles,omitempty"`
+ Devices []*Device `protobuf:"bytes,3,rep,name=devices,proto3" json:"devices,omitempty"`
+ Version int32 `protobuf:"varint,4,opt,name=version,proto3" json:"version,omitempty"`
unknownFields protoimpl.UnknownFields
-
- SyncTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=sync_time,json=syncTime,proto3" json:"sync_time,omitempty"`
- Profiles []*Profile `protobuf:"bytes,2,rep,name=profiles,proto3" json:"profiles,omitempty"`
- Devices []*Device `protobuf:"bytes,3,rep,name=devices,proto3" json:"devices,omitempty"`
- Version int32 `protobuf:"varint,4,opt,name=version,proto3" json:"version,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *FileCache) Reset() {
@@ -92,13 +92,10 @@ func (x *FileCache) GetVersion() int32 {
}
type Profile struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- FilterConfig *FilterConfig `protobuf:"bytes,1,opt,name=filter_config,json=filterConfig,proto3" json:"filter_config,omitempty"`
- Access *Access `protobuf:"bytes,2,opt,name=access,proto3" json:"access,omitempty"`
- // Types that are assignable to BlockingMode:
+ state protoimpl.MessageState `protogen:"open.v1"`
+ FilterConfig *FilterConfig `protobuf:"bytes,1,opt,name=filter_config,json=filterConfig,proto3" json:"filter_config,omitempty"`
+ Access *Access `protobuf:"bytes,2,opt,name=access,proto3" json:"access,omitempty"`
+ // Types that are valid to be assigned to BlockingMode:
//
// *Profile_BlockingModeCustomIp
// *Profile_BlockingModeNxdomain
@@ -117,6 +114,8 @@ type Profile struct {
FilteringEnabled bool `protobuf:"varint,16,opt,name=filtering_enabled,json=filteringEnabled,proto3" json:"filtering_enabled,omitempty"`
IpLogEnabled bool `protobuf:"varint,17,opt,name=ip_log_enabled,json=ipLogEnabled,proto3" json:"ip_log_enabled,omitempty"`
QueryLogEnabled bool `protobuf:"varint,18,opt,name=query_log_enabled,json=queryLogEnabled,proto3" json:"query_log_enabled,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *Profile) Reset() {
@@ -163,37 +162,45 @@ func (x *Profile) GetAccess() *Access {
return nil
}
-func (m *Profile) GetBlockingMode() isProfile_BlockingMode {
- if m != nil {
- return m.BlockingMode
+func (x *Profile) GetBlockingMode() isProfile_BlockingMode {
+ if x != nil {
+ return x.BlockingMode
}
return nil
}
func (x *Profile) GetBlockingModeCustomIp() *BlockingModeCustomIP {
- if x, ok := x.GetBlockingMode().(*Profile_BlockingModeCustomIp); ok {
- return x.BlockingModeCustomIp
+ if x != nil {
+ if x, ok := x.BlockingMode.(*Profile_BlockingModeCustomIp); ok {
+ return x.BlockingModeCustomIp
+ }
}
return nil
}
func (x *Profile) GetBlockingModeNxdomain() *BlockingModeNXDOMAIN {
- if x, ok := x.GetBlockingMode().(*Profile_BlockingModeNxdomain); ok {
- return x.BlockingModeNxdomain
+ if x != nil {
+ if x, ok := x.BlockingMode.(*Profile_BlockingModeNxdomain); ok {
+ return x.BlockingModeNxdomain
+ }
}
return nil
}
func (x *Profile) GetBlockingModeNullIp() *BlockingModeNullIP {
- if x, ok := x.GetBlockingMode().(*Profile_BlockingModeNullIp); ok {
- return x.BlockingModeNullIp
+ if x != nil {
+ if x, ok := x.BlockingMode.(*Profile_BlockingModeNullIp); ok {
+ return x.BlockingModeNullIp
+ }
}
return nil
}
func (x *Profile) GetBlockingModeRefused() *BlockingModeREFUSED {
- if x, ok := x.GetBlockingMode().(*Profile_BlockingModeRefused); ok {
- return x.BlockingModeRefused
+ if x != nil {
+ if x, ok := x.BlockingMode.(*Profile_BlockingModeRefused); ok {
+ return x.BlockingModeRefused
+ }
}
return nil
}
@@ -311,14 +318,13 @@ func (*Profile_BlockingModeNullIp) isProfile_BlockingMode() {}
func (*Profile_BlockingModeRefused) isProfile_BlockingMode() {}
type FilterConfig struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Custom *FilterConfig_Custom `protobuf:"bytes,1,opt,name=custom,proto3" json:"custom,omitempty"`
+ Parental *FilterConfig_Parental `protobuf:"bytes,2,opt,name=parental,proto3" json:"parental,omitempty"`
+ RuleList *FilterConfig_RuleList `protobuf:"bytes,3,opt,name=rule_list,json=ruleList,proto3" json:"rule_list,omitempty"`
+ SafeBrowsing *FilterConfig_SafeBrowsing `protobuf:"bytes,4,opt,name=safe_browsing,json=safeBrowsing,proto3" json:"safe_browsing,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Custom *FilterConfig_Custom `protobuf:"bytes,1,opt,name=custom,proto3" json:"custom,omitempty"`
- Parental *FilterConfig_Parental `protobuf:"bytes,2,opt,name=parental,proto3" json:"parental,omitempty"`
- RuleList *FilterConfig_RuleList `protobuf:"bytes,3,opt,name=rule_list,json=ruleList,proto3" json:"rule_list,omitempty"`
- SafeBrowsing *FilterConfig_SafeBrowsing `protobuf:"bytes,4,opt,name=safe_browsing,json=safeBrowsing,proto3" json:"safe_browsing,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *FilterConfig) Reset() {
@@ -380,12 +386,11 @@ func (x *FilterConfig) GetSafeBrowsing() *FilterConfig_SafeBrowsing {
}
type DayInterval struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Start uint32 `protobuf:"varint,1,opt,name=start,proto3" json:"start,omitempty"`
+ End uint32 `protobuf:"varint,2,opt,name=end,proto3" json:"end,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Start uint32 `protobuf:"varint,1,opt,name=start,proto3" json:"start,omitempty"`
- End uint32 `protobuf:"varint,2,opt,name=end,proto3" json:"end,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *DayInterval) Reset() {
@@ -433,12 +438,11 @@ func (x *DayInterval) GetEnd() uint32 {
}
type BlockingModeCustomIP struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Ipv4 [][]byte `protobuf:"bytes,1,rep,name=ipv4,proto3" json:"ipv4,omitempty"`
+ Ipv6 [][]byte `protobuf:"bytes,2,rep,name=ipv6,proto3" json:"ipv6,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Ipv4 [][]byte `protobuf:"bytes,1,rep,name=ipv4,proto3" json:"ipv4,omitempty"`
- Ipv6 [][]byte `protobuf:"bytes,2,rep,name=ipv6,proto3" json:"ipv6,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *BlockingModeCustomIP) Reset() {
@@ -486,9 +490,9 @@ func (x *BlockingModeCustomIP) GetIpv6() [][]byte {
}
type BlockingModeNXDOMAIN struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *BlockingModeNXDOMAIN) Reset() {
@@ -522,9 +526,9 @@ func (*BlockingModeNXDOMAIN) Descriptor() ([]byte, []int) {
}
type BlockingModeNullIP struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *BlockingModeNullIP) Reset() {
@@ -558,9 +562,9 @@ func (*BlockingModeNullIP) Descriptor() ([]byte, []int) {
}
type BlockingModeREFUSED struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *BlockingModeREFUSED) Reset() {
@@ -594,10 +598,7 @@ func (*BlockingModeREFUSED) Descriptor() ([]byte, []int) {
}
type Device struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
+ state protoimpl.MessageState `protogen:"open.v1"`
Authentication *AuthenticationSettings `protobuf:"bytes,6,opt,name=authentication,proto3" json:"authentication,omitempty"`
DeviceId string `protobuf:"bytes,1,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"`
DeviceName string `protobuf:"bytes,3,opt,name=device_name,json=deviceName,proto3" json:"device_name,omitempty"`
@@ -605,6 +606,8 @@ type Device struct {
LinkedIp []byte `protobuf:"bytes,2,opt,name=linked_ip,json=linkedIp,proto3" json:"linked_ip,omitempty"`
DedicatedIps [][]byte `protobuf:"bytes,4,rep,name=dedicated_ips,json=dedicatedIps,proto3" json:"dedicated_ips,omitempty"`
FilteringEnabled bool `protobuf:"varint,5,opt,name=filtering_enabled,json=filteringEnabled,proto3" json:"filtering_enabled,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *Device) Reset() {
@@ -687,15 +690,14 @@ func (x *Device) GetFilteringEnabled() bool {
}
type Access struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- AllowlistAsn []uint32 `protobuf:"varint,4,rep,packed,name=allowlist_asn,json=allowlistAsn,proto3" json:"allowlist_asn,omitempty"`
- AllowlistCidr []*CidrRange `protobuf:"bytes,1,rep,name=allowlist_cidr,json=allowlistCidr,proto3" json:"allowlist_cidr,omitempty"`
- BlocklistAsn []uint32 `protobuf:"varint,5,rep,packed,name=blocklist_asn,json=blocklistAsn,proto3" json:"blocklist_asn,omitempty"`
- BlocklistCidr []*CidrRange `protobuf:"bytes,2,rep,name=blocklist_cidr,json=blocklistCidr,proto3" json:"blocklist_cidr,omitempty"`
- BlocklistDomainRules []string `protobuf:"bytes,3,rep,name=blocklist_domain_rules,json=blocklistDomainRules,proto3" json:"blocklist_domain_rules,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ AllowlistAsn []uint32 `protobuf:"varint,4,rep,packed,name=allowlist_asn,json=allowlistAsn,proto3" json:"allowlist_asn,omitempty"`
+ AllowlistCidr []*CidrRange `protobuf:"bytes,1,rep,name=allowlist_cidr,json=allowlistCidr,proto3" json:"allowlist_cidr,omitempty"`
+ BlocklistAsn []uint32 `protobuf:"varint,5,rep,packed,name=blocklist_asn,json=blocklistAsn,proto3" json:"blocklist_asn,omitempty"`
+ BlocklistCidr []*CidrRange `protobuf:"bytes,2,rep,name=blocklist_cidr,json=blocklistCidr,proto3" json:"blocklist_cidr,omitempty"`
+ BlocklistDomainRules []string `protobuf:"bytes,3,rep,name=blocklist_domain_rules,json=blocklistDomainRules,proto3" json:"blocklist_domain_rules,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *Access) Reset() {
@@ -764,12 +766,11 @@ func (x *Access) GetBlocklistDomainRules() []string {
}
type CidrRange struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
+ Prefix uint32 `protobuf:"varint,2,opt,name=prefix,proto3" json:"prefix,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
- Prefix uint32 `protobuf:"varint,2,opt,name=prefix,proto3" json:"prefix,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *CidrRange) Reset() {
@@ -817,15 +818,14 @@ func (x *CidrRange) GetPrefix() uint32 {
}
type AuthenticationSettings struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- DohAuthOnly bool `protobuf:"varint,1,opt,name=doh_auth_only,json=dohAuthOnly,proto3" json:"doh_auth_only,omitempty"`
- // Types that are assignable to DohPasswordHash:
+ state protoimpl.MessageState `protogen:"open.v1"`
+ DohAuthOnly bool `protobuf:"varint,1,opt,name=doh_auth_only,json=dohAuthOnly,proto3" json:"doh_auth_only,omitempty"`
+ // Types that are valid to be assigned to DohPasswordHash:
//
// *AuthenticationSettings_PasswordHashBcrypt
DohPasswordHash isAuthenticationSettings_DohPasswordHash `protobuf_oneof:"doh_password_hash"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *AuthenticationSettings) Reset() {
@@ -865,16 +865,18 @@ func (x *AuthenticationSettings) GetDohAuthOnly() bool {
return false
}
-func (m *AuthenticationSettings) GetDohPasswordHash() isAuthenticationSettings_DohPasswordHash {
- if m != nil {
- return m.DohPasswordHash
+func (x *AuthenticationSettings) GetDohPasswordHash() isAuthenticationSettings_DohPasswordHash {
+ if x != nil {
+ return x.DohPasswordHash
}
return nil
}
func (x *AuthenticationSettings) GetPasswordHashBcrypt() []byte {
- if x, ok := x.GetDohPasswordHash().(*AuthenticationSettings_PasswordHashBcrypt); ok {
- return x.PasswordHashBcrypt
+ if x != nil {
+ if x, ok := x.DohPasswordHash.(*AuthenticationSettings_PasswordHashBcrypt); ok {
+ return x.PasswordHashBcrypt
+ }
}
return nil
}
@@ -890,13 +892,12 @@ type AuthenticationSettings_PasswordHashBcrypt struct {
func (*AuthenticationSettings_PasswordHashBcrypt) isAuthenticationSettings_DohPasswordHash() {}
type Ratelimiter struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ ClientCidr []*CidrRange `protobuf:"bytes,1,rep,name=client_cidr,json=clientCidr,proto3" json:"client_cidr,omitempty"`
+ Rps uint32 `protobuf:"varint,2,opt,name=rps,proto3" json:"rps,omitempty"`
+ Enabled bool `protobuf:"varint,3,opt,name=enabled,proto3" json:"enabled,omitempty"`
unknownFields protoimpl.UnknownFields
-
- ClientCidr []*CidrRange `protobuf:"bytes,1,rep,name=client_cidr,json=clientCidr,proto3" json:"client_cidr,omitempty"`
- Rps uint32 `protobuf:"varint,2,opt,name=rps,proto3" json:"rps,omitempty"`
- Enabled bool `protobuf:"varint,3,opt,name=enabled,proto3" json:"enabled,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *Ratelimiter) Reset() {
@@ -951,14 +952,11 @@ func (x *Ratelimiter) GetEnabled() bool {
}
type FilterConfig_Custom struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Rules []string `protobuf:"bytes,3,rep,name=rules,proto3" json:"rules,omitempty"`
+ Enabled bool `protobuf:"varint,4,opt,name=enabled,proto3" json:"enabled,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
- UpdateTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty"`
- Rules []string `protobuf:"bytes,3,rep,name=rules,proto3" json:"rules,omitempty"`
- Enabled bool `protobuf:"varint,4,opt,name=enabled,proto3" json:"enabled,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *FilterConfig_Custom) Reset() {
@@ -991,20 +989,6 @@ func (*FilterConfig_Custom) Descriptor() ([]byte, []int) {
return file_filecache_proto_rawDescGZIP(), []int{2, 0}
}
-func (x *FilterConfig_Custom) GetId() string {
- if x != nil {
- return x.Id
- }
- return ""
-}
-
-func (x *FilterConfig_Custom) GetUpdateTime() *timestamppb.Timestamp {
- if x != nil {
- return x.UpdateTime
- }
- return nil
-}
-
func (x *FilterConfig_Custom) GetRules() []string {
if x != nil {
return x.Rules
@@ -1020,16 +1004,15 @@ func (x *FilterConfig_Custom) GetEnabled() bool {
}
type FilterConfig_Parental struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
+ state protoimpl.MessageState `protogen:"open.v1"`
PauseSchedule *FilterConfig_Schedule `protobuf:"bytes,1,opt,name=pause_schedule,json=pauseSchedule,proto3" json:"pause_schedule,omitempty"`
BlockedServices []string `protobuf:"bytes,2,rep,name=blocked_services,json=blockedServices,proto3" json:"blocked_services,omitempty"`
Enabled bool `protobuf:"varint,3,opt,name=enabled,proto3" json:"enabled,omitempty"`
AdultBlockingEnabled bool `protobuf:"varint,4,opt,name=adult_blocking_enabled,json=adultBlockingEnabled,proto3" json:"adult_blocking_enabled,omitempty"`
SafeSearchGeneralEnabled bool `protobuf:"varint,5,opt,name=safe_search_general_enabled,json=safeSearchGeneralEnabled,proto3" json:"safe_search_general_enabled,omitempty"`
SafeSearchYoutubeEnabled bool `protobuf:"varint,6,opt,name=safe_search_youtube_enabled,json=safeSearchYoutubeEnabled,proto3" json:"safe_search_youtube_enabled,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *FilterConfig_Parental) Reset() {
@@ -1105,12 +1088,11 @@ func (x *FilterConfig_Parental) GetSafeSearchYoutubeEnabled() bool {
}
type FilterConfig_Schedule struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Week *FilterConfig_WeeklySchedule `protobuf:"bytes,1,opt,name=week,proto3" json:"week,omitempty"`
+ TimeZone string `protobuf:"bytes,2,opt,name=time_zone,json=timeZone,proto3" json:"time_zone,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Week *FilterConfig_WeeklySchedule `protobuf:"bytes,1,opt,name=week,proto3" json:"week,omitempty"`
- TimeZone string `protobuf:"bytes,2,opt,name=time_zone,json=timeZone,proto3" json:"time_zone,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *FilterConfig_Schedule) Reset() {
@@ -1158,17 +1140,16 @@ func (x *FilterConfig_Schedule) GetTimeZone() string {
}
type FilterConfig_WeeklySchedule struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Mon *DayInterval `protobuf:"bytes,1,opt,name=mon,proto3" json:"mon,omitempty"`
+ Tue *DayInterval `protobuf:"bytes,2,opt,name=tue,proto3" json:"tue,omitempty"`
+ Wed *DayInterval `protobuf:"bytes,3,opt,name=wed,proto3" json:"wed,omitempty"`
+ Thu *DayInterval `protobuf:"bytes,4,opt,name=thu,proto3" json:"thu,omitempty"`
+ Fri *DayInterval `protobuf:"bytes,5,opt,name=fri,proto3" json:"fri,omitempty"`
+ Sat *DayInterval `protobuf:"bytes,6,opt,name=sat,proto3" json:"sat,omitempty"`
+ Sun *DayInterval `protobuf:"bytes,7,opt,name=sun,proto3" json:"sun,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Mon *DayInterval `protobuf:"bytes,1,opt,name=mon,proto3" json:"mon,omitempty"`
- Tue *DayInterval `protobuf:"bytes,2,opt,name=tue,proto3" json:"tue,omitempty"`
- Wed *DayInterval `protobuf:"bytes,3,opt,name=wed,proto3" json:"wed,omitempty"`
- Thu *DayInterval `protobuf:"bytes,4,opt,name=thu,proto3" json:"thu,omitempty"`
- Fri *DayInterval `protobuf:"bytes,5,opt,name=fri,proto3" json:"fri,omitempty"`
- Sat *DayInterval `protobuf:"bytes,6,opt,name=sat,proto3" json:"sat,omitempty"`
- Sun *DayInterval `protobuf:"bytes,7,opt,name=sun,proto3" json:"sun,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *FilterConfig_WeeklySchedule) Reset() {
@@ -1251,12 +1232,11 @@ func (x *FilterConfig_WeeklySchedule) GetSun() *DayInterval {
}
type FilterConfig_RuleList struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Ids []string `protobuf:"bytes,1,rep,name=ids,proto3" json:"ids,omitempty"`
+ Enabled bool `protobuf:"varint,2,opt,name=enabled,proto3" json:"enabled,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Ids []string `protobuf:"bytes,1,rep,name=ids,proto3" json:"ids,omitempty"`
- Enabled bool `protobuf:"varint,2,opt,name=enabled,proto3" json:"enabled,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *FilterConfig_RuleList) Reset() {
@@ -1304,13 +1284,12 @@ func (x *FilterConfig_RuleList) GetEnabled() bool {
}
type FilterConfig_SafeBrowsing struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
- DangerousDomainsEnabled bool `protobuf:"varint,2,opt,name=dangerous_domains_enabled,json=dangerousDomainsEnabled,proto3" json:"dangerous_domains_enabled,omitempty"`
- NewlyRegisteredDomainsEnabled bool `protobuf:"varint,3,opt,name=newly_registered_domains_enabled,json=newlyRegisteredDomainsEnabled,proto3" json:"newly_registered_domains_enabled,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
+ DangerousDomainsEnabled bool `protobuf:"varint,2,opt,name=dangerous_domains_enabled,json=dangerousDomainsEnabled,proto3" json:"dangerous_domains_enabled,omitempty"`
+ NewlyRegisteredDomainsEnabled bool `protobuf:"varint,3,opt,name=newly_registered_domains_enabled,json=newlyRegisteredDomainsEnabled,proto3" json:"newly_registered_domains_enabled,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *FilterConfig_SafeBrowsing) Reset() {
@@ -1366,7 +1345,7 @@ func (x *FilterConfig_SafeBrowsing) GetNewlyRegisteredDomainsEnabled() bool {
var File_filecache_proto protoreflect.FileDescriptor
-var file_filecache_proto_rawDesc = []byte{
+var file_filecache_proto_rawDesc = string([]byte{
0x0a, 0x0f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x12, 0x09, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x1a, 0x1e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75,
@@ -1449,7 +1428,7 @@ var file_filecache_proto_rawDesc = []byte{
0x72, 0x79, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x12,
0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x4c, 0x6f, 0x67, 0x45, 0x6e,
0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, 0x0f, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e,
- 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0xeb, 0x0a, 0x0a, 0x0c, 0x46, 0x69, 0x6c, 0x74, 0x65,
+ 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0xa9, 0x0a, 0x0a, 0x0c, 0x46, 0x69, 0x6c, 0x74, 0x65,
0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x06, 0x63, 0x75, 0x73, 0x74, 0x6f,
0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c,
0x65, 0x64, 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
@@ -1466,154 +1445,150 @@ var file_filecache_proto_rawDesc = []byte{
0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e,
0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x53, 0x61, 0x66,
0x65, 0x42, 0x72, 0x6f, 0x77, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x0c, 0x73, 0x61, 0x66, 0x65, 0x42,
- 0x72, 0x6f, 0x77, 0x73, 0x69, 0x6e, 0x67, 0x1a, 0x85, 0x01, 0x0a, 0x06, 0x43, 0x75, 0x73, 0x74,
- 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02,
- 0x69, 0x64, 0x12, 0x3b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d,
- 0x65, 0x18, 0x02, 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, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12,
- 0x14, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05,
- 0x72, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
- 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x1a,
- 0xcc, 0x02, 0x0a, 0x08, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x12, 0x47, 0x0a, 0x0e,
- 0x70, 0x61, 0x75, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62,
- 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x53, 0x63,
- 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x70, 0x61, 0x75, 0x73, 0x65, 0x53, 0x63, 0x68,
- 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64,
- 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52,
- 0x0f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,
- 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28,
- 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x61, 0x64,
- 0x75, 0x6c, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61,
- 0x62, 0x6c, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x61, 0x64, 0x75, 0x6c,
- 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
- 0x12, 0x3d, 0x0a, 0x1b, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f,
- 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18,
- 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x73, 0x61, 0x66, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63,
- 0x68, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12,
- 0x3d, 0x0a, 0x1b, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x79,
- 0x6f, 0x75, 0x74, 0x75, 0x62, 0x65, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x06,
- 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x73, 0x61, 0x66, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68,
- 0x59, 0x6f, 0x75, 0x74, 0x75, 0x62, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x1a, 0x63,
- 0x0a, 0x08, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x3a, 0x0a, 0x04, 0x77, 0x65,
- 0x65, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69,
- 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69,
- 0x67, 0x2e, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65,
- 0x52, 0x04, 0x77, 0x65, 0x65, 0x6b, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a,
- 0x6f, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x5a,
- 0x6f, 0x6e, 0x65, 0x1a, 0xb6, 0x02, 0x0a, 0x0e, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x53, 0x63,
- 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x28, 0x0a, 0x03, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20,
- 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e,
- 0x44, 0x61, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x52, 0x03, 0x6d, 0x6f, 0x6e,
- 0x12, 0x28, 0x0a, 0x03, 0x74, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e,
- 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x44, 0x61, 0x79, 0x49, 0x6e, 0x74,
- 0x65, 0x72, 0x76, 0x61, 0x6c, 0x52, 0x03, 0x74, 0x75, 0x65, 0x12, 0x28, 0x0a, 0x03, 0x77, 0x65,
- 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c,
- 0x65, 0x64, 0x62, 0x2e, 0x44, 0x61, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x52,
- 0x03, 0x77, 0x65, 0x64, 0x12, 0x28, 0x0a, 0x03, 0x74, 0x68, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28,
+ 0x72, 0x6f, 0x77, 0x73, 0x69, 0x6e, 0x67, 0x1a, 0x44, 0x0a, 0x06, 0x43, 0x75, 0x73, 0x74, 0x6f,
+ 0x6d, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09,
+ 0x52, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c,
+ 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65,
+ 0x64, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x1a, 0xcc, 0x02,
+ 0x0a, 0x08, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x12, 0x47, 0x0a, 0x0e, 0x70, 0x61,
+ 0x75, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x46,
+ 0x69, 0x6c, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x53, 0x63, 0x68, 0x65,
+ 0x64, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x70, 0x61, 0x75, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x64,
+ 0x75, 0x6c, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x73,
+ 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x62,
+ 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x18,
+ 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52,
+ 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x61, 0x64, 0x75, 0x6c,
+ 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c,
+ 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x61, 0x64, 0x75, 0x6c, 0x74, 0x42,
+ 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x3d,
+ 0x0a, 0x1b, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x67, 0x65,
+ 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20,
+ 0x01, 0x28, 0x08, 0x52, 0x18, 0x73, 0x61, 0x66, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x47,
+ 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x3d, 0x0a,
+ 0x1b, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x79, 0x6f, 0x75,
+ 0x74, 0x75, 0x62, 0x65, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01,
+ 0x28, 0x08, 0x52, 0x18, 0x73, 0x61, 0x66, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x59, 0x6f,
+ 0x75, 0x74, 0x75, 0x62, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x1a, 0x63, 0x0a, 0x08,
+ 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x3a, 0x0a, 0x04, 0x77, 0x65, 0x65, 0x6b,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65,
+ 0x64, 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
+ 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x04,
+ 0x77, 0x65, 0x65, 0x6b, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, 0x6e,
+ 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x5a, 0x6f, 0x6e,
+ 0x65, 0x1a, 0xb6, 0x02, 0x0a, 0x0e, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x53, 0x63, 0x68, 0x65,
+ 0x64, 0x75, 0x6c, 0x65, 0x12, 0x28, 0x0a, 0x03, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x44, 0x61,
- 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x52, 0x03, 0x74, 0x68, 0x75, 0x12, 0x28,
- 0x0a, 0x03, 0x66, 0x72, 0x69, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72,
+ 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x52, 0x03, 0x6d, 0x6f, 0x6e, 0x12, 0x28,
+ 0x0a, 0x03, 0x74, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72,
0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x44, 0x61, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72,
- 0x76, 0x61, 0x6c, 0x52, 0x03, 0x66, 0x72, 0x69, 0x12, 0x28, 0x0a, 0x03, 0x73, 0x61, 0x74, 0x18,
- 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64,
- 0x62, 0x2e, 0x44, 0x61, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x52, 0x03, 0x73,
- 0x61, 0x74, 0x12, 0x28, 0x0a, 0x03, 0x73, 0x75, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x76, 0x61, 0x6c, 0x52, 0x03, 0x74, 0x75, 0x65, 0x12, 0x28, 0x0a, 0x03, 0x77, 0x65, 0x64, 0x18,
+ 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64,
+ 0x62, 0x2e, 0x44, 0x61, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x52, 0x03, 0x77,
+ 0x65, 0x64, 0x12, 0x28, 0x0a, 0x03, 0x74, 0x68, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x16, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x44, 0x61, 0x79, 0x49,
- 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x52, 0x03, 0x73, 0x75, 0x6e, 0x1a, 0x36, 0x0a, 0x08,
- 0x52, 0x75, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18,
- 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e,
- 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61,
- 0x62, 0x6c, 0x65, 0x64, 0x1a, 0xad, 0x01, 0x0a, 0x0c, 0x53, 0x61, 0x66, 0x65, 0x42, 0x72, 0x6f,
- 0x77, 0x73, 0x69, 0x6e, 0x67, 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,
- 0x3a, 0x0a, 0x19, 0x64, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73, 0x5f, 0x64, 0x6f, 0x6d,
- 0x61, 0x69, 0x6e, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01,
- 0x28, 0x08, 0x52, 0x17, 0x64, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73, 0x44, 0x6f, 0x6d,
- 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x47, 0x0a, 0x20, 0x6e,
- 0x65, 0x77, 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x5f,
- 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18,
- 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, 0x6e, 0x65, 0x77, 0x6c, 0x79, 0x52, 0x65, 0x67, 0x69,
- 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61,
- 0x62, 0x6c, 0x65, 0x64, 0x22, 0x35, 0x0a, 0x0b, 0x44, 0x61, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72,
- 0x76, 0x61, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01,
- 0x28, 0x0d, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64,
- 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x6e, 0x64, 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, 0x03, 0x28,
- 0x0c, 0x52, 0x04, 0x69, 0x70, 0x76, 0x34, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36, 0x18,
- 0x02, 0x20, 0x03, 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, 0xa6, 0x02, 0x0a, 0x06, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x49, 0x0a, 0x0e, 0x61,
- 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20,
- 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 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, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,
- 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63,
- 0x65, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61,
- 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,
- 0x4e, 0x61, 0x6d, 0x65, 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, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x69,
- 0x6e, 0x6b, 0x65, 0x64, 0x5f, 0x69, 0x70, 0x18, 0x02, 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, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c,
- 0x64, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x49, 0x70, 0x73, 0x12, 0x2b, 0x0a, 0x11,
- 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65,
- 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69,
- 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x82, 0x02, 0x0a, 0x06, 0x41, 0x63,
- 0x63, 0x65, 0x73, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73,
- 0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x61, 0x6c, 0x6c,
- 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x6e, 0x12, 0x3b, 0x0a, 0x0e, 0x61, 0x6c, 0x6c,
- 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28,
+ 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x52, 0x03, 0x74, 0x68, 0x75, 0x12, 0x28, 0x0a, 0x03,
+ 0x66, 0x72, 0x69, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x66,
+ 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x44, 0x61, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61,
+ 0x6c, 0x52, 0x03, 0x66, 0x72, 0x69, 0x12, 0x28, 0x0a, 0x03, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e,
+ 0x44, 0x61, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x52, 0x03, 0x73, 0x61, 0x74,
+ 0x12, 0x28, 0x0a, 0x03, 0x73, 0x75, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e,
+ 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x44, 0x61, 0x79, 0x49, 0x6e, 0x74,
+ 0x65, 0x72, 0x76, 0x61, 0x6c, 0x52, 0x03, 0x73, 0x75, 0x6e, 0x1a, 0x36, 0x0a, 0x08, 0x52, 0x75,
+ 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20,
+ 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62,
+ 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c,
+ 0x65, 0x64, 0x1a, 0xad, 0x01, 0x0a, 0x0c, 0x53, 0x61, 0x66, 0x65, 0x42, 0x72, 0x6f, 0x77, 0x73,
+ 0x69, 0x6e, 0x67, 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, 0x3a, 0x0a,
+ 0x19, 0x64, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69,
+ 0x6e, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08,
+ 0x52, 0x17, 0x64, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73, 0x44, 0x6f, 0x6d, 0x61, 0x69,
+ 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x47, 0x0a, 0x20, 0x6e, 0x65, 0x77,
+ 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x64, 0x6f,
+ 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20,
+ 0x01, 0x28, 0x08, 0x52, 0x1d, 0x6e, 0x65, 0x77, 0x6c, 0x79, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74,
+ 0x65, 0x72, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c,
+ 0x65, 0x64, 0x22, 0x35, 0x0a, 0x0b, 0x44, 0x61, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61,
+ 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
+ 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x6e, 0x64, 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, 0x03, 0x28, 0x0c, 0x52,
+ 0x04, 0x69, 0x70, 0x76, 0x34, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36, 0x18, 0x02, 0x20,
+ 0x03, 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, 0xa6,
+ 0x02, 0x0a, 0x06, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x49, 0x0a, 0x0e, 0x61, 0x75, 0x74,
+ 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 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, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69,
+ 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49,
+ 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
+ 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61,
+ 0x6d, 0x65, 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, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x69, 0x6e, 0x6b,
+ 0x65, 0x64, 0x5f, 0x69, 0x70, 0x18, 0x02, 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, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x64, 0x65,
+ 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x49, 0x70, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x69,
+ 0x6c, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18,
+ 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67,
+ 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x82, 0x02, 0x0a, 0x06, 0x41, 0x63, 0x63, 0x65,
+ 0x73, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x5f,
+ 0x61, 0x73, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77,
+ 0x6c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x6e, 0x12, 0x3b, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77,
+ 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
+ 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 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, 0x23, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73,
+ 0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x62, 0x6c, 0x6f,
+ 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x6e, 0x12, 0x3b, 0x0a, 0x0e, 0x62, 0x6c, 0x6f,
+ 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 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, 0x23, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c,
- 0x69, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x62,
- 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x6e, 0x12, 0x3b, 0x0a, 0x0e, 0x62,
- 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x20,
- 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 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, 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, 0x03, 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, 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, 0x70, 0x0a, 0x0b, 0x52, 0x61, 0x74, 0x65, 0x6c, 0x69, 0x6d,
- 0x69, 0x74, 0x65, 0x72, 0x12, 0x35, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63,
- 0x69, 0x64, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x66,
- 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52,
- 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x72,
- 0x70, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x72, 0x70, 0x73, 0x12, 0x18, 0x0a,
- 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07,
- 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, 0x0f, 0x5a, 0x0d, 0x2e, 0x2f, 0x66, 0x69, 0x6c,
- 0x65, 0x63, 0x61, 0x63, 0x68, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
-}
+ 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69,
+ 0x73, 0x74, 0x43, 0x69, 0x64, 0x72, 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, 0x03, 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, 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, 0x70, 0x0a, 0x0b, 0x52, 0x61, 0x74, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74,
+ 0x65, 0x72, 0x12, 0x35, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x69, 0x64,
+ 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c,
+ 0x65, 0x64, 0x62, 0x2e, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0a, 0x63,
+ 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x70, 0x73,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x72, 0x70, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65,
+ 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e,
+ 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, 0x0f, 0x5a, 0x0d, 0x2e, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63,
+ 0x61, 0x63, 0x68, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+})
var (
file_filecache_proto_rawDescOnce sync.Once
- file_filecache_proto_rawDescData = file_filecache_proto_rawDesc
+ file_filecache_proto_rawDescData []byte
)
func file_filecache_proto_rawDescGZIP() []byte {
file_filecache_proto_rawDescOnce.Do(func() {
- file_filecache_proto_rawDescData = protoimpl.X.CompressGZIP(file_filecache_proto_rawDescData)
+ file_filecache_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_filecache_proto_rawDesc), len(file_filecache_proto_rawDesc)))
})
return file_filecache_proto_rawDescData
}
@@ -1662,21 +1637,20 @@ var file_filecache_proto_depIdxs = []int32{
10, // 16: profiledb.Access.allowlist_cidr:type_name -> profiledb.CidrRange
10, // 17: profiledb.Access.blocklist_cidr:type_name -> profiledb.CidrRange
10, // 18: profiledb.Ratelimiter.client_cidr:type_name -> profiledb.CidrRange
- 19, // 19: profiledb.FilterConfig.Custom.update_time:type_name -> google.protobuf.Timestamp
- 15, // 20: profiledb.FilterConfig.Parental.pause_schedule:type_name -> profiledb.FilterConfig.Schedule
- 16, // 21: profiledb.FilterConfig.Schedule.week:type_name -> profiledb.FilterConfig.WeeklySchedule
- 3, // 22: profiledb.FilterConfig.WeeklySchedule.mon:type_name -> profiledb.DayInterval
- 3, // 23: profiledb.FilterConfig.WeeklySchedule.tue:type_name -> profiledb.DayInterval
- 3, // 24: profiledb.FilterConfig.WeeklySchedule.wed:type_name -> profiledb.DayInterval
- 3, // 25: profiledb.FilterConfig.WeeklySchedule.thu:type_name -> profiledb.DayInterval
- 3, // 26: profiledb.FilterConfig.WeeklySchedule.fri:type_name -> profiledb.DayInterval
- 3, // 27: profiledb.FilterConfig.WeeklySchedule.sat:type_name -> profiledb.DayInterval
- 3, // 28: profiledb.FilterConfig.WeeklySchedule.sun:type_name -> profiledb.DayInterval
- 29, // [29:29] is the sub-list for method output_type
- 29, // [29:29] is the sub-list for method input_type
- 29, // [29:29] is the sub-list for extension type_name
- 29, // [29:29] is the sub-list for extension extendee
- 0, // [0:29] is the sub-list for field type_name
+ 15, // 19: profiledb.FilterConfig.Parental.pause_schedule:type_name -> profiledb.FilterConfig.Schedule
+ 16, // 20: profiledb.FilterConfig.Schedule.week:type_name -> profiledb.FilterConfig.WeeklySchedule
+ 3, // 21: profiledb.FilterConfig.WeeklySchedule.mon:type_name -> profiledb.DayInterval
+ 3, // 22: profiledb.FilterConfig.WeeklySchedule.tue:type_name -> profiledb.DayInterval
+ 3, // 23: profiledb.FilterConfig.WeeklySchedule.wed:type_name -> profiledb.DayInterval
+ 3, // 24: profiledb.FilterConfig.WeeklySchedule.thu:type_name -> profiledb.DayInterval
+ 3, // 25: profiledb.FilterConfig.WeeklySchedule.fri:type_name -> profiledb.DayInterval
+ 3, // 26: profiledb.FilterConfig.WeeklySchedule.sat:type_name -> profiledb.DayInterval
+ 3, // 27: profiledb.FilterConfig.WeeklySchedule.sun:type_name -> profiledb.DayInterval
+ 28, // [28:28] is the sub-list for method output_type
+ 28, // [28:28] is the sub-list for method input_type
+ 28, // [28:28] is the sub-list for extension type_name
+ 28, // [28:28] is the sub-list for extension extendee
+ 0, // [0:28] is the sub-list for field type_name
}
func init() { file_filecache_proto_init() }
@@ -1697,7 +1671,7 @@ func file_filecache_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
- RawDescriptor: file_filecache_proto_rawDesc,
+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_filecache_proto_rawDesc), len(file_filecache_proto_rawDesc)),
NumEnums: 0,
NumMessages: 19,
NumExtensions: 0,
@@ -1708,7 +1682,6 @@ func file_filecache_proto_init() {
MessageInfos: file_filecache_proto_msgTypes,
}.Build()
File_filecache_proto = out.File
- file_filecache_proto_rawDesc = nil
file_filecache_proto_goTypes = nil
file_filecache_proto_depIdxs = nil
}
diff --git a/internal/profiledb/internal/filecachepb/filecache.proto b/internal/profiledb/internal/filecachepb/filecache.proto
index 24586f1..80dd7db 100644
--- a/internal/profiledb/internal/filecachepb/filecache.proto
+++ b/internal/profiledb/internal/filecachepb/filecache.proto
@@ -43,8 +43,8 @@ message Profile {
message FilterConfig {
message Custom {
- string id = 1;
- google.protobuf.Timestamp update_time = 2;
+ reserved 1;
+ reserved 2;
repeated string rules = 3;
bool enabled = 4;
}
diff --git a/internal/profiledb/internal/filecachepb/filecachepb.go b/internal/profiledb/internal/filecachepb/filecachepb.go
index 35286e2..9bd93e8 100644
--- a/internal/profiledb/internal/filecachepb/filecachepb.go
+++ b/internal/profiledb/internal/filecachepb/filecachepb.go
@@ -3,6 +3,7 @@ package filecachepb
import (
"fmt"
+ "log/slog"
"net/netip"
"time"
@@ -13,6 +14,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter/custom"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb/internal"
"github.com/c2h5oh/datasize"
@@ -21,8 +23,12 @@ import (
)
// toInternal converts the protobuf-encoded data into a cache structure.
-func toInternal(fc *FileCache, respSzEst datasize.ByteSize) (c *internal.FileCache, err error) {
- profiles, err := profilesToInternal(fc.Profiles, respSzEst)
+func toInternal(
+ fc *FileCache,
+ baseCustomLogger *slog.Logger,
+ respSzEst datasize.ByteSize,
+) (c *internal.FileCache, err error) {
+ profiles, err := profilesToInternal(fc.Profiles, baseCustomLogger, respSzEst)
if err != nil {
return nil, fmt.Errorf("converting profiles: %w", err)
}
@@ -53,12 +59,13 @@ func toProtobuf(c *internal.FileCache) (pbFileCache *FileCache) {
// profilesToInternal converts protobuf profile structures into internal ones.
func profilesToInternal(
pbProfiles []*Profile,
+ baseCustomLogger *slog.Logger,
respSzEst datasize.ByteSize,
) (profiles []*agd.Profile, err error) {
profiles = make([]*agd.Profile, 0, len(pbProfiles))
for i, pbProf := range pbProfiles {
var prof *agd.Profile
- prof, err = pbProf.toInternal(respSzEst)
+ prof, err = pbProf.toInternal(baseCustomLogger, respSzEst)
if err != nil {
return nil, fmt.Errorf("profile at index %d: %w", i, err)
}
@@ -70,7 +77,10 @@ func profilesToInternal(
}
// toInternal converts a protobuf profile structure to an internal one.
-func (x *Profile) toInternal(respSzEst datasize.ByteSize) (prof *agd.Profile, err error) {
+func (x *Profile) toInternal(
+ baseCustomLogger *slog.Logger,
+ respSzEst datasize.ByteSize,
+) (prof *agd.Profile, err error) {
m, err := blockingModeToInternal(x.BlockingMode)
if err != nil {
return nil, fmt.Errorf("blocking mode: %w", err)
@@ -82,12 +92,20 @@ func (x *Profile) toInternal(respSzEst datasize.ByteSize) (prof *agd.Profile, er
return nil, fmt.Errorf("pause schedule: %w", err)
}
+ // Consider the rules to have been prevalidated.
+ rules := unsafelyConvertStrSlice[string, filter.RuleText](pbFltConf.Custom.Rules)
+
+ var flt filter.Custom
+ if len(rules) > 0 {
+ flt = custom.New(&custom.Config{
+ Logger: baseCustomLogger.With("client_id", x.ProfileId),
+ Rules: rules,
+ })
+ }
+
fltConf := &filter.ConfigClient{
Custom: &filter.ConfigCustom{
- ID: pbFltConf.Custom.Id,
- UpdateTime: pbFltConf.Custom.UpdateTime.AsTime(),
- // Consider the rules to have been prevalidated.
- Rules: unsafelyConvertStrSlice[string, filter.RuleText](pbFltConf.Custom.Rules),
+ Filter: flt,
Enabled: pbFltConf.Custom.Enabled,
},
Parental: &filter.ConfigParental{
@@ -385,12 +403,15 @@ func profilesToProtobuf(profiles []*agd.Profile) (pbProfiles []*Profile) {
// filterConfigToProtobuf converts the filtering configration to protobuf.
func filterConfigToProtobuf(c *filter.ConfigClient) (fc *FilterConfig) {
+ var rules []string
+ if c.Custom.Enabled {
+ rules = unsafelyConvertStrSlice[filter.RuleText, string](c.Custom.Filter.Rules())
+ }
+
return &FilterConfig{
Custom: &FilterConfig_Custom{
- Id: string(c.Custom.ID),
- UpdateTime: timestamppb.New(c.Custom.UpdateTime),
- Rules: unsafelyConvertStrSlice[filter.RuleText, string](c.Custom.Rules),
- Enabled: c.Custom.Enabled,
+ Rules: rules,
+ Enabled: c.Custom.Enabled,
},
Parental: &FilterConfig_Parental{
PauseSchedule: scheduleToProtobuf(c.Parental.PauseSchedule),
diff --git a/internal/profiledb/internal/filecachepb/filecachepb_internal_test.go b/internal/profiledb/internal/filecachepb/filecachepb_internal_test.go
index 5c0f49b..cebc04a 100644
--- a/internal/profiledb/internal/filecachepb/filecachepb_internal_test.go
+++ b/internal/profiledb/internal/filecachepb/filecachepb_internal_test.go
@@ -9,10 +9,14 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb/internal"
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb/internal/profiledbtest"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
)
+// testLogger is the common logger for tests.
+var testLogger = slogutil.NewDiscardLogger()
+
// Sinks for benchmarks
var (
bytesSink []byte
@@ -74,7 +78,7 @@ func BenchmarkCache(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for range b.N {
- gotCache, errSink = toInternal(fileCacheSink, profiledbtest.RespSzEst)
+ gotCache, errSink = toInternal(fileCacheSink, testLogger, profiledbtest.RespSzEst)
}
require.NoError(b, errSink)
diff --git a/internal/profiledb/internal/filecachepb/filecachepb_test.go b/internal/profiledb/internal/filecachepb/filecachepb_test.go
index 9c9f31d..78442f8 100644
--- a/internal/profiledb/internal/filecachepb/filecachepb_test.go
+++ b/internal/profiledb/internal/filecachepb/filecachepb_test.go
@@ -1,6 +1,13 @@
package filecachepb_test
-import "time"
+import (
+ "time"
+
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
+)
// testTimeout is the common timeout for tests.
const testTimeout = 1 * time.Second
+
+// testLogger is the common logger for tests.
+var testLogger = slogutil.NewDiscardLogger()
diff --git a/internal/profiledb/internal/filecachepb/storage.go b/internal/profiledb/internal/filecachepb/storage.go
index 29eb710..9f980b5 100644
--- a/internal/profiledb/internal/filecachepb/storage.go
+++ b/internal/profiledb/internal/filecachepb/storage.go
@@ -15,17 +15,40 @@ import (
// Storage is the file-cache storage that encodes data using protobuf.
type Storage struct {
- logger *slog.Logger
- path string
- respSzEst datasize.ByteSize
+ logger *slog.Logger
+ baseCustomLogger *slog.Logger
+ path string
+ respSzEst datasize.ByteSize
}
-// New returns a new protobuf-encoded file-cache storage.
-func New(logger *slog.Logger, cachePath string, respSzEst datasize.ByteSize) (s *Storage) {
+// Config is the configuration structure for the protobuf-encoded file-cache
+// storage.
+type Config struct {
+ // Logger is used for logging the operation of profile database. It must
+ // not be nil.
+ Logger *slog.Logger
+
+ // BaseCustomLogger is the base logger used for the custom filters. It must
+ // not be nil.
+ BaseCustomLogger *slog.Logger
+
+ // CacheFilePath is the path to the profile cache file. It must be set.
+ CacheFilePath string
+
+ // ResponseSizeEstimate is the estimate of the size of one DNS response for
+ // the purposes of custom ratelimiting. Responses over this estimate are
+ // counted as several responses. It must be positive.
+ ResponseSizeEstimate datasize.ByteSize
+}
+
+// New returns a new protobuf-encoded file-cache storage. c must not be nil and
+// must be valid.
+func New(c *Config) (s *Storage) {
return &Storage{
- logger: logger,
- path: cachePath,
- respSzEst: respSzEst,
+ logger: c.Logger,
+ baseCustomLogger: c.BaseCustomLogger,
+ path: c.CacheFilePath,
+ respSzEst: c.ResponseSizeEstimate,
}
}
@@ -63,7 +86,7 @@ func (s *Storage) Load(ctx context.Context) (c *internal.FileCache, err error) {
)
}
- return toInternal(fc, s.respSzEst)
+ return toInternal(fc, s.baseCustomLogger, s.respSzEst)
}
// Store implements the [internal.FileCacheStorage] interface for *Storage.
diff --git a/internal/profiledb/internal/filecachepb/storage_test.go b/internal/profiledb/internal/filecachepb/storage_test.go
index 4b0c7d1..f7f6421 100644
--- a/internal/profiledb/internal/filecachepb/storage_test.go
+++ b/internal/profiledb/internal/filecachepb/storage_test.go
@@ -9,7 +9,6 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb/internal"
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb/internal/filecachepb"
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb/internal/profiledbtest"
- "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -18,7 +17,12 @@ import (
func TestStorage(t *testing.T) {
prof, dev := profiledbtest.NewProfile(t)
cachePath := filepath.Join(t.TempDir(), "profiles.pb")
- s := filecachepb.New(slogutil.NewDiscardLogger(), cachePath, profiledbtest.RespSzEst)
+ s := filecachepb.New(&filecachepb.Config{
+ Logger: testLogger,
+ BaseCustomLogger: testLogger,
+ CacheFilePath: cachePath,
+ ResponseSizeEstimate: profiledbtest.RespSzEst,
+ })
require.NotNil(t, s)
fc := &internal.FileCache{
@@ -42,7 +46,12 @@ func TestStorage(t *testing.T) {
func TestStorage_Load_noFile(t *testing.T) {
cachePath := filepath.Join(t.TempDir(), "profiles.pb")
- s := filecachepb.New(slogutil.NewDiscardLogger(), cachePath, profiledbtest.RespSzEst)
+ s := filecachepb.New(&filecachepb.Config{
+ Logger: testLogger,
+ BaseCustomLogger: testLogger,
+ CacheFilePath: cachePath,
+ ResponseSizeEstimate: profiledbtest.RespSzEst,
+ })
require.NotNil(t, s)
ctx := testutil.ContextWithTimeout(t, testTimeout)
diff --git a/internal/profiledb/internal/internal.go b/internal/profiledb/internal/internal.go
index cd4e7f1..061c847 100644
--- a/internal/profiledb/internal/internal.go
+++ b/internal/profiledb/internal/internal.go
@@ -13,7 +13,7 @@ import (
// FileCacheVersion is the version of cached data structure. It must be
// manually incremented on every change in [agd.Device], [agd.Profile], and any
// file-cache structures.
-const FileCacheVersion = 15
+const FileCacheVersion = 16
// CacheVersionError is returned from [FileCacheStorage.Load] method if the
// stored cache version doesn't match current [FileCacheVersion].
diff --git a/internal/profiledb/internal/profiledbtest/profiledbtest.go b/internal/profiledb/internal/profiledbtest/profiledbtest.go
index b694cfc..ffb4bf0 100644
--- a/internal/profiledb/internal/profiledbtest/profiledbtest.go
+++ b/internal/profiledb/internal/profiledbtest/profiledbtest.go
@@ -12,7 +12,9 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter/custom"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/c2h5oh/datasize"
"github.com/stretchr/testify/require"
)
@@ -77,13 +79,16 @@ func NewProfile(tb testing.TB) (p *agd.Profile, d *agd.Device) {
Enabled: true,
}
+ customConf := &custom.Config{
+ Logger: slogutil.NewDiscardLogger(),
+ Rules: []filter.RuleText{"|blocked-by-custom.example^"},
+ }
+
return &agd.Profile{
FilterConfig: &filter.ConfigClient{
Custom: &filter.ConfigCustom{
- ID: string(ProfileID),
- UpdateTime: time.Now().UTC(),
- Rules: []filter.RuleText{"|blocked-by-custom.example"},
- Enabled: true,
+ Filter: custom.New(customConf),
+ Enabled: true,
},
Parental: parental,
RuleList: &filter.ConfigRuleList{
diff --git a/internal/profiledb/profiledb.go b/internal/profiledb/profiledb.go
index db57f4d..c466db5 100644
--- a/internal/profiledb/profiledb.go
+++ b/internal/profiledb/profiledb.go
@@ -12,14 +12,13 @@ import (
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb/internal"
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb/internal/filecachepb"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/osutil"
- "github.com/AdguardTeam/golibs/timeutil"
+ "github.com/AdguardTeam/golibs/service"
"github.com/c2h5oh/datasize"
)
@@ -124,6 +123,9 @@ type Config struct {
// Logger is used for logging the operation of profile database.
Logger *slog.Logger
+ // BaseCustomLogger is the base logger used for the custom filters.
+ BaseCustomLogger *slog.Logger
+
// Storage returns the data for this profile DB.
Storage Storage
@@ -239,8 +241,12 @@ func New(c *Config) (db *Default, err error) {
if c.CacheFilePath == "none" {
cacheStorage = internal.EmptyFileCacheStorage{}
} else if ext := filepath.Ext(c.CacheFilePath); ext == ".pb" {
- logger := c.Logger.With("cache_type", "pb")
- cacheStorage = filecachepb.New(logger, c.CacheFilePath, c.ResponseSizeEstimate)
+ cacheStorage = filecachepb.New(&filecachepb.Config{
+ Logger: c.Logger.With("cache_type", "pb"),
+ BaseCustomLogger: c.BaseCustomLogger,
+ CacheFilePath: c.CacheFilePath,
+ ResponseSizeEstimate: c.ResponseSizeEstimate,
+ })
} else {
return nil, fmt.Errorf("file %q is not protobuf", c.CacheFilePath)
}
@@ -278,9 +284,9 @@ func New(c *Config) (db *Default, err error) {
}
// type check
-var _ agdservice.Refresher = (*Default)(nil)
+var _ service.Refresher = (*Default)(nil)
-// Refresh implements the [agdservice.Refresher] interface for *Default. It
+// Refresh implements the [service.Refresher] interface for *Default. It
// updates the internal maps and the synchronization time using the data it
// receives from the storage.
//
@@ -374,9 +380,7 @@ func (db *Default) fetchProfiles(
) (sr *StorageProfilesResponse, err error) {
syncTime := db.syncTime
if isFullSync {
- db.logger.InfoContext(ctx, "full sync", "since_last_attempt", timeutil.Duration{
- Duration: sinceLastAttempt,
- })
+ db.logger.InfoContext(ctx, "full sync", "since_last_attempt", sinceLastAttempt)
syncTime = time.Time{}
}
@@ -412,9 +416,7 @@ func (db *Default) needsFullSync(ctx context.Context) (sinceFull time.Duration,
db.logger.WarnContext(
ctx,
"previous sync finished with error",
- "since_last_successful_sync", timeutil.Duration{
- Duration: sinceFull,
- },
+ "since_last_successful_sync", sinceFull,
"last_successful_sync_time", lastFull,
)
@@ -453,9 +455,7 @@ func (db *Default) loadFileCache(ctx context.Context) (err error) {
"version", c.Version,
"prof_num", profNum,
"dev_num", devNum,
- "elapsed", timeutil.Duration{
- Duration: time.Since(start),
- },
+ "elapsed", time.Since(start),
)
if profNum == 0 || devNum == 0 {
diff --git a/internal/profiledb/profiledb_test.go b/internal/profiledb/profiledb_test.go
index 5a30fe2..c23ccd2 100644
--- a/internal/profiledb/profiledb_test.go
+++ b/internal/profiledb/profiledb_test.go
@@ -33,6 +33,9 @@ var (
// testTimeout is the common timeout for tests.
const testTimeout = 1 * time.Second
+// testLogger is the common logger for tests.
+var testLogger = slogutil.NewDiscardLogger()
+
// newDefaultProfileDB returns a new default profile database for tests.
// devicesCh receives the devices that the storage should return in its
// response.
@@ -70,7 +73,8 @@ func newDefaultProfileDB(tb testing.TB, devices <-chan []*agd.Device) (db *profi
}
db, err := profiledb.New(&profiledb.Config{
- Logger: slogutil.NewDiscardLogger(),
+ Logger: testLogger,
+ BaseCustomLogger: testLogger,
Storage: ps,
ErrColl: agdtest.NewErrorCollector(),
Metrics: profiledb.EmptyMetrics{},
@@ -421,8 +425,12 @@ func TestDefaultProfileDB_fileCache_success(t *testing.T) {
prof, dev := profiledbtest.NewProfile(t)
cacheFilePath := filepath.Join(t.TempDir(), "profiles.pb")
- logger := slogutil.NewDiscardLogger()
- pbCache := filecachepb.New(logger, cacheFilePath, profiledbtest.RespSzEst)
+ pbCache := filecachepb.New(&filecachepb.Config{
+ Logger: testLogger,
+ BaseCustomLogger: testLogger,
+ CacheFilePath: cacheFilePath,
+ ResponseSizeEstimate: profiledbtest.RespSzEst,
+ })
ctx := testutil.ContextWithTimeout(t, testTimeout)
err := pbCache.Store(ctx, &internal.FileCache{
@@ -434,7 +442,8 @@ func TestDefaultProfileDB_fileCache_success(t *testing.T) {
require.NoError(t, err)
db, err := profiledb.New(&profiledb.Config{
- Logger: logger,
+ Logger: testLogger,
+ BaseCustomLogger: testLogger,
Storage: ps,
ErrColl: agdtest.NewErrorCollector(),
Metrics: profiledb.EmptyMetrics{},
@@ -479,8 +488,12 @@ func TestDefaultProfileDB_fileCache_badVersion(t *testing.T) {
}
cacheFilePath := filepath.Join(t.TempDir(), "profiles.pb")
- logger := slogutil.NewDiscardLogger()
- pbCache := filecachepb.New(logger, cacheFilePath, profiledbtest.RespSzEst)
+ pbCache := filecachepb.New(&filecachepb.Config{
+ Logger: testLogger,
+ BaseCustomLogger: testLogger,
+ CacheFilePath: cacheFilePath,
+ ResponseSizeEstimate: profiledbtest.RespSzEst,
+ })
ctx := testutil.ContextWithTimeout(t, testTimeout)
err := pbCache.Store(ctx, &internal.FileCache{
@@ -489,7 +502,8 @@ func TestDefaultProfileDB_fileCache_badVersion(t *testing.T) {
require.NoError(t, err)
db, err := profiledb.New(&profiledb.Config{
- Logger: logger,
+ Logger: testLogger,
+ BaseCustomLogger: testLogger,
Storage: ps,
ErrColl: agdtest.NewErrorCollector(),
Metrics: profiledb.EmptyMetrics{},
@@ -542,7 +556,8 @@ func TestDefaultProfileDB_CreateAutoDevice(t *testing.T) {
}
db, err := profiledb.New(&profiledb.Config{
- Logger: slogutil.NewDiscardLogger(),
+ Logger: testLogger,
+ BaseCustomLogger: testLogger,
Storage: ps,
ErrColl: agdtest.NewErrorCollector(),
Metrics: profiledb.EmptyMetrics{},
diff --git a/internal/querylog/fs.go b/internal/querylog/fs.go
index c59dc8f..8943b10 100644
--- a/internal/querylog/fs.go
+++ b/internal/querylog/fs.go
@@ -7,50 +7,36 @@ import (
"fmt"
"log/slog"
"math"
+ "math/rand/v2"
"net/netip"
"os"
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
- "github.com/AdguardTeam/AdGuardDNS/internal/metrics"
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdrand"
"github.com/AdguardTeam/AdGuardDNS/internal/optslog"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/mathutil"
"github.com/AdguardTeam/golibs/syncutil"
- "golang.org/x/exp/rand"
+ "github.com/c2h5oh/datasize"
)
// FileSystemConfig is the configuration of the file system query log. All
// fields must not be empty.
type FileSystemConfig struct {
- // Logger is used for debug logging.
+ // Logger is used for debug logging. It must not be nil.
Logger *slog.Logger
- // Path is the path to the log file.
+ // Metrics is used for the collection of the query log statistics. It must
+ // not be nil.
+ Metrics Metrics
+
+ // Path is the path to the log file. It must not be empty.
Path string
// RandSeed is used to set the "rn" property in JSON objects.
- RandSeed uint64
-}
-
-// NewFileSystem creates a new file system query log. The log is safe for
-// concurrent use. c must not be nil.
-func NewFileSystem(c *FileSystemConfig) (l *FileSystem) {
- rng := rand.New(&rand.LockedSource{})
- rng.Seed(c.RandSeed)
-
- return &FileSystem{
- logger: c.Logger,
- bufferPool: syncutil.NewPool(func() (v *entryBuffer) {
- return &entryBuffer{
- ent: &jsonlEntry{},
- buf: &bytes.Buffer{},
- }
- }),
- rng: rng,
- path: c.Path,
- }
+ RandSeed [32]byte
}
// entryBuffer is a struct with two fields for caching entry that is being
@@ -73,10 +59,34 @@ type FileSystem struct {
// resulting JSON.
rng *rand.Rand
+ // metrics is used for the collection of the query log statistics.
+ metrics Metrics
+
// path is the path to the query log file.
path string
}
+// NewFileSystem creates a new file system query log. The log is safe for
+// concurrent use. c must not be nil.
+func NewFileSystem(c *FileSystemConfig) (l *FileSystem) {
+ src := rand.NewChaCha8(c.RandSeed)
+ // #nosec G404 -- We don't need a real random, pseudorandom is enough.
+ rng := rand.New(agdrand.NewLockedSource(src))
+
+ return &FileSystem{
+ logger: c.Logger,
+ bufferPool: syncutil.NewPool(func() (v *entryBuffer) {
+ return &entryBuffer{
+ ent: &jsonlEntry{},
+ buf: &bytes.Buffer{},
+ }
+ }),
+ rng: rng,
+ metrics: c.Metrics,
+ path: c.Path,
+ }
+}
+
// type check
var _ Interface = (*FileSystem)(nil)
@@ -95,8 +105,8 @@ func (l *FileSystem) Write(ctx context.Context, e *Entry) (err error) {
startTime := time.Now()
defer func() {
- metrics.QueryLogWriteDuration.Observe(time.Since(startTime).Seconds())
- metrics.QueryLogItemsCount.Inc()
+ l.metrics.ObserveWriteDuration(ctx, time.Since(startTime))
+ l.metrics.IncrementItemsCount(ctx)
}()
entBuf := l.bufferPool.Get()
@@ -151,7 +161,9 @@ func (l *FileSystem) Write(ctx context.Context, e *Entry) (err error) {
return fmt.Errorf("writing log: %w", err)
}
- metrics.QueryLogItemSize.Observe(float64(written))
+ // #nosec G115 -- [bytes.Buffer.WriteTo] returns the number of bytes
+ // written, which is always a non-negative number.
+ l.metrics.ObserveItemSize(ctx, datasize.ByteSize(written))
return nil
}
diff --git a/internal/querylog/fs_test.go b/internal/querylog/fs_test.go
index 7a9ad99..784cc15 100644
--- a/internal/querylog/fs_test.go
+++ b/internal/querylog/fs_test.go
@@ -21,8 +21,9 @@ func TestFileSystem_Write(t *testing.T) {
l := querylog.NewFileSystem(&querylog.FileSystemConfig{
Logger: slogutil.NewDiscardLogger(),
+ Metrics: querylog.EmptyMetrics{},
Path: f.Name(),
- RandSeed: 0,
+ RandSeed: [32]byte{},
})
ctx := context.Background()
@@ -57,7 +58,7 @@ func TestFileSystem_Write(t *testing.T) {
"e":5,
"q":1,
"r":0,
- "rn":35121,
+ "rn":13933,
"f":2,
"s":1,
"p":8
@@ -94,7 +95,7 @@ func TestFileSystem_Write(t *testing.T) {
"e":5,
"q":1,
"r":3,
- "rn":47387,
+ "rn":10182,
"f":1,
"s":1,
"p":8
@@ -112,8 +113,9 @@ func BenchmarkFileSystem_Write_file(b *testing.B) {
l := querylog.NewFileSystem(&querylog.FileSystemConfig{
Logger: slogutil.NewDiscardLogger(),
+ Metrics: querylog.EmptyMetrics{},
Path: f.Name(),
- RandSeed: 0,
+ RandSeed: [32]byte{},
})
e := testEntry()
diff --git a/internal/querylog/metrics.go b/internal/querylog/metrics.go
new file mode 100644
index 0000000..ced2031
--- /dev/null
+++ b/internal/querylog/metrics.go
@@ -0,0 +1,38 @@
+package querylog
+
+import (
+ "context"
+ "time"
+
+ "github.com/c2h5oh/datasize"
+)
+
+// Metrics is an interface that is used for the collection of the query log
+// statistics.
+type Metrics interface {
+ // IncrementItemsCount increments the total number of query log entries
+ // written.
+ IncrementItemsCount(ctx context.Context)
+
+ // ObserveItemSize stores the size of written query log entry.
+ ObserveItemSize(ctx context.Context, size datasize.ByteSize)
+
+ // ObserveWriteDuration stores the duration of the write operation.
+ ObserveWriteDuration(ctx context.Context, dur time.Duration)
+}
+
+// EmptyMetrics is the implementation of the [Metrics] interface that does
+// nothing.
+type EmptyMetrics struct{}
+
+// type check
+var _ Metrics = EmptyMetrics{}
+
+// IncrementItemsCount implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) IncrementItemsCount(_ context.Context) {}
+
+// ObserveItemSize implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) ObserveItemSize(_ context.Context, _ datasize.ByteSize) {}
+
+// ObserveWriteDuration implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) ObserveWriteDuration(_ context.Context, _ time.Duration) {}
diff --git a/internal/remotekv/consulkv/consulkv.go b/internal/remotekv/consulkv/consulkv.go
index d799a07..ad8c1e5 100644
--- a/internal/remotekv/consulkv/consulkv.go
+++ b/internal/remotekv/consulkv/consulkv.go
@@ -202,7 +202,7 @@ func (kv *KV) Set(ctx context.Context, key string, val []byte) (err error) {
sessReq := &consulSessionRequest{
Name: fmt.Sprintf("ad_guard_dns_session_%d", time.Now().UnixNano()),
Behavior: consulSessionBehavior,
- TTL: timeutil.Duration{Duration: kv.ttl},
+ TTL: timeutil.Duration(kv.ttl),
}
b, err := json.Marshal(sessReq)
if err != nil {
diff --git a/internal/remotekv/rediskv/metrics.go b/internal/remotekv/rediskv/metrics.go
index fc9b3df..72e30aa 100644
--- a/internal/remotekv/rediskv/metrics.go
+++ b/internal/remotekv/rediskv/metrics.go
@@ -7,7 +7,7 @@ import "context"
type Metrics interface {
// UpdateMetrics updates the total number of active connections and
// increments the total number of errors if necessary.
- UpdateMetrics(ctx context.Context, val uint, isSuccess bool)
+ UpdateMetrics(ctx context.Context, val uint, err error)
}
// EmptyMetrics is the implementation of the [Metrics] interface that does
@@ -18,4 +18,4 @@ type EmptyMetrics struct{}
var _ Metrics = EmptyMetrics{}
// UpdateMetrics implements the [Metrics] interface for EmptyMetrics.
-func (EmptyMetrics) UpdateMetrics(_ context.Context, _ uint, _ bool) {}
+func (EmptyMetrics) UpdateMetrics(_ context.Context, _ uint, _ error) {}
diff --git a/internal/remotekv/rediskv/rediskv.go b/internal/remotekv/rediskv/rediskv.go
index 2d9e196..6067f5e 100644
--- a/internal/remotekv/rediskv/rediskv.go
+++ b/internal/remotekv/rediskv/rediskv.go
@@ -144,7 +144,7 @@ func (kv *RedisKV) Get(ctx context.Context, key string) (val []byte, ok bool, er
defer func() {
// #nosec G115 -- Assume that pool.ActiveCount is always non-negative.
- kv.metrics.UpdateMetrics(ctx, uint(kv.pool.ActiveCount()), err == nil)
+ kv.metrics.UpdateMetrics(ctx, uint(kv.pool.ActiveCount()), err)
}()
c, err := kv.pool.GetContext(ctx)
@@ -170,7 +170,7 @@ func (kv *RedisKV) Set(ctx context.Context, key string, val []byte) (err error)
defer func() {
// #nosec G115 -- Assume that pool.ActiveCount is always non-negative.
- kv.metrics.UpdateMetrics(ctx, uint(kv.pool.ActiveCount()), err == nil)
+ kv.metrics.UpdateMetrics(ctx, uint(kv.pool.ActiveCount()), err)
}()
c, err := kv.pool.GetContext(ctx)
diff --git a/internal/rulestat/http.go b/internal/rulestat/http.go
index d23e340..8f9611a 100644
--- a/internal/rulestat/http.go
+++ b/internal/rulestat/http.go
@@ -12,12 +12,11 @@ import (
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
- "github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
+ "github.com/AdguardTeam/golibs/service"
)
// statFilterListLegacyID is the ID of the filtering rule list for which we
@@ -36,6 +35,7 @@ type HTTP struct {
url *url.URL
http *agdhttp.Client
errColl errcoll.Interface
+ metrics Metrics
// mu protects stats and recordedHits.
mu *sync.Mutex
@@ -55,6 +55,9 @@ type HTTPConfig struct {
// ErrColl is used to collect errors during refreshes.
ErrColl errcoll.Interface
+ // Metrics is used for the collection of the filtering rule statistics.
+ Metrics Metrics
+
// URL is the URL to which the statistics is uploaded.
URL *url.URL
}
@@ -69,6 +72,7 @@ func NewHTTP(c *HTTPConfig) (s *HTTP) {
Timeout: 30 * time.Second,
}),
errColl: c.ErrColl,
+ metrics: c.Metrics,
mu: &sync.Mutex{},
stats: statsSet{},
@@ -79,7 +83,7 @@ func NewHTTP(c *HTTPConfig) (s *HTTP) {
var _ Interface = (*HTTP)(nil)
// Collect implements the Interface interface for *HTTP.
-func (s *HTTP) Collect(_ context.Context, id filter.ID, text filter.RuleText) {
+func (s *HTTP) Collect(ctx context.Context, id filter.ID, text filter.RuleText) {
if id != filter.IDAdGuardDNS {
return
}
@@ -90,7 +94,7 @@ func (s *HTTP) Collect(_ context.Context, id filter.ID, text filter.RuleText) {
defer s.mu.Unlock()
s.recordedHits++
- metrics.RuleStatCacheSize.Set(float64(s.recordedHits))
+ s.metrics.SetHitCount(ctx, s.recordedHits)
texts := s.stats[id]
if texts != nil {
@@ -105,26 +109,24 @@ func (s *HTTP) Collect(_ context.Context, id filter.ID, text filter.RuleText) {
}
// type check
-var _ agdservice.Refresher = (*HTTP)(nil)
+var _ service.Refresher = (*HTTP)(nil)
-// Refresh implements the [agdservice.Refresher] interface for *HTTP. It
-// uploads the collected statistics to s.u and starts collecting a new set of
+// Refresh implements the [service.Refresher] interface for *HTTP. It uploads
+// the collected statistics to s.u and starts collecting a new set of
// statistics.
func (s *HTTP) Refresh(ctx context.Context) (err error) {
s.logger.InfoContext(ctx, "refresh started")
defer s.logger.InfoContext(ctx, "refresh finished")
+ defer func() { s.metrics.HandleUploadStatus(ctx, err) }()
+
err = s.refresh(ctx)
if err != nil {
errcoll.Collect(ctx, s.errColl, s.logger, "uploading rulestat", err)
- metrics.SetStatusGauge(metrics.RuleStatUploadStatus, err)
return err
}
- metrics.RuleStatUploadTimestamp.SetToCurrentTime()
- metrics.SetStatusGauge(metrics.RuleStatUploadStatus, nil)
-
return nil
}
diff --git a/internal/rulestat/http_test.go b/internal/rulestat/http_test.go
index c251ffb..ef154c0 100644
--- a/internal/rulestat/http_test.go
+++ b/internal/rulestat/http_test.go
@@ -42,8 +42,9 @@ func TestHTTP_Collect(t *testing.T) {
rw.WriteHeader(http.StatusOK)
}))
conf := &rulestat.HTTPConfig{
- ErrColl: agdtest.NewErrorCollector(),
Logger: slogutil.NewDiscardLogger(),
+ ErrColl: agdtest.NewErrorCollector(),
+ Metrics: rulestat.EmptyMetrics{},
URL: u,
}
@@ -96,6 +97,7 @@ func TestHTTP_Refresh_errors(t *testing.T) {
testutil.AssertErrorMsg(t, "uploading rulestat: "+wantErrMsg, err)
},
},
+ Metrics: rulestat.EmptyMetrics{},
URL: &url.URL{
Scheme: "badscheme",
Host: "0.0.0.0",
@@ -117,7 +119,8 @@ func TestHTTP_Refresh_errors(t *testing.T) {
require.NotNil(t, err)
},
},
- URL: u,
+ Metrics: rulestat.EmptyMetrics{},
+ URL: u,
})
var serr *agdhttp.StatusError
diff --git a/internal/rulestat/metrics.go b/internal/rulestat/metrics.go
new file mode 100644
index 0000000..14de64a
--- /dev/null
+++ b/internal/rulestat/metrics.go
@@ -0,0 +1,29 @@
+package rulestat
+
+import (
+ "context"
+)
+
+// Metrics is an interface that is used for the collection of the filtering rule
+// statistics.
+type Metrics interface {
+ // SetHitCount the number of rule hits that have not yet been uploaded.
+ SetHitCount(ctx context.Context, count int64)
+
+ // HandleUploadStatus handles the upload status of the filtering rule
+ // statistics.
+ HandleUploadStatus(ctx context.Context, err error)
+}
+
+// EmptyMetrics is the implementation of the [Metrics] interface that does
+// nothing.
+type EmptyMetrics struct{}
+
+// type check
+var _ Metrics = EmptyMetrics{}
+
+// SetHitCount implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) SetHitCount(_ context.Context, _ int64) {}
+
+// HandleUploadStatus implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) HandleUploadStatus(_ context.Context, _ error) {}
diff --git a/internal/tlsconfig/manager.go b/internal/tlsconfig/manager.go
index 9bbeaba..e1d9fb2 100644
--- a/internal/tlsconfig/manager.go
+++ b/internal/tlsconfig/manager.go
@@ -10,9 +10,9 @@ import (
"path/filepath"
"sync"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/service"
)
// Manager stores and updates TLS configurations.
@@ -203,9 +203,9 @@ func (m *DefaultManager) CloneWithMetrics(
}
// type check
-var _ agdservice.Refresher = (*DefaultManager)(nil)
+var _ service.Refresher = (*DefaultManager)(nil)
-// Refresh implements the [agdservice.Refresher] interface for *DefaultManager.
+// Refresh implements the [service.Refresher] interface for *DefaultManager.
func (m *DefaultManager) Refresh(ctx context.Context) (err error) {
m.logger.DebugContext(ctx, "refresh started")
defer m.logger.DebugContext(ctx, "refresh finished")
@@ -228,12 +228,13 @@ func (m *DefaultManager) Refresh(ctx context.Context) (err error) {
return true
}
- if m.certStorage.update(cp, cert) {
- m.logger.InfoContext(ctx, "refreshed certificate", "cert", cp.certPath, "key", cp.keyPath)
- } else {
- m.logger.WarnContext(ctx, "certificate did not refresh", "cert", cp.certPath, "key", cp.keyPath)
+ msg, lvl := "refreshed certificate", slog.LevelInfo
+ if !m.certStorage.update(cp, cert) {
+ msg, lvl = "certificate did not refresh", slog.LevelWarn
}
+ m.logger.Log(ctx, lvl, msg, "cert", cp.certPath, "key", cp.keyPath)
+
return true
})
@@ -268,8 +269,9 @@ func (m *DefaultManager) RotateTickets(ctx context.Context) (err error) {
}
defer func() {
+ m.metrics.SetSessionTicketRotationStatus(ctx, err)
+
if err != nil {
- m.metrics.SetSessionTicketRotationStatus(ctx, false)
errcoll.Collect(ctx, m.errColl, m.logger, "ticket rotation failed", err)
}
}()
@@ -303,8 +305,6 @@ func (m *DefaultManager) RotateTickets(ctx context.Context) (err error) {
"num_tickets", len(tickets),
)
- m.metrics.SetSessionTicketRotationStatus(ctx, true)
-
return nil
}
diff --git a/internal/tlsconfig/metrics.go b/internal/tlsconfig/metrics.go
index 0b4be1a..f0943a7 100644
--- a/internal/tlsconfig/metrics.go
+++ b/internal/tlsconfig/metrics.go
@@ -27,7 +27,7 @@ type Metrics interface {
// SetSessionTicketRotationStatus sets the TLS session ticket rotation
// status.
- SetSessionTicketRotationStatus(ctx context.Context, enabled bool)
+ SetSessionTicketRotationStatus(ctx context.Context, err error)
}
// EmptyMetrics is the implementation of the [Metrics] interface that does
@@ -65,4 +65,4 @@ func (EmptyMetrics) SetCertificateInfo(_ context.Context, _, _ string, _ time.Ti
// SetSessionTicketRotationStatus implements the [Metrics] interface for
// EmptyMetrics.
-func (EmptyMetrics) SetSessionTicketRotationStatus(_ context.Context, _ bool) {}
+func (EmptyMetrics) SetSessionTicketRotationStatus(_ context.Context, _ error) {}
diff --git a/internal/tools/go.mod b/internal/tools/go.mod
index 303103c..72733b4 100644
--- a/internal/tools/go.mod
+++ b/internal/tools/go.mod
@@ -1,6 +1,6 @@
module github.com/AdguardTeam/AdGuardDNS/internal/tools
-go 1.23.4
+go 1.23.6
require (
github.com/fzipp/gocyclo v0.6.0
@@ -8,63 +8,62 @@ require (
github.com/gordonklaus/ineffassign v0.1.0
github.com/jstemmer/go-junit-report/v2 v2.1.0
github.com/kisielk/errcheck v1.8.0
- github.com/securego/gosec/v2 v2.21.4
- github.com/uudashr/gocognit v1.1.3
- golang.org/x/tools v0.28.0
- golang.org/x/vuln v1.1.3
+ github.com/securego/gosec/v2 v2.22.0
+ github.com/uudashr/gocognit v1.2.0
+ golang.org/x/tools v0.29.0
+ golang.org/x/vuln v1.1.4
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1
- google.golang.org/protobuf v1.35.2
+ google.golang.org/protobuf v1.36.5
honnef.co/go/tools v0.5.1
mvdan.cc/gofumpt v0.7.0
mvdan.cc/sh/v3 v3.10.0
- mvdan.cc/unparam v0.0.0-20240917084806-57a3b4290ba3
+ mvdan.cc/unparam v0.0.0-20241226123437-447d509598f3
)
require (
- cloud.google.com/go v0.116.0 // indirect
- cloud.google.com/go/ai v0.9.0 // indirect
- cloud.google.com/go/auth v0.12.0 // indirect
- cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect
- cloud.google.com/go/compute/metadata v0.5.2 // indirect
- cloud.google.com/go/longrunning v0.6.3 // indirect
+ cloud.google.com/go v0.118.2 // indirect
+ cloud.google.com/go/ai v0.10.0 // indirect
+ cloud.google.com/go/auth v0.14.1 // indirect
+ cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
+ cloud.google.com/go/compute/metadata v0.6.0 // indirect
+ cloud.google.com/go/longrunning v0.6.4 // indirect
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect
github.com/ccojocar/zxcvbn-go v1.0.2 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
- github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/google/generative-ai-go v0.19.0 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/renameio/v2 v2.0.0 // indirect
- github.com/google/s2a-go v0.1.8 // indirect
+ github.com/google/s2a-go v0.1.9 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
- github.com/googleapis/gax-go/v2 v2.14.0 // indirect
+ github.com/googleapis/gax-go/v2 v2.14.1 // indirect
github.com/gookit/color v1.5.4 // indirect
- github.com/rogpeppe/go-internal v1.13.1 // indirect
+ github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
- go.opencensus.io v0.24.0 // indirect
- go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0 // indirect
- go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 // indirect
- go.opentelemetry.io/otel v1.32.0 // indirect
- go.opentelemetry.io/otel/metric v1.32.0 // indirect
- go.opentelemetry.io/otel/trace v1.32.0 // indirect
- golang.org/x/crypto v0.30.0 // indirect
- golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d // indirect
- golang.org/x/exp/typeparams v0.0.0-20241204233417-43b7b7cde48d // indirect
- golang.org/x/mod v0.22.0 // indirect
- golang.org/x/net v0.32.0 // indirect
- golang.org/x/oauth2 v0.24.0 // indirect
- golang.org/x/sync v0.10.0 // indirect
- golang.org/x/sys v0.28.0 // indirect
- golang.org/x/telemetry v0.0.0-20241204182053-c0ac0e154df3 // indirect
- golang.org/x/term v0.27.0 // indirect
- golang.org/x/text v0.21.0 // indirect
- golang.org/x/time v0.8.0 // indirect
- google.golang.org/api v0.210.0 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect
- google.golang.org/grpc v1.68.1 // indirect
+ go.opentelemetry.io/auto/sdk v1.1.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
+ go.opentelemetry.io/otel v1.34.0 // indirect
+ go.opentelemetry.io/otel/metric v1.34.0 // indirect
+ go.opentelemetry.io/otel/trace v1.34.0 // indirect
+ golang.org/x/crypto v0.32.0 // indirect
+ golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect
+ golang.org/x/exp/typeparams v0.0.0-20250207012021-f9890c6ad9f3 // indirect
+ golang.org/x/mod v0.23.0 // indirect
+ golang.org/x/net v0.34.0 // indirect
+ golang.org/x/oauth2 v0.26.0 // indirect
+ golang.org/x/sync v0.11.0 // indirect
+ golang.org/x/sys v0.30.0 // indirect
+ golang.org/x/telemetry v0.0.0-20250206143958-557cf9c30e9f // indirect
+ golang.org/x/term v0.29.0 // indirect
+ golang.org/x/text v0.22.0 // indirect
+ golang.org/x/time v0.10.0 // indirect
+ google.golang.org/api v0.220.0 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489 // indirect
+ google.golang.org/grpc v1.70.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
mvdan.cc/editorconfig v0.3.0 // indirect
)
diff --git a/internal/tools/go.sum b/internal/tools/go.sum
index ac81594..cf9a46f 100644
--- a/internal/tools/go.sum
+++ b/internal/tools/go.sum
@@ -1,31 +1,21 @@
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
-cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
-cloud.google.com/go/ai v0.9.0 h1:r1Ig8O8+Qr3Ia3WfoO+gokD0fxB2Rk4quppuKjmGMsY=
-cloud.google.com/go/ai v0.9.0/go.mod h1:28bKM/oxmRgxmRgI1GLumFv+NSkt+DscAg/gF+54zzY=
-cloud.google.com/go/auth v0.12.0 h1:ARAD8r0lkiHw2go7kEnmviF6TOYhzLM+yDGcDt9mP68=
-cloud.google.com/go/auth v0.12.0/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI=
-cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU=
-cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8=
-cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo=
-cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k=
-cloud.google.com/go/longrunning v0.6.3 h1:A2q2vuyXysRcwzqDpMMLSI6mb6o39miS52UEG/Rd2ng=
-cloud.google.com/go/longrunning v0.6.3/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+cloud.google.com/go v0.118.2 h1:bKXO7RXMFDkniAAvvuMrAPtQ/VHrs9e7J5UT3yrGdTY=
+cloud.google.com/go v0.118.2/go.mod h1:CFO4UPEPi8oV21xoezZCrd3d81K4fFkDTEJu4R8K+9M=
+cloud.google.com/go/ai v0.10.0 h1:hwj6CI6sMKubXodoJJGTy/c2T1RbbLGM6TL3QoAvzU8=
+cloud.google.com/go/ai v0.10.0/go.mod h1:kvnt2KeHqX8+41PVeMRBETDyQAp/RFvBWGdx/aGjNMo=
+cloud.google.com/go/auth v0.14.1 h1:AwoJbzUdxA/whv1qj3TLKwh3XX5sikny2fc40wUl+h0=
+cloud.google.com/go/auth v0.14.1/go.mod h1:4JHUxlGXisL0AW8kXPtUF6ztuOksyfUQNFjfsOCXkPM=
+cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M=
+cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc=
+cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
+cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
+cloud.google.com/go/longrunning v0.6.4 h1:3tyw9rO3E2XVXzSApn1gyEEnH2K9SynNQjMlBi3uHLg=
+cloud.google.com/go/longrunning v0.6.4/go.mod h1:ttZpLCe6e7EXvn9OxpBRx7kZEB0efv8yBO6YnVMfhJs=
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg=
github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
@@ -39,20 +29,6 @@ github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
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/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
-github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
-github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
-github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
-github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
-github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
-github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs=
@@ -61,30 +37,23 @@ github.com/google/generative-ai-go v0.19.0 h1:R71szggh8wHMCUlEMsW2A/3T+5LdEIkiaH
github.com/google/generative-ai-go v0.19.0/go.mod h1:JYolL13VG7j79kM5BtHz4qwONHkeJQzOCkKXnpqtS/E=
github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786 h1:rcv+Ippz6RAtvaGgKxc+8FQIpxHgsF+HBzPyYL2cyVU=
github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786/go.mod h1:apVn/GCasLZUVpAJ6oWAuyP7Ne7CEsQbTnc0plM3m+o=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA=
-github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
+github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
+github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
-github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
-github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
-github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
+github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
-github.com/googleapis/gax-go/v2 v2.14.0 h1:f+jMrjBPl+DL9nI4IQzLUxMq7XrAqFYB7hBPqMNIe8o=
-github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk=
+github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
+github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s=
@@ -97,152 +66,108 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-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.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
-github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc=
+github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
+github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
+github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
+github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
-github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
-github.com/securego/gosec/v2 v2.21.4 h1:Le8MSj0PDmOnHJgUATjD96PaXRvCpKC+DGJvwyy0Mlk=
-github.com/securego/gosec/v2 v2.21.4/go.mod h1:Jtb/MwRQfRxCXyCm1rfM1BEiiiTfUOdyzzAhlr6lUTA=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a h1:w3tdWGKbLGBPtR/8/oO74W6hmz0qE5q0z9aqSAewaaM=
+github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a/go.mod h1:S8kfXMp+yh77OxPD4fdM6YUknrZpQxLhvxzS4gDHENY=
+github.com/securego/gosec/v2 v2.22.0 h1:bV/Ii5YSQtbobXuIFBXrfr91l5N4qslEdFHE9E0I/10=
+github.com/securego/gosec/v2 v2.22.0/go.mod h1:sR5n3LzZ/52rn4xxRBJk38iPe/hjiA0CkVcyiAHNCrM=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
-github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
-github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
-github.com/uudashr/gocognit v1.1.3 h1:l+a111VcDbKfynh+airAy/DJQKaXh2m9vkoysMPSZyM=
-github.com/uudashr/gocognit v1.1.3/go.mod h1:aKH8/e8xbTRBwjbCkwZ8qt4l2EpKXl31KMHgSS+lZ2U=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/uudashr/gocognit v1.2.0 h1:3BU9aMr1xbhPlvJLSydKwdLN3tEUUrzPSSM8S4hDYRA=
+github.com/uudashr/gocognit v1.2.0/go.mod h1:k/DdKPI6XBZO1q7HgoV2juESI2/Ofj9AcHPZhBBdrTU=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
-go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0 h1:qtFISDHKolvIxzSs0gIaiPUPR0Cucb0F2coHC7ZLdps=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0/go.mod h1:Y+Pop1Q6hCOnETWTW4NROK/q1hv50hM7yDaUTjG8lp8=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94=
-go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
-go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
-go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
-go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
-go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
-go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
+go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
+go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
+go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
+go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
+go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
+go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
+go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
+go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
+go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
+go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-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-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-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/exp/typeparams v0.0.0-20241204233417-43b7b7cde48d h1:WQHXGzzI2u+AAlupPRpQbdG4WVvVZ7d2dg/CkpEu1hI=
-golang.org/x/exp/typeparams v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
+golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
+golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA=
+golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
+golang.org/x/exp/typeparams v0.0.0-20250207012021-f9890c6ad9f3 h1:w2c+/ogVo2eFFhGTMddgOF7WQkdOPwjh+MRS8wUnujk=
+golang.org/x/exp/typeparams v0.0.0-20250207012021-f9890c6ad9f3/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-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.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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
+golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-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/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
-golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
+golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
+golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE=
+golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
-golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
+golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-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/telemetry v0.0.0-20241204182053-c0ac0e154df3 h1:rCLsPBq7l0E9Z451UgkWFkaWYhgt7dGmAlpD6hLjK5I=
-golang.org/x/telemetry v0.0.0-20241204182053-c0ac0e154df3/go.mod h1:8h4Hgq+jcTvCDv2+i7NrfWwpYHcESleo2nGHxLbFLJ4=
+golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
+golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/telemetry v0.0.0-20250206143958-557cf9c30e9f h1:C8gBEOYcNZK84ngc8O5MU4ouNcnlgqsKinp/gLXK0+A=
+golang.org/x/telemetry v0.0.0-20250206143958-557cf9c30e9f/go.mod h1:Ng+6E7PnWNge4EifZkPKeQUnm5iyAoH8qQgw3pLCiF4=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
-golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
+golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
+golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-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/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
+golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
+golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
+golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
-golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
-golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
-golang.org/x/vuln v1.1.3 h1:NPGnvPOTgnjBc9HTaUx+nj+EaUYxl5SJOWqaDYGaFYw=
-golang.org/x/vuln v1.1.3/go.mod h1:7Le6Fadm5FOqE9C926BCD0g12NWyhg7cxV4BwcPFuNY=
+golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
+golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
+golang.org/x/vuln v1.1.4 h1:Ju8QsuyhX3Hk8ma3CesTbO8vfJD9EvUBgHvkxHBzj0I=
+golang.org/x/vuln v1.1.4/go.mod h1:F+45wmU18ym/ca5PLTPLsSzr2KppzswxPP603ldA67s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/api v0.210.0 h1:HMNffZ57OoZCRYSbdWVRoqOa8V8NIHLL0CzdBPLztWk=
-google.golang.org/api v0.210.0/go.mod h1:B9XDZGnx2NtyjzVkOVTGrFSAVZgPcbedzKg/gTLwqBs=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a h1:OAiGFfOiA0v9MRYsSidp3ubZaBnteRUyn3xB2ZQ5G/E=
-google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a/go.mod h1:jehYqy3+AhJU9ve55aNOaSml7wUXjF9x6z2LcCfpAhY=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a h1:hgh8P4EuoxpsuKMXX/To36nOFD7vixReXgn8lPGnt+o=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
-google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
-google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0=
-google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw=
+google.golang.org/api v0.220.0 h1:3oMI4gdBgB72WFVwE1nerDD8W3HUOS4kypK6rRLbGns=
+google.golang.org/api v0.220.0/go.mod h1:26ZAlY6aN/8WgpCzjPNy18QpYaz7Zgg1h0qe1GkZEmY=
+google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489 h1:fCuMM4fowGzigT89NCIsW57Pk9k2D12MMi2ODn+Nk+o=
+google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489 h1:5bKytslY8ViY0Cj/ewmRtrWHW64bNF03cAatUUFCdFI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk=
+google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
+google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA=
-google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
-google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
-google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
-google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
-google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
-google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
-google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
+google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.5.1 h1:4bH5o3b5ZULQ4UrBmP+63W9r7qIkqJClEA9ko5YKx+I=
honnef.co/go/tools v0.5.1/go.mod h1:e9irvo83WDG9/irijV44wr3tbhcFeRnfpVlRqVwpzMs=
mvdan.cc/editorconfig v0.3.0 h1:D1D2wLYEYGpawWT5SpM5pRivgEgXjtEXwC9MWhEY0gQ=
@@ -251,5 +176,5 @@ mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU=
mvdan.cc/gofumpt v0.7.0/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo=
mvdan.cc/sh/v3 v3.10.0 h1:v9z7N1DLZ7owyLM/SXZQkBSXcwr2IGMm2LY2pmhVXj4=
mvdan.cc/sh/v3 v3.10.0/go.mod h1:z/mSSVyLFGZzqb3ZIKojjyqIx/xbmz/UHdCSv9HmqXY=
-mvdan.cc/unparam v0.0.0-20240917084806-57a3b4290ba3 h1:YkmTN1n5U60NM02j7TCSWRlW3fqNiuXe/eVXf0dLFN8=
-mvdan.cc/unparam v0.0.0-20240917084806-57a3b4290ba3/go.mod h1:z5yboO1sP1Q9pcfvS597TpfbNXQjphDlkCJHzt13ybc=
+mvdan.cc/unparam v0.0.0-20241226123437-447d509598f3 h1:OPdLMIX29kquQXSiXmnwzHP1bc+JlH0S2l8SfVK9yWE=
+mvdan.cc/unparam v0.0.0-20241226123437-447d509598f3/go.mod h1:VQc4l9ccF55E7EwPxcGqwierxEf0KG8MRR8hJ9tpngw=
diff --git a/internal/websvc/blockpage.go b/internal/websvc/blockpage.go
index 87859e1..2694b81 100644
--- a/internal/websvc/blockpage.go
+++ b/internal/websvc/blockpage.go
@@ -5,6 +5,7 @@ import (
"compress/gzip"
"context"
"fmt"
+ "log/slog"
"net/http"
"os"
"strings"
@@ -12,35 +13,20 @@ import (
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
+ "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/httputil"
+ "github.com/AdguardTeam/golibs/service"
"github.com/prometheus/client_golang/prometheus"
)
-// blockPageName is a type alias for strings that contain a block-page name for
-// logging and metrics.
-type blockPageName = string
-
-// blockPageName values.
-const (
- adultBlockingName blockPageName = "adult blocking"
- generalBlockingName blockPageName = "general blocking"
- safeBrowsingName blockPageName = "safe browsing"
-)
-
-// BlockPageServerConfig is the blocking page server configuration.
-type BlockPageServerConfig struct {
- // ContentFilePath is the path to HTML block page content file.
- ContentFilePath string
-
- // Bind are the addresses on which to serve the block page.
- Bind []*BindData
-}
-
// blockPageServer serves the blocking page contents.
type blockPageServer struct {
+ // logger is used to log the refreshes.
+ logger *slog.Logger
+
// mu protects content and gzipContent.
mu *sync.RWMutex
@@ -53,45 +39,54 @@ type blockPageServer struct {
// contentFilePath is the path to HTML block page content file.
contentFilePath string
- // name is the server identification used for logging and metrics.
- name blockPageName
+ // group is the server group used for logging and metrics.
+ group serverGroup
// bind are the addresses on which to serve the block page.
bind []*BindData
}
-// newBlockPageServer initializes a new instance of blockPageServer. The server
-// must be refreshed with [blockPageServer.Refresh] before use.
-func newBlockPageServer(conf *BlockPageServerConfig, srvName blockPageName) (srv *blockPageServer) {
+// newBlockPageServer initializes a new instance of blockPageServer. If conf is
+// nil, srv is nil. The server must be refreshed with [blockPageServer.Refresh]
+// before use.
+func newBlockPageServer(
+ conf *BlockPageServerConfig,
+ baseLogger *slog.Logger,
+ g serverGroup,
+) (srv *blockPageServer) {
if conf == nil {
return nil
}
return &blockPageServer{
+ logger: baseLogger.With(loggerKeyGroup, g),
mu: &sync.RWMutex{},
contentFilePath: conf.ContentFilePath,
- name: srvName,
+ group: g,
bind: conf.Bind,
}
}
// type check
-var _ agdservice.Refresher = (*blockPageServer)(nil)
+var _ service.Refresher = (*blockPageServer)(nil)
-// Refresh implements the [agdservice.Refresher] interface for *blockPageServer.
+// Refresh implements the [service.Refresher] interface for *blockPageServer.
// srv may be nil.
-func (srv *blockPageServer) Refresh(_ context.Context) (err error) {
+func (srv *blockPageServer) Refresh(ctx context.Context) (err error) {
if srv == nil {
return nil
}
+ srv.logger.InfoContext(ctx, "refresh started")
+ defer srv.logger.InfoContext(ctx, "refresh finished")
+
// TODO(d.kolyshev): Compare with current srv content before updating.
content, err := os.ReadFile(srv.contentFilePath)
if err != nil {
- return fmt.Errorf("block page server %q: reading block page file: %w", srv.name, err)
+ return fmt.Errorf("block page server %q: reading block page file: %w", srv.group, err)
}
- gzipContent := mustGzip(srv.name, content)
+ gzipContent := mustGzip(srv.group, content)
srv.mu.Lock()
defer srv.mu.Unlock()
@@ -102,40 +97,43 @@ func (srv *blockPageServer) Refresh(_ context.Context) (err error) {
return nil
}
-// blockPageServers is a helper function that converts a *blockPageServer into
-// HTTP servers.
-func blockPageServers(srv *blockPageServer, timeout time.Duration) (srvs []*http.Server) {
+// newBlockPageServers is a helper function that converts a *blockPageServer
+// into HTTP servers.
+func newBlockPageServers(
+ baseLogger *slog.Logger,
+ srv *blockPageServer,
+ timeout time.Duration,
+) (srvs []*server) {
if srv == nil {
return nil
}
- h := srv.blockPageHandler()
+ srvHdrMw := httputil.ServerHeaderMiddleware(agdhttp.UserAgent())
+ handler := srv.blockPageHandler()
+
for _, b := range srv.bind {
- addr := b.Address.String()
- errLog := log.StdLog(fmt.Sprintf("websvc: %s: %s", srv.name, addr), log.DEBUG)
- srvs = append(srvs, &http.Server{
- Addr: addr,
- Handler: h,
- TLSConfig: b.TLS,
- ErrorLog: errLog,
- ReadTimeout: timeout,
- WriteTimeout: timeout,
- IdleTimeout: timeout,
- ReadHeaderTimeout: timeout,
- })
+ logger := baseLogger.With(loggerKeyGroup, srv.group)
+ h := httputil.Wrap(
+ handler,
+ srvHdrMw,
+ httputil.NewLogMiddleware(logger, slog.LevelDebug),
+ )
+
+ srvs = append(srvs, newServer(&serverConfig{
+ BaseLogger: logger,
+ TLSConf: b.TLS,
+ Handler: h,
+ InitialAddress: b.Address,
+ Timeout: timeout,
+ }))
}
return srvs
}
// blockPageHandler returns an HTTP handler serving the block page content.
-// name is used for logging and metrics and must be one of blockPageName values.
func (srv *blockPageServer) blockPageHandler() (h http.Handler) {
f := func(w http.ResponseWriter, r *http.Request) {
- // Set the Server header here, so that all responses carry it.
- respHdr := w.Header()
- respHdr.Set(httphdr.Server, agdhttp.UserAgent())
-
switch r.URL.Path {
case "/favicon.ico":
// Don't serve the HTML page to the favicon requests.
@@ -143,12 +141,12 @@ func (srv *blockPageServer) blockPageHandler() (h http.Handler) {
case "/robots.txt":
// Don't serve the HTML page to the robots.txt requests. Serve the
// predefined response instead.
- serveRobotsDisallow(respHdr, w, srv.name)
+ serveRobotsDisallow(r.Context(), w.Header(), w)
default:
srv.mu.RLock()
defer srv.mu.RUnlock()
- serveBlockPage(w, r, srv.name, srv.content, srv.gzipContent)
+ serveBlockPage(w, r, srv.group, srv.content, srv.gzipContent)
}
}
@@ -182,17 +180,11 @@ func mustGzip(name string, b []byte) (compressed []byte) {
// serveBlockPage serves the block-page content taking compression headers into
// account.
-func serveBlockPage(
- w http.ResponseWriter,
- r *http.Request,
- name string,
- blockPage []byte,
- gzipped []byte,
-) {
+func serveBlockPage(w http.ResponseWriter, r *http.Request, g serverGroup, orig, gzipped []byte) {
respHdr := w.Header()
respHdr.Set(httphdr.ContentType, agdhttp.HdrValTextHTML)
- content := blockPage
+ content := orig
// TODO(a.garipov): Parse the quality value.
//
@@ -203,31 +195,33 @@ func serveBlockPage(
content = gzipped
}
- // Use HTTP 500 status code to signal that this is a block page.
- // See AGDNS-1952.
+ // Use HTTP 500 status code to signal that this is a block page. See
+ // AGDNS-1952.
w.WriteHeader(http.StatusInternalServerError)
_, err := w.Write(content)
if err != nil {
- logErrorByType(err, "websvc: %s: writing response: %s", name, err)
+ ctx := r.Context()
+ l := slogutil.MustLoggerFromContext(ctx)
+ l.Log(ctx, levelForError(err), "writing block page", slogutil.KeyError, err)
}
- incBlockPageMetrics(name)
+ incBlockPageMetrics(g)
}
-// incBlockPageMetrics increments the metrics for the block-page view
-// counts depending on the name of the block page.
-func incBlockPageMetrics(name blockPageName) {
+// incBlockPageMetrics increments the metrics for the block-page view counts
+// depending on the server group.
+func incBlockPageMetrics(g serverGroup) {
var totalCtr prometheus.Counter
- switch name {
- case adultBlockingName:
+ switch g {
+ case srvGrpAdultBlockingPage:
totalCtr = metrics.WebSvcAdultBlockingPageRequestsTotal
- case generalBlockingName:
+ case srvGrpGeneralBlockingPage:
totalCtr = metrics.WebSvcGeneralBlockingPageRequestsTotal
- case safeBrowsingName:
+ case srvGrpSafeBrowsingPage:
totalCtr = metrics.WebSvcSafeBrowsingPageRequestsTotal
default:
- panic(fmt.Errorf("metrics: bad websvc block-page metric name %q", name))
+ panic(fmt.Errorf("metrics: block-page server group: %w: %q", errors.ErrBadEnumValue, g))
}
totalCtr.Inc()
diff --git a/internal/websvc/blockpage_test.go b/internal/websvc/blockpage_test.go
index fa14bc2..97da744 100644
--- a/internal/websvc/blockpage_test.go
+++ b/internal/websvc/blockpage_test.go
@@ -10,6 +10,7 @@ import (
"testing"
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/AdGuardDNS/internal/websvc"
"github.com/AdguardTeam/golibs/httphdr"
"github.com/AdguardTeam/golibs/netutil/urlutil"
@@ -36,7 +37,7 @@ func TestBlockPageServers(t *testing.T) {
robotsStatus = http.StatusOK
)
- // TODO(a.garipov): Do not use hardcoded ports.
+ // TODO(a.garipov): Do not use hardcoded ports.
testCases := []struct {
updateConfig func(c *websvc.Config, bps *websvc.BlockPageServerConfig)
addr netip.AddrPort
@@ -74,7 +75,11 @@ func TestBlockPageServers(t *testing.T) {
}
conf := &websvc.Config{
- Timeout: testTimeout,
+ Logger: testLogger,
+ StaticContent: http.NotFoundHandler(),
+ DNSCheck: http.NotFoundHandler(),
+ ErrColl: agdtest.NewErrorCollector(),
+ Timeout: testTimeout,
}
tc.updateConfig(conf, bps)
@@ -89,7 +94,11 @@ func TestBlockPageServers(t *testing.T) {
func TestBlockPageServers_noBlockPages(t *testing.T) {
conf := &websvc.Config{
- Timeout: testTimeout,
+ Logger: testLogger,
+ StaticContent: http.NotFoundHandler(),
+ DNSCheck: http.NotFoundHandler(),
+ ErrColl: agdtest.NewErrorCollector(),
+ Timeout: testTimeout,
}
svc := websvc.New(conf)
@@ -102,7 +111,7 @@ func TestBlockPageServers_noBlockPages(t *testing.T) {
}
func TestBlockPageServers_gzip(t *testing.T) {
- // TODO(a.garipov): Do not use hardcoded ports.
+ // TODO(a.garipov): Do not use hardcoded ports.
addr := netip.MustParseAddrPort("127.0.0.1:3001")
bps := &websvc.BlockPageServerConfig{
ContentFilePath: filepath.Join("testdata", blockPageFileName),
@@ -113,7 +122,12 @@ func TestBlockPageServers_gzip(t *testing.T) {
}
conf := &websvc.Config{
+ Logger: testLogger,
GeneralBlocking: bps,
+ StaticContent: http.NotFoundHandler(),
+ DNSCheck: http.NotFoundHandler(),
+ ErrColl: agdtest.NewErrorCollector(),
+ Timeout: testTimeout,
}
startService(t, conf)
diff --git a/internal/websvc/config.go b/internal/websvc/config.go
new file mode 100644
index 0000000..72d8346
--- /dev/null
+++ b/internal/websvc/config.go
@@ -0,0 +1,93 @@
+package websvc
+
+import (
+ "crypto/tls"
+ "log/slog"
+ "net/http"
+ "net/netip"
+ "net/url"
+ "time"
+
+ "github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
+)
+
+// Config is the AdGuard DNS web service configuration structure.
+type Config struct {
+ // Logger is used for logging the operation of the web service. It must not
+ // be nil.
+ Logger *slog.Logger
+
+ // AdultBlocking is the optional adult-blocking block-page web server.
+ AdultBlocking *BlockPageServerConfig
+
+ // GeneralBlocking is the optional general block-page web server.
+ GeneralBlocking *BlockPageServerConfig
+
+ // SafeBrowsing is the optional safe-browsing block-page web server.
+ SafeBrowsing *BlockPageServerConfig
+
+ // LinkedIP is the optional linked IP web server.
+ LinkedIP *LinkedIPServer
+
+ // RootRedirectURL is the optional URL to which root HTTP requests are
+ // redirected. If not set, these requests are responded with a 404 page.
+ RootRedirectURL *url.URL
+
+ // StaticContent is the content that is served statically at the given
+ // paths. It must not be nil; use [http.NotFoundHandler] if not needed.
+ StaticContent http.Handler
+
+ // DNSCheck is the HTTP handler for DNS checks. It must not be nil.
+ DNSCheck http.Handler
+
+ // ErrColl is used to collect linked IP proxy errors and other errors. It
+ // must not be nil.
+ ErrColl errcoll.Interface
+
+ // Error404 is the optional content of the HTML page for the 404 status. If
+ // not set, a simple plain-text 404 response is served.
+ Error404 []byte
+
+ // Error500 is the optional content of the HTML page for the 500 status. If
+ // not set, a simple plain-text 500 response is served.
+ Error500 []byte
+
+ // NonDoHBind are the bind addresses and optional TLS configuration for the
+ // web service in addition to the ones in the DNS-over-HTTPS handlers. All
+ // items must not be nil.
+ NonDoHBind []*BindData
+
+ // Timeout is the timeout for all server operations. It must be positive.
+ Timeout time.Duration
+}
+
+// BlockPageServerConfig is the blocking page server configuration.
+type BlockPageServerConfig struct {
+ // ContentFilePath is the path to HTML block page content file. It must not
+ // be empty.
+ ContentFilePath string
+
+ // Bind are the addresses on which to serve the block page. At least one
+ // must be provided. All items must not be nil.
+ Bind []*BindData
+}
+
+// BindData is data for binding one HTTP server to an address.
+type BindData struct {
+ // TLS is the optional TLS configuration.
+ TLS *tls.Config
+
+ // Address is the binding address. It must not be empty.
+ Address netip.AddrPort
+}
+
+// LinkedIPServer is the linked IP server configuration.
+type LinkedIPServer struct {
+ // TargetURL is the URL to which linked IP API requests are proxied. It
+ // must not be nil.
+ TargetURL *url.URL
+
+ // Bind are the addresses on which to serve the linked IP API. At least one
+ // must be provided. All items must not be nil.
+ Bind []*BindData
+}
diff --git a/internal/websvc/handler.go b/internal/websvc/handler.go
index 8a13ac3..daee5be 100644
--- a/internal/websvc/handler.go
+++ b/internal/websvc/handler.go
@@ -1,34 +1,26 @@
package websvc
import (
+ "context"
"io"
+ "log/slog"
"net/http"
"net/http/httptest"
"os"
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
- "github.com/AdguardTeam/AdGuardDNS/internal/optlog"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/httphdr"
- "github.com/AdguardTeam/golibs/log"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"golang.org/x/sys/unix"
)
-// HTTP Handlers
-
// type check
var _ http.Handler = (*Service)(nil)
// ServeHTTP implements the http.Handler interface for *Service.
func (svc *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- respHdr := w.Header()
- respHdr.Set(httphdr.Server, agdhttp.UserAgent())
-
- m, p, rAddr := r.Method, r.URL.Path, r.RemoteAddr
- optlog.Debug3("websvc: starting req %s %s from %s", m, p, rAddr)
- defer optlog.Debug3("websvc: finished req %s %s from %s", m, p, rAddr)
-
if svc == nil {
http.NotFound(w, r)
@@ -37,26 +29,39 @@ func (svc *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// TODO(a.garipov): Refactor the 404 and 500 handling and use
// [httputil.CodeRecorderResponseWriter] instead.
+ ctx := r.Context()
rec := httptest.NewRecorder()
- svc.serveHTTP(rec, r)
+ svc.serveHTTP(ctx, rec, r)
- action, body := svc.processRec(respHdr, rec)
+ action, body := svc.processRec(w.Header(), rec)
w.WriteHeader(rec.Code)
_, err := w.Write(body)
if err != nil {
- logErrorByType(err, "websvc: handler: %s: %s", action, err)
+ logWriteError(ctx, action, err)
+ }
+}
+
+// logWriteError logs err at the appropriate level if ctx contains a logger.
+//
+// TODO(a.garipov): This is not a proper solution; remove once dnsserver
+// starts adding a logger of its own.
+func logWriteError(ctx context.Context, action string, err error) {
+ l, ok := slogutil.LoggerFromContext(ctx)
+ if ok {
+ l.Log(ctx, levelForError(err), "writing response", "action", action, slogutil.KeyError, err)
}
}
// serveHTTP processes the HTTP request.
-func (svc *Service) serveHTTP(rec *httptest.ResponseRecorder, r *http.Request) {
+func (svc *Service) serveHTTP(ctx context.Context, rec *httptest.ResponseRecorder, r *http.Request) {
+ // TODO(a.garipov): Use mux routes.
switch r.URL.Path {
case "/dnscheck/test":
svc.dnsCheck.ServeHTTP(rec, r)
metrics.WebSvcDNSCheckTestRequestsTotal.Inc()
case "/robots.txt":
- serveRobotsDisallow(rec.Header(), rec, "handler")
+ serveRobotsDisallow(ctx, rec.Header(), rec)
case "/":
if svc.rootRedirectURL == "" {
http.NotFound(rec, r)
@@ -87,7 +92,7 @@ func (svc *Service) processRec(
) (action string, body []byte) {
switch rec.Code {
case http.StatusNotFound:
- action = "writing 404"
+ action = "404 page"
if len(svc.error404) != 0 {
body = svc.error404
respHdr.Set(httphdr.ContentType, agdhttp.HdrValTextHTML)
@@ -95,7 +100,7 @@ func (svc *Service) processRec(
metrics.WebSvcError404RequestsTotal.Inc()
case http.StatusInternalServerError:
- action = "writing 500"
+ action = "500 page"
if len(svc.error500) != 0 {
body = svc.error500
respHdr.Set(httphdr.ContentType, agdhttp.HdrValTextHTML)
@@ -103,7 +108,7 @@ func (svc *Service) processRec(
metrics.WebSvcError500RequestsTotal.Inc()
default:
- action = "writing response"
+ action = "response"
for k, v := range rec.Header() {
respHdr[k] = v
}
@@ -117,25 +122,25 @@ func (svc *Service) processRec(
}
// serveRobotsDisallow writes predefined disallow-all response.
-func serveRobotsDisallow(hdr http.Header, w http.ResponseWriter, name string) {
+func serveRobotsDisallow(ctx context.Context, hdr http.Header, w http.ResponseWriter) {
hdr.Set(httphdr.ContentType, agdhttp.HdrValTextPlain)
_, err := io.WriteString(w, agdhttp.RobotsDisallowAll)
if err != nil {
- logErrorByType(err, "websvc: %s: writing response: %s", name, err)
+ logWriteError(ctx, "robots.txt", err)
}
metrics.WebSvcRobotsTxtRequestsTotal.Inc()
}
-// logErrorByType writes err to the error log, unless err is a network error or
-// a timeout error, in which case it is written to the debug log.
-func logErrorByType(err error, format string, args ...any) {
- // TODO(d.kolyshev): Consider adding more error types.
- if errors.Is(err, os.ErrDeadlineExceeded) || errors.Is(err, unix.EPIPE) ||
+// levelForError returns a logging level depending on whether err is a network
+// or a timeout error.
+func levelForError(err error) (lvl slog.Level) {
+ if errors.Is(err, os.ErrDeadlineExceeded) ||
+ errors.Is(err, unix.EPIPE) ||
errors.Is(err, unix.ECONNRESET) {
- log.Debug(format, args...)
- } else {
- log.Error(format, args...)
+ return slog.LevelDebug
}
+
+ return slog.LevelError
}
diff --git a/internal/websvc/handler_test.go b/internal/websvc/handler_test.go
index 881dee4..f8a0012 100644
--- a/internal/websvc/handler_test.go
+++ b/internal/websvc/handler_test.go
@@ -8,9 +8,8 @@ import (
"strings"
"testing"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/AdGuardDNS/internal/websvc"
- "github.com/AdguardTeam/golibs/httphdr"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
@@ -32,9 +31,12 @@ func TestService_ServeHTTP(t *testing.T) {
}
c := &websvc.Config{
+ Logger: testLogger,
RootRedirectURL: rootRedirectURL,
StaticContent: http.NotFoundHandler(),
DNSCheck: mockHandler,
+ ErrColl: agdtest.NewErrorCollector(),
+ Timeout: testTimeout,
}
svc := websvc.New(c)
@@ -83,23 +85,6 @@ func assertResponse(
svc.ServeHTTP(rw, r)
assert.Equal(t, statusCode, rw.Code)
- assert.Equal(t, agdhttp.UserAgent(), rw.Header().Get(httphdr.Server))
return rw
}
-
-// assertResponseWithHeaders is a helper function that checks status code and
-// headers of HTTP response.
-func assertResponseWithHeaders(
- t *testing.T,
- svc *websvc.Service,
- path string,
- statusCode int,
- respHdr http.Header,
-) {
- t.Helper()
-
- rw := assertResponse(t, svc, path, statusCode)
-
- assert.Equal(t, respHdr, rw.Header())
-}
diff --git a/internal/websvc/linkip.go b/internal/websvc/linkip.go
index 12b284f..a506d46 100644
--- a/internal/websvc/linkip.go
+++ b/internal/websvc/linkip.go
@@ -2,6 +2,7 @@ package websvc
import (
"fmt"
+ "log/slog"
"net"
"net/http"
"net/http/httputil"
@@ -13,30 +14,25 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
- "github.com/AdguardTeam/AdGuardDNS/internal/optlog"
"github.com/AdguardTeam/golibs/httphdr"
- "github.com/AdguardTeam/golibs/log"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
)
-// Linked IP Proxy
-
// linkedIPProxy proxies selected requests to a remote address.
type linkedIPProxy struct {
httpProxy *httputil.ReverseProxy
errColl errcoll.Interface
- logPrefix string
}
-// linkedIPHandler returns a linked IP proxy handler.
-func linkedIPHandler(
+// newLinkedIPHandler returns a linked IP proxy handler. All arguments must be
+// set.
+func newLinkedIPHandler(
apiURL *url.URL,
errColl errcoll.Interface,
- name string,
+ proxyLogger *slog.Logger,
timeout time.Duration,
) (h http.Handler) {
- logPrefix := fmt.Sprintf("websvc: linked ip proxy %s", name)
-
// Use a Rewrite func to make sure we send the correct Host header and don't
// send anything besides the path.
rewrite := func(r *httputil.ProxyRequest) {
@@ -45,6 +41,10 @@ func linkedIPHandler(
// Make sure that all requests are marked with our user agent.
r.Out.Header.Set(httphdr.UserAgent, agdhttp.UserAgent())
+
+ // Set the X-Forwarded-* headers for the backend to inspect cert
+ // validation requests.
+ r.SetXForwarded()
}
// Use largely the same transport as http.DefaultTransport, but with a
@@ -81,28 +81,20 @@ func linkedIPHandler(
handlerWithError := func(_ http.ResponseWriter, r *http.Request, err error) {
ctx := r.Context()
reqID, _ := agd.RequestIDFromContext(ctx)
- errcoll.Collectf(
- ctx,
- errColl,
- "%s: proxying %s %s: req %s: %w",
- logPrefix,
- r.Method,
- r.URL.Path,
- reqID,
- err,
- )
+
+ l := slogutil.MustLoggerFromContext(ctx).With("req_id", reqID)
+ errcoll.Collect(ctx, errColl, l, "proxying", err)
}
return &linkedIPProxy{
httpProxy: &httputil.ReverseProxy{
Rewrite: rewrite,
Transport: transport,
- ErrorLog: log.StdLog(logPrefix, log.DEBUG),
+ ErrorLog: slog.NewLogLogger(proxyLogger.Handler(), slog.LevelDebug),
ModifyResponse: modifyResponse,
ErrorHandler: handlerWithError,
},
- errColl: errColl,
- logPrefix: logPrefix,
+ errColl: errColl,
}
}
@@ -115,9 +107,9 @@ func (prx *linkedIPProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
respHdr := w.Header()
respHdr.Set(httphdr.Server, agdhttp.UserAgent())
+ ctx := r.Context()
+ l := slogutil.MustLoggerFromContext(ctx)
m, p, rAddr := r.Method, r.URL.Path, r.RemoteAddr
- optlog.Debug3("websvc: starting req %s %s from %s", m, p, rAddr)
- defer optlog.Debug3("websvc: finished req %s %s from %s", m, p, rAddr)
if shouldProxy(m, p) {
// TODO(a.garipov): Consider moving some or all this request
@@ -134,8 +126,8 @@ func (prx *linkedIPProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Set the real IP.
ip, err := netutil.SplitHost(rAddr)
if err != nil {
- ctx := r.Context()
- prx.errColl.Collect(ctx, fmt.Errorf("%s: getting ip: %w", prx.logPrefix, err))
+ err = fmt.Errorf("websvc: linked ip proxy: getting ip: %w", err)
+ prx.errColl.Collect(ctx, err)
// Send a 500 error, despite the fact that this is probably a client
// error, because this is the code that the frontend expects.
@@ -151,13 +143,13 @@ func (prx *linkedIPProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
r = r.WithContext(agd.WithRequestID(r.Context(), reqID))
hdr.Set(httphdr.XRequestID, reqID.String())
- log.Debug("%s: proxying %s %s: req %s", prx.logPrefix, m, p, reqID)
+ l.DebugContext(ctx, "starting to proxy", "req_id", reqID)
prx.httpProxy.ServeHTTP(w, r)
metrics.WebSvcLinkedIPProxyRequestsTotal.Inc()
} else if r.URL.Path == "/robots.txt" {
- serveRobotsDisallow(respHdr, w, prx.logPrefix)
+ serveRobotsDisallow(ctx, respHdr, w)
} else {
http.NotFound(w, r)
}
@@ -170,6 +162,8 @@ func (prx *linkedIPProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// - GET /linkip/{device_id}/{encrypted}/status
// - POST /ddns/{device_id}/{encrypted}/{domain}
// - POST /linkip/{device_id}/{encrypted}
+//
+// TODO(a.garipov): Use mux routes.
func shouldProxy(method, urlPath string) (ok bool) {
parts := strings.SplitN(strings.TrimPrefix(urlPath, "/"), "/", 5)
if l := len(parts); l < 3 || l > 4 {
diff --git a/internal/websvc/linkip_internal_test.go b/internal/websvc/linkip_internal_test.go
index 46aad12..e091243 100644
--- a/internal/websvc/linkip_internal_test.go
+++ b/internal/websvc/linkip_internal_test.go
@@ -7,11 +7,11 @@ import (
"strings"
"sync/atomic"
"testing"
- "time"
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/golibs/httphdr"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
@@ -19,6 +19,14 @@ import (
)
func TestLinkedIPProxy_ServeHTTP(t *testing.T) {
+ const (
+ badRemoteIP = "192.0.2.2"
+
+ realRemoteIP = "192.0.2.1"
+ realRemoteAddr = realRemoteIP + ":12345"
+ realHost = "link-ip.example"
+ )
+
var (
apiURL *url.URL
numReq atomic.Uint64
@@ -30,19 +38,19 @@ func TestLinkedIPProxy_ServeHTTP(t *testing.T) {
hdr := r.Header
require.Equal(pt, agdhttp.UserAgent(), hdr.Get(httphdr.UserAgent))
- require.NotEmpty(pt, hdr.Get(httphdr.XConnectingIP))
require.NotEmpty(pt, hdr.Get(httphdr.XRequestID))
+ require.Equal(pt, apiURL.Host, r.Host)
+ require.Equal(pt, realRemoteIP, hdr.Get(httphdr.XForwardedFor))
+ require.Equal(pt, realRemoteIP, hdr.Get(httphdr.XConnectingIP))
+ require.Equal(pt, realHost, hdr.Get(httphdr.XForwardedHost))
+ require.Equal(pt, urlutil.SchemeHTTP, hdr.Get(httphdr.XForwardedProto))
+
require.Empty(pt, hdr.Get(httphdr.CFConnectingIP))
require.Empty(pt, hdr.Get(httphdr.Forwarded))
require.Empty(pt, hdr.Get(httphdr.TrueClientIP))
- require.Empty(pt, hdr.Get(httphdr.XForwardedFor))
- require.Empty(pt, hdr.Get(httphdr.XForwardedHost))
- require.Empty(pt, hdr.Get(httphdr.XForwardedProto))
require.Empty(pt, hdr.Get(httphdr.XRealIP))
- require.Equal(pt, apiURL.Host, r.Host)
-
numReq.Add(1)
})
@@ -52,7 +60,7 @@ func TestLinkedIPProxy_ServeHTTP(t *testing.T) {
apiURL, err := url.Parse(srv.URL)
require.NoError(t, err)
- h := linkedIPHandler(apiURL, agdtest.NewErrorCollector(), "test", 2*time.Second)
+ h := newLinkedIPHandler(apiURL, agdtest.NewErrorCollector(), testLogger, testTimeout)
testCases := []struct {
name string
@@ -107,20 +115,26 @@ func TestLinkedIPProxy_ServeHTTP(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- r := httptest.NewRequest(tc.method, (&url.URL{
+ ctx := testutil.ContextWithTimeout(t, testTimeout)
+ ctx = slogutil.ContextWithLogger(ctx, testLogger)
+
+ r := httptest.NewRequestWithContext(ctx, tc.method, (&url.URL{
Scheme: urlutil.SchemeHTTP,
- Host: "link-ip.example",
+ Host: realHost,
Path: tc.path,
}).String(), strings.NewReader(""))
- // Set some test headers.
- r.Header.Set(httphdr.CFConnectingIP, "1.1.1.1")
- r.Header.Set(httphdr.Forwarded, "1.1.1.1")
- r.Header.Set(httphdr.TrueClientIP, "1.1.1.1")
- r.Header.Set(httphdr.XForwardedFor, "1.1.1.1")
- r.Header.Set(httphdr.XForwardedHost, "forward.example")
- r.Header.Set(httphdr.XForwardedProto, "https")
- r.Header.Set(httphdr.XRealIP, "1.1.1.1")
+ // Set the IP address that should be proxied.
+ r.RemoteAddr = realRemoteAddr
+
+ // Set some test headers to make sure they're not proxied.
+ r.Header.Set(httphdr.CFConnectingIP, badRemoteIP)
+ r.Header.Set(httphdr.Forwarded, badRemoteIP)
+ r.Header.Set(httphdr.TrueClientIP, badRemoteIP)
+ r.Header.Set(httphdr.XForwardedFor, badRemoteIP)
+ r.Header.Set(httphdr.XForwardedHost, "bad.example")
+ r.Header.Set(httphdr.XForwardedProto, "foo")
+ r.Header.Set(httphdr.XRealIP, badRemoteIP)
rw := httptest.NewRecorder()
diff --git a/internal/websvc/server.go b/internal/websvc/server.go
new file mode 100644
index 0000000..a9c43c3
--- /dev/null
+++ b/internal/websvc/server.go
@@ -0,0 +1,184 @@
+package websvc
+
+import (
+ "context"
+ "crypto/tls"
+ "fmt"
+ "log/slog"
+ "net"
+ "net/http"
+ "net/netip"
+ "net/url"
+ "sync"
+ "time"
+
+ "github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
+ "github.com/AdguardTeam/golibs/netutil/urlutil"
+)
+
+// server contains an *http.Server as well as entities and data associated with
+// it.
+//
+// TODO(a.garipov): Join with similar structs in other projects and move to
+// golibs/netutil/httputil.
+//
+// TODO(a.garipov): Once the above standardization is complete, consider
+// merging debugsvc and websvc into a single httpsvc.
+type server struct {
+ // mu protects http, logger, listener, and url.
+ mu *sync.Mutex
+ http *http.Server
+ logger *slog.Logger
+ listener net.Listener
+ url *url.URL
+
+ initialAddr netip.AddrPort
+}
+
+// loggerKeyServer is the key used by [server] to identify itself.
+const loggerKeyServer = "server"
+
+// serverConfig is the configuration of a server.
+type serverConfig struct {
+ // BaseLogger is used to create the initial logger for the server. It must
+ // not be nil.
+ BaseLogger *slog.Logger
+
+ // TLSConf is the optional TLS configuration.
+ TLSConf *tls.Config
+
+ // BaseContext is an optional function that that returns the base context
+ // for incoming requests on this server. See [http.Server.BaseContext].
+ BaseContext func(l net.Listener) (ctx context.Context)
+
+ // Handler is the HTTP handler for this server. It must not be nil.
+ Handler http.Handler
+
+ // InitialAddress is the initial address for the server. It may have a zero
+ // port, in which case the real port will be set in [server.serve]. It must
+ // be set.
+ InitialAddress netip.AddrPort
+
+ // Timeout is the optional timeout for all operations.
+ //
+ // TODO(a.garipov): Consider more fine-grained timeouts.
+ Timeout time.Duration
+}
+
+// newServer returns a *server that is ready to serve HTTP queries. The TCP
+// listener is not started. c must not be nil and must be valid.
+func newServer(c *serverConfig) (s *server) {
+ u := &url.URL{
+ Scheme: urlutil.SchemeHTTP,
+ Host: c.InitialAddress.String(),
+ }
+
+ if c.TLSConf != nil {
+ u.Scheme = urlutil.SchemeHTTPS
+ }
+
+ logger := c.BaseLogger.With(loggerKeyServer, u)
+
+ return &server{
+ mu: &sync.Mutex{},
+ http: &http.Server{
+ Handler: c.Handler,
+ TLSConfig: c.TLSConf,
+ ReadTimeout: c.Timeout,
+ ReadHeaderTimeout: c.Timeout,
+ WriteTimeout: c.Timeout,
+ IdleTimeout: c.Timeout,
+ ErrorLog: slog.NewLogLogger(logger.Handler(), slog.LevelDebug),
+ BaseContext: c.BaseContext,
+ },
+ logger: logger,
+ url: u,
+
+ initialAddr: c.InitialAddress,
+ }
+}
+
+// localAddr returns the local address of the server if the server has started
+// listening; otherwise, it returns nil.
+func (s *server) localAddr() (addr net.Addr) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ if l := s.listener; l != nil {
+ return l.Addr()
+ }
+
+ return nil
+}
+
+// serve starts s. baseLogger is used as a base logger for s. If s fails to
+// serve with anything other than [http.ErrServerClosed], it causes an unhandled
+// panic. It is intended to be used as a goroutine.
+//
+// TODO(a.garipov): Improve error handling.
+func (s *server) serve(ctx context.Context, baseLogger *slog.Logger) {
+ tcpListener, err := net.ListenTCP("tcp", net.TCPAddrFromAddrPort(s.initialAddr))
+ if err != nil {
+ s.logger.ErrorContext(ctx, "listening tcp", slogutil.KeyError, err)
+
+ panic(fmt.Errorf("websvc: listening tcp: %w", err))
+ }
+
+ var listener net.Listener
+ if s.http.TLSConfig == nil {
+ listener = tcpListener
+ } else {
+ listener = tls.NewListener(tcpListener, s.http.TLSConfig)
+ }
+
+ func() {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ s.listener = listener
+
+ // Reassign the address in case the port was zero.
+ s.url.Host = listener.Addr().String()
+ s.logger = baseLogger.With(loggerKeyServer, s.url)
+ s.http.ErrorLog = slog.NewLogLogger(s.logger.Handler(), slog.LevelDebug)
+ }()
+
+ s.logger.InfoContext(ctx, "starting")
+ defer s.logger.InfoContext(ctx, "started")
+
+ err = s.http.Serve(listener)
+ if err == nil || errors.Is(err, http.ErrServerClosed) {
+ return
+ }
+
+ s.logger.ErrorContext(ctx, "serving", slogutil.KeyError, err)
+
+ panic(fmt.Errorf("websvc: serving: %w", err))
+}
+
+// shutdown shuts s down.
+func (s *server) shutdown(ctx context.Context) (err error) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ var errs []error
+ err = s.http.Shutdown(ctx)
+ if err != nil {
+ errs = append(errs, fmt.Errorf("shutting down server %s: %w", s.url, err))
+ }
+
+ // Close the listener separately, as it might not have been closed if the
+ // context has been canceled.
+ //
+ // NOTE: The listener could remain uninitialized if [net.ListenTCP] failed
+ // in [s.serve].
+ if l := s.listener; l != nil {
+ err = l.Close()
+ if err != nil && !errors.Is(err, net.ErrClosed) {
+ errs = append(errs, fmt.Errorf("closing listener for server %s: %w", s.url, err))
+ }
+ }
+
+ return errors.Join(errs...)
+}
diff --git a/internal/websvc/static.go b/internal/websvc/static.go
index 8dc5bef..29d0cc8 100644
--- a/internal/websvc/static.go
+++ b/internal/websvc/static.go
@@ -3,6 +3,8 @@ package websvc
import (
"maps"
"net/http"
+
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
)
// StaticContent serves static content with the given content type. Elements
@@ -37,6 +39,8 @@ func (sc StaticContent) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, err := w.Write(f.Content)
if err != nil {
- logErrorByType(err, "websvc: static content: writing %s: %s", p, err)
+ ctx := r.Context()
+ l := slogutil.MustLoggerFromContext(ctx)
+ l.Log(ctx, levelForError(err), "writing static content", slogutil.KeyError, err)
}
}
diff --git a/internal/websvc/static_test.go b/internal/websvc/static_test.go
index 1a331c2..dc16849 100644
--- a/internal/websvc/static_test.go
+++ b/internal/websvc/static_test.go
@@ -4,9 +4,11 @@ import (
"net/http"
"testing"
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/AdGuardDNS/internal/websvc"
"github.com/AdguardTeam/golibs/httphdr"
"github.com/AdguardTeam/golibs/testutil"
+ "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -21,7 +23,11 @@ func TestService_ServeHTTP_static(t *testing.T) {
}
c := &websvc.Config{
+ Logger: testLogger,
StaticContent: staticContent,
+ DNSCheck: http.NotFoundHandler(),
+ ErrColl: agdtest.NewErrorCollector(),
+ Timeout: testTimeout,
}
svc := websvc.New(c)
@@ -39,7 +45,22 @@ func TestService_ServeHTTP_static(t *testing.T) {
respHdr := http.Header{
httphdr.ContentType: []string{"image/x-icon"},
- httphdr.Server: []string{"AdGuardDNS/"},
}
assertResponseWithHeaders(t, svc, "/favicon.ico", http.StatusOK, respHdr)
}
+
+// assertResponseWithHeaders is a helper function that checks status code and
+// headers of HTTP response.
+func assertResponseWithHeaders(
+ t *testing.T,
+ svc *websvc.Service,
+ path string,
+ statusCode int,
+ respHdr http.Header,
+) {
+ t.Helper()
+
+ rw := assertResponse(t, svc, path, statusCode)
+
+ assert.Equal(t, respHdr, rw.Header())
+}
diff --git a/internal/websvc/websvc.go b/internal/websvc/websvc.go
index 88dbc84..e9a6ec5 100644
--- a/internal/websvc/websvc.go
+++ b/internal/websvc/websvc.go
@@ -3,104 +3,39 @@ package websvc
import (
"context"
- "crypto/tls"
"fmt"
+ "log/slog"
"net/http"
- "net/netip"
- "net/url"
- "time"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
- "github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
- "github.com/AdguardTeam/golibs/log"
+ "github.com/AdguardTeam/golibs/netutil/httputil"
"github.com/AdguardTeam/golibs/service"
)
-// Config is the AdGuard DNS web service configuration structure.
-type Config struct {
- // AdultBlocking is the optional adult-blocking block-page web server.
- AdultBlocking *BlockPageServerConfig
-
- // GeneralBlocking is the optional general block-page web server.
- GeneralBlocking *BlockPageServerConfig
-
- // SafeBrowsing is the optional safe-browsing block-page web server.
- SafeBrowsing *BlockPageServerConfig
-
- // LinkedIP is the optional linked IP web server.
- LinkedIP *LinkedIPServer
-
- // RootRedirectURL is the URL to which root HTTP requests are redirected.
- // If not set, these requests are responded with a 404 page.
- RootRedirectURL *url.URL
-
- // StaticContent is the content that is served statically at the given
- // paths. It must not be nil; use [http.NotFoundHandler] if not needed.
- StaticContent http.Handler
-
- // DNSCheck is the HTTP handler for DNS checks.
- DNSCheck http.Handler
-
- // ErrColl is used to collect linked IP proxy errors and other errors.
- ErrColl errcoll.Interface
-
- // Error404 is the content of the HTML page for the 404 status. If not set,
- // a simple plain text 404 response is served.
- Error404 []byte
-
- // Error500 is the content of the HTML page for the 500 status. If not set,
- // a simple plain text 500 response is served.
- Error500 []byte
-
- // NonDoHBind are the bind addresses and optional TLS configuration for the
- // web service in addition to the ones in the DNS-over-HTTPS handlers.
- NonDoHBind []*BindData
-
- // Timeout is the timeout for all server operations.
- Timeout time.Duration
-}
-
-// LinkedIPServer is the linked IP server configuration.
-type LinkedIPServer struct {
- // TargetURL is the URL to which linked IP API requests are proxied.
- TargetURL *url.URL
-
- // Bind are the addresses on which to serve the linked IP API.
- Bind []*BindData
-}
-
-// BindData is data for binding one HTTP server to an address.
-type BindData struct {
- // TLS is the optional TLS configuration.
- TLS *tls.Config
-
- // Address is the binding address.
- Address netip.AddrPort
-}
-
// Service is the AdGuard DNS web service. A nil *Service serves a simple
// plain-text 404 page.
type Service struct {
- rootRedirectURL string
-
- staticContent http.Handler
-
- dnsCheck http.Handler
-
- error404 []byte
- error500 []byte
+ logger *slog.Logger
adultBlockingBPS *blockPageServer
generalBlockingBPS *blockPageServer
safeBrowsingBPS *blockPageServer
- linkedIP []*http.Server
- adultBlocking []*http.Server
- generalBlocking []*http.Server
- safeBrowsing []*http.Server
- nonDoH []*http.Server
+ dnsCheck http.Handler
+ staticContent http.Handler
+
+ rootRedirectURL string
+
+ error404 []byte
+ error500 []byte
+
+ adultBlocking []*server
+ generalBlocking []*server
+ linkedIP []*server
+ nonDoH []*server
+ safeBrowsing []*server
}
// New returns a new properly initialized *Service. If c is nil, svc is a nil
@@ -111,25 +46,26 @@ func New(c *Config) (svc *Service) {
return nil
}
- adultBlockingBPS := newBlockPageServer(c.AdultBlocking, adultBlockingName)
- generalBlockingBPS := newBlockPageServer(c.GeneralBlocking, generalBlockingName)
- safeBrowsingBPS := newBlockPageServer(c.SafeBrowsing, safeBrowsingName)
+ adultBlockingBPS := newBlockPageServer(c.AdultBlocking, c.Logger, srvGrpAdultBlockingPage)
+ generalBlockingBPS := newBlockPageServer(c.GeneralBlocking, c.Logger, srvGrpGeneralBlockingPage)
+ safeBrowsingBPS := newBlockPageServer(c.SafeBrowsing, c.Logger, srvGrpSafeBrowsingPage)
svc = &Service{
- staticContent: c.StaticContent,
-
- dnsCheck: c.DNSCheck,
-
- error404: c.Error404,
- error500: c.Error500,
+ logger: c.Logger,
adultBlockingBPS: adultBlockingBPS,
generalBlockingBPS: generalBlockingBPS,
safeBrowsingBPS: safeBrowsingBPS,
- adultBlocking: blockPageServers(adultBlockingBPS, c.Timeout),
- generalBlocking: blockPageServers(generalBlockingBPS, c.Timeout),
- safeBrowsing: blockPageServers(safeBrowsingBPS, c.Timeout),
+ dnsCheck: c.DNSCheck,
+ staticContent: c.StaticContent,
+
+ error404: c.Error404,
+ error500: c.Error500,
+
+ adultBlocking: newBlockPageServers(c.Logger, adultBlockingBPS, c.Timeout),
+ generalBlocking: newBlockPageServers(c.Logger, generalBlockingBPS, c.Timeout),
+ safeBrowsing: newBlockPageServers(c.Logger, safeBrowsingBPS, c.Timeout),
}
if c.RootRedirectURL != nil {
@@ -138,42 +74,60 @@ func New(c *Config) (svc *Service) {
svc.rootRedirectURL = c.RootRedirectURL.String()
}
- if l := c.LinkedIP; l != nil && l.TargetURL != nil {
+ if l := c.LinkedIP; l != nil {
+ logger := svc.logger.With(loggerKeyGroup, srvGrpLinkedIP)
for _, b := range l.Bind {
- addr := b.Address.String()
- h := linkedIPHandler(l.TargetURL, c.ErrColl, addr, c.Timeout)
- errLog := log.StdLog(fmt.Sprintf("websvc: linked ip: %s", addr), log.DEBUG)
- svc.linkedIP = append(svc.linkedIP, &http.Server{
- Addr: addr,
- Handler: h,
- TLSConfig: b.TLS,
- ErrorLog: errLog,
- ReadTimeout: c.Timeout,
- WriteTimeout: c.Timeout,
- IdleTimeout: c.Timeout,
- ReadHeaderTimeout: c.Timeout,
- })
+ proxyLogger := logger.With("proxy_addr", b.Address)
+ h := httputil.Wrap(
+ newLinkedIPHandler(l.TargetURL, c.ErrColl, proxyLogger, c.Timeout),
+ httputil.NewLogMiddleware(logger, slog.LevelDebug),
+ )
+
+ svc.linkedIP = append(svc.linkedIP, newServer(&serverConfig{
+ BaseLogger: logger,
+ TLSConf: b.TLS,
+ Handler: h,
+ InitialAddress: b.Address,
+ Timeout: c.Timeout,
+ }))
}
}
for _, b := range c.NonDoHBind {
- addr := b.Address.String()
- errLog := log.StdLog(fmt.Sprintf("websvc: non-doh: %s", addr), log.DEBUG)
- svc.nonDoH = append(svc.nonDoH, &http.Server{
- Addr: addr,
- Handler: svc,
- TLSConfig: b.TLS,
- ErrorLog: errLog,
- ReadTimeout: c.Timeout,
- WriteTimeout: c.Timeout,
- IdleTimeout: c.Timeout,
- ReadHeaderTimeout: c.Timeout,
- })
+ logger := svc.logger.With(loggerKeyGroup, srvGrpNonDoH)
+ h := httputil.Wrap(
+ svc,
+ httputil.ServerHeaderMiddleware(agdhttp.UserAgent()),
+ httputil.NewLogMiddleware(logger, slog.LevelDebug),
+ )
+
+ svc.nonDoH = append(svc.nonDoH, newServer(&serverConfig{
+ BaseLogger: logger,
+ TLSConf: b.TLS,
+ Handler: h,
+ InitialAddress: b.Address,
+ Timeout: c.Timeout,
+ }))
}
return svc
}
+// serverGroup is a semantic alias for names of server groups.
+type serverGroup = string
+
+// Valid server groups.
+const (
+ srvGrpAdultBlockingPage serverGroup = "adult_blocking_page"
+ srvGrpGeneralBlockingPage serverGroup = "general_blocking_page"
+ srvGrpLinkedIP serverGroup = "linked_ip"
+ srvGrpNonDoH serverGroup = "non_doh"
+ srvGrpSafeBrowsingPage serverGroup = "safe_browsing_page"
+)
+
+// loggerKeyGroup is the key used by server groups
+const loggerKeyGroup = "group"
+
// type check
var _ service.Interface = (*Service)(nil)
@@ -182,70 +136,42 @@ var _ service.Interface = (*Service)(nil)
// may be nil. err is always nil; if any endpoint fails to start, it panics.
//
// TODO(a.garipov): Wait for the services to go online.
-//
-// TODO(a.garipov): Use the context for cancelation.
-func (svc *Service) Start(_ context.Context) (err error) {
+func (svc *Service) Start(ctx context.Context) (err error) {
if svc == nil {
return nil
}
- for _, srv := range svc.linkedIP {
- go mustStartServer(srv)
+ svc.logger.InfoContext(ctx, "starting")
+ defer svc.logger.InfoContext(ctx, "started")
- log.Info("websvc: linked ip %s: server is started", srv.Addr)
+ for _, srv := range svc.linkedIP {
+ logger := svc.logger.With(loggerKeyGroup, srvGrpLinkedIP)
+ go srv.serve(ctx, logger)
}
for _, srv := range svc.adultBlocking {
- go mustStartServer(srv)
-
- log.Info("websvc: adult blocking %s: server is started", srv.Addr)
+ logger := svc.logger.With(loggerKeyGroup, srvGrpAdultBlockingPage)
+ go srv.serve(ctx, logger)
}
for _, srv := range svc.generalBlocking {
- go mustStartServer(srv)
-
- log.Info("websvc: general blocking %s: server is started", srv.Addr)
+ logger := svc.logger.With(loggerKeyGroup, srvGrpGeneralBlockingPage)
+ go srv.serve(ctx, logger)
}
for _, srv := range svc.safeBrowsing {
- go mustStartServer(srv)
-
- log.Info("websvc: safe browsing %s: server is started", srv.Addr)
+ logger := svc.logger.With(loggerKeyGroup, srvGrpSafeBrowsingPage)
+ go srv.serve(ctx, logger)
}
for _, srv := range svc.nonDoH {
- go mustStartServer(srv)
-
- log.Info("websvc: non-doh %s: server is started", srv.Addr)
+ logger := svc.logger.With(loggerKeyGroup, srvGrpNonDoH)
+ go srv.serve(ctx, logger)
}
return nil
}
-// mustStartServer is a helper function that starts srv and panics if there are
-// any errors. It panics if one of the servers could not start, bringing down
-// the whole service.
-func mustStartServer(srv *http.Server) {
- if srv.TLSConfig == nil {
- err := srv.ListenAndServe()
- if err != nil && !errors.Is(err, http.ErrServerClosed) {
- panic(err)
- }
-
- return
- }
-
- l, err := tls.Listen("tcp", srv.Addr, srv.TLSConfig)
- if err != nil {
- panic(err)
- }
-
- err = srv.Serve(l)
- if err != nil && !errors.Is(err, http.ErrServerClosed) {
- panic(err)
- }
-}
-
// Shutdown implements the [service.Interface] interface for *Service. svc may
// be nil.
func (svc *Service) Shutdown(ctx context.Context) (err error) {
@@ -253,20 +179,23 @@ func (svc *Service) Shutdown(ctx context.Context) (err error) {
return nil
}
- serverGroups := container.KeyValues[string, []*http.Server]{{
- Key: "linked ip",
+ svc.logger.InfoContext(ctx, "shutting down")
+ defer svc.logger.InfoContext(ctx, "shut down")
+
+ serverGroups := container.KeyValues[serverGroup, []*server]{{
+ Key: srvGrpLinkedIP,
Value: svc.linkedIP,
}, {
- Key: adultBlockingName,
+ Key: srvGrpAdultBlockingPage,
Value: svc.adultBlocking,
}, {
- Key: generalBlockingName,
+ Key: srvGrpGeneralBlockingPage,
Value: svc.generalBlocking,
}, {
- Key: safeBrowsingName,
+ Key: srvGrpSafeBrowsingPage,
Value: svc.safeBrowsing,
}, {
- Key: "non-doh",
+ Key: srvGrpNonDoH,
Value: svc.nonDoH,
}}
@@ -281,33 +210,31 @@ func (svc *Service) Shutdown(ctx context.Context) (err error) {
return nil
}
-// shutdownServers is a helper function that shuts down srvs and logs successful
-// shutdowns.
-func shutdownServers(ctx context.Context, srvs []*http.Server, name string) (err error) {
- for _, srv := range srvs {
- err = srv.Shutdown(ctx)
+// shutdownServers is a helper function that shuts down srvs.
+func shutdownServers(ctx context.Context, srvs []*server, g serverGroup) (err error) {
+ var errs []error
+ for i, srv := range srvs {
+ err = srv.shutdown(ctx)
if err != nil {
- return fmt.Errorf("%s server %s shutdown: %w", name, srv.Addr, err)
+ errs = append(errs, fmt.Errorf("server group %s: server at index %d: %w", g, i, err))
}
-
- log.Info("websvc: %s %s: server is shutdown", name, srv.Addr)
}
- return nil
+ return errors.Join(errs...)
}
// type check
-var _ agdservice.Refresher = (*Service)(nil)
+var _ service.Refresher = (*Service)(nil)
-// Refresh implements the [agdservice.Refresher] interface for *Service. svc
-// may be nil.
+// Refresh implements the [service.Refresher] interface for *Service. svc may
+// be nil.
func (svc *Service) Refresh(ctx context.Context) (err error) {
if svc == nil {
return nil
}
- log.Info("websvc: refresh started")
- defer log.Info("websvc: refresh finished")
+ svc.logger.InfoContext(ctx, "refresh started")
+ defer svc.logger.InfoContext(ctx, "refresh finished")
servers := []*blockPageServer{
svc.adultBlockingBPS,
@@ -319,7 +246,7 @@ func (svc *Service) Refresh(ctx context.Context) (err error) {
for _, srv := range servers {
err = srv.Refresh(ctx)
if err != nil {
- errs = append(errs, fmt.Errorf("refreshing %s block page server: %w", srv.name, err))
+ errs = append(errs, fmt.Errorf("refreshing %s block page server: %w", srv.group, err))
}
}
diff --git a/internal/websvc/websvc_internal_test.go b/internal/websvc/websvc_internal_test.go
new file mode 100644
index 0000000..f370367
--- /dev/null
+++ b/internal/websvc/websvc_internal_test.go
@@ -0,0 +1,50 @@
+package websvc
+
+import (
+ "fmt"
+ "net"
+ "time"
+
+ "github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
+)
+
+// testTimeout is the common timeout for tests.
+//
+// TODO(a.garipov): Make the test external and DRY with one in websvc_test.go
+const testTimeout = 1 * time.Second
+
+// testLogger is the common logger for tests.
+//
+// TODO(a.garipov): Make the test external and DRY with one in websvc_test.go
+var testLogger = slogutil.NewDiscardLogger()
+
+// LocalAddrs returns the local addresses of the servers in group g. Addrs may
+// contain nils.
+//
+// TODO(a.garipov): Use in tests.
+func (svc *Service) LocalAddrs(g serverGroup) (addrs []net.Addr) {
+ switch g {
+ case srvGrpAdultBlockingPage:
+ return serverAddrs(svc.adultBlocking)
+ case srvGrpGeneralBlockingPage:
+ return serverAddrs(svc.generalBlocking)
+ case srvGrpLinkedIP:
+ return serverAddrs(svc.linkedIP)
+ case srvGrpNonDoH:
+ return serverAddrs(svc.nonDoH)
+ case srvGrpSafeBrowsingPage:
+ return serverAddrs(svc.safeBrowsing)
+ default:
+ panic(fmt.Errorf("server group: %w: %q", errors.ErrBadEnumValue, g))
+ }
+}
+
+// serverAddrs collects the addresses of the servers.
+func serverAddrs(srvs []*server) (addrs []net.Addr) {
+ for _, s := range srvs {
+ addrs = append(addrs, s.localAddr())
+ }
+
+ return addrs
+}
diff --git a/internal/websvc/websvc_test.go b/internal/websvc/websvc_test.go
index 2a6158c..84905dc 100644
--- a/internal/websvc/websvc_test.go
+++ b/internal/websvc/websvc_test.go
@@ -9,22 +9,30 @@ import (
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/AdGuardDNS/internal/websvc"
"github.com/AdguardTeam/golibs/httphdr"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
+// testTimeout is the common timeout for tests.
const testTimeout = 1 * time.Second
-func TestMain(m *testing.M) {
- testutil.DiscardLogOutput(m)
-}
+// testLogger is the common logger for tests.
+var testLogger = slogutil.NewDiscardLogger()
func TestNew(t *testing.T) {
- startService(t, &websvc.Config{})
+ startService(t, &websvc.Config{
+ Logger: testLogger,
+ StaticContent: http.NotFoundHandler(),
+ DNSCheck: http.NotFoundHandler(),
+ ErrColl: agdtest.NewErrorCollector(),
+ Timeout: testTimeout,
+ })
}
func TestService_NonDoH(t *testing.T) {
@@ -38,6 +46,7 @@ func TestService_NonDoH(t *testing.T) {
require.NoError(pt, err)
})
+ // TODO(a.garipov): Do not use hardcoded ports.
nonDoHPort := netip.MustParseAddrPort("127.0.0.1:3003")
nonDoHBind := []*websvc.BindData{{
TLS: nil,
@@ -46,9 +55,11 @@ func TestService_NonDoH(t *testing.T) {
notFoundContent := []byte("not found")
c := &websvc.Config{
+ Logger: testLogger,
StaticContent: http.NotFoundHandler(),
DNSCheck: mockHandler,
NonDoHBind: nonDoHBind,
+ ErrColl: agdtest.NewErrorCollector(),
Error404: notFoundContent,
Timeout: testTimeout,
}
diff --git a/scripts/make/go-lint.sh b/scripts/make/go-lint.sh
index 952bdb2..9e1c6af 100644
--- a/scripts/make/go-lint.sh
+++ b/scripts/make/go-lint.sh
@@ -3,7 +3,7 @@
# This comment is used to simplify checking local copies of the script. Bump
# this number every time a significant change is made to this script.
#
-# AdGuard-Project-Version: 12
+# AdGuard-Project-Version: 14
verbose="${VERBOSE:-0}"
readonly verbose
@@ -26,8 +26,8 @@ set -f -u
# Simple analyzers
-# blocklist_imports is a simple check against unwanted packages. The following
-# packages are banned:
+# blocklist_imports is a simple best-effort check against unwanted packages.
+# The following packages are banned:
#
# * Package errors is replaced by our own package in the
# github.com/AdguardTeam/golibs module.
@@ -67,8 +67,11 @@ set -f -u
# * internal/profiledb/internal/filecachepb/unsafe.go: a “safe” unsafe helper
# to prevent excessive allocations.
#
-# TODO(a.garipov): Add golibs/log, client_golang/prometheus/promauto.
+# TODO(a.garipov): Add client_golang/prometheus/promauto.
blocklist_imports() {
+ import_or_tab="$(printf '^\\(import \\|\t\\)')"
+ readonly import_or_tab
+
find . \
-type 'f' \
'(' \
@@ -79,15 +82,16 @@ blocklist_imports() {
-exec \
'grep' \
'-H' \
- '-e' '[[:space:]]"errors"$' \
- '-e' '[[:space:]]"golang.org/x/exp/maps"$' \
- '-e' '[[:space:]]"golang.org/x/exp/slices"$' \
- '-e' '[[:space:]]"golang.org/x/net/context"$' \
- '-e' '[[:space:]]"io/ioutil"$' \
- '-e' '[[:space:]]"log"$' \
- '-e' '[[:space:]]"reflect"$' \
- '-e' '[[:space:]]"sort"$' \
- '-e' '[[:space:]]"unsafe"$' \
+ '-e' "$import_or_tab"'"errors"$' \
+ '-e' "$import_or_tab"'"github.com/AdguardTeam/golibs/log"$' \
+ '-e' "$import_or_tab"'"golang.org/x/exp/maps"$' \
+ '-e' "$import_or_tab"'"golang.org/x/exp/slices"$' \
+ '-e' "$import_or_tab"'"golang.org/x/net/context"$' \
+ '-e' "$import_or_tab"'"io/ioutil"$' \
+ '-e' "$import_or_tab"'"log"$' \
+ '-e' "$import_or_tab"'"reflect"$' \
+ '-e' "$import_or_tab"'"sort"$' \
+ '-e' "$import_or_tab"'"unsafe"$' \
'-n' \
'{}' \
';'
@@ -222,7 +226,7 @@ if [ "$shadow_output" != '' ]; then
exit 1
fi
-run_linter gosec --quiet ./... "$dnssrvmod"
+run_linter gosec --exclude-generated --quiet ./... "$dnssrvmod"
run_linter errcheck ./... "$dnssrvmod"