diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9c4ef7a..b56c719 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,47 @@ The format is **not** based on [Keep a Changelog][kec], since the project **does
[kec]: https://keepachangelog.com/en/1.0.0/
[sem]: https://semver.org/spec/v2.0.0.html
+## AGDNS-2507 / Build 926
+
+- Profile's file cache version was incremented. The file cache structure has been optimized, so messages like the following are to be expected:
+
+ ```none
+ profiledb: warning: error loading fs cache err="decoding protobuf: proto: cannot parse invalid wire-format data"
+ ```
+
+## AGDNS-2327 / Build 916
+
+- Profile's file cache version was incremented. The new field `BlockChromePrefetch` has been added to profile's object.
+
+- The objects within the `filtering_groups` have a new property, `block_chrome_prefetch`. So replace this:
+
+ ```yaml
+ filtering_groups:
+ -
+ id: default
+ # …
+ block_firefox_canary: true
+ block_private_relay: true
+ ```
+
+ with this:
+
+ ```yaml
+ filtering_groups:
+ -
+ id: default
+ # …
+ block_chrome_prefetch: true
+ block_firefox_canary: true
+ block_private_relay: true
+ ```
+
+## AGDNS-2514 / Build 908
+
+- The environment variable `DNSCHECK_CACHE_KV_SIZE` has been added.
+
+- The property `kv.type` within the `check` object now supports the `cache` value.
+
## AGDNS-2484/ Build 886
- Property `type` of the `ratelimit` object has been moved to the underlying `allowlist` object. So replace this:
diff --git a/Makefile b/Makefile
index cbdf6a4..2ac1ff9 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.2
+GOTOOLCHAIN = go1.23.4
RACE = 0
REVISION = $${REVISION:-$$(git rev-parse --short HEAD)}
VERSION = 0
diff --git a/config.dist.yaml b/config.dist.yaml
index c12d370..198ea5d 100644
--- a/config.dist.yaml
+++ b/config.dist.yaml
@@ -175,8 +175,8 @@ check:
# Domains to use for DNS checking.
kv:
# Defines the type of remote key-value storage. Allowed values are
- # "backend", "consul", and "redis".
- type: 'consul'
+ # "backend", "cache", "consul", and "redis".
+ type: 'cache'
# For how long to keep the information about the client.
ttl: 30s
# Domains to use for DNS checking.
@@ -341,8 +341,9 @@ filtering_groups:
enabled: true
block_dangerous_domains: true
block_newly_registered_domains: false
- block_private_relay: false
+ block_chrome_prefetch: true
block_firefox_canary: true
+ block_private_relay: false
- id: 'family'
parental:
enabled: true
@@ -357,8 +358,9 @@ filtering_groups:
enabled: true
block_dangerous_domains: true
block_newly_registered_domains: false
- block_private_relay: false
+ block_chrome_prefetch: true
block_firefox_canary: true
+ block_private_relay: false
- id: 'non_filtering'
rule_lists:
enabled: false
@@ -368,8 +370,9 @@ filtering_groups:
enabled: false
block_dangerous_domains: true
block_newly_registered_domains: false
- block_private_relay: false
+ block_chrome_prefetch: false
block_firefox_canary: true
+ block_private_relay: false
# The configuration for the device-listening feature. Works only on Linux with
# SO_BINDTODEVICE support.
diff --git a/doc/configuration.md b/doc/configuration.md
index 34e45e3..09f6763 100644
--- a/doc/configuration.md
+++ b/doc/configuration.md
@@ -378,7 +378,7 @@ The `check` object has the following properties:
- `kv`: Remote key-value storage settings. It has the following properties:
- - `type`: Type of the remote KV storage. Allowed values are `backend`, `consul`, and `redis`.
+ - `type`: Type of the remote KV storage. Allowed values are `backend`, `cache`, `consul`, and `redis`.
**Example:** `consul`.
@@ -386,6 +386,8 @@ The `check` object has the following properties:
For `backend`, the TTL must be greater than `0s`.
+ For `cache`, the TTL is not used.
+
For `consul`, the TTL must be between `10s` and `1d`. Note that the actual TTL can be up to twice as long.
For `redis`, the TTL must be greater than or equal to `1ms`.
@@ -662,14 +664,18 @@ The items of the `filtering_groups` array have the following properties:
**Example:** `true`.
-- `private_relay`: If true, Apple Private Relay queries are blocked for requests using this filtering group.
+- `block_chrome_prefetch`: If true, Chrome prefetch domain queries are blocked for requests using this filtering group, forcing the preferch proxy into preflight mode.
- **Example:** `false`.
+ **Example:** `true`.
- `block_firefox_canary`: If true, Firefox canary domain queries are blocked for requests using this filtering group.
**Example:** `true`.
+- `private_relay`: If true, Apple Private Relay queries are blocked for requests using this filtering group.
+
+ **Example:** `false`.
+
## Network interface listeners
> [!NOTE]
diff --git a/doc/debughttp.md b/doc/debughttp.md
index 03bccd5..7a6de92 100644
--- a/doc/debughttp.md
+++ b/doc/debughttp.md
@@ -112,6 +112,7 @@ Supported IDs:
- `profiledb`
- `rulestat`
- `ticket_rotator`
+- `tlsconfig`
The special ID `*`, when used alone, causes all available refresh tasks to be performed. Use with caution.
diff --git a/doc/environment.md b/doc/environment.md
index 76c5d49..b8053e2 100644
--- a/doc/environment.md
+++ b/doc/environment.md
@@ -16,6 +16,7 @@ AdGuard DNS uses [environment variables][wiki-env] to store some of the more sen
- [`CONSUL_ALLOWLIST_URL`](#CONSUL_ALLOWLIST_URL)
- [`CONSUL_DNSCHECK_KV_URL`](#CONSUL_DNSCHECK_KV_URL)
- [`CONSUL_DNSCHECK_SESSION_URL`](#CONSUL_DNSCHECK_SESSION_URL)
+- [`DNSCHECK_CACHE_KV_SIZE`](#DNSCHECK_CACHE_KV_SIZE)
- [`DNSCHECK_REMOTEKV_API_KEY`](#DNSCHECK_REMOTEKV_API_KEY)
- [`DNSCHECK_REMOTEKV_URL`](#DNSCHECK_REMOTEKV_URL)
- [`FILTER_CACHE_PATH`](#FILTER_CACHE_PATH)
@@ -142,6 +143,16 @@ The HTTP(S) URL of the session API of the Consul instance used as a key-value da
**Example:** `http://localhost:8500/v1/session/create`
+## `DNSCHECK_CACHE_KV_SIZE`
+
+The maximum number of the local cache key-value database entries for the DNS server checking.
+
+**Default:** No default value, a positive value is required if the [type][conf-dnscheck-type] of the database is set to `cache`.
+
+**Example:** `1000`
+
+[conf-dnscheck-type]: configuration.md#check-kv-type
+
## `DNSCHECK_REMOTEKV_API_KEY`
The API key to use when authenticating queries to the backend key-value database API, if any. The API key should be valid as defined by [RFC 6750].
diff --git a/go.mod b/go.mod
index 2279a23..1dac177 100644
--- a/go.mod
+++ b/go.mod
@@ -1,10 +1,10 @@
module github.com/AdguardTeam/AdGuardDNS
-go 1.23.2
+go 1.23.4
require (
github.com/AdguardTeam/AdGuardDNS/internal/dnsserver v0.0.0-20240607112746-5690301129fe
- github.com/AdguardTeam/golibs v0.30.1
+ github.com/AdguardTeam/golibs v0.30.4
github.com/AdguardTeam/urlfilter v0.20.0
github.com/ameshkov/dnscrypt/v2 v2.3.0
github.com/axiomhq/hyperloglog v0.2.0
@@ -19,15 +19,15 @@ require (
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible
github.com/prometheus/client_golang v1.20.5
github.com/prometheus/client_model v0.6.1
- github.com/prometheus/common v0.60.0
- github.com/quic-go/quic-go v0.48.1
+ github.com/prometheus/common v0.60.1
+ github.com/quic-go/quic-go v0.48.2
github.com/stretchr/testify v1.9.0
- golang.org/x/crypto v0.28.0
- golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
- golang.org/x/net v0.30.0
- golang.org/x/sys v0.26.0
- golang.org/x/time v0.7.0
- google.golang.org/grpc v1.67.1
+ golang.org/x/crypto v0.30.0
+ golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
+ golang.org/x/net v0.32.0
+ golang.org/x/sys v0.28.0
+ golang.org/x/time v0.8.0
+ google.golang.org/grpc v1.68.0
google.golang.org/protobuf v1.35.1
gopkg.in/yaml.v2 v2.4.0
)
@@ -41,20 +41,20 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
- github.com/google/pprof v0.0.0-20241023014458-598669927662 // indirect
+ github.com/google/pprof v0.0.0-20241203143554-1e3fdc7de467 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
- github.com/onsi/ginkgo/v2 v2.20.2 // indirect
+ github.com/onsi/ginkgo/v2 v2.22.0 // indirect
github.com/panjf2000/ants/v2 v2.10.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
go.uber.org/mock v0.5.0 // indirect
- golang.org/x/mod v0.21.0 // indirect
- golang.org/x/sync v0.8.0 // indirect
- golang.org/x/text v0.19.0 // indirect
- golang.org/x/tools v0.26.0 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
+ golang.org/x/mod v0.22.0 // indirect
+ golang.org/x/sync v0.10.0 // indirect
+ golang.org/x/text v0.21.0 // indirect
+ golang.org/x/tools v0.28.0 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index 125acca..4905022 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,5 @@
-github.com/AdguardTeam/golibs v0.30.1 h1:/yv7dq2h7WXw/jTDxkE3FP9zHerRT+i03PZRHJX4fPU=
-github.com/AdguardTeam/golibs v0.30.1/go.mod h1:FkwcNQEJoGsgDGXcalrVa/4gWbE68KsmE2guXWtBQUE=
+github.com/AdguardTeam/golibs v0.30.4 h1:zfFX1v4hkOCz6BifkneiBW2PCwSK523kYNr+VwaFrIw=
+github.com/AdguardTeam/golibs v0.30.4/go.mod h1:Ir9dlHfb8nRQsG3Qgo1zoGL+k1qMbcBtb8tcnsvzdAE=
github.com/AdguardTeam/urlfilter v0.20.0 h1:X32qiuVCVd8WDYCEsbdZKfXMzwdVqrdulamtUi4rmzs=
github.com/AdguardTeam/urlfilter v0.20.0/go.mod h1:gjrywLTxfJh6JOkwi9SU+frhP7kVVEZ5exFGkR99qpk=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
@@ -37,12 +37,14 @@ github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/gomodule/redigo v1.9.2 h1:HrutZBLhSIU8abiSfW8pj8mPhOyMYjZT/wcA4/L9L9s=
github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/pprof v0.0.0-20241023014458-598669927662 h1:SKMkD83p7FwUqKmBsPdLHF5dNyxq3jOWwu9w9UyH5vA=
-github.com/google/pprof v0.0.0-20241023014458-598669927662/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
+github.com/google/pprof v0.0.0-20241203143554-1e3fdc7de467 h1:keEZFtbLJugfE0qHn+Ge1JCE71spzkchQobDf3mzS/4=
+github.com/google/pprof v0.0.0-20241203143554-1e3fdc7de467/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
@@ -59,10 +61,10 @@ github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
-github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
-github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
-github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
+github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
+github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
+github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
+github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc=
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
github.com/panjf2000/ants/v2 v2.10.0 h1:zhRg1pQUtkyRiOFo2Sbqwjp0GfBNo9cUY2/Grpx1p+8=
@@ -81,14 +83,14 @@ github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA=
-github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
+github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
+github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
-github.com/quic-go/quic-go v0.48.1 h1:y/8xmfWI9qmGTc+lBr4jKRUWLGSlSigv847ULJ4hYXA=
-github.com/quic-go/quic-go v0.48.1/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
+github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
+github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
@@ -111,29 +113,29 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
-golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
-golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
-golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
-golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
-golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
-golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
-golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
-golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
+golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY=
+golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
+golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
+golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
+golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
+golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
+golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
+golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
-golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
-golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
-golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
-golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
-golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
-golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
-golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
-golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
-google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
-google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
+golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
+golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
+golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
+golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
+golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
+golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
+golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 h1:XVhgTWWV3kGQlwJHR3upFWZeTsei6Oks1apkZSeonIE=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
+google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
+google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/go.work b/go.work
index b86714e..f91c55e 100644
--- a/go.work
+++ b/go.work
@@ -1,4 +1,4 @@
-go 1.23.2
+go 1.23.4
use (
.
diff --git a/go.work.sum b/go.work.sum
index 1d88e00..9d83983 100644
--- a/go.work.sum
+++ b/go.work.sum
@@ -1,6 +1,7 @@
cel.dev/expr v0.15.0 h1:O1jzfJCQBfL5BFoYktaxwIhuttaQPsVWerH9/EEKx0w=
cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg=
cel.dev/expr v0.16.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg=
+cel.dev/expr v0.16.1/go.mod h1:AsGA5zb3WruAEQeQng1RZdGEXmBj0jvMWh6l5SnNuC8=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
@@ -161,6 +162,7 @@ github.com/AdguardTeam/golibs v0.25.3/go.mod h1:HaTyS2wCbxFudjht9N/+/Qf1b5cMad2B
github.com/AdguardTeam/golibs v0.30.0/go.mod h1:vjw1OVZG6BYyoqGRY88U4LCJLOMfhBFhU0UJBdaSAuQ=
github.com/AdguardTeam/golibs v0.30.1 h1:/yv7dq2h7WXw/jTDxkE3FP9zHerRT+i03PZRHJX4fPU=
github.com/AdguardTeam/golibs v0.30.1/go.mod h1:FkwcNQEJoGsgDGXcalrVa/4gWbE68KsmE2guXWtBQUE=
+github.com/AdguardTeam/golibs v0.30.3/go.mod h1:Ir9dlHfb8nRQsG3Qgo1zoGL+k1qMbcBtb8tcnsvzdAE=
github.com/AdguardTeam/gomitmproxy v0.2.0 h1:rvCOf17pd1/CnMyMQW891zrEiIQBpQ8cIGjKN9pinUU=
github.com/AdguardTeam/gomitmproxy v0.2.1 h1:p9gr8Er1TYvf+7ic81Ax1sZ62UNCsMTZNbm7tC59S9o=
github.com/AdguardTeam/gomitmproxy v0.2.1/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU=
@@ -251,6 +253,7 @@ github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50/go.mod h1:5e1+Vvlzido6
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw=
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
+github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q=
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d h1:t5Wuyh53qYyg9eqn4BbnlIT+vmhyww0TatL+zT3uWgI=
@@ -367,8 +370,6 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
-github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@@ -401,6 +402,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
+github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
+github.com/google/pprof v0.0.0-20241101162523-b92577c0c142/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
@@ -488,6 +491,7 @@ github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw
github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
+github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
@@ -572,6 +576,8 @@ github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3Ro
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
+github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI=
+github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=
github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=
github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=
@@ -589,12 +595,14 @@ github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
+github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
+github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/quic-go/qtls-go1-20 v0.3.3/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
@@ -739,6 +747,7 @@ go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8p
go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
+go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go4.org v0.0.0-20180809161055-417644f6feb5 h1:+hE86LblG4AyDgwMCLTE6FOlM9+qjHSYS+rKqxUVdsM=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d h1:E2M5QgjZ/Jg+ObCQAudsXxuTsLj7Nl5RV/lZcQZmKSo=
@@ -758,6 +767,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
+golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
+golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20221019170559-20944726eadf/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
@@ -769,6 +780,8 @@ golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQ
golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
+golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
+golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -784,6 +797,7 @@ golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -820,6 +834,9 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
+golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
+golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
+golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -852,6 +869,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -889,6 +908,9 @@ golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240208230135-b75ee8823808 h1:+Kc94D8UVEVxJnLXp/+FMfqQARZtWHfVrcRtcG8aT3g=
golang.org/x/telemetry v0.0.0-20240208230135-b75ee8823808/go.mod h1:KG1lNk5ZFNssSZLrpVb4sMXKMpGwGXOxSG3rnu2gZQQ=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2 h1:IRJeR9r1pYWsHKTRe/IInb7lYvbBVIqOgsX/u0mbOWY=
@@ -925,6 +947,8 @@ golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
+golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
+golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@@ -938,6 +962,9 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
+golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
+golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
+golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
@@ -963,7 +990,11 @@ golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
+golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
+golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
+golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
+golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1014,6 +1045,7 @@ google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw=
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU=
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo=
+google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I=
google.golang.org/genproto/googleapis/bytestream v0.0.0-20240304161311-37d4d3c04a78/go.mod h1:vh/N7795ftP0AkN1w8XKqN4w1OdUKXW5Eummda+ofv8=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240304161311-37d4d3c04a78/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
@@ -1044,6 +1076,7 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
diff --git a/internal/agd/agd_test.go b/internal/agd/agd_test.go
index 7ddbe2d..d20ec2e 100644
--- a/internal/agd/agd_test.go
+++ b/internal/agd/agd_test.go
@@ -2,17 +2,13 @@ package agd_test
import (
"strings"
- "testing"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
- "github.com/AdguardTeam/golibs/testutil"
)
-func TestMain(m *testing.M) {
- testutil.DiscardLogOutput(m)
-}
-
// Common long strings for tests.
+//
+// TODO(a.garipov): Move to a new validation package.
var (
testLongStr = strings.Repeat("a", 200)
testLongStrUnicode = strings.Repeat("ы", 200)
diff --git a/internal/agd/device_test.go b/internal/agd/device_test.go
index 7e40610..2d80cb4 100644
--- a/internal/agd/device_test.go
+++ b/internal/agd/device_test.go
@@ -32,9 +32,10 @@ func TestNewDeviceName(t *testing.T) {
in: testLongStr,
wantErrMsg: `bad device name "` + testLongStr + `": too long: got 200 runes, max 128`,
}, {
- name: "too_long_unicode",
- in: testLongStrUnicode,
- wantErrMsg: `bad device name "` + testLongStrUnicode + `": too long: got 200 runes, max 128`,
+ name: "too_long_unicode",
+ in: testLongStrUnicode,
+ wantErrMsg: `bad device name "` + testLongStrUnicode +
+ `": too long: got 200 runes, max 128`,
}}
for _, tc := range testCases {
diff --git a/internal/agd/filteringgroup.go b/internal/agd/filteringgroup.go
new file mode 100644
index 0000000..0725150
--- /dev/null
+++ b/internal/agd/filteringgroup.go
@@ -0,0 +1,28 @@
+package agd
+
+import "github.com/AdguardTeam/AdGuardDNS/internal/filter"
+
+// FilteringGroup represents a set of filtering settings.
+type FilteringGroup struct {
+ // FilterConfig is the configuration of the filters used for this filtering
+ // group. It must not be nil.
+ FilterConfig *filter.ConfigGroup
+
+ // ID is the unique ID of this filtering group. It must be set.
+ ID FilteringGroupID
+
+ // BlockChromePrefetch shows if the Chrome prefetch proxy feature should be
+ // disabled for requests using this filtering group.
+ BlockChromePrefetch bool
+
+ // BlockFirefoxCanary shows if Firefox canary domain is blocked for
+ // requests using this filtering group.
+ BlockFirefoxCanary bool
+
+ // BlockPrivateRelay shows if Apple Private Relay is blocked for requests
+ // using this filtering group.
+ BlockPrivateRelay bool
+}
+
+// FilteringGroupID is the ID of a filter group. It is an opaque string.
+type FilteringGroupID string
diff --git a/internal/agd/filterlist.go b/internal/agd/filterlist.go
deleted file mode 100644
index cb81dda..0000000
--- a/internal/agd/filterlist.go
+++ /dev/null
@@ -1,173 +0,0 @@
-package agd
-
-import (
- "fmt"
- "unicode/utf8"
-
- "github.com/AdguardTeam/golibs/errors"
-)
-
-// FilterListID is the ID of a filter list. It is an opaque string.
-type FilterListID string
-
-// Special FilterListID values shared across the AdGuard DNS system.
-//
-// DO NOT change these as other parts of the system depend on these values.
-const (
- // FilterListIDNone means that no filter were applied at all.
- FilterListIDNone FilterListID = ""
-
- // FilterListIDBlockedService is the shared filter-list ID used when a
- // request was blocked by the service blocker.
- FilterListIDBlockedService FilterListID = "blocked_service"
-
- // FilterListIDCustom is the special shared filter-list ID used when
- // a request was filtered by a custom profile rule.
- FilterListIDCustom FilterListID = "custom"
-
- // FilterListIDAdultBlocking is the special shared filter-list ID used when
- // a request was filtered by the adult content blocking filter.
- FilterListIDAdultBlocking FilterListID = "adult_blocking"
-
- // FilterListIDSafeBrowsing is the special shared filter-list ID used when
- // a request was filtered by the safe browsing filter.
- FilterListIDSafeBrowsing FilterListID = "safe_browsing"
-
- // FilterListIDNewRegDomains is the special shared filter-list ID used when
- // a request was filtered by the newly registered domains filter.
- FilterListIDNewRegDomains FilterListID = "newly_registered_domains"
-
- // FilterListIDGeneralSafeSearch is the shared filter-list ID used when
- // a request was modified by the general safe search filter.
- FilterListIDGeneralSafeSearch FilterListID = "general_safe_search"
-
- // FilterListIDYoutubeSafeSearch is the special shared filter-list ID used
- // when a request was modified by the YouTube safe search filter.
- FilterListIDYoutubeSafeSearch FilterListID = "youtube_safe_search"
-
- // FilterListIDAdGuardDNS is the special filter-list ID of the main AdGuard
- // DNS filtering-rule list. For this list, rule statistics are collected.
- FilterListIDAdGuardDNS FilterListID = "adguard_dns_filter"
-
- // FilterListIDAdGuardPopup is the special filter-list ID of the AdGuard DNS
- // list of popup domains.
- FilterListIDAdGuardPopup FilterListID = "adguard_popup_filter"
-)
-
-// The maximum and minimum lengths of a filter list ID.
-const (
- MaxFilterListIDLen = 128
- MinFilterListIDLen = 1
-)
-
-// NewFilterListID converts a simple string into a FilterListID and makes sure
-// that it's valid. This should be preferred to a simple type conversion.
-func NewFilterListID(s string) (id FilterListID, err error) {
- defer func() { err = errors.Annotate(err, "bad filter list id %q: %w", s) }()
-
- err = ValidateInclusion(len(s), MaxFilterListIDLen, MinFilterListIDLen, UnitByte)
- if err != nil {
- return FilterListIDNone, err
- }
-
- // Allow only the printable, non-whitespace ASCII characters. Technically
- // we only need to exclude carriage return, line feed, and slash characters,
- // but let's be more strict just in case.
- if i, r := firstNonIDRune(s, true); i != -1 {
- return FilterListIDNone, fmt.Errorf("bad rune %q at index %d", r, i)
- }
-
- return FilterListID(s), nil
-}
-
-// SupportsDNSRewrite returns true if the $dnsrewrite rules in filtering-rule
-// lists with this ID should be processed.
-func (id FilterListID) SupportsDNSRewrite() (ok bool) {
- switch id {
- case
- FilterListIDAdGuardPopup,
- FilterListIDCustom,
- FilterListIDGeneralSafeSearch,
- FilterListIDYoutubeSafeSearch:
- return true
- default:
- return false
- }
-}
-
-// FilterRuleText is the text of a single rule within a filter.
-type FilterRuleText string
-
-// MaxFilterRuleTextRuneLen is the maximum length of a filter rule in runes.
-const MaxFilterRuleTextRuneLen = 1024
-
-// NewFilterRuleText converts a simple string into a FilterRuleText and makes
-// sure that it's valid. This should be preferred to a simple type conversion.
-func NewFilterRuleText(s string) (t FilterRuleText, err error) {
- defer func() { err = errors.Annotate(err, "bad filter rule text %q: %w", s) }()
-
- err = ValidateInclusion(utf8.RuneCountInString(s), MaxFilterRuleTextRuneLen, 0, UnitRune)
- if err != nil {
- return "", err
- }
-
- return FilterRuleText(s), nil
-}
-
-// FilteringGroup represents a set of filtering settings.
-//
-// TODO(a.garipov): Consider making it closer to the config file and the backend
-// response by grouping parental, rule list, and safe browsing settings into
-// separate structs.
-type FilteringGroup struct {
- // ID is the unique ID of this filtering group.
- ID FilteringGroupID
-
- // RuleListIDs are the filtering rule list IDs used for this filtering
- // group. They are ignored if RuleListsEnabled is false.
- RuleListIDs []FilterListID
-
- // RuleListsEnabled shows whether the rule-list based filtering is enabled.
- // This must be true in order for all parameters below to work.
- RuleListsEnabled bool
-
- // ParentalEnabled shows whether the parental protection functionality is
- // enabled. This must be true in order for all parameters below to
- // work.
- ParentalEnabled bool
-
- // BlockAdult shows whether the adult content blocking safe browsing
- // filtering should be enforced.
- BlockAdult bool
-
- // SafeBrowsingEnabled shows whether the general safe browsing filtering
- // should be enforced.
- SafeBrowsingEnabled bool
-
- // BlockDangerousDomains shows whether the dangerous domains safe browsing
- // filtering should be enforced.
- BlockDangerousDomains bool
-
- // BlockNewlyRegisteredDomains shows whether the newly registered domains
- // safe browsing filtering should be enforced.
- BlockNewlyRegisteredDomains bool
-
- // GeneralSafeSearch shows whether the general safe search filtering should
- // be enforced.
- GeneralSafeSearch bool
-
- // YoutubeSafeSearch shows whether the YouTube safe search filtering should
- // be enforced.
- YoutubeSafeSearch bool
-
- // BlockPrivateRelay shows if Apple Private Relay is blocked for requests
- // using this filtering group.
- BlockPrivateRelay bool
-
- // BlockFirefoxCanary shows if Firefox canary domain is blocked for
- // requests using this filtering group.
- BlockFirefoxCanary bool
-}
-
-// FilteringGroupID is the ID of a filter group. It is an opaque string.
-type FilteringGroupID string
diff --git a/internal/agd/profile.go b/internal/agd/profile.go
index 9eeb2e6..68b2331 100644
--- a/internal/agd/profile.go
+++ b/internal/agd/profile.go
@@ -2,13 +2,11 @@ package agd
import (
"fmt"
- "math"
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/access"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
- "github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
)
// Profile contains information about an AdGuard DNS profile. In other parts of
@@ -17,141 +15,62 @@ import (
//
// NOTE: Do not change fields of this structure without incrementing
// [internal/profiledb/internal.FileCacheVersion].
-//
-// TODO(a.garipov): Consider making it closer to the config file and the backend
-// response by grouping parental, rule list, and safe browsing settings into
-// separate structs.
type Profile struct {
- // Parental are the parental settings for this profile. They are ignored if
- // FilteringEnabled is set to false.
- //
- // NOTE: Do not change fields of this structure without incrementing
- // [internal/profiledb/internal.FileCacheVersion].
- Parental *ParentalProtectionSettings
-
- // Ratelimiter is the custom ratelimiter for this profile. It must not be
+ // FilterConfig is the configuration of the filters used for this profile
+ // and all its devices that don't have filtering disabled. It must not be
// nil.
- //
- // NOTE: Do not change fields of this structure without incrementing
- // [internal/profiledb/internal.FileCacheVersion].
- Ratelimiter Ratelimiter
-
- // SafeBrowsing are the safe browsing settings for this profile. They are
- // ignored if FilteringEnabled is set to false.
- //
- // NOTE: Do not change fields of this structure without incrementing
- // [internal/profiledb/internal.FileCacheVersion].
- SafeBrowsing *SafeBrowsingSettings
+ FilterConfig *filter.ConfigClient
// Access is the access manager for this profile. It must not be nil.
- //
- // NOTE: Do not change fields of this structure without incrementing
- // [internal/profiledb/internal.FileCacheVersion].
Access access.Profile
// BlockingMode defines the way blocked responses are constructed. It must
// not be nil.
- //
- // NOTE: Do not change fields of this structure without incrementing
- // [internal/profiledb/internal.FileCacheVersion].
BlockingMode dnsmsg.BlockingMode
+ // Ratelimiter is the custom ratelimiter for this profile. It must not be
+ // nil.
+ Ratelimiter Ratelimiter
+
// ID is the unique ID of this profile. It must not be empty.
- //
- // NOTE: Do not change fields of this structure without incrementing
- // [internal/profiledb/internal.FileCacheVersion].
ID ProfileID
- // UpdateTime shows the last time this profile was updated from the backend.
- // This is NOT the time of update in the backend's database, since the
- // backend doesn't send this information.
- //
- // NOTE: Do not change fields of this structure without incrementing
- // [internal/profiledb/internal.FileCacheVersion].
- UpdateTime time.Time
-
// DeviceIDs are the IDs of devices attached to this profile.
- //
- // NOTE: Do not change fields of this structure without incrementing
- // [internal/profiledb/internal.FileCacheVersion].
DeviceIDs []DeviceID
- // RuleListIDs are the IDs of the filtering rule lists enabled for this
- // profile. They are ignored if FilteringEnabled or RuleListsEnabled are
- // set to false.
- //
- // NOTE: Do not change fields of this structure without incrementing
- // [internal/profiledb/internal.FileCacheVersion].
- RuleListIDs []FilterListID
-
- // CustomRules are the custom filtering rules for this profile. They are
- // ignored if RuleListsEnabled is set to false.
- //
- // NOTE: Do not change fields of this structure without incrementing
- // [internal/profiledb/internal.FileCacheVersion].
- CustomRules []FilterRuleText
-
// FilteredResponseTTL is the time-to-live value used for responses sent to
// the devices of this profile.
- //
- // NOTE: Do not change fields of this structure without incrementing
- // [internal/profiledb/internal.FileCacheVersion].
FilteredResponseTTL time.Duration
- // FilteringEnabled defines whether queries from devices of this profile
- // should be filtered in any way at all.
- //
- // NOTE: Do not change fields of this structure without incrementing
- // [internal/profiledb/internal.FileCacheVersion].
- FilteringEnabled bool
-
- // RuleListsEnabled defines whether queries from devices of this profile
- // should be filtered using the filtering rule lists in RuleListIDs.
- // Requires FilteringEnabled to be set to true.
- //
- // NOTE: Do not change fields of this structure without incrementing
- // [internal/profiledb/internal.FileCacheVersion].
- RuleListsEnabled bool
-
- // QueryLogEnabled defines whether query logs should be saved for the
- // devices of this profile.
- //
- // NOTE: Do not change fields of this structure without incrementing
- // [internal/profiledb/internal.FileCacheVersion].
- QueryLogEnabled bool
-
- // Deleted shows if this profile is deleted.
- //
- // NOTE: Do not change fields of this structure without incrementing
- // [internal/profiledb/internal.FileCacheVersion].
- Deleted bool
-
- // BlockPrivateRelay shows if Apple Private Relay queries are blocked for
- // requests from all devices in this profile.
- //
- // NOTE: Do not change fields of this structure without incrementing
- // [internal/profiledb/internal.FileCacheVersion].
- BlockPrivateRelay bool
-
- // BlockFirefoxCanary shows if Firefox canary domain is blocked for
- // requests from all devices in this profile.
- //
- // NOTE: Do not change fields of this structure without incrementing
- // [internal/profiledb/internal.FileCacheVersion].
- BlockFirefoxCanary bool
-
- // IPLogEnabled shows if client IP addresses are logged.
- //
- // NOTE: Do not change fields of this structure without incrementing
- // [internal/profiledb/internal.FileCacheVersion].
- IPLogEnabled bool
-
// AutoDevicesEnabled shows if the automatic creation of devices using
// HumanIDs should be enabled for this profile.
- //
- // NOTE: Do not change fields of this structure without incrementing
- // [internal/profiledb/internal.FileCacheVersion].
AutoDevicesEnabled bool
+
+ // BlockChromePrefetch shows if the Chrome prefetch proxy feature should be
+ // forced into preflight mode for all devices in this profile.
+ BlockChromePrefetch bool
+
+ // BlockFirefoxCanary shows if Firefox canary domain is blocked for
+ // requests from all devices in this profile.
+ BlockFirefoxCanary bool
+
+ // BlockPrivateRelay shows if Apple Private Relay queries are blocked for
+ // requests from all devices in this profile.
+ BlockPrivateRelay bool
+
+ // Deleted shows if this profile is deleted.
+ Deleted bool
+
+ // FilteringEnabled defines whether queries from devices of this profile
+ // should be filtered in any way at all.
+ FilteringEnabled bool
+
+ // IPLogEnabled shows if client IP addresses are logged.
+ IPLogEnabled bool
+
+ // QueryLogEnabled defines whether query logs should be saved for the
+ // devices of this profile.
+ QueryLogEnabled bool
}
// ProfileID is the ID of a profile. It is an opaque string.
@@ -179,165 +98,3 @@ func NewProfileID(s string) (id ProfileID, err error) {
return ProfileID(s), nil
}
-
-// DayRange is a range within a single day. Start and End are minutes from the
-// start of day, with 0 being 00:00:00.(0) and 1439, 23:59:59.(9).
-//
-// Additionally, if both Start and End are set to [math.MaxUint16], the range is
-// a special zero-length range. This is needed, because when both Start and End
-// are zero, such DayRange indicates one minute after midnight; as well as to
-// reduce the amount of pointers and thus GC time.
-//
-// TODO(a.garipov): Refactor. See AGDNS-1516.
-type DayRange struct {
- Start uint16
- End uint16
-}
-
-// MaxDayRangeMinutes is the maximum value for DayRange.Start and DayRange.End
-// fields, excluding the zero-length range ones.
-const MaxDayRangeMinutes = 24*60 - 1
-
-// ZeroLengthDayRange returns a new zero-length day range.
-func ZeroLengthDayRange() (r DayRange) {
- return DayRange{
- Start: math.MaxUint16,
- End: math.MaxUint16,
- }
-}
-
-// IsZeroLength returns true if r is a zero-length range.
-func (r DayRange) IsZeroLength() (ok bool) {
- return r.Start == math.MaxUint16 && r.End == math.MaxUint16
-}
-
-// Validate returns the day range validation errors, if any.
-func (r DayRange) Validate() (err error) {
- defer func() { err = errors.Annotate(err, "bad day range: %w") }()
-
- switch {
- case r.IsZeroLength():
- return nil
- case r.End < r.Start:
- return fmt.Errorf("end %d less than start %d", r.End, r.Start)
- case r.Start > MaxDayRangeMinutes:
- return fmt.Errorf("start %d greater than %d", r.Start, MaxDayRangeMinutes)
- case r.End > MaxDayRangeMinutes:
- return fmt.Errorf("end %d greater than %d", r.End, MaxDayRangeMinutes)
- default:
- return nil
- }
-}
-
-// WeeklySchedule is a schedule for one week. The index is the same as
-// time.Weekday values. That is, 0 is Sunday, 1 is Monday, etc. An empty
-// DayRange means that there is no schedule for this day.
-type WeeklySchedule [7]DayRange
-
-// ParentalProtectionSchedule is the schedule of a client's parental protection.
-// All fields must not be nil.
-//
-// NOTE: Do not change fields of this structure without incrementing
-// [internal/profiledb/internal.FileCacheVersion].
-type ParentalProtectionSchedule struct {
- // Week is the parental protection schedule for every day of the week.
- Week *WeeklySchedule
-
- // TimeZone is the profile's time zone.
- TimeZone *agdtime.Location
-}
-
-// Contains returns true if t is within the allowed schedule.
-func (s *ParentalProtectionSchedule) Contains(t time.Time) (ok bool) {
- t = t.In(&s.TimeZone.Location)
- r := s.Week[int(t.Weekday())]
- if r.IsZeroLength() {
- return false
- }
-
- day := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, &s.TimeZone.Location)
- start := day.Add(time.Duration(r.Start) * time.Minute)
- end := day.Add(time.Duration(r.End+1)*time.Minute - 1*time.Nanosecond)
-
- return !t.Before(start) && !t.After(end)
-}
-
-// ParentalProtectionSettings are the parental protection settings of a profile.
-//
-// NOTE: Do not change fields of this structure without incrementing
-// [internal/profiledb/internal.FileCacheVersion].
-type ParentalProtectionSettings struct {
- Schedule *ParentalProtectionSchedule
-
- // BlockedServices are the IDs of the services blocked for this profile.
- BlockedServices []BlockedServiceID
-
- // Enabled tells whether the parental protection should be enabled at all.
- // This must be true in order for all parameters below to work.
- Enabled bool
-
- // BlockAdult tells if AdGuard DNS should enforce blocking of adult content
- // using the safe browsing filter.
- BlockAdult bool
-
- // GeneralSafeSearch tells if AdGuard DNS should enforce general safe search
- // in most search engines.
- GeneralSafeSearch bool
-
- // YoutubeSafeSearch tells if AdGuard DNS should enforce safe search on
- // YouTube.
- YoutubeSafeSearch bool
-}
-
-// SafeBrowsingSettings are the safe browsing settings of a profile.
-//
-// NOTE: Do not change fields of this structure without incrementing
-// [internal/profiledb/internal.FileCacheVersion].
-type SafeBrowsingSettings struct {
- // Enabled defines whether queries from devices of this profile should be
- // filtered using the safe browsing filter. This must be true in order for
- // all parameters below to work.
- Enabled bool
-
- // BlockDangerousDomains shows whether the dangerous domains safe browsing
- // filtering should be enforced.
- BlockDangerousDomains bool
-
- // BlockNewlyRegisteredDomains shows whether the newly registered domains
- // safe browsing filtering should be enforced.
- BlockNewlyRegisteredDomains bool
-}
-
-// BlockedServiceID is the ID of a blocked service. While these are usually
-// human-readable, clients should treat them as opaque strings.
-//
-// When a request is blocked by the service blocker, this ID is used as the
-// text of the blocking rule.
-type BlockedServiceID string
-
-// The maximum and minimum lengths of a blocked service ID.
-const (
- MaxBlockedServiceIDLen = 64
- MinBlockedServiceIDLen = 1
-)
-
-// NewBlockedServiceID converts a simple string into a BlockedServiceID and
-// makes sure that it's valid. This should be preferred to a simple type
-// conversion.
-func NewBlockedServiceID(s string) (id BlockedServiceID, err error) {
- defer func() { err = errors.Annotate(err, "bad blocked service id %q: %w", s) }()
-
- err = ValidateInclusion(len(s), MaxBlockedServiceIDLen, MinBlockedServiceIDLen, UnitByte)
- if err != nil {
- return "", err
- }
-
- // Allow only the printable, non-whitespace ASCII characters. Technically
- // we only need to exclude carriage return, line feed, and slash characters,
- // but let's be more strict just in case.
- if i, r := firstNonIDRune(s, true); i != -1 {
- return "", fmt.Errorf("bad char %q at index %d", r, i)
- }
-
- return BlockedServiceID(s), nil
-}
diff --git a/internal/agd/ratelimit.go b/internal/agd/ratelimit.go
index ccf77b3..ba6e348 100644
--- a/internal/agd/ratelimit.go
+++ b/internal/agd/ratelimit.go
@@ -63,7 +63,7 @@ func (GlobalRatelimiter) Config() (_ *RatelimitConfig) { return &RatelimitConfig
// CountResponses implements the [Ratelimiter] interface for GlobalRatelimiter.
func (GlobalRatelimiter) CountResponses(_ context.Context, _ *dns.Msg, _ netip.Addr) {}
-// DefaultRatelimiter is the default [Ratelimiter] impelentation.
+// DefaultRatelimiter is the default [Ratelimiter] implementation.
//
// TODO(a.garipov): Add tests.
type DefaultRatelimiter struct {
diff --git a/internal/agd/server.go b/internal/agd/server.go
index ac7b540..4f0bb14 100644
--- a/internal/agd/server.go
+++ b/internal/agd/server.go
@@ -19,7 +19,7 @@ type Server struct {
DNSCrypt *DNSCryptConfig
// TLS is the TLS configuration for this server, if any.
- TLS *tls.Config
+ TLS *TLSConfig
// QUICConf is the QUIC configuration for this server.
QUICConf *QUICConfig
@@ -200,3 +200,13 @@ type QUICConfig struct {
// QUICLimitsEnabled, if true, enables QUIC limiting.
QUICLimitsEnabled bool
}
+
+// TLSConfig is the TLS configuration of a DNS server. Metrics and ALPs must be
+// set for saved configurations.
+type TLSConfig struct {
+ // Default is the defult TLS configuration. It must not be nil.
+ Default *tls.Config
+
+ // H3 is the TLS configuration for DoH3.
+ H3 *tls.Config
+}
diff --git a/internal/agd/servergroup.go b/internal/agd/servergroup.go
index 0c32895..e0c7024 100644
--- a/internal/agd/servergroup.go
+++ b/internal/agd/servergroup.go
@@ -1,8 +1,6 @@
package agd
import (
- "crypto/tls"
-
"github.com/AdguardTeam/golibs/container"
"github.com/miekg/dns"
)
@@ -14,10 +12,11 @@ type ServerGroup struct {
// Resolvers (DDR) handlers. DDR must not be nil.
DDR *DDR
- // TLS are the TLS settings for this server group. If Servers contains at
- // least one server with a non-plain protocol (see [Protocol.IsPlain]), TLS
- // must not be nil.
- TLS *TLS
+ // DeviceDomains is the list of domain names used to detect device IDs from
+ // clients' server names.
+ //
+ // TODO(s.chzhen): Consider using a custom type.
+ DeviceDomains []string
// Name is the unique name of the server group.
Name ServerGroupName
@@ -36,20 +35,6 @@ type ServerGroup struct {
// ServerGroupName is the name of a server group.
type ServerGroupName string
-// TLS is the TLS configuration of a DNS server group.
-type TLS struct {
- // Conf is the server's TLS configuration.
- Conf *tls.Config
-
- // DeviceDomains are the domain names used to detect device IDs from
- // clients' server names.
- DeviceDomains []string
-
- // SessionKeys are paths to files containing the TLS session keys for this
- // server.
- SessionKeys []string
-}
-
// DDR is the configuration for the server group's Discovery Of Designated
// Resolvers (DDR) handlers.
type DDR struct {
diff --git a/internal/agdcache/lru.go b/internal/agdcache/lru.go
index 3635b0e..d1621fe 100644
--- a/internal/agdcache/lru.go
+++ b/internal/agdcache/lru.go
@@ -10,7 +10,11 @@ import (
// LRUConfig is a configuration structure of a cache.
type LRUConfig struct {
- Size int
+ // Count is the maximum number of elements to keep in the cache. It must be
+ // positive.
+ //
+ // TODO(a.garipov): Make uint64.
+ Count int
}
// LRU is an [Interface] implementation.
@@ -21,7 +25,7 @@ type LRU[K, T any] struct {
// NewLRU returns a new initialized LRU cache.
func NewLRU[K, T any](conf *LRUConfig) (c *LRU[K, T]) {
return &LRU[K, T]{
- cache: gcache.New(conf.Size).LRU().Build(),
+ cache: gcache.New(conf.Count).LRU().Build(),
}
}
diff --git a/internal/agdcache/lru_test.go b/internal/agdcache/lru_test.go
index d6a1dd8..bdd9a90 100644
--- a/internal/agdcache/lru_test.go
+++ b/internal/agdcache/lru_test.go
@@ -16,7 +16,7 @@ func TestLRU(t *testing.T) {
)
cache := agdcache.NewLRU[string, int](&agdcache.LRUConfig{
- Size: 10,
+ Count: 10,
})
cache.Set(key, val)
diff --git a/internal/agdpasswd/authenticator_test.go b/internal/agdpasswd/authenticator_test.go
index f61929c..e57b758 100644
--- a/internal/agdpasswd/authenticator_test.go
+++ b/internal/agdpasswd/authenticator_test.go
@@ -5,16 +5,11 @@ import (
"testing"
"github.com/AdguardTeam/AdGuardDNS/internal/agdpasswd"
- "github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/bcrypt"
)
-func TestMain(m *testing.M) {
- testutil.DiscardLogOutput(m)
-}
-
func TestPasswordHashBcrypt_Authenticate(t *testing.T) {
t.Parallel()
diff --git a/internal/agdservice/refresh.go b/internal/agdservice/refresh.go
index 17da016..fbde0ab 100644
--- a/internal/agdservice/refresh.go
+++ b/internal/agdservice/refresh.go
@@ -22,6 +22,18 @@ type Refresher interface {
Refresh(ctx context.Context) (err error)
}
+// RefresherFunc is an adapter to allow the use of ordinary functions as
+// [Refresher].
+type RefresherFunc func(ctx context.Context) (err error)
+
+// type check
+var _ Refresher = RefresherFunc(nil)
+
+// Refresh implements the [Refresher] interface for RefresherFunc.
+func (f RefresherFunc) Refresh(ctx context.Context) (err error) {
+ return f(ctx)
+}
+
// RefreshWorker is an [Interface] implementation that updates its [Refresher]
// every tick of the provided ticker.
type RefreshWorker struct {
diff --git a/internal/agdservice/refresh_test.go b/internal/agdservice/refresh_test.go
index 56f1d31..85541a8 100644
--- a/internal/agdservice/refresh_test.go
+++ b/internal/agdservice/refresh_test.go
@@ -96,9 +96,9 @@ func TestRefreshWorker(t *testing.T) {
})
t.Run("error", func(t *testing.T) {
- errRefr, syncCh := newTestRefresher(t, testError)
+ refrWithError, syncCh := newTestRefresher(t, testError)
- w := agdservice.NewRefreshWorker(newRefrConfig(t, errRefr, testIvl, false))
+ w := agdservice.NewRefreshWorker(newRefrConfig(t, refrWithError, testIvl, false))
err := w.Start(testutil.ContextWithTimeout(t, testTimeout))
require.NoError(t, err)
@@ -110,9 +110,9 @@ func TestRefreshWorker(t *testing.T) {
})
t.Run("error_on_shutdown", func(t *testing.T) {
- errRefr, syncCh := newTestRefresher(t, testError)
+ refrWithError, syncCh := newTestRefresher(t, testError)
- w := agdservice.NewRefreshWorker(newRefrConfig(t, errRefr, testIvlLong, true))
+ w := agdservice.NewRefreshWorker(newRefrConfig(t, refrWithError, testIvlLong, true))
err := w.Start(testutil.ContextWithTimeout(t, testTimeout))
require.NoError(t, err)
diff --git a/internal/agdtest/interface.go b/internal/agdtest/interface.go
index e492594..47e7ba8 100644
--- a/internal/agdtest/interface.go
+++ b/internal/agdtest/interface.go
@@ -11,6 +11,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdpasswd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/billstat"
"github.com/AdguardTeam/AdGuardDNS/internal/dnscheck"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsdb"
@@ -111,6 +112,21 @@ func (r *Refresher) Refresh(ctx context.Context) (err error) {
return r.OnRefresh(ctx)
}
+// Package agdtime
+
+// type check
+var _ agdtime.Clock = (*Clock)(nil)
+
+// Clock is a [agdtime.Clock] for tests.
+type Clock struct {
+ OnNow func() (now time.Time)
+}
+
+// Now implements the [agdtime.Clock] interface for *Clock.
+func (c *Clock) Now() (now time.Time) {
+ return c.OnNow()
+}
+
// Package billstat
// type check
@@ -220,34 +236,24 @@ var _ filter.Interface = (*Filter)(nil)
// Filter is a [filter.Interface] for tests.
type Filter struct {
- OnFilterRequest func(
- ctx context.Context,
- req *dns.Msg,
- ri *agd.RequestInfo,
- ) (r filter.Result, err error)
- OnFilterResponse func(
- ctx context.Context,
- resp *dns.Msg,
- ri *agd.RequestInfo,
- ) (r filter.Result, err error)
+ OnFilterRequest func(ctx context.Context, req *filter.Request) (r filter.Result, err error)
+ OnFilterResponse func(ctx context.Context, resp *filter.Response) (r filter.Result, err error)
}
// FilterRequest implements the [filter.Interface] interface for *Filter.
func (f *Filter) FilterRequest(
ctx context.Context,
- req *dns.Msg,
- ri *agd.RequestInfo,
+ req *filter.Request,
) (r filter.Result, err error) {
- return f.OnFilterRequest(ctx, req, ri)
+ return f.OnFilterRequest(ctx, req)
}
// FilterResponse implements the [filter.Interface] interface for *Filter.
func (f *Filter) FilterResponse(
ctx context.Context,
- resp *dns.Msg,
- ri *agd.RequestInfo,
+ resp *filter.Response,
) (r filter.Result, err error) {
- return f.OnFilterResponse(ctx, resp, ri)
+ return f.OnFilterResponse(ctx, resp)
}
// type check
@@ -274,21 +280,18 @@ var _ filter.Storage = (*FilterStorage)(nil)
// FilterStorage is a [filter.Storage] for tests.
type FilterStorage struct {
- OnFilterFromContext func(ctx context.Context, ri *agd.RequestInfo) (f filter.Interface)
- OnHasListID func(id agd.FilterListID) (ok bool)
+ OnForConfig func(ctx context.Context, c filter.Config) (f filter.Interface)
+ OnHasListID func(id filter.ID) (ok bool)
}
-// FilterFromContext implements the [filter.Storage] interface for
+// ForConfig implements the [filter.Storage] interface for
// *FilterStorage.
-func (s *FilterStorage) FilterFromContext(
- ctx context.Context,
- ri *agd.RequestInfo,
-) (f filter.Interface) {
- return s.OnFilterFromContext(ctx, ri)
+func (s *FilterStorage) ForConfig(ctx context.Context, c filter.Config) (f filter.Interface) {
+ return s.OnForConfig(ctx, c)
}
// HasListID implements the [filter.Storage] interface for *FilterStorage.
-func (s *FilterStorage) HasListID(id agd.FilterListID) (ok bool) {
+func (s *FilterStorage) HasListID(id filter.ID) (ok bool) {
return s.OnHasListID(id)
}
@@ -522,11 +525,11 @@ var _ rulestat.Interface = (*RuleStat)(nil)
// RuleStat is a [rulestat.Interface] for tests.
type RuleStat struct {
- OnCollect func(ctx context.Context, id agd.FilterListID, text agd.FilterRuleText)
+ OnCollect func(ctx context.Context, id filter.ID, text filter.RuleText)
}
// Collect implements the [rulestat.Interface] interface for *RuleStat.
-func (s *RuleStat) Collect(ctx context.Context, id agd.FilterListID, text agd.FilterRuleText) {
+func (s *RuleStat) Collect(ctx context.Context, id filter.ID, text filter.RuleText) {
s.OnCollect(ctx, id, text)
}
diff --git a/internal/agdtime/agdtime.go b/internal/agdtime/agdtime.go
index c0da4c8..e0efc2c 100644
--- a/internal/agdtime/agdtime.go
+++ b/internal/agdtime/agdtime.go
@@ -8,6 +8,24 @@ import (
"github.com/AdguardTeam/golibs/errors"
)
+// Clock is an interface for time-related operations.
+//
+// TODO(a.garipov): Expand with operations like After or Tick.
+//
+// TODO(a.garipov): Move to golibs/timeutil.
+type Clock interface {
+ Now() (now time.Time)
+}
+
+// SystemClock is a [Clock] that uses the functions from package time.
+type SystemClock struct{}
+
+// type check
+var _ Clock = SystemClock{}
+
+// Now implements the [Clock] interface for SystemClock.
+func (SystemClock) Now() (now time.Time) { return time.Now() }
+
// Location is a wrapper around time.Location that can de/serialize itself from
// and to JSON.
//
diff --git a/internal/backendpb/backendpb.go b/internal/backendpb/backendpb.go
index 9b1fdd5..45dc3b1 100644
--- a/internal/backendpb/backendpb.go
+++ b/internal/backendpb/backendpb.go
@@ -8,7 +8,6 @@ import (
"fmt"
"net/url"
- "github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/golibs/httphdr"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
@@ -41,11 +40,6 @@ func newClient(apiURL *url.URL) (client *grpc.ClientConn, err error) {
return conn, nil
}
-// reportf is a helper method for reporting non-critical errors.
-func reportf(ctx context.Context, errColl errcoll.Interface, format string, args ...any) {
- errcoll.Collectf(ctx, errColl, "backendpb: "+format, args...)
-}
-
// ctxWithAuthentication adds the API key authentication header to the outgoing
// request context if apiKey is not empty. If it is empty, ctx is parent.
func ctxWithAuthentication(parent context.Context, apiKey string) (ctx context.Context) {
diff --git a/internal/backendpb/backendpb_internal_test.go b/internal/backendpb/backendpb_internal_test.go
index ad3e039..7c012ed 100644
--- a/internal/backendpb/backendpb_internal_test.go
+++ b/internal/backendpb/backendpb_internal_test.go
@@ -2,18 +2,13 @@ package backendpb
import (
"net/netip"
- "testing"
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
- "github.com/AdguardTeam/golibs/testutil"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/c2h5oh/datasize"
)
-func TestMain(m *testing.M) {
- testutil.DiscardLogOutput(m)
-}
-
// Common IDs for tests and their string forms.
//
// TODO(a.garipov): Move all or most tests into external and unexport these.
@@ -37,5 +32,8 @@ var TestUpdTime = time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
// TODO(a.garipov): Add to golibs/netutil.
var TestBind = netip.MustParsePrefix("0.0.0.0/0")
+// TestLogger is the common logger for tests.
+var TestLogger = slogutil.NewDiscardLogger()
+
// TestRespSzEst is a response-size estimate for tests.
const TestRespSzEst datasize.ByteSize = 1 * datasize.KB
diff --git a/internal/backendpb/billstat.go b/internal/backendpb/billstat.go
index 3547bed..86fff24 100644
--- a/internal/backendpb/billstat.go
+++ b/internal/backendpb/billstat.go
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"io"
+ "log/slog"
"net/url"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
@@ -16,13 +17,18 @@ import (
// BillStatConfig is the configuration structure for the business logic backend
// billing statistics uploader.
type BillStatConfig struct {
+ // Logger is used for logging the operation of the billing statistics
+ // uploader. It must not be nil.
+ Logger *slog.Logger
+
+ // GRPCMetrics is used for the collection of the protobuf communication
+ // statistics.
+ GRPCMetrics GRPCMetrics
+
// ErrColl is the error collector that is used to collect critical and
// non-critical errors.
ErrColl errcoll.Interface
- // Metrics is used for the collection of the protobuf errors.
- Metrics Metrics
-
// Endpoint is the backend API URL. The scheme should be either "grpc" or
// "grpcs".
Endpoint *url.URL
@@ -31,6 +37,20 @@ type BillStatConfig struct {
APIKey string
}
+// BillStat is the implementation of the [billstat.Uploader] interface that
+// uploads the billing statistics to the business logic backend. It is safe for
+// concurrent use.
+//
+// TODO(a.garipov): Consider uniting with [ProfileStorage] into a single
+// backendpb.Client.
+type BillStat struct {
+ logger *slog.Logger
+ errColl errcoll.Interface
+ grpcMetrics GRPCMetrics
+ client DNSServiceClient
+ apiKey string
+}
+
// NewBillStat creates a new billing statistics uploader. c must not be nil.
func NewBillStat(c *BillStatConfig) (b *BillStat, err error) {
client, err := newClient(c.Endpoint)
@@ -40,26 +60,14 @@ func NewBillStat(c *BillStatConfig) (b *BillStat, err error) {
}
return &BillStat{
- errColl: c.ErrColl,
- metrics: c.Metrics,
- client: NewDNSServiceClient(client),
- apiKey: c.APIKey,
+ logger: c.Logger,
+ errColl: c.ErrColl,
+ grpcMetrics: c.GRPCMetrics,
+ client: NewDNSServiceClient(client),
+ apiKey: c.APIKey,
}, nil
}
-// BillStat is the implementation of the [billstat.Uploader] interface that
-// uploads the billing statistics to the business logic backend. It is safe for
-// concurrent use.
-//
-// TODO(a.garipov): Consider uniting with [ProfileStorage] into a single
-// backendpb.Client.
-type BillStat struct {
- errColl errcoll.Interface
- metrics Metrics
- client DNSServiceClient
- apiKey string
-}
-
// type check
var _ billstat.Uploader = (*BillStat)(nil)
@@ -72,12 +80,13 @@ func (b *BillStat) Upload(ctx context.Context, records billstat.Records) (err er
ctx = ctxWithAuthentication(ctx, b.apiKey)
stream, err := b.client.SaveDevicesBillingStat(ctx)
if err != nil {
- return fmt.Errorf("opening stream: %w", fixGRPCError(ctx, b.metrics, err))
+ return fmt.Errorf("opening stream: %w", fixGRPCError(ctx, b.grpcMetrics, err))
}
for deviceID, record := range records {
if record == nil {
- reportf(ctx, b.errColl, "device %q: null record", deviceID)
+ err = fmt.Errorf("device %q: null record", deviceID)
+ errcoll.Collect(ctx, b.errColl, b.logger, "uploading records", err)
continue
}
@@ -87,7 +96,7 @@ func (b *BillStat) Upload(ctx context.Context, records billstat.Records) (err er
return fmt.Errorf(
"uploading device %q record: %w",
deviceID,
- fixGRPCError(ctx, b.metrics, sendErr),
+ fixGRPCError(ctx, b.grpcMetrics, sendErr),
)
}
}
diff --git a/internal/backendpb/billstat_test.go b/internal/backendpb/billstat_test.go
index 9f397cc..f91091f 100644
--- a/internal/backendpb/billstat_test.go
+++ b/internal/backendpb/billstat_test.go
@@ -101,13 +101,14 @@ func TestBillStat_Upload(t *testing.T) {
errColl := &agdtest.ErrorCollector{
OnCollect: func(_ context.Context, err error) {
- testutil.AssertErrorMsg(t, `backendpb: device "invalid": null record`, err)
+ testutil.AssertErrorMsg(t, `uploading records: device "invalid": null record`, err)
},
}
b, err := backendpb.NewBillStat(&backendpb.BillStatConfig{
- ErrColl: errColl,
- Metrics: backendpb.EmptyMetrics{},
+ Logger: backendpb.TestLogger,
+ ErrColl: errColl,
+ GRPCMetrics: backendpb.EmptyGRPCMetrics{},
Endpoint: &url.URL{
Scheme: "grpc",
Host: l.Addr().String(),
diff --git a/internal/backendpb/device.go b/internal/backendpb/device.go
index 3971d23..97bb33e 100644
--- a/internal/backendpb/device.go
+++ b/internal/backendpb/device.go
@@ -3,6 +3,7 @@ package backendpb
import (
"context"
"fmt"
+ "log/slog"
"net/netip"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
@@ -19,7 +20,8 @@ func devicesToInternal(
ds []*DeviceSettings,
bindSet netutil.SubnetSet,
errColl errcoll.Interface,
- mtrc Metrics,
+ logger *slog.Logger,
+ mtrc ProfileDBMetrics,
) (out []*agd.Device, ids []agd.DeviceID) {
l := len(ds)
if l == 0 {
@@ -35,7 +37,8 @@ func devicesToInternal(
id = d.Id
}
- reportf(ctx, errColl, "bad device settings for device with id %q: %w", id, err)
+ err = fmt.Errorf("bad settings for device with id %q: %w", id, err)
+ errcoll.Collect(ctx, errColl, logger, "converting device", err)
// TODO(s.chzhen): Add a return result structure and move the
// metrics collection to the layer above.
diff --git a/internal/backendpb/dns.pb.go b/internal/backendpb/dns.pb.go
index aa71698..0c07bdb 100644
--- a/internal/backendpb/dns.pb.go
+++ b/internal/backendpb/dns.pb.go
@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
-// protoc-gen-go v1.35.1
+// protoc-gen-go v1.35.2
// protoc v5.28.3
// source: dns.proto
@@ -242,11 +242,12 @@ type DNSProfile struct {
// *DNSProfile_BlockingModeNxdomain
// *DNSProfile_BlockingModeNullIp
// *DNSProfile_BlockingModeRefused
- BlockingMode isDNSProfile_BlockingMode `protobuf_oneof:"blocking_mode"`
- IpLogEnabled bool `protobuf:"varint,17,opt,name=ip_log_enabled,json=ipLogEnabled,proto3" json:"ip_log_enabled,omitempty"`
- Access *AccessSettings `protobuf:"bytes,18,opt,name=access,proto3" json:"access,omitempty"`
- AutoDevicesEnabled bool `protobuf:"varint,19,opt,name=auto_devices_enabled,json=autoDevicesEnabled,proto3" json:"auto_devices_enabled,omitempty"`
- RateLimit *RateLimitSettings `protobuf:"bytes,20,opt,name=rate_limit,json=rateLimit,proto3" json:"rate_limit,omitempty"`
+ BlockingMode isDNSProfile_BlockingMode `protobuf_oneof:"blocking_mode"`
+ IpLogEnabled bool `protobuf:"varint,17,opt,name=ip_log_enabled,json=ipLogEnabled,proto3" json:"ip_log_enabled,omitempty"`
+ Access *AccessSettings `protobuf:"bytes,18,opt,name=access,proto3" json:"access,omitempty"`
+ AutoDevicesEnabled bool `protobuf:"varint,19,opt,name=auto_devices_enabled,json=autoDevicesEnabled,proto3" json:"auto_devices_enabled,omitempty"`
+ RateLimit *RateLimitSettings `protobuf:"bytes,20,opt,name=rate_limit,json=rateLimit,proto3" json:"rate_limit,omitempty"`
+ BlockChromePrefetch bool `protobuf:"varint,21,opt,name=block_chrome_prefetch,json=blockChromePrefetch,proto3" json:"block_chrome_prefetch,omitempty"`
}
func (x *DNSProfile) Reset() {
@@ -426,6 +427,13 @@ func (x *DNSProfile) GetRateLimit() *RateLimitSettings {
return nil
}
+func (x *DNSProfile) GetBlockChromePrefetch() bool {
+ if x != nil {
+ return x.BlockChromePrefetch
+ }
+ return false
+}
+
type isDNSProfile_BlockingMode interface {
isDNSProfile_BlockingMode()
}
@@ -2001,7 +2009,7 @@ var file_dns_proto_rawDesc = []byte{
0x09, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x73, 0x79,
- 0x6e, 0x63, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xad, 0x08, 0x0a, 0x0a, 0x44, 0x4e, 0x53, 0x50, 0x72,
+ 0x6e, 0x63, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xe1, 0x08, 0x0a, 0x0a, 0x44, 0x4e, 0x53, 0x50, 0x72,
0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x6e, 0x73, 0x5f, 0x69, 0x64, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x64, 0x6e, 0x73, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x11,
0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65,
@@ -2067,223 +2075,226 @@ var file_dns_proto_rawDesc = []byte{
0x65, 0x64, 0x12, 0x31, 0x0a, 0x0a, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74,
0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d,
0x69, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x09, 0x72, 0x61, 0x74, 0x65,
- 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x0f, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e,
- 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x85, 0x01, 0x0a, 0x14, 0x53, 0x61, 0x66, 0x65, 0x42,
- 0x72, 0x6f, 0x77, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12,
- 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
- 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x36, 0x0a, 0x17, 0x62, 0x6c, 0x6f,
- 0x63, 0x6b, 0x5f, 0x64, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73, 0x5f, 0x64, 0x6f, 0x6d,
- 0x61, 0x69, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x62, 0x6c, 0x6f, 0x63,
- 0x6b, 0x44, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
- 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x72, 0x64, 0x18, 0x03,
- 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x72, 0x64, 0x22, 0x8a,
- 0x02, 0x0a, 0x0e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
- 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69,
- 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69,
- 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08,
- 0x52, 0x10, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c,
- 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x5f, 0x69, 0x70, 0x18,
- 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x49, 0x70, 0x12,
- 0x23, 0x0a, 0x0d, 0x64, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x70, 0x73,
- 0x18, 0x05, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x64, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65,
- 0x64, 0x49, 0x70, 0x73, 0x12, 0x3f, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69,
- 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x41,
- 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x74,
- 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63,
- 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x68, 0x75, 0x6d, 0x61, 0x6e, 0x5f, 0x69,
- 0x64, 0x5f, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x68,
- 0x75, 0x6d, 0x61, 0x6e, 0x49, 0x64, 0x4c, 0x6f, 0x77, 0x65, 0x72, 0x22, 0x87, 0x02, 0x0a, 0x10,
- 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
- 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x6c,
- 0x6f, 0x63, 0x6b, 0x5f, 0x61, 0x64, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52,
- 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x64, 0x75, 0x6c, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x67,
- 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72,
- 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61,
- 0x6c, 0x53, 0x61, 0x66, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x2e, 0x0a, 0x13, 0x79,
- 0x6f, 0x75, 0x74, 0x75, 0x62, 0x65, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72,
- 0x63, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x79, 0x6f, 0x75, 0x74, 0x75, 0x62,
- 0x65, 0x53, 0x61, 0x66, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x29, 0x0a, 0x10, 0x62,
- 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18,
- 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x53, 0x65,
- 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75,
- 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64,
- 0x75, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x73, 0x63, 0x68,
- 0x65, 0x64, 0x75, 0x6c, 0x65, 0x22, 0x54, 0x0a, 0x10, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c,
- 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x6d, 0x7a,
- 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x6d, 0x7a, 0x12, 0x2e, 0x0a, 0x0b, 0x77,
- 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
- 0x32, 0x0c, 0x2e, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0b,
- 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x22, 0xd8, 0x01, 0x0a, 0x0b,
- 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x03, 0x6d,
- 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61,
- 0x6e, 0x67, 0x65, 0x52, 0x03, 0x6d, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x03, 0x74, 0x75, 0x65, 0x18,
- 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65,
- 0x52, 0x03, 0x74, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x03, 0x77, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01,
- 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x77,
- 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x03, 0x74, 0x68, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32,
- 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x74, 0x68, 0x75, 0x12,
- 0x1b, 0x0a, 0x03, 0x66, 0x72, 0x69, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44,
- 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x66, 0x72, 0x69, 0x12, 0x1b, 0x0a, 0x03,
- 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52,
- 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x73, 0x61, 0x74, 0x12, 0x1b, 0x0a, 0x03, 0x73, 0x75, 0x6e,
- 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67,
- 0x65, 0x52, 0x03, 0x73, 0x75, 0x6e, 0x22, 0x68, 0x0a, 0x08, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e,
- 0x67, 0x65, 0x12, 0x2f, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
- 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x73, 0x74,
- 0x61, 0x72, 0x74, 0x12, 0x2b, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
- 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
- 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x65, 0x6e, 0x64,
- 0x22, 0x3f, 0x0a, 0x11, 0x52, 0x75, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x53, 0x65, 0x74,
- 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
- 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12,
- 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64,
- 0x73, 0x22, 0x3e, 0x0a, 0x14, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64,
- 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x49, 0x50, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76,
- 0x34, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x70, 0x76, 0x34, 0x12, 0x12, 0x0a,
- 0x04, 0x69, 0x70, 0x76, 0x36, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x70, 0x76,
- 0x36, 0x22, 0x16, 0x0a, 0x14, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64,
- 0x65, 0x4e, 0x58, 0x44, 0x4f, 0x4d, 0x41, 0x49, 0x4e, 0x22, 0x14, 0x0a, 0x12, 0x42, 0x6c, 0x6f,
- 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x75, 0x6c, 0x6c, 0x49, 0x50, 0x22,
- 0x15, 0x0a, 0x13, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x52,
- 0x45, 0x46, 0x55, 0x53, 0x45, 0x44, 0x22, 0xe3, 0x01, 0x0a, 0x11, 0x44, 0x65, 0x76, 0x69, 0x63,
- 0x65, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x12, 0x48, 0x0a, 0x12,
- 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x69,
- 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
- 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73,
- 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69,
- 0x74, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,
- 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63,
- 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f,
- 0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6c, 0x69,
- 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72,
- 0x6f, 0x74, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f,
- 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x61,
- 0x73, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20,
- 0x01, 0x28, 0x0d, 0x52, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x22, 0x90, 0x02, 0x0a,
- 0x0e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12,
- 0x31, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x63, 0x69, 0x64,
- 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61,
- 0x6e, 0x67, 0x65, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x43, 0x69,
- 0x64, 0x72, 0x12, 0x31, 0x0a, 0x0e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f,
- 0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69, 0x64,
- 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73,
- 0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69,
- 0x73, 0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x61, 0x6c,
- 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x6c,
- 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28,
- 0x0d, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x6e, 0x12,
- 0x34, 0x0a, 0x16, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x64, 0x6f, 0x6d,
- 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52,
- 0x14, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
- 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
- 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22,
- 0x3d, 0x0a, 0x09, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07,
- 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61,
- 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78,
- 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0x85,
- 0x01, 0x0a, 0x16, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
- 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x64, 0x6f, 0x68,
- 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
- 0x52, 0x0b, 0x64, 0x6f, 0x68, 0x41, 0x75, 0x74, 0x68, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x32, 0x0a,
- 0x14, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x62,
- 0x63, 0x72, 0x79, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x12, 0x70,
- 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x48, 0x61, 0x73, 0x68, 0x42, 0x63, 0x72, 0x79, 0x70,
- 0x74, 0x42, 0x13, 0x0a, 0x11, 0x64, 0x6f, 0x68, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
- 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x22, 0x75, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
- 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a,
- 0x06, 0x64, 0x6e, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x64,
- 0x6e, 0x73, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x68, 0x75, 0x6d, 0x61, 0x6e, 0x5f, 0x69, 0x64,
- 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x68, 0x75, 0x6d, 0x61, 0x6e, 0x49, 0x64, 0x12,
- 0x2c, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03,
- 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0b, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70,
- 0x65, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3f, 0x0a,
- 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73,
- 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18,
- 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65,
- 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x06, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x22, 0x68,
- 0x0a, 0x10, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x45, 0x72, 0x72,
- 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20,
- 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3a, 0x0a, 0x0b,
- 0x72, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
- 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
- 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x72, 0x65,
- 0x74, 0x72, 0x79, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x22, 0x34, 0x0a, 0x18, 0x44, 0x65, 0x76, 0x69,
- 0x63, 0x65, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x45, 0x78, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x45,
- 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18,
- 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x2b,
- 0x0a, 0x0f, 0x42, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x45, 0x72, 0x72, 0x6f,
- 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x35, 0x0a, 0x19, 0x41,
- 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x61, 0x69,
- 0x6c, 0x65, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73,
+ 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x63,
+ 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x74, 0x63, 0x68, 0x18, 0x15,
+ 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x68, 0x72, 0x6f, 0x6d,
+ 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x74, 0x63, 0x68, 0x42, 0x0f, 0x0a, 0x0d, 0x62, 0x6c, 0x6f,
+ 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x85, 0x01, 0x0a, 0x14, 0x53,
+ 0x61, 0x66, 0x65, 0x42, 0x72, 0x6f, 0x77, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69,
+ 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x36, 0x0a,
+ 0x17, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73,
+ 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15,
+ 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73, 0x44, 0x6f,
+ 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e,
+ 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e,
+ 0x72, 0x64, 0x22, 0x8a, 0x02, 0x0a, 0x0e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65, 0x74,
+ 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x69, 0x6c,
+ 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03,
+ 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x45,
+ 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64,
+ 0x5f, 0x69, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x65,
+ 0x64, 0x49, 0x70, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64,
+ 0x5f, 0x69, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x64, 0x65, 0x64, 0x69,
+ 0x63, 0x61, 0x74, 0x65, 0x64, 0x49, 0x70, 0x73, 0x12, 0x3f, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x68,
+ 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x17, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x65,
+ 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x68, 0x75, 0x6d,
+ 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x5f, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x0c, 0x68, 0x75, 0x6d, 0x61, 0x6e, 0x49, 0x64, 0x4c, 0x6f, 0x77, 0x65, 0x72, 0x22,
+ 0x87, 0x02, 0x0a, 0x10, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x53, 0x65, 0x74, 0x74,
+ 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1f,
+ 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x61, 0x64, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x08, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x64, 0x75, 0x6c, 0x74, 0x12,
+ 0x2e, 0x0a, 0x13, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f,
+ 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x67, 0x65,
+ 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x53, 0x61, 0x66, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12,
+ 0x2e, 0x0a, 0x13, 0x79, 0x6f, 0x75, 0x74, 0x75, 0x62, 0x65, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f,
+ 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x79, 0x6f,
+ 0x75, 0x74, 0x75, 0x62, 0x65, 0x53, 0x61, 0x66, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12,
+ 0x29, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69,
+ 0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
+ 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x08, 0x73, 0x63,
+ 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x53,
+ 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52,
+ 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x22, 0x54, 0x0a, 0x10, 0x53, 0x63, 0x68,
+ 0x65, 0x64, 0x75, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x10, 0x0a,
+ 0x03, 0x74, 0x6d, 0x7a, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x6d, 0x7a, 0x12,
+ 0x2e, 0x0a, 0x0b, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e,
+ 0x67, 0x65, 0x52, 0x0b, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x22,
+ 0xd8, 0x01, 0x0a, 0x0b, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12,
+ 0x1b, 0x0a, 0x03, 0x6d, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44,
+ 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x6d, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x03,
+ 0x74, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52,
+ 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x74, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x03, 0x77, 0x65, 0x64,
+ 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67,
+ 0x65, 0x52, 0x03, 0x77, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x03, 0x74, 0x68, 0x75, 0x18, 0x04, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03,
+ 0x74, 0x68, 0x75, 0x12, 0x1b, 0x0a, 0x03, 0x66, 0x72, 0x69, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x66, 0x72, 0x69,
+ 0x12, 0x1b, 0x0a, 0x03, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e,
+ 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x73, 0x61, 0x74, 0x12, 0x1b, 0x0a,
+ 0x03, 0x73, 0x75, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x44, 0x61, 0x79,
+ 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x73, 0x75, 0x6e, 0x22, 0x68, 0x0a, 0x08, 0x44, 0x61,
+ 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x2f, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2b, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
+ 0x03, 0x65, 0x6e, 0x64, 0x22, 0x3f, 0x0a, 0x11, 0x52, 0x75, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74,
+ 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61,
+ 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62,
+ 0x6c, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
+ 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x3e, 0x0a, 0x14, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e,
+ 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x49, 0x50, 0x12, 0x12, 0x0a,
+ 0x04, 0x69, 0x70, 0x76, 0x34, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x70, 0x76,
+ 0x34, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,
+ 0x04, 0x69, 0x70, 0x76, 0x36, 0x22, 0x16, 0x0a, 0x14, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e,
+ 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x58, 0x44, 0x4f, 0x4d, 0x41, 0x49, 0x4e, 0x22, 0x14, 0x0a,
+ 0x12, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x75, 0x6c,
+ 0x6c, 0x49, 0x50, 0x22, 0x15, 0x0a, 0x13, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d,
+ 0x6f, 0x64, 0x65, 0x52, 0x45, 0x46, 0x55, 0x53, 0x45, 0x44, 0x22, 0xe3, 0x01, 0x0a, 0x11, 0x44,
+ 0x65, 0x76, 0x69, 0x63, 0x65, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74,
+ 0x12, 0x48, 0x0a, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74,
+ 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67,
+ 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54,
+ 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x63,
+ 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65,
+ 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64,
+ 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6c, 0x69, 0x65, 0x6e,
+ 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x14,
+ 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28,
+ 0x0d, 0x52, 0x03, 0x61, 0x73, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65,
+ 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73,
+ 0x22, 0x90, 0x02, 0x0a, 0x0e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69,
+ 0x6e, 0x67, 0x73, 0x12, 0x31, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74,
+ 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69,
+ 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69,
+ 0x73, 0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x31, 0x0a, 0x0e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c,
+ 0x69, 0x73, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a,
+ 0x2e, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63,
+ 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c,
+ 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d,
+ 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x6e, 0x12, 0x23,
+ 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18,
+ 0x04, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74,
+ 0x41, 0x73, 0x6e, 0x12, 0x34, 0x0a, 0x16, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74,
+ 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20,
+ 0x03, 0x28, 0x09, 0x52, 0x14, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x44, 0x6f,
+ 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61,
+ 0x62, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62,
+ 0x6c, 0x65, 0x64, 0x22, 0x3d, 0x0a, 0x09, 0x43, 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65,
+ 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72,
+ 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66,
+ 0x69, 0x78, 0x22, 0x85, 0x01, 0x0a, 0x16, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x22, 0x0a,
+ 0x0d, 0x64, 0x6f, 0x68, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x64, 0x6f, 0x68, 0x41, 0x75, 0x74, 0x68, 0x4f, 0x6e, 0x6c,
+ 0x79, 0x12, 0x32, 0x0a, 0x14, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61,
+ 0x73, 0x68, 0x5f, 0x62, 0x63, 0x72, 0x79, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48,
+ 0x00, 0x52, 0x12, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x48, 0x61, 0x73, 0x68, 0x42,
+ 0x63, 0x72, 0x79, 0x70, 0x74, 0x42, 0x13, 0x0a, 0x11, 0x64, 0x6f, 0x68, 0x5f, 0x70, 0x61, 0x73,
+ 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x22, 0x75, 0x0a, 0x13, 0x43, 0x72,
+ 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+ 0x74, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x6e, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x05, 0x64, 0x6e, 0x73, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x68, 0x75, 0x6d, 0x61,
+ 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x68, 0x75, 0x6d, 0x61,
+ 0x6e, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x74, 0x79,
+ 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0b, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63,
+ 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70,
+ 0x65, 0x22, 0x3f, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63,
+ 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x06, 0x64, 0x65, 0x76,
+ 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x44, 0x65, 0x76, 0x69,
+ 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x06, 0x64, 0x65, 0x76, 0x69,
+ 0x63, 0x65, 0x22, 0x68, 0x0a, 0x10, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65,
+ 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
+ 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
+ 0x12, 0x3a, 0x0a, 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x52, 0x0a, 0x72, 0x65, 0x74, 0x72, 0x79, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x22, 0x34, 0x0a, 0x18,
+ 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x45, 0x78, 0x63, 0x65, 0x65,
+ 0x64, 0x65, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61,
- 0x67, 0x65, 0x22, 0x6c, 0x0a, 0x11, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x53,
- 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c,
- 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65,
- 0x64, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x70, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03,
- 0x72, 0x70, 0x73, 0x12, 0x2b, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x69,
- 0x64, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69, 0x64, 0x72, 0x52,
- 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x69, 0x64, 0x72,
- 0x22, 0x26, 0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x47, 0x65, 0x74, 0x52,
- 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
- 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x64, 0x0a, 0x13, 0x52, 0x65, 0x6d, 0x6f,
- 0x74, 0x65, 0x4b, 0x56, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
- 0x14, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52,
- 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2e, 0x0a, 0x05, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x02,
- 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
- 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x48, 0x00, 0x52, 0x05,
- 0x65, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x67,
- 0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71,
- 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02,
- 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2b, 0x0a, 0x03, 0x74, 0x74,
- 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
- 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69,
- 0x6f, 0x6e, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x22, 0x15, 0x0a, 0x13, 0x52, 0x65, 0x6d, 0x6f, 0x74,
- 0x65, 0x4b, 0x56, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x87,
- 0x01, 0x0a, 0x0a, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a,
- 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x57, 0x49,
- 0x4e, 0x44, 0x4f, 0x57, 0x53, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4e, 0x44, 0x52, 0x4f,
- 0x49, 0x44, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x4d, 0x41, 0x43, 0x10, 0x03, 0x12, 0x07, 0x0a,
- 0x03, 0x49, 0x4f, 0x53, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x49, 0x4e, 0x55, 0x58, 0x10,
- 0x05, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x52, 0x10, 0x06, 0x12, 0x0c, 0x0a,
- 0x08, 0x53, 0x4d, 0x41, 0x52, 0x54, 0x5f, 0x54, 0x56, 0x10, 0x07, 0x12, 0x10, 0x0a, 0x0c, 0x47,
- 0x41, 0x4d, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x53, 0x4f, 0x4c, 0x45, 0x10, 0x08, 0x12, 0x09, 0x0a,
- 0x05, 0x4f, 0x54, 0x48, 0x45, 0x52, 0x10, 0x09, 0x32, 0xd0, 0x01, 0x0a, 0x0a, 0x44, 0x4e, 0x53,
- 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x0e, 0x67, 0x65, 0x74, 0x44, 0x4e,
- 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x13, 0x2e, 0x44, 0x4e, 0x53, 0x50,
- 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0b,
- 0x2e, 0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x30, 0x01, 0x12, 0x46, 0x0a,
- 0x16, 0x73, 0x61, 0x76, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x42, 0x69, 0x6c, 0x6c,
- 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x12, 0x12, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65,
- 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f,
- 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
- 0x70, 0x74, 0x79, 0x28, 0x01, 0x12, 0x44, 0x0a, 0x15, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44,
- 0x65, 0x76, 0x69, 0x63, 0x65, 0x42, 0x79, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x14,
- 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71,
- 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76,
- 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x61, 0x0a, 0x10, 0x52,
- 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12,
- 0x4d, 0x0a, 0x14, 0x67, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x53,
- 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x19, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69,
- 0x6d, 0x69, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
- 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x53, 0x65,
- 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x75,
- 0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
- 0x65, 0x12, 0x30, 0x0a, 0x03, 0x67, 0x65, 0x74, 0x12, 0x13, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74,
- 0x65, 0x4b, 0x56, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e,
+ 0x67, 0x65, 0x22, 0x2b, 0x0a, 0x0f, 0x42, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+ 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22,
+ 0x35, 0x0a, 0x19, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07,
+ 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d,
+ 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x6c, 0x0a, 0x11, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69,
+ 0x6d, 0x69, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65,
+ 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e,
+ 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x70, 0x73, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x0d, 0x52, 0x03, 0x72, 0x70, 0x73, 0x12, 0x2b, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e,
+ 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43,
+ 0x69, 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
+ 0x43, 0x69, 0x64, 0x72, 0x22, 0x26, 0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56,
+ 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65,
+ 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x64, 0x0a, 0x13,
0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
- 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x03, 0x73, 0x65, 0x74, 0x12, 0x13, 0x2e, 0x52, 0x65, 0x6d,
- 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
- 0x14, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73,
- 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3d, 0x0a, 0x21, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x67,
- 0x75, 0x61, 0x72, 0x64, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x64, 0x6e, 0x73,
- 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x42, 0x10, 0x44, 0x4e, 0x53, 0x50,
- 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0xa2, 0x02,
- 0x03, 0x44, 0x4e, 0x53, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0c, 0x48, 0x00, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2e, 0x0a, 0x05, 0x65, 0x6d, 0x70,
+ 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
+ 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
+ 0x48, 0x00, 0x52, 0x05, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x22, 0x67, 0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x53, 0x65,
+ 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61,
+ 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2b,
+ 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f,
+ 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75,
+ 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x22, 0x15, 0x0a, 0x13, 0x52,
+ 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+ 0x73, 0x65, 0x2a, 0x87, 0x01, 0x0a, 0x0a, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70,
+ 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x0b,
+ 0x0a, 0x07, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x53, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x41,
+ 0x4e, 0x44, 0x52, 0x4f, 0x49, 0x44, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x4d, 0x41, 0x43, 0x10,
+ 0x03, 0x12, 0x07, 0x0a, 0x03, 0x49, 0x4f, 0x53, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x49,
+ 0x4e, 0x55, 0x58, 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x52, 0x10,
+ 0x06, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x4d, 0x41, 0x52, 0x54, 0x5f, 0x54, 0x56, 0x10, 0x07, 0x12,
+ 0x10, 0x0a, 0x0c, 0x47, 0x41, 0x4d, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x53, 0x4f, 0x4c, 0x45, 0x10,
+ 0x08, 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x54, 0x48, 0x45, 0x52, 0x10, 0x09, 0x32, 0xd0, 0x01, 0x0a,
+ 0x0a, 0x44, 0x4e, 0x53, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x0e, 0x67,
+ 0x65, 0x74, 0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x13, 0x2e,
+ 0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x30,
+ 0x01, 0x12, 0x46, 0x0a, 0x16, 0x73, 0x61, 0x76, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73,
+ 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x12, 0x12, 0x2e, 0x44, 0x65,
+ 0x76, 0x69, 0x63, 0x65, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x1a,
+ 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
+ 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, 0x01, 0x12, 0x44, 0x0a, 0x15, 0x63, 0x72, 0x65,
+ 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x42, 0x79, 0x48, 0x75, 0x6d, 0x61, 0x6e,
+ 0x49, 0x64, 0x12, 0x14, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63,
+ 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74,
+ 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32,
+ 0x61, 0x0a, 0x10, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x53, 0x65, 0x72, 0x76,
+ 0x69, 0x63, 0x65, 0x12, 0x4d, 0x0a, 0x14, 0x67, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69,
+ 0x6d, 0x69, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x19, 0x2e, 0x52, 0x61,
+ 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52,
+ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d,
+ 0x69, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+ 0x73, 0x65, 0x32, 0x75, 0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x53, 0x65,
+ 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x30, 0x0a, 0x03, 0x67, 0x65, 0x74, 0x12, 0x13, 0x2e, 0x52,
+ 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+ 0x74, 0x1a, 0x14, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x47, 0x65, 0x74, 0x52,
+ 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x03, 0x73, 0x65, 0x74, 0x12, 0x13,
+ 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75,
+ 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4b, 0x56, 0x53, 0x65,
+ 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3d, 0x0a, 0x21, 0x63, 0x6f, 0x6d,
+ 0x2e, 0x61, 0x64, 0x67, 0x75, 0x61, 0x72, 0x64, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64,
+ 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x42, 0x10,
+ 0x44, 0x4e, 0x53, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f,
+ 0x50, 0x01, 0xa2, 0x02, 0x03, 0x44, 0x4e, 0x53, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
diff --git a/internal/backendpb/dns.proto b/internal/backendpb/dns.proto
index 4c14fb3..6f0803a 100644
--- a/internal/backendpb/dns.proto
+++ b/internal/backendpb/dns.proto
@@ -108,6 +108,7 @@ message DNSProfile {
AccessSettings access = 18;
bool auto_devices_enabled = 19;
RateLimitSettings rate_limit = 20;
+ bool block_chrome_prefetch = 21;
}
message SafeBrowsingSettings {
diff --git a/internal/backendpb/error.go b/internal/backendpb/error.go
index c7064b7..f4666dd 100644
--- a/internal/backendpb/error.go
+++ b/internal/backendpb/error.go
@@ -9,30 +9,15 @@ import (
status "google.golang.org/grpc/status"
)
-// GRPCError is a type alias for string that contains the gRPC error type.
-//
-// TODO(s.chzhen): Rewrite as soon as the import cycle is resolved.
-type GRPCError = string
-
-// gRPC errors of [GRPCError] type.
-const (
- GRPCErrAuthentication GRPCError = "auth"
- GRPCErrBadRequest GRPCError = "bad_req"
- GRPCErrDeviceQuota GRPCError = "dev_quota"
- GRPCErrOther GRPCError = "other"
- GRPCErrRateLimit GRPCError = "rate_limit"
- GRPCErrTimeout GRPCError = "timeout"
-)
-
// fixGRPCError converts a gRPC error into an application error, if necessary.
// That includes gRPC deadlines, which do not match [context.DeadlineExceeded]
// correctly.
//
// It also updates the backend gRPC metrics depending on the type, see
-// [Metrics.IncrementGRPCErrorCount].
-func fixGRPCError(ctx context.Context, mtrc Metrics, err error) (res error) {
+// [GRPCMetrics.IncrementErrorCount].
+func fixGRPCError(ctx context.Context, mtrc GRPCMetrics, err error) (res error) {
metricsType := GRPCErrOther
- defer func() { mtrc.IncrementGRPCErrorCount(ctx, metricsType) }()
+ defer func() { mtrc.IncrementErrorCount(ctx, metricsType) }()
s, ok := status.FromError(err)
if !ok {
diff --git a/internal/backendpb/metrics.go b/internal/backendpb/metrics.go
index 1707931..42b1acf 100644
--- a/internal/backendpb/metrics.go
+++ b/internal/backendpb/metrics.go
@@ -5,13 +5,47 @@ import (
"time"
)
-// Metrics is an interface that is used for the collection of the protobuf
-// errors statistics.
-type Metrics interface {
- // IncrementGRPCErrorCount increments the gRPC error count of errType.
- // errType must be one of GRPCError values.
- IncrementGRPCErrorCount(ctx context.Context, errType GRPCError)
+// GRPCError is a type alias for string that contains the gRPC error type.
+//
+// See [GRPCMetrics.IncrementErrorCount].
+//
+// TODO(s.chzhen): Rewrite as soon as the import cycle is resolved.
+type GRPCError = string
+// gRPC errors of [GRPCError] type.
+//
+// NOTE: Keep in sync with [metrics.GRPCError].
+const (
+ GRPCErrAuthentication GRPCError = "auth"
+ GRPCErrBadRequest GRPCError = "bad_req"
+ GRPCErrDeviceQuota GRPCError = "dev_quota"
+ GRPCErrOther GRPCError = "other"
+ GRPCErrRateLimit GRPCError = "rate_limit"
+ GRPCErrTimeout GRPCError = "timeout"
+)
+
+// GRPCMetrics is an interface that is used for the collection of the protobuf
+// communication statistics.
+type GRPCMetrics interface {
+ // IncrementErrorCount increments the gRPC error count of errType. errType
+ // must be one of [GRPCError] values.
+ IncrementErrorCount(ctx context.Context, errType GRPCError)
+}
+
+// EmptyGRPCMetrics is the implementation of the [GRPCMetrics] interface that
+// does nothing.
+type EmptyGRPCMetrics struct{}
+
+// type check
+var _ GRPCMetrics = EmptyGRPCMetrics{}
+
+// IncrementErrorCount implements the [GRPCMetrics] interface for
+// EmptyGRPCMetrics.
+func (EmptyGRPCMetrics) IncrementErrorCount(_ context.Context, _ GRPCError) {}
+
+// ProfileDBMetrics is an interface that is used for the collection of the
+// profile database statistics.
+type ProfileDBMetrics interface {
// IncrementInvalidDevicesCount increments the number of invalid devices.
IncrementInvalidDevicesCount(ctx context.Context)
@@ -19,18 +53,61 @@ type Metrics interface {
UpdateStats(ctx context.Context, avgRecv, avgDec time.Duration)
}
-// EmptyMetrics is the implementation of the [Metrics] interface that does
-// nothing.
-type EmptyMetrics struct{}
+// EmptyProfileDBMetrics is the implementation of the [ProfileDBMetrics]
+// interface that does nothing.
+type EmptyProfileDBMetrics struct{}
// type check
-var _ Metrics = EmptyMetrics{}
+var _ ProfileDBMetrics = EmptyProfileDBMetrics{}
-// IncrementGRPCErrorCount implements the [Metrics] interface for EmptyMetrics.
-func (EmptyMetrics) IncrementGRPCErrorCount(_ context.Context, errType GRPCError) {}
+// IncrementInvalidDevicesCount implements the [ProfileDBMetrics] interface for
+// EmptyProfileDBMetrics.
+func (EmptyProfileDBMetrics) IncrementInvalidDevicesCount(_ context.Context) {}
-// IncrementInvalidDevicesCount implements the [Metrics] interface for EmptyMetrics.
-func (EmptyMetrics) IncrementInvalidDevicesCount(_ context.Context) {}
+// UpdateStats implements the [ProfileDBMetrics] interface for
+// EmptyProfileDBMetrics.
+func (EmptyProfileDBMetrics) UpdateStats(_ context.Context, _, _ time.Duration) {}
-// UpdateStats implements the [Metrics] interface for EmptyMetrics.
-func (EmptyMetrics) UpdateStats(_ context.Context, _, _ time.Duration) {}
+// RemoteKVOp is the type alias for string that contains remote key-value
+// storage operation name.
+//
+// See [RemoteKVMetrics.ObserveOperation].
+type RemoteKVOp = string
+
+// Remote key-value storage operation names for [RemoteKVOp].
+//
+// NOTE: Keep in sync with [metrics.RemoteKVOp].
+const (
+ RemoteKVOpGet RemoteKVOp = "get"
+ RemoteKVOpSet RemoteKVOp = "set"
+)
+
+// RemoteKVMetrics is an interface that is used for the collection of the remote
+// key-value storage statistics.
+//
+// TODO(e.burkov): It may actually describe metrics for any remote key-value
+// storage implementation, consider declaring it in the [remotekv] package.
+type RemoteKVMetrics interface {
+ // ObserveOperation updates the remote key-value storage statistics. op
+ // must be one of [RemoteKVOp] values.
+ ObserveOperation(ctx context.Context, op string, dur time.Duration)
+
+ // IncrementLookups increments the number of lookups. hit is true if the
+ // lookup returned a value.
+ IncrementLookups(ctx context.Context, hit bool)
+}
+
+// EmptyRemoteKVMetrics is the implementation of the [RemoteKVMetrics] interface
+// that does nothing.
+type EmptyRemoteKVMetrics struct{}
+
+// type check
+var _ RemoteKVMetrics = EmptyRemoteKVMetrics{}
+
+// ObserveOperation implements the [RemoteKVMetrics] interface for
+// EmptyRemoteKVMetrics.
+func (EmptyRemoteKVMetrics) ObserveOperation(_ context.Context, _ string, _ time.Duration) {}
+
+// IncrementLookups implements the [RemoteKVMetrics] interface for
+// EmptyRemoteKVMetrics.
+func (EmptyRemoteKVMetrics) IncrementLookups(_ context.Context, _ bool) {}
diff --git a/internal/backendpb/profile.go b/internal/backendpb/profile.go
index 0f4a216..ec6e8d3 100644
--- a/internal/backendpb/profile.go
+++ b/internal/backendpb/profile.go
@@ -3,6 +3,7 @@ package backendpb
import (
"context"
"fmt"
+ "log/slog"
"net/netip"
"time"
@@ -11,6 +12,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/netutil"
@@ -20,20 +22,21 @@ import (
// toInternal converts the protobuf-encoded data into a profile structure and
// its device structures.
//
-// TODO(a.garipov): Refactor into a method of [*ProfileStorage]?
+// TODO(a.garipov): Refactor into methods of [*ProfileStorage].
func (x *DNSProfile) toInternal(
ctx context.Context,
updTime time.Time,
bindSet netutil.SubnetSet,
errColl errcoll.Interface,
- mtrc Metrics,
+ logger *slog.Logger,
+ mtrc ProfileDBMetrics,
respSzEst datasize.ByteSize,
) (profile *agd.Profile, devices []*agd.Device, err error) {
if x == nil {
return nil, nil, fmt.Errorf("profile is nil")
}
- parental, err := x.Parental.toInternal(ctx, errColl)
+ parental, err := x.Parental.toInternal(ctx, errColl, logger)
if err != nil {
return nil, nil, fmt.Errorf("parental: %w", err)
}
@@ -43,8 +46,7 @@ func (x *DNSProfile) toInternal(
return nil, nil, fmt.Errorf("blocking mode: %w", err)
}
- devices, deviceIds := devicesToInternal(ctx, x.Devices, bindSet, errColl, mtrc)
- listsEnabled, listIDs := x.RuleLists.toInternal(ctx, errColl)
+ devices, deviceIds := devicesToInternal(ctx, x.Devices, bindSet, errColl, logger, mtrc)
profID, err := agd.NewProfileID(x.DnsId)
if err != nil {
@@ -56,52 +58,63 @@ func (x *DNSProfile) toInternal(
fltRespTTL = respTTL.AsDuration()
}
+ customRules := rulesToInternal(ctx, x.CustomRules, errColl, logger)
+ custom := &filter.ConfigCustom{
+ ID: string(x.DnsId),
+ UpdateTime: updTime,
+ Rules: customRules,
+ // TODO(a.garipov): Consider adding an explicit flag to the protocol.
+ Enabled: len(customRules) > 0,
+ }
+
return &agd.Profile{
- Parental: parental,
+ FilterConfig: &filter.ConfigClient{
+ Custom: custom,
+ Parental: parental,
+ RuleList: x.RuleLists.toInternal(ctx, errColl, logger),
+ SafeBrowsing: x.SafeBrowsing.toInternal(),
+ },
+ Access: x.Access.toInternal(ctx, errColl, logger),
BlockingMode: m,
+ Ratelimiter: x.RateLimit.toInternal(ctx, errColl, logger, respSzEst),
ID: profID,
- UpdateTime: updTime,
DeviceIDs: deviceIds,
- RuleListIDs: listIDs,
- CustomRules: rulesToInternal(ctx, x.CustomRules, errColl),
FilteredResponseTTL: fltRespTTL,
- FilteringEnabled: x.FilteringEnabled,
- Ratelimiter: x.RateLimit.toInternal(ctx, errColl, respSzEst),
- SafeBrowsing: x.SafeBrowsing.toInternal(),
- Access: x.Access.toInternal(ctx, errColl),
- RuleListsEnabled: listsEnabled,
- QueryLogEnabled: x.QueryLogEnabled,
- Deleted: x.Deleted,
- BlockPrivateRelay: x.BlockPrivateRelay,
- BlockFirefoxCanary: x.BlockFirefoxCanary,
- IPLogEnabled: x.IpLogEnabled,
AutoDevicesEnabled: x.AutoDevicesEnabled,
+ BlockChromePrefetch: x.BlockChromePrefetch,
+ BlockFirefoxCanary: x.BlockFirefoxCanary,
+ BlockPrivateRelay: x.BlockPrivateRelay,
+ Deleted: x.Deleted,
+ FilteringEnabled: x.FilteringEnabled,
+ IPLogEnabled: x.IpLogEnabled,
+ QueryLogEnabled: x.QueryLogEnabled,
}, devices, nil
}
-// toInternal converts a protobuf parental-settings structure to an internal
-// one. If x is nil, toInternal returns nil.
+// toInternal converts a protobuf parental-protection settings structure to an
+// internal one. If x is nil, toInternal returns a disabled configuration.
func (x *ParentalSettings) toInternal(
ctx context.Context,
errColl errcoll.Interface,
-) (s *agd.ParentalProtectionSettings, err error) {
+ logger *slog.Logger,
+) (c *filter.ConfigParental, err error) {
+ c = &filter.ConfigParental{}
if x == nil {
- return nil, nil
+ return c, nil
}
- schedule, err := x.Schedule.toInternal()
+ c.AdultBlockingEnabled = x.BlockAdult
+ c.BlockedServices = blockedSvcsToInternal(ctx, errColl, logger, x.BlockedServices)
+ c.Enabled = x.Enabled
+ c.SafeSearchGeneralEnabled = x.GeneralSafeSearch
+ c.SafeSearchYouTubeEnabled = x.YoutubeSafeSearch
+
+ c.PauseSchedule, err = x.Schedule.toInternal()
if err != nil {
- return nil, fmt.Errorf("schedule: %w", err)
+ return nil, fmt.Errorf("pause schedule: %w", err)
}
- return &agd.ParentalProtectionSettings{
- Schedule: schedule,
- BlockedServices: blockedSvcsToInternal(ctx, errColl, x.BlockedServices),
- Enabled: x.Enabled,
- BlockAdult: x.BlockAdult,
- GeneralSafeSearch: x.GeneralSafeSearch,
- YoutubeSafeSearch: x.YoutubeSafeSearch,
- }, nil
+ return c, nil
}
// toInternal converts protobuf rate-limiting settings to an internal structure.
@@ -109,6 +122,7 @@ func (x *ParentalSettings) toInternal(
func (x *RateLimitSettings) toInternal(
ctx context.Context,
errColl errcoll.Interface,
+ logger *slog.Logger,
respSzEst datasize.ByteSize,
) (r agd.Ratelimiter) {
if x == nil || !x.Enabled {
@@ -116,24 +130,26 @@ func (x *RateLimitSettings) toInternal(
}
return agd.NewDefaultRatelimiter(&agd.RatelimitConfig{
- ClientSubnets: cidrRangeToInternal(ctx, errColl, x.ClientCidr),
+ ClientSubnets: cidrRangeToInternal(ctx, errColl, logger, x.ClientCidr),
RPS: x.Rps,
Enabled: x.Enabled,
}, respSzEst)
}
-// toInternal converts protobuf safe-browsing settings to an internal structure.
-// If x is nil, toInternal returns nil.
-func (x *SafeBrowsingSettings) toInternal() (sb *agd.SafeBrowsingSettings) {
+// toInternal converts protobuf safe-browsing settings to an internal
+// safe-browsing configuration. If x is nil, toInternal returns a disabled
+// configuration.
+func (x *SafeBrowsingSettings) toInternal() (c *filter.ConfigSafeBrowsing) {
+ c = &filter.ConfigSafeBrowsing{}
if x == nil {
- return nil
+ return c
}
- return &agd.SafeBrowsingSettings{
- Enabled: x.Enabled,
- BlockDangerousDomains: x.BlockDangerousDomains,
- BlockNewlyRegisteredDomains: x.BlockNrd,
- }
+ c.Enabled = x.Enabled
+ c.DangerousDomainsEnabled = x.BlockDangerousDomains
+ c.NewlyRegisteredDomainsEnabled = x.BlockNrd
+
+ return c
}
// toInternal converts protobuf access settings to an internal structure. If x
@@ -141,14 +157,15 @@ func (x *SafeBrowsingSettings) toInternal() (sb *agd.SafeBrowsingSettings) {
func (x *AccessSettings) toInternal(
ctx context.Context,
errColl errcoll.Interface,
+ logger *slog.Logger,
) (a access.Profile) {
if x == nil || !x.Enabled {
return access.EmptyProfile{}
}
return access.NewDefaultProfile(&access.ProfileConfig{
- AllowedNets: cidrRangeToInternal(ctx, errColl, x.AllowlistCidr),
- BlockedNets: cidrRangeToInternal(ctx, errColl, x.BlocklistCidr),
+ AllowedNets: cidrRangeToInternal(ctx, errColl, logger, x.AllowlistCidr),
+ BlockedNets: cidrRangeToInternal(ctx, errColl, logger, x.BlocklistCidr),
AllowedASN: asnToInternal(x.AllowlistAsn),
BlockedASN: asnToInternal(x.BlocklistAsn),
BlocklistDomainRules: x.BlocklistDomainRules,
@@ -160,12 +177,14 @@ func (x *AccessSettings) toInternal(
func cidrRangeToInternal(
ctx context.Context,
errColl errcoll.Interface,
+ logger *slog.Logger,
cidrs []*CidrRange,
) (out []netip.Prefix) {
for i, c := range cidrs {
addr, ok := netip.AddrFromSlice(c.Address)
if !ok {
- reportf(ctx, errColl, "bad cidr at index %d: %v", i, c.Address)
+ err := fmt.Errorf("bad cidr at index %d: %v", i, c.Address)
+ errcoll.Collect(ctx, errColl, logger, "converting cidrs", err)
continue
}
@@ -187,71 +206,71 @@ func asnToInternal(asns []uint32) (out []geoip.ASN) {
}
// blockedSvcsToInternal is a helper that converts the blocked service IDs from
-// the backend response to AdGuard DNS blocked service IDs.
+// the backend response to AdGuard DNS blocked-service IDs.
func blockedSvcsToInternal(
ctx context.Context,
errColl errcoll.Interface,
+ logger *slog.Logger,
respSvcs []string,
-) (svcs []agd.BlockedServiceID) {
+) (ids []filter.BlockedServiceID) {
l := len(respSvcs)
if l == 0 {
return nil
}
- svcs = make([]agd.BlockedServiceID, 0, l)
- for i, s := range respSvcs {
- id, err := agd.NewBlockedServiceID(s)
+ ids = make([]filter.BlockedServiceID, 0, l)
+ for i, idStr := range respSvcs {
+ id, err := filter.NewBlockedServiceID(idStr)
if err != nil {
- reportf(ctx, errColl, "blocked service at index %d: %w", i, err)
+ err = fmt.Errorf("at index %d: %w", i, err)
+ errcoll.Collect(ctx, errColl, logger, "converting blocked services", err)
continue
}
- svcs = append(svcs, id)
+ ids = append(ids, id)
}
- return svcs
+ return ids
}
// toInternal converts a protobuf protection-schedule structure to an internal
// one. If x is nil, toInternal returns nil.
-func (x *ScheduleSettings) toInternal() (sch *agd.ParentalProtectionSchedule, err error) {
+func (x *ScheduleSettings) toInternal() (c *filter.ConfigSchedule, err error) {
if x == nil {
return nil, nil
}
- sch = &agd.ParentalProtectionSchedule{}
+ c = &filter.ConfigSchedule{
+ Week: &filter.WeeklySchedule{},
+ }
- sch.TimeZone, err = agdtime.LoadLocation(x.Tmz)
+ c.TimeZone, err = agdtime.LoadLocation(x.Tmz)
if err != nil {
return nil, fmt.Errorf("loading timezone: %w", err)
}
- sch.Week = &agd.WeeklySchedule{}
-
w := x.WeeklyRange
days := []*DayRange{w.Sun, w.Mon, w.Tue, w.Wed, w.Thu, w.Fri, w.Sat}
for i, d := range days {
if d == nil {
- sch.Week[i] = agd.ZeroLengthDayRange()
-
continue
}
- sch.Week[i] = agd.DayRange{
+ ivl := &filter.DayInterval{
Start: uint16(d.Start.AsDuration().Minutes()),
- End: uint16(d.End.AsDuration().Minutes()),
+ End: uint16(d.End.AsDuration().Minutes() + 1),
}
- }
- for i, r := range sch.Week {
- err = r.Validate()
+ err = ivl.Validate()
if err != nil {
return nil, fmt.Errorf("weekday %s: %w", time.Weekday(i), err)
}
+
+ c.Week[i] = ivl
}
- return sch, nil
+ return c, nil
}
// toInternal converts a protobuf custom blocking-mode to an internal one.
@@ -312,17 +331,19 @@ func rulesToInternal(
ctx context.Context,
respRules []string,
errColl errcoll.Interface,
-) (rules []agd.FilterRuleText) {
+ logger *slog.Logger,
+) (rules []filter.RuleText) {
l := len(respRules)
if l == 0 {
return nil
}
- rules = make([]agd.FilterRuleText, 0, l)
+ rules = make([]filter.RuleText, 0, l)
for i, r := range respRules {
- text, err := agd.NewFilterRuleText(r)
+ text, err := filter.NewRuleText(r)
if err != nil {
- reportf(ctx, errColl, "rule at index %d: %w", i, err)
+ err = fmt.Errorf("at index %d: %w", i, err)
+ errcoll.Collect(ctx, errColl, logger, "converting rules", err)
continue
}
@@ -334,32 +355,32 @@ func rulesToInternal(
}
// toInternal is a helper that converts the filter lists from the backend
-// response to AdGuard DNS filter list ids. If x is nil, toInternal returns
-// false and nil.
+// response to AdGuard DNS rule-list configuration. If x is nil, toInternal
+// returns a disabled configuration.
func (x *RuleListsSettings) toInternal(
ctx context.Context,
errColl errcoll.Interface,
-) (enabled bool, filterLists []agd.FilterListID) {
+ logger *slog.Logger,
+) (c *filter.ConfigRuleList) {
+ c = &filter.ConfigRuleList{}
if x == nil {
- return false, nil
+ return c
}
- l := len(x.Ids)
- if l == 0 {
- return x.Enabled, nil
- }
+ c.Enabled = x.Enabled
+ c.IDs = make([]filter.ID, 0, len(x.Ids))
- filterLists = make([]agd.FilterListID, 0, l)
- for i, f := range x.Ids {
- id, err := agd.NewFilterListID(f)
+ for i, idStr := range x.Ids {
+ id, err := filter.NewID(idStr)
if err != nil {
- reportf(ctx, errColl, "filter id: at index %d: %w", i, err)
+ err = fmt.Errorf("at index %d: %w", i, err)
+ errcoll.Collect(ctx, errColl, logger, "converting filter id", err)
continue
}
- filterLists = append(filterLists, id)
+ c.IDs = append(c.IDs, id)
}
- return x.Enabled, filterLists
+ return c
}
diff --git a/internal/backendpb/profile_internal_test.go b/internal/backendpb/profile_internal_test.go
index 91f4be7..7ef9dc4 100644
--- a/internal/backendpb/profile_internal_test.go
+++ b/internal/backendpb/profile_internal_test.go
@@ -12,6 +12,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/golibs/testutil"
"github.com/c2h5oh/datasize"
@@ -35,7 +36,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
- EmptyMetrics{},
+ TestLogger,
+ EmptyProfileDBMetrics{},
TestRespSzEst,
)
require.NoError(t, err)
@@ -58,14 +60,15 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
savingErrColl,
- EmptyMetrics{},
+ TestLogger,
+ EmptyProfileDBMetrics{},
TestRespSzEst,
)
require.NoError(t, err)
testutil.AssertErrorMsg(
t,
- `backendpb: bad device settings for device with id "inv-d-ip":`+
- " dedicated ips: ip at index 0: unexpected slice size",
+ `converting device: bad settings for device with id "inv-d-ip":`+
+ ` dedicated ips: ip at index 0: unexpected slice size`,
errCollErr,
)
@@ -89,13 +92,14 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
bindSet,
savingErrColl,
- EmptyMetrics{},
+ TestLogger,
+ EmptyProfileDBMetrics{},
TestRespSzEst,
)
require.NoError(t, err)
testutil.AssertErrorMsg(
t,
- `backendpb: bad device settings for device with id "`+TestDeviceIDStr+`":`+
+ `converting device: bad settings for device with id "`+TestDeviceIDStr+`":`+
" dedicated ips: at index 0: \"1.1.1.2\" is not in bind data",
errCollErr,
)
@@ -114,7 +118,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
- EmptyMetrics{},
+ TestLogger,
+ EmptyProfileDBMetrics{},
TestRespSzEst,
)
testutil.AssertErrorMsg(t, "profile is nil", err)
@@ -133,7 +138,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
- EmptyMetrics{},
+ TestLogger,
+ EmptyProfileDBMetrics{},
TestRespSzEst,
)
require.NoError(t, err)
@@ -155,12 +161,13 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
- EmptyMetrics{},
+ TestLogger,
+ EmptyProfileDBMetrics{},
TestRespSzEst,
)
testutil.AssertErrorMsg(
t,
- "parental: schedule: loading timezone: unknown time zone invalid",
+ "parental: pause schedule: loading timezone: unknown time zone invalid",
err,
)
})
@@ -179,12 +186,13 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
- EmptyMetrics{},
+ TestLogger,
+ EmptyProfileDBMetrics{},
TestRespSzEst,
)
testutil.AssertErrorMsg(
t,
- "parental: schedule: weekday Sunday: bad day range: end 0 less than start 16",
+ "parental: pause schedule: weekday Sunday: end: out of range: 1 is less than start 16",
err,
)
})
@@ -201,7 +209,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
- EmptyMetrics{},
+ TestLogger,
+ EmptyProfileDBMetrics{},
TestRespSzEst,
)
testutil.AssertErrorMsg(t, "blocking mode: bad custom ipv4: unexpected slice size", err)
@@ -219,7 +228,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
- EmptyMetrics{},
+ TestLogger,
+ EmptyProfileDBMetrics{},
TestRespSzEst,
)
testutil.AssertErrorMsg(t, "blocking mode: bad custom ipv6: unexpected slice size", err)
@@ -238,7 +248,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
- EmptyMetrics{},
+ TestLogger,
+ EmptyProfileDBMetrics{},
TestRespSzEst,
)
testutil.AssertErrorMsg(t, "blocking mode: no valid custom ips found", err)
@@ -255,7 +266,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
- EmptyMetrics{},
+ TestLogger,
+ EmptyProfileDBMetrics{},
TestRespSzEst,
)
require.NoError(t, err)
@@ -279,7 +291,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
- EmptyMetrics{},
+ TestLogger,
+ EmptyProfileDBMetrics{},
TestRespSzEst,
)
require.NoError(t, err)
@@ -302,7 +315,8 @@ func TestDNSProfile_ToInternal(t *testing.T) {
TestUpdTime,
TestBind,
errColl,
- EmptyMetrics{},
+ TestLogger,
+ EmptyProfileDBMetrics{},
TestRespSzEst,
)
require.NoError(t, err)
@@ -464,6 +478,7 @@ func NewTestDNSProfile(tb testing.TB) (dp *DNSProfile) {
Prefix: 24,
}},
},
+ BlockChromePrefetch: true,
}
}
@@ -484,35 +499,37 @@ func newProfile(tb testing.TB) (p *agd.Profile) {
wantLoc, err := agdtime.LoadLocation("GMT")
require.NoError(tb, err)
- dayRange := agd.DayRange{
+ dayIvl := &filter.DayInterval{
Start: 0,
- End: 59,
+ End: 60,
}
- wantParental := &agd.ParentalProtectionSettings{
- Schedule: &agd.ParentalProtectionSchedule{
- Week: &agd.WeeklySchedule{
- agd.ZeroLengthDayRange(),
- dayRange,
- dayRange,
- dayRange,
- dayRange,
- dayRange,
- agd.ZeroLengthDayRange(),
+ wantParental := &filter.ConfigParental{
+ PauseSchedule: &filter.ConfigSchedule{
+ Week: &filter.WeeklySchedule{
+ nil,
+ dayIvl,
+ dayIvl,
+ dayIvl,
+ dayIvl,
+ dayIvl,
+ nil,
},
TimeZone: wantLoc,
},
- BlockedServices: []agd.BlockedServiceID{"youtube"},
- Enabled: false,
- BlockAdult: false,
- GeneralSafeSearch: false,
- YoutubeSafeSearch: false,
+ BlockedServices: []filter.BlockedServiceID{
+ "youtube",
+ },
+ Enabled: false,
+ AdultBlockingEnabled: false,
+ SafeSearchGeneralEnabled: false,
+ SafeSearchYouTubeEnabled: false,
}
- wantSafeBrowsing := &agd.SafeBrowsingSettings{
- Enabled: true,
- BlockDangerousDomains: true,
- BlockNewlyRegisteredDomains: false,
+ wantSafeBrowsing := &filter.ConfigSafeBrowsing{
+ Enabled: true,
+ DangerousDomainsEnabled: true,
+ NewlyRegisteredDomainsEnabled: false,
}
wantBlockingMode := &dnsmsg.BlockingModeCustomIP{
@@ -535,30 +552,39 @@ func newProfile(tb testing.TB) (p *agd.Profile) {
}, 1*datasize.KB)
return &agd.Profile{
- Parental: wantParental,
+ FilterConfig: &filter.ConfigClient{
+ Custom: &filter.ConfigCustom{
+ ID: TestProfileIDStr,
+ UpdateTime: TestUpdTime,
+ Rules: []filter.RuleText{"||example.org^"},
+ Enabled: true,
+ },
+ Parental: wantParental,
+ RuleList: &filter.ConfigRuleList{
+ IDs: []filter.ID{"1"},
+ Enabled: true,
+ },
+ SafeBrowsing: wantSafeBrowsing,
+ },
+ Access: wantAccess,
BlockingMode: wantBlockingMode,
+ Ratelimiter: wantRateLimiter,
ID: TestProfileID,
- UpdateTime: TestUpdTime,
DeviceIDs: []agd.DeviceID{
TestDeviceID,
"2222bbbb",
"3333cccc",
"4444dddd",
},
- RuleListIDs: []agd.FilterListID{"1"},
- CustomRules: []agd.FilterRuleText{"||example.org^"},
FilteredResponseTTL: 10 * time.Second,
- Ratelimiter: wantRateLimiter,
- SafeBrowsing: wantSafeBrowsing,
- Access: wantAccess,
- RuleListsEnabled: true,
- FilteringEnabled: true,
- QueryLogEnabled: true,
- Deleted: false,
- BlockPrivateRelay: true,
- BlockFirefoxCanary: true,
- IPLogEnabled: true,
AutoDevicesEnabled: true,
+ BlockChromePrefetch: true,
+ BlockFirefoxCanary: true,
+ BlockPrivateRelay: true,
+ Deleted: false,
+ FilteringEnabled: true,
+ IPLogEnabled: true,
+ QueryLogEnabled: true,
}
}
@@ -631,7 +657,8 @@ func BenchmarkDNSProfile_ToInternal(b *testing.B) {
TestUpdTime,
TestBind,
errColl,
- EmptyMetrics{},
+ TestLogger,
+ EmptyProfileDBMetrics{},
TestRespSzEst,
)
}
@@ -639,10 +666,10 @@ func BenchmarkDNSProfile_ToInternal(b *testing.B) {
require.NotNil(b, profSink)
require.NoError(b, errSink)
- // Most recent result, on a ThinkPad X13:
+ // Most recent results:
// goos: linux
// goarch: amd64
// pkg: github.com/AdguardTeam/AdGuardDNS/internal/backendpb
// cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
- // BenchmarkDNSProfile_ToInternal-16 67160 22130 ns/op 3048 B/op 51 allocs/op
+ // BenchmarkDNSProfile_ToInternal-16 79732 14686 ns/op 3160 B/op 59 allocs/op
}
diff --git a/internal/backendpb/profiledb.go b/internal/backendpb/profiledb.go
index 25d75e9..3477150 100644
--- a/internal/backendpb/profiledb.go
+++ b/internal/backendpb/profiledb.go
@@ -31,12 +31,16 @@ type ProfileStorageConfig struct {
// non-critical errors. It must not be nil.
ErrColl errcoll.Interface
- // Logger is used as the base logger for the profile storage. It must not
- // be nil.
+ // Logger is used for logging the operation of the profile storage. It must
+ // not be nil.
Logger *slog.Logger
- // Metrics is used for the collection of the protobuf errors.
- Metrics Metrics
+ // GRPCMetrics is used for the collection of the protobuf communication
+ // statistics.
+ GRPCMetrics GRPCMetrics
+
+ // Metrics is used for the collection of the profiles storage statistics.
+ Metrics ProfileDBMetrics
// Endpoint is the backend API URL. The scheme should be either "grpc" or
// "grpcs". It must not be nil.
@@ -63,7 +67,8 @@ type ProfileStorage struct {
errColl errcoll.Interface
client DNSServiceClient
logger *slog.Logger
- metrics Metrics
+ grpcMetrics GRPCMetrics
+ metrics ProfileDBMetrics
apiKey string
respSzEst datasize.ByteSize
maxProfSize datasize.ByteSize
@@ -83,6 +88,7 @@ func NewProfileStorage(c *ProfileStorageConfig) (s *ProfileStorage, err error) {
errColl: c.ErrColl,
client: NewDNSServiceClient(client),
logger: c.Logger,
+ grpcMetrics: c.GRPCMetrics,
metrics: c.Metrics,
apiKey: c.APIKey,
respSzEst: c.ResponseSizeEstimate,
@@ -115,7 +121,7 @@ func (s *ProfileStorage) CreateAutoDevice(
DeviceType: DeviceType(req.DeviceType),
})
if err != nil {
- return nil, fmt.Errorf("calling backend: %w", fixGRPCError(ctx, s.metrics, err))
+ return nil, fmt.Errorf("calling backend: %w", fixGRPCError(ctx, s.grpcMetrics, err))
}
d, err := backendResp.Device.toInternal(s.bindSet)
@@ -140,7 +146,7 @@ func (s *ProfileStorage) Profiles(
respSzOpt := grpc.MaxCallRecvMsgSize(int(s.maxProfSize.Bytes()))
stream, err := s.client.GetDNSProfiles(ctx, toProtobuf(req), respSzOpt)
if err != nil {
- return nil, fmt.Errorf("loading profiles: %w", fixGRPCError(ctx, s.metrics, err))
+ return nil, fmt.Errorf("loading profiles: %w", fixGRPCError(ctx, s.grpcMetrics, err))
}
defer func() { err = errors.WithDeferred(err, stream.CloseSend()) }()
@@ -165,7 +171,7 @@ func (s *ProfileStorage) Profiles(
return nil, fmt.Errorf(
"receiving profile #%d: %w",
n,
- fixGRPCError(ctx, s.metrics, profErr),
+ fixGRPCError(ctx, s.grpcMetrics, profErr),
)
}
stats.endRecv()
@@ -176,11 +182,12 @@ func (s *ProfileStorage) Profiles(
time.Now(),
s.bindSet,
s.errColl,
+ s.logger,
s.metrics,
s.respSzEst,
)
if profErr != nil {
- reportf(ctx, s.errColl, "loading profile: %w", profErr)
+ errcoll.Collect(ctx, s.errColl, s.logger, "loading profile", profErr)
continue
}
diff --git a/internal/backendpb/profiledb_test.go b/internal/backendpb/profiledb_test.go
index b9511f7..f872646 100644
--- a/internal/backendpb/profiledb_test.go
+++ b/internal/backendpb/profiledb_test.go
@@ -13,7 +13,6 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/AdGuardDNS/internal/backendpb"
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb"
- "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/c2h5oh/datasize"
"github.com/stretchr/testify/assert"
@@ -67,10 +66,11 @@ func TestProfileStorage_CreateAutoDevice(t *testing.T) {
require.NoError(t, err)
s, err := backendpb.NewProfileStorage(&backendpb.ProfileStorageConfig{
- BindSet: backendpb.TestBind,
- ErrColl: agdtest.NewErrorCollector(),
- Logger: slogutil.NewDiscardLogger(),
- Metrics: backendpb.EmptyMetrics{},
+ BindSet: backendpb.TestBind,
+ ErrColl: agdtest.NewErrorCollector(),
+ Logger: backendpb.TestLogger,
+ GRPCMetrics: backendpb.EmptyGRPCMetrics{},
+ Metrics: backendpb.EmptyProfileDBMetrics{},
Endpoint: &url.URL{
Scheme: "grpc",
Host: l.Addr().String(),
@@ -157,10 +157,11 @@ 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: slogutil.NewDiscardLogger(),
- Metrics: backendpb.EmptyMetrics{},
+ BindSet: netip.MustParsePrefix("0.0.0.0/0"),
+ ErrColl: agdtest.NewErrorCollector(),
+ Logger: backendpb.TestLogger,
+ GRPCMetrics: backendpb.EmptyGRPCMetrics{},
+ Metrics: backendpb.EmptyProfileDBMetrics{},
Endpoint: &url.URL{
Scheme: "grpc",
Host: l.Addr().String(),
@@ -195,8 +196,10 @@ func BenchmarkProfileStorage_Profiles(b *testing.B) {
require.NoError(b, errSink)
require.NotNil(b, respSink)
- // goos: darwin
- // goarch: arm64
- // pkg: github.com/AdguardTeam/AdGuardDNS/internal/backendpb
- // BenchmarkProfileStorage_Profiles-8 11599 104725 ns/op 18281 B/op 328 allocs/op
+ // Most recent results:
+ // goos: linux
+ // goarch: amd64
+ // pkg: github.com/AdguardTeam/AdGuardDNS/internal/backendpb
+ // cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
+ // BenchmarkProfileStorage_Profiles-16 4501 258657 ns/op 20020 B/op 350 allocs/op
}
diff --git a/internal/backendpb/ratelimiter.go b/internal/backendpb/ratelimiter.go
index fa67d14..5572752 100644
--- a/internal/backendpb/ratelimiter.go
+++ b/internal/backendpb/ratelimiter.go
@@ -19,8 +19,9 @@ type RateLimiterConfig struct {
// not be nil.
Logger *slog.Logger
- // GRPCMetrics is used for the collection of the protobuf errors.
- GRPCMetrics Metrics
+ // GRPCMetrics is used for the collection of the protobuf communication
+ // statistics.
+ GRPCMetrics GRPCMetrics
// Metrics is used to collect allowlist statistics.
Metrics consul.Metrics
@@ -44,7 +45,7 @@ type RateLimiterConfig struct {
// that retrieves the rate limit settings from the business logic backend.
type RateLimiter struct {
logger *slog.Logger
- grpcMetrics Metrics
+ grpcMetrics GRPCMetrics
metrics consul.Metrics
allowlist *ratelimit.DynamicAllowlist
errColl errcoll.Interface
@@ -92,7 +93,7 @@ func (l *RateLimiter) Refresh(ctx context.Context) (err error) {
}
allowedSubnets := backendResp.AllowedSubnets
- prefixes := cidrRangeToInternal(ctx, l.errColl, allowedSubnets)
+ prefixes := cidrRangeToInternal(ctx, l.errColl, l.logger, allowedSubnets)
l.allowlist.Update(prefixes)
l.logger.InfoContext(ctx, "refresh successful", "num_records", len(prefixes))
diff --git a/internal/backendpb/ratelimiter_test.go b/internal/backendpb/ratelimiter_test.go
index dc86e7e..340b888 100644
--- a/internal/backendpb/ratelimiter_test.go
+++ b/internal/backendpb/ratelimiter_test.go
@@ -11,7 +11,6 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/backendpb"
"github.com/AdguardTeam/AdGuardDNS/internal/consul"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit"
- "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -83,9 +82,9 @@ func TestRateLimiter_Refresh(t *testing.T) {
allowlist := ratelimit.NewDynamicAllowlist(nil, nil)
l, err := backendpb.NewRateLimiter(&backendpb.RateLimiterConfig{
- Logger: slogutil.NewDiscardLogger(),
+ Logger: backendpb.TestLogger,
Metrics: consul.EmptyMetrics{},
- GRPCMetrics: backendpb.EmptyMetrics{},
+ GRPCMetrics: backendpb.EmptyGRPCMetrics{},
Allowlist: allowlist,
Endpoint: &url.URL{
Scheme: "grpc",
diff --git a/internal/backendpb/remotekv.go b/internal/backendpb/remotekv.go
index 09810d4..2c721da 100644
--- a/internal/backendpb/remotekv.go
+++ b/internal/backendpb/remotekv.go
@@ -7,18 +7,20 @@ import (
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/remotekv"
+ "github.com/AdguardTeam/golibs/errors"
"google.golang.org/protobuf/types/known/durationpb"
)
// RemoteKVConfig is the configuration for the business logic backend key-value
// storage.
type RemoteKVConfig struct {
- // Metrics is used for the collection of the remote key-value storage
+ // Metrics is used for the collection of the backend remote key-value
+ // storage statistics.
+ Metrics RemoteKVMetrics
+
+ // GRPCMetrics is used for the collection of the protobuf communication
// statistics.
- //
- // TODO(e.burkov): Perhaps, it worths of a separate metrics interface,
- // since it's only used for the collection of the protobuf errors.
- Metrics Metrics
+ GRPCMetrics GRPCMetrics
// Endpoint is the backend API URL. The scheme should be either "grpc" or
// "grpcs".
@@ -35,10 +37,11 @@ type RemoteKVConfig struct {
// uses the business logic backend as the key-value storage. It is safe for
// concurrent use.
type RemoteKV struct {
- metrics Metrics
- client RemoteKVServiceClient
- apiKey string
- ttl time.Duration
+ grpcMetrics GRPCMetrics
+ metrics RemoteKVMetrics
+ client RemoteKVServiceClient
+ apiKey string
+ ttl time.Duration
}
// NewRemoteKV returns a new [RemoteKV] that retrieves information from the
@@ -51,10 +54,11 @@ func NewRemoteKV(c *RemoteKVConfig) (kv *RemoteKV, err error) {
}
return &RemoteKV{
- metrics: c.Metrics,
- client: NewRemoteKVServiceClient(client),
- apiKey: c.APIKey,
- ttl: c.TTL,
+ grpcMetrics: c.GRPCMetrics,
+ metrics: c.Metrics,
+ client: NewRemoteKVServiceClient(client),
+ apiKey: c.APIKey,
+ ttl: c.TTL,
}, nil
}
@@ -68,14 +72,34 @@ func (kv *RemoteKV) Get(ctx context.Context, key string) (val []byte, ok bool, e
}
ctx = ctxWithAuthentication(ctx, kv.apiKey)
+
+ start := time.Now()
resp, err := kv.client.Get(ctx, req)
if err != nil {
- return nil, false, fmt.Errorf("getting %q key: %w", key, fixGRPCError(ctx, kv.metrics, err))
+ err = fmt.Errorf("getting %q key: %w", key, fixGRPCError(ctx, kv.grpcMetrics, err))
+
+ return nil, false, err
}
- val = resp.GetData()
+ kv.metrics.ObserveOperation(ctx, RemoteKVOpGet, time.Since(start))
- return val, val != nil, nil
+ defer func() { kv.metrics.IncrementLookups(ctx, ok) }()
+
+ received := resp.GetValue()
+
+ switch received := received.(type) {
+ case *RemoteKVGetResponse_Data:
+ return received.Data, true, nil
+ case *RemoteKVGetResponse_Empty:
+ return nil, false, nil
+ default:
+ return nil, false, fmt.Errorf(
+ "getting %q key: response type: %w: %T(%[3]v)",
+ key,
+ errors.ErrBadEnumValue,
+ received,
+ )
+ }
}
// Set implements the [remotekv.Interface] interface for *RemoteKV.
@@ -87,10 +111,14 @@ func (kv *RemoteKV) Set(ctx context.Context, key string, val []byte) (err error)
}
ctx = ctxWithAuthentication(ctx, kv.apiKey)
+
+ start := time.Now()
_, err = kv.client.Set(ctx, req)
if err != nil {
- return fmt.Errorf("setting %q key: %w", key, fixGRPCError(ctx, kv.metrics, err))
+ return fmt.Errorf("setting %q key: %w", key, fixGRPCError(ctx, kv.grpcMetrics, err))
}
+ kv.metrics.ObserveOperation(ctx, RemoteKVOpSet, time.Since(start))
+
return nil
}
diff --git a/internal/backendpb/remotekv_test.go b/internal/backendpb/remotekv_test.go
index 612ae7e..0f367f5 100644
--- a/internal/backendpb/remotekv_test.go
+++ b/internal/backendpb/remotekv_test.go
@@ -65,7 +65,8 @@ func TestRemoteKV_Get(t *testing.T) {
t.Cleanup(grpcSrv.GracefulStop)
kv, err := backendpb.NewRemoteKV(&backendpb.RemoteKVConfig{
- Metrics: backendpb.EmptyMetrics{},
+ GRPCMetrics: backendpb.EmptyGRPCMetrics{},
+ Metrics: backendpb.EmptyRemoteKVMetrics{},
Endpoint: &url.URL{
Scheme: "grpc",
Host: l.Addr().String(),
diff --git a/internal/backendpb/stats.go b/internal/backendpb/stats.go
index 3165ae5..0bbed3c 100644
--- a/internal/backendpb/stats.go
+++ b/internal/backendpb/stats.go
@@ -54,7 +54,7 @@ func (s *profilesCallStats) endDec() {
}
// report writes the statistics to the log and the metrics.
-func (s *profilesCallStats) report(ctx context.Context, mtrc Metrics) {
+func (s *profilesCallStats) report(ctx context.Context, mtrc ProfileDBMetrics) {
lvl := slog.LevelDebug
if s.isFullSync {
lvl = slog.LevelInfo
diff --git a/internal/bindtodevice/chanpacketconn_linux.go b/internal/bindtodevice/chanpacketconn_linux.go
index bca6b10..e0c7070 100644
--- a/internal/bindtodevice/chanpacketconn_linux.go
+++ b/internal/bindtodevice/chanpacketconn_linux.go
@@ -54,7 +54,9 @@ func newChanPacketConn(
sessions: sessions,
writeRequests: writeRequests,
- sessionsGauge: metrics.BindToDeviceUDPSessionsChanSize.WithLabelValues(subnet.String()),
+ sessionsGauge: metrics.BindToDeviceUDPSessionsChanSize.WithLabelValues(
+ subnet.String(),
+ ),
writeRequestsGauge: writeRequestsGauge,
deadlineMu: &sync.RWMutex{},
diff --git a/internal/bindtodevice/socket_linux.go b/internal/bindtodevice/socket_linux.go
index 54f7bc7..9c850e2 100644
--- a/internal/bindtodevice/socket_linux.go
+++ b/internal/bindtodevice/socket_linux.go
@@ -70,7 +70,12 @@ func listenControlWithSO(
opts,
newIntSetSockOptFunc("IP_RECVORIGDSTADDR", unix.IPPROTO_IP, unix.IP_RECVORIGDSTADDR, 1),
newIntSetSockOptFunc("IP_FREEBIND", unix.IPPROTO_IP, unix.IP_FREEBIND, 1),
- newIntSetSockOptFunc("IPV6_RECVORIGDSTADDR", unix.IPPROTO_IPV6, unix.IPV6_RECVORIGDSTADDR, 1),
+ newIntSetSockOptFunc(
+ "IPV6_RECVORIGDSTADDR",
+ unix.IPPROTO_IPV6,
+ unix.IPV6_RECVORIGDSTADDR,
+ 1,
+ ),
newIntSetSockOptFunc("IPV6_FREEBIND", unix.IPPROTO_IPV6, unix.IPV6_FREEBIND, 1),
)
default:
diff --git a/internal/cmd/backend.go b/internal/cmd/backend.go
index d37728e..d8824c5 100644
--- a/internal/cmd/backend.go
+++ b/internal/cmd/backend.go
@@ -6,14 +6,9 @@ import (
"log/slog"
"time"
- "github.com/AdguardTeam/AdGuardDNS/internal/backendpb"
- "github.com/AdguardTeam/AdGuardDNS/internal/billstat"
- "github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/logutil/slogutil"
- "github.com/AdguardTeam/golibs/netutil"
- "github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/AdguardTeam/golibs/timeutil"
)
@@ -91,24 +86,3 @@ func initProfDB(
return nil
}
-
-// newBillStatUploader creates and returns a billstat uploader depending on the
-// provided API URL.
-func newBillStatUploader(
- envs *environment,
- errColl errcoll.Interface,
- mtrc backendpb.Metrics,
-) (s billstat.Uploader, err error) {
- apiURL := netutil.CloneURL(&envs.BillStatURL.URL)
- err = urlutil.ValidateGRPCURL(apiURL)
- if err != nil {
- return nil, fmt.Errorf("billstat api url: %w", err)
- }
-
- return backendpb.NewBillStat(&backendpb.BillStatConfig{
- ErrColl: errColl,
- Metrics: mtrc,
- Endpoint: apiURL,
- APIKey: envs.BillStatAPIKey,
- })
-}
diff --git a/internal/cmd/builder.go b/internal/cmd/builder.go
index a081d7e..c286311 100644
--- a/internal/cmd/builder.go
+++ b/internal/cmd/builder.go
@@ -16,6 +16,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/backendpb"
"github.com/AdguardTeam/AdGuardDNS/internal/billstat"
"github.com/AdguardTeam/AdGuardDNS/internal/bindtodevice"
@@ -32,6 +33,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnssvc"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter/filterstorage"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
@@ -57,6 +59,7 @@ const (
debugIDProfileDB = "profiledb"
debugIDRuleStat = "rulestat"
debugIDTicketRotator = "ticket_rotator"
+ debugIDTLSConfig = "tlsconfig"
debugIDWebSvc = "websvc"
)
@@ -76,6 +79,7 @@ type builder struct {
cacheManager *agdcache.DefaultManager
cloner *dnsmsg.Cloner
conf *configuration
+ debugRefrs debugsvc.Refreshers
env *environment
errColl errcoll.Interface
geoIPError chan error
@@ -91,7 +95,7 @@ type builder struct {
access *access.Global
adultBlocking *hashprefix.Filter
adultBlockingHashes *hashprefix.Storage
- backendGRPCMtrc *metrics.BackendPB
+ backendGRPCMtrc *metrics.BackendGRPC
billStat billstat.Recorder
bindSet netutil.SubnetSet
btdManager *bindtodevice.Manager
@@ -100,7 +104,8 @@ type builder struct {
dnsCheck dnscheck.Interface
dnsDB dnsdb.Interface
dnsSvc *dnssvc.Service
- filterStorage *filter.DefaultStorage
+ filterMtrc filter.Metrics
+ filterStorage *filterstorage.Default
filteringGroups map[agd.FilteringGroupID]*agd.FilteringGroup
fwdHandler *forward.Handler
geoIP *geoip.File
@@ -110,12 +115,11 @@ type builder struct {
newRegDomainsHashes *hashprefix.Storage
profileDB profiledb.Interface
rateLimit *ratelimit.Backoff
- debugRefrs debugsvc.Refreshers
ruleStat rulestat.Interface
safeBrowsing *hashprefix.Filter
safeBrowsingHashes *hashprefix.Storage
sdeConf *dnsmsg.StructuredDNSErrorsConfig
- tlsMtrc tlsconfig.Metrics
+ tlsManager *tlsconfig.DefaultManager
webSvc *websvc.Service
// The fields below are initialized later, just like with the fields above,
@@ -201,8 +205,8 @@ func (b *builder) initGeoIP(ctx context.Context) {
CacheManager: b.cacheManager,
ASNPath: asn,
CountryPath: ctry,
- HostCacheSize: c.HostCacheSize,
- IPCacheSize: c.IPCacheSize,
+ HostCacheCount: c.HostCacheSize,
+ IPCacheCount: c.IPCacheSize,
AllTopASNs: geoip.DefaultTopASNs,
CountryTopASNs: geoip.DefaultCountryTopASNs,
})
@@ -225,6 +229,11 @@ func (b *builder) initHashPrefixFilters(ctx context.Context) (err error) {
matchers := map[string]*hashprefix.Storage{}
+ b.filterMtrc, err = metrics.NewFilter(b.mtrcNamespace, b.promRegisterer)
+ if err != nil {
+ return fmt.Errorf("registering filter metrics: %w", err)
+ }
+
// TODO(a.garipov): Merge the three functions below together.
err = b.initAdultBlocking(ctx, matchers, maxSize, cacheDir)
@@ -251,8 +260,7 @@ func (b *builder) initHashPrefixFilters(ctx context.Context) (err error) {
// initAdultBlocking initializes the adult-blocking filter and hash storage. It
// also adds the refresher with ID
-// [hashprefix.IDPrefix]/[agd.FilterListIDAdultBlocking] to the debug
-// refreshers.
+// [hashprefix.IDPrefix]/[filter.IDAdultBlocking] to the debug refreshers.
//
// It must be called from [builder.initHashPrefixFilters].
func (b *builder) initAdultBlocking(
@@ -272,7 +280,7 @@ func (b *builder) initAdultBlocking(
}
c := b.conf.AdultBlocking
- id := agd.FilterListIDAdultBlocking
+ id := filter.IDAdultBlocking
prefix := path.Join(hashprefix.IDPrefix, string(id))
b.adultBlocking, err = hashprefix.NewFilter(&hashprefix.FilterConfig{
Logger: b.baseLogger.With(slogutil.KeyPrefix, prefix),
@@ -281,6 +289,7 @@ func (b *builder) initAdultBlocking(
Hashes: b.adultBlockingHashes,
URL: &b.env.AdultBlockingURL.URL,
ErrColl: b.errColl,
+ Metrics: b.filterMtrc,
ID: id,
CachePath: filepath.Join(cacheDir, string(id)),
ReplacementHost: c.BlockHost,
@@ -289,8 +298,8 @@ func (b *builder) initAdultBlocking(
CacheTTL: c.CacheTTL.Duration,
// TODO(a.garipov): Make all sizes [datasize.ByteSize] and rename cache
// entity counts to fooCount.
- CacheSize: c.CacheSize,
- MaxSize: maxSize,
+ CacheCount: c.CacheSize,
+ MaxSize: maxSize,
})
if err != nil {
return fmt.Errorf("creating filter: %w", err)
@@ -327,8 +336,7 @@ func (b *builder) initAdultBlocking(
// initNewRegDomains initializes the newly-registered domain filter and hash
// storage. It also adds the refresher with ID
-// [hashprefix.IDPrefix]/[agd.FilterListIDNewRegDomains] to the debug
-// refreshers.
+// [hashprefix.IDPrefix]/[filter.IDNewRegDomains] to the debug refreshers.
//
// It must be called from [builder.initHashPrefixFilters].
func (b *builder) initNewRegDomains(
@@ -349,7 +357,7 @@ func (b *builder) initNewRegDomains(
// Reuse the general safe-browsing filter configuration with a new URL and
// ID.
c := b.conf.SafeBrowsing
- id := agd.FilterListIDNewRegDomains
+ id := filter.IDNewRegDomains
prefix := path.Join(hashprefix.IDPrefix, string(id))
b.newRegDomains, err = hashprefix.NewFilter(&hashprefix.FilterConfig{
Logger: b.baseLogger.With(slogutil.KeyPrefix, prefix),
@@ -358,13 +366,14 @@ func (b *builder) initNewRegDomains(
Hashes: b.newRegDomainsHashes,
URL: &b.env.NewRegDomainsURL.URL,
ErrColl: b.errColl,
+ Metrics: b.filterMtrc,
ID: id,
CachePath: filepath.Join(cacheDir, string(id)),
ReplacementHost: c.BlockHost,
Staleness: c.RefreshIvl.Duration,
RefreshTimeout: c.RefreshTimeout.Duration,
CacheTTL: c.CacheTTL.Duration,
- CacheSize: c.CacheSize,
+ CacheCount: c.CacheSize,
MaxSize: maxSize,
})
if err != nil {
@@ -399,8 +408,8 @@ func (b *builder) initNewRegDomains(
}
// initSafeBrowsing initializes the safe-browsing filter and hash storage. It
-// also adds the refresher with ID
-// [hashprefix.IDPrefix]/[agd.FilterListIDSafeBrowsing] to the debug refreshers.
+// also adds the refresher with ID [hashprefix.IDPrefix]/[filter.IDSafeBrowsing]
+// to the debug refreshers.
//
// It must be called from [builder.initHashPrefixFilters].
func (b *builder) initSafeBrowsing(
@@ -420,7 +429,7 @@ func (b *builder) initSafeBrowsing(
}
c := b.conf.SafeBrowsing
- id := agd.FilterListIDSafeBrowsing
+ id := filter.IDSafeBrowsing
prefix := path.Join(hashprefix.IDPrefix, string(id))
b.safeBrowsing, err = hashprefix.NewFilter(&hashprefix.FilterConfig{
Logger: b.baseLogger.With(slogutil.KeyPrefix, prefix),
@@ -429,13 +438,14 @@ func (b *builder) initSafeBrowsing(
Hashes: b.safeBrowsingHashes,
URL: &b.env.SafeBrowsingURL.URL,
ErrColl: b.errColl,
+ Metrics: b.filterMtrc,
ID: id,
CachePath: filepath.Join(cacheDir, string(id)),
ReplacementHost: c.BlockHost,
Staleness: c.RefreshIvl.Duration,
RefreshTimeout: c.RefreshTimeout.Duration,
CacheTTL: c.CacheTTL.Duration,
- CacheSize: c.CacheSize,
+ CacheCount: c.CacheSize,
MaxSize: maxSize,
})
if err != nil {
@@ -477,26 +487,78 @@ func (b *builder) initSafeBrowsing(
// [builder.initHashPrefixFilters] must be called before this method.
func (b *builder) initFilterStorage(ctx context.Context) (err error) {
c := b.conf.Filters
- b.filterStorage = filter.NewDefaultStorage(c.toInternal(
- b.baseLogger,
- b.errColl,
- b.cacheManager,
- b.env,
- b.safeBrowsing,
- b.adultBlocking,
- b.newRegDomains,
- ))
+ refrIvl := c.RefreshIvl.Duration
+ refrTimeout := c.RefreshTimeout.Duration
- err = b.filterStorage.RefreshInitial(ctx)
+ b.filterStorage, err = filterstorage.New(&filterstorage.Config{
+ BaseLogger: b.baseLogger,
+ Logger: b.baseLogger.With(slogutil.KeyPrefix, filter.StoragePrefix),
+ BlockedServices: &filterstorage.ConfigBlockedServices{
+ IndexURL: &b.env.BlockedServiceIndexURL.URL,
+ // TODO(a.garipov): Consider adding a separate parameter here.
+ IndexMaxSize: c.MaxSize,
+ // TODO(a.garipov): Consider making configurable.
+ IndexRefreshTimeout: 3 * time.Minute,
+ // TODO(a.garipov): Consider adding a separate parameter here.
+ IndexStaleness: refrIvl,
+ // TODO(a.garipov): Consider adding a separate parameter here.
+ ResultCacheCount: c.RuleListCache.Size,
+ // TODO(a.garipov): Consider adding a separate parameter here.
+ ResultCacheEnabled: c.RuleListCache.Enabled,
+ Enabled: bool(b.env.BlockedServiceEnabled),
+ },
+ Custom: &filterstorage.ConfigCustom{
+ CacheCount: c.CustomFilterCacheSize,
+ },
+ HashPrefix: &filterstorage.ConfigHashPrefix{
+ Adult: b.adultBlocking,
+ Dangerous: b.safeBrowsing,
+ NewlyRegistered: b.newRegDomains,
+ },
+ RuleLists: &filterstorage.ConfigRuleLists{
+ IndexURL: &b.env.FilterIndexURL.URL,
+ // TODO(a.garipov): Consider adding a separate parameter here.
+ IndexMaxSize: c.MaxSize,
+ MaxSize: c.MaxSize,
+ IndexRefreshTimeout: c.IndexRefreshTimeout.Duration,
+ // TODO(a.garipov): Consider adding a separate parameter here.
+ IndexStaleness: refrIvl,
+ RefreshTimeout: refrTimeout,
+ // TODO(a.garipov): Consider adding a separate parameter here.
+ Staleness: refrIvl,
+ ResultCacheCount: c.RuleListCache.Size,
+ ResultCacheEnabled: c.RuleListCache.Enabled,
+ },
+ SafeSearchGeneral: b.newSafeSearchConfig(
+ b.env.GeneralSafeSearchURL,
+ filter.IDGeneralSafeSearch,
+ bool(b.env.GeneralSafeSearchEnabled),
+ ),
+ SafeSearchYouTube: b.newSafeSearchConfig(
+ b.env.YoutubeSafeSearchURL,
+ filter.IDYoutubeSafeSearch,
+ bool(b.env.YoutubeSafeSearchEnabled),
+ ),
+ CacheManager: b.cacheManager,
+ Clock: agdtime.SystemClock{},
+ ErrColl: b.errColl,
+ Metrics: b.filterMtrc,
+ CacheDir: b.env.FilterCachePath,
+ })
if err != nil {
return fmt.Errorf("creating default filter storage: %w", err)
}
+ err = b.filterStorage.RefreshInitial(ctx)
+ if err != nil {
+ return fmt.Errorf("refreshing default filter storage: %w", err)
+ }
+
refr := agdservice.NewRefreshWorker(&agdservice.RefreshWorkerConfig{
- Context: newCtxWithTimeoutCons(c.RefreshIvl.Duration),
+ Context: newCtxWithTimeoutCons(refrTimeout),
Refresher: b.filterStorage,
Logger: b.baseLogger.With(slogutil.KeyPrefix, "filters/storage_refresh"),
- Interval: c.RefreshIvl.Duration,
+ Interval: refrIvl,
RefreshOnShutdown: false,
RandomizeStart: false,
})
@@ -514,6 +576,35 @@ func (b *builder) initFilterStorage(ctx context.Context) (err error) {
return nil
}
+// newSafeSearchConfig returns a new safe-search configuration for the given URL
+// and ID if enabled; otherwise, it returns an empty configuration.
+func (b *builder) newSafeSearchConfig(
+ u *urlutil.URL,
+ id filter.ID,
+ enabled bool,
+) (c *filterstorage.ConfigSafeSearch) {
+ if !enabled {
+ return &filterstorage.ConfigSafeSearch{}
+ }
+
+ fltConf := b.conf.Filters
+
+ return &filterstorage.ConfigSafeSearch{
+ URL: &u.URL,
+ ID: id,
+ // TODO(a.garipov): Consider adding a separate parameter here.
+ MaxSize: fltConf.MaxSize,
+ // TODO(a.garipov): Consider making this configurable.
+ ResultCacheTTL: 1 * time.Hour,
+ // TODO(a.garipov): Consider adding a separate parameter here.
+ RefreshTimeout: fltConf.RefreshTimeout.Duration,
+ // TODO(a.garipov): Consider adding a separate parameter here.
+ Staleness: fltConf.RefreshIvl.Duration,
+ ResultCacheCount: fltConf.SafeSearchCacheSize,
+ Enabled: true,
+ }
+}
+
// initFilteringGroups initializes the filtering groups.
//
// [builder.initFilterStorage] must be called before this method.
@@ -606,26 +697,54 @@ func (b *builder) initMsgConstructor(ctx context.Context) (err error) {
return nil
}
+// initTLSManager initializes the TLS manager and the TLS-related metrics. It
+// also adds the refresher with ID [debugIDTLSConfig] to the debug refreshers.
+func (b *builder) initTLSManager(ctx context.Context) (err error) {
+ mtrc, err := metrics.NewTLSConfig(b.mtrcNamespace, b.promRegisterer)
+ if err != nil {
+ return fmt.Errorf("registering tls metrics: %w", err)
+ }
+
+ logFile := b.env.SSLKeyLogFile
+ if logFile != "" {
+ b.logger.WarnContext(ctx, "tls key logging is enabled", "file", logFile)
+ }
+
+ ticketPaths := b.conf.ServerGroups.collectSessTicketPaths()
+
+ mgr, err := tlsconfig.NewDefaultManager(&tlsconfig.DefaultManagerConfig{
+ Logger: b.baseLogger.With(slogutil.KeyPrefix, "tlsconfig"),
+ ErrColl: b.errColl,
+ Metrics: mtrc,
+ KeyLogFilename: logFile,
+ SessionTicketPaths: ticketPaths,
+ })
+ if err != nil {
+ return fmt.Errorf("initializing tls manager: %w", err)
+ }
+
+ b.tlsManager = mgr
+ b.debugRefrs[debugIDTLSConfig] = mgr
+
+ b.logger.DebugContext(ctx, "initialized tls manager")
+
+ return nil
+}
+
// initServerGroups initializes the server groups.
//
// The following methods must be called before this one:
// - [builder.initBindToDevice]
// - [builder.initFilteringGroups]
// - [builder.initMsgConstructor]
+// - [builder.initTLSManager]
func (b *builder) initServerGroups(ctx context.Context) (err error) {
- mtrc, err := metrics.NewTLSConfig(b.mtrcNamespace, b.promRegisterer)
- if err != nil {
- return fmt.Errorf("registering tls metrics: %w", err)
- }
-
- b.tlsMtrc = mtrc
-
c := b.conf
b.serverGroups, err = c.ServerGroups.toInternal(
ctx,
- mtrc,
b.messages,
b.btdManager,
+ b.tlsManager,
b.filteringGroups,
c.RateLimit,
c.DNS,
@@ -699,22 +818,12 @@ func (b *builder) startBindToDevice(ctx context.Context) (err error) {
return nil
}
-// initTLS initializes the optional TLS key logging and session-ticket rotation.
-// It also adds the refresher with ID [debugIDTicketRotator] to the debug
-// refreshers.
+// initTicketRotator initializes the TLS session ticket rotator. It also adds
+// the refresher with ID [debugIDTicketRotator] to the debug refreshers.
//
-// [builder.initServerGroups] must be called before this method.
-func (b *builder) initTLS(ctx context.Context) (err error) {
- if f := b.env.SSLKeyLogFile; f != "" {
- b.logger.WarnContext(ctx, "IMPORTANT: TLS KEY LOGGING IS ENABLED", "ssl_key_log_file", f)
-
- err = enableTLSKeyLogging(b.serverGroups, f)
- if err != nil {
- return fmt.Errorf("enabling tls key logging: %w", err)
- }
- }
-
- tickRot := newTicketRotator(b.baseLogger, b.errColl, b.tlsMtrc, b.serverGroups)
+// [builder.initTLSManager] must be called before this method.
+func (b *builder) initTicketRotator(ctx context.Context) (err error) {
+ tickRot := agdservice.RefresherFunc(b.tlsManager.RotateTickets)
err = tickRot.Refresh(ctx)
if err != nil {
return fmt.Errorf("initial session ticket refresh: %w", err)
@@ -744,6 +853,7 @@ func (b *builder) initTLS(ctx context.Context) (err error) {
}
// initGRPCMetrics initializes the gRPC metrics if necessary.
+// [builder.initServerGroups] must be called before this method.
func (b *builder) initGRPCMetrics(ctx context.Context) (err error) {
switch {
case
@@ -756,18 +866,19 @@ func (b *builder) initGRPCMetrics(ctx context.Context) (err error) {
return nil
}
- b.backendGRPCMtrc, err = metrics.NewBackendPB(b.mtrcNamespace, b.promRegisterer)
+ b.backendGRPCMtrc, err = metrics.NewBackendGRPC(b.mtrcNamespace, b.promRegisterer)
if err != nil {
- return fmt.Errorf("registering backendbp metrics: %w", err)
+ return fmt.Errorf("registering backend grpc metrics: %w", err)
}
- b.logger.DebugContext(ctx, "initialized grpc metrics")
+ b.logger.DebugContext(ctx, "initialized backend grpc metrics")
return nil
}
// initBillStat initializes the billing-statistics recorder if necessary. It
// also adds the refresher with ID [debugIDBillStat] to the debug refreshers.
+// [builder.initGRPCMetrics] must be called before this method.
func (b *builder) initBillStat(ctx context.Context) (err error) {
if !b.profilesEnabled {
b.billStat = billstat.EmptyRecorder{}
@@ -775,7 +886,7 @@ func (b *builder) initBillStat(ctx context.Context) (err error) {
return nil
}
- upl, err := newBillStatUploader(b.env, b.errColl, b.backendGRPCMtrc)
+ upl, err := b.newBillStatUploader()
if err != nil {
return fmt.Errorf("creating billstat uploader: %w", err)
}
@@ -819,11 +930,28 @@ func (b *builder) initBillStat(ctx context.Context) (err error) {
return nil
}
+// newBillStatUploader creates and returns a billstat uploader depending on the
+// provided API URL.
+func (b *builder) newBillStatUploader() (s billstat.Uploader, err error) {
+ apiURL := netutil.CloneURL(&b.env.BillStatURL.URL)
+ err = urlutil.ValidateGRPCURL(apiURL)
+ if err != nil {
+ return nil, fmt.Errorf("billstat api url: %w", err)
+ }
+
+ return backendpb.NewBillStat(&backendpb.BillStatConfig{
+ Logger: b.baseLogger.With(slogutil.KeyPrefix, "billstat_uploader"),
+ ErrColl: b.errColl,
+ GRPCMetrics: b.backendGRPCMtrc,
+ Endpoint: apiURL,
+ APIKey: b.env.BillStatAPIKey,
+ })
+}
+
// initProfileDB initializes the profile database if necessary.
//
-// [builder.initGRPCMetrics] and [builder.initServerGroups] must be called
-// before this method. It also adds the refresher with ID [debugIDProfileDB] to
-// the debug refreshers.
+// [builder.initGRPCMetrics] must be called before this method. It also adds
+// the refresher with ID [debugIDProfileDB] to the debug refreshers.
func (b *builder) initProfileDB(ctx context.Context) (err error) {
if !b.profilesEnabled {
b.profileDB = &profiledb.Disabled{}
@@ -837,12 +965,18 @@ func (b *builder) initProfileDB(ctx context.Context) (err error) {
return fmt.Errorf("profile api url: %w", err)
}
+ backendProfileDBMtrc, err := metrics.NewBackendProfileDB(b.mtrcNamespace, b.promRegisterer)
+ if err != nil {
+ return fmt.Errorf("registering backend grpc profile metrics: %w", err)
+ }
+
respSzEst := b.conf.RateLimit.ResponseSizeEstimate
strg, err := backendpb.NewProfileStorage(&backendpb.ProfileStorageConfig{
BindSet: b.bindSet,
ErrColl: b.errColl,
- Logger: b.baseLogger.With(slogutil.KeyPrefix, "backendpb"),
- Metrics: b.backendGRPCMtrc,
+ Logger: b.baseLogger.With(slogutil.KeyPrefix, "profilestorage"),
+ GRPCMetrics: b.backendGRPCMtrc,
+ Metrics: backendProfileDBMtrc,
Endpoint: apiURL,
APIKey: b.env.ProfilesAPIKey,
ResponseSizeEstimate: respSzEst,
@@ -917,6 +1051,7 @@ func (b *builder) initDNSCheck(ctx context.Context) (err error) {
c := b.conf.Check
checkConf, err := c.toInternal(
+ b.baseLogger,
b.env,
b.messages,
b.errColl,
@@ -1051,12 +1186,10 @@ func (b *builder) initRateLimiter(ctx context.Context) (err error) {
}
// initWeb initializes the web service, starts it, and registers it in the
-// signal handler.
-//
-// [builder.initServerGroups] must be called before this method.
+// signal handler. [builder.initDNSCheck] must be call before this method.
func (b *builder) initWeb(ctx context.Context) (err error) {
c := b.conf.Web
- webConf, err := c.toInternal(ctx, b.env, b.dnsCheck, b.errColl, b.tlsMtrc)
+ webConf, err := c.toInternal(ctx, b.env, b.dnsCheck, b.errColl, b.tlsManager)
if err != nil {
return fmt.Errorf("converting web configuration: %w", err)
}
@@ -1142,7 +1275,6 @@ func (b *builder) waitGeoIP(ctx context.Context) (err error) {
// - [builder.initAccess]
// - [builder.initBillStat]
// - [builder.initBindToDevice]
-// - [builder.initDNSCheck]
// - [builder.initFilterStorage]
// - [builder.initFilteringGroups]
// - [builder.initMsgConstructor]
@@ -1153,7 +1285,7 @@ func (b *builder) waitGeoIP(ctx context.Context) (err error) {
// - [builder.waitGeoIP]
func (b *builder) initDNS(ctx context.Context) (err error) {
b.fwdHandler = forward.NewHandler(b.conf.Upstream.toInternal(b.baseLogger))
- b.dnsDB = b.conf.DNSDB.toInternal(b.errColl)
+ b.dnsDB = b.conf.DNSDB.toInternal(b.baseLogger, b.errColl)
dnsHdlrsConf := &dnssvc.HandlersConfig{
BaseLogger: b.baseLogger,
diff --git a/internal/cmd/check.go b/internal/cmd/check.go
index 142f771..72f572f 100644
--- a/internal/cmd/check.go
+++ b/internal/cmd/check.go
@@ -2,10 +2,12 @@ package cmd
import (
"fmt"
+ "log/slog"
"net/netip"
"strings"
"time"
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
"github.com/AdguardTeam/AdGuardDNS/internal/backendpb"
"github.com/AdguardTeam/AdGuardDNS/internal/dnscheck"
@@ -17,6 +19,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/remotekv/rediskv"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/timeutil"
"github.com/c2h5oh/datasize"
@@ -50,14 +53,15 @@ type checkConfig struct {
// toInternal converts c to the DNS server check configuration for the DNS
// server. c must be valid.
func (c *checkConfig) toInternal(
+ baseLogger *slog.Logger,
envs *environment,
messages *dnsmsg.Constructor,
errColl errcoll.Interface,
namespace string,
reg prometheus.Registerer,
- backendMtrc backendpb.Metrics,
+ grpcMtrc backendpb.GRPCMetrics,
) (conf *dnscheck.RemoteKVConfig, err error) {
- kv, err := newRemoteKV(c.RemoteKV, envs, namespace, reg, backendMtrc)
+ kv, err := c.RemoteKV.newRemoteKV(envs, namespace, reg, grpcMtrc)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return nil, err
@@ -69,6 +73,7 @@ func (c *checkConfig) toInternal(
}
return &dnscheck.RemoteKVConfig{
+ Logger: baseLogger.With(slogutil.KeyPrefix, "dnscheck"),
Messages: messages,
RemoteKV: kv,
ErrColl: errColl,
@@ -87,33 +92,42 @@ const maxRespSize = 1 * datasize.MB
// [remotekv.KeyNamespace].
const keyNamespaceCheck = "check"
-// newRemoteKV returns a new properly initialized remote key-value storage.
-func newRemoteKV(
- c *remoteKVConfig,
+// newRemoteKV returns a new properly initialized remote key-value storage. c
+// must be valid. grpcMtrc should be registered before calling this method.
+func (c *remoteKVConfig) newRemoteKV(
envs *environment,
namespace string,
reg prometheus.Registerer,
- backendMtrc backendpb.Metrics,
+ grpcMtrc backendpb.GRPCMetrics,
) (kv remotekv.Interface, err error) {
switch c.Type {
case kvModeBackend:
- kv, err = backendpb.NewRemoteKV(&backendpb.RemoteKVConfig{
- Metrics: backendMtrc,
- Endpoint: &envs.DNSCheckRemoteKVURL.URL,
- APIKey: envs.DNSCheckRemoteKVAPIKey,
- TTL: c.TTL.Duration,
- })
+ var backendKVMtrc *metrics.BackendRemoteKV
+ backendKVMtrc, err = metrics.NewBackendRemoteKV(namespace, reg)
+ if err != nil {
+ return nil, fmt.Errorf("registering backend kv metrics: %w", err)
+ }
+
+ kv, err = c.newBackendRemoteKV(envs, backendKVMtrc, grpcMtrc)
if err != nil {
return nil, fmt.Errorf("initializing backend dnscheck kv: %w", err)
}
+ case kvModeCache:
+ // TODO(e.burkov): The local cache in [dnscheck.RemoteKV] becomes
+ // pointless with this mode.
+ return remotekv.NewCache(&remotekv.CacheConfig{
+ Cache: agdcache.NewLRU[string, []byte](&agdcache.LRUConfig{
+ Count: envs.DNSCheckCacheKVSize,
+ }),
+ }), nil
case kvModeRedis:
- var redisKVMtrc rediskv.Metrics
+ var redisKVMtrc *metrics.RedisKV
redisKVMtrc, err = metrics.NewRedisKV(namespace, reg)
if err != nil {
return nil, fmt.Errorf("registering redis kv metrics: %w", err)
}
- redisKV := rediskv.NewRedisKV(&rediskv.RedisKVConfig{
+ kv = rediskv.NewRedisKV(&rediskv.RedisKVConfig{
Metrics: redisKVMtrc,
Addr: &netutil.HostPort{
Host: envs.RedisAddr,
@@ -124,37 +138,81 @@ func newRemoteKV(
IdleTimeout: envs.RedisIdleTimeout.Duration,
TTL: c.TTL.Duration,
})
-
- kv = remotekv.NewKeyNamespace(&remotekv.KeyNamespaceConfig{
- KV: redisKV,
- Prefix: fmt.Sprintf("%s:%s:", envs.RedisKeyPrefix, keyNamespaceCheck),
- })
case kvModeConsul:
- consulKVURL := envs.ConsulDNSCheckKVURL
- consulSessionURL := envs.ConsulDNSCheckSessionURL
- if consulKVURL == nil || consulSessionURL == nil {
- return remotekv.Empty{}, nil
- }
-
- kv, err = consulkv.NewKV(&consulkv.Config{
- URL: &consulKVURL.URL,
- SessionURL: &consulSessionURL.URL,
- Client: agdhttp.NewClient(&agdhttp.ClientConfig{
- // TODO(ameshkov): Consider making configurable.
- Timeout: 15 * time.Second,
- }),
- // TODO(ameshkov): Consider making configurable.
- Limiter: rate.NewLimiter(rate.Limit(200)/60, 1),
- TTL: c.TTL.Duration,
- MaxRespSize: maxRespSize,
- })
+ kv, err = c.newConsulRemoteKV(envs)
if err != nil {
return nil, fmt.Errorf("initializing consul dnscheck kv: %w", err)
}
default:
+ panic(fmt.Errorf("dnscheck kv type: %w: %q", errors.ErrBadEnumValue, c.Type))
+ }
+
+ return remotekv.NewKeyNamespace(&remotekv.KeyNamespaceConfig{
+ KV: kv,
+ Prefix: newRemoveKVPrefix(envs, c.Type),
+ }), nil
+}
+
+// newBackendRemoteKV returns a new properly initialized backend remote
+// key-value storage. c must be valid.
+//
+// TODO(e.burkov): Add key namespace.
+func (c *remoteKVConfig) newBackendRemoteKV(
+ envs *environment,
+ backendKVMtrc *metrics.BackendRemoteKV,
+ grpcMtrc backendpb.GRPCMetrics,
+) (kv *backendpb.RemoteKV, err error) {
+ kv, err = backendpb.NewRemoteKV(&backendpb.RemoteKVConfig{
+ GRPCMetrics: grpcMtrc,
+ Metrics: backendKVMtrc,
+ Endpoint: &envs.DNSCheckRemoteKVURL.URL,
+ APIKey: envs.DNSCheckRemoteKVAPIKey,
+ TTL: c.TTL.Duration,
+ })
+ if err != nil {
+ return nil, fmt.Errorf("initializing backend dnscheck kv: %w", err)
+ }
+
+ return kv, nil
+}
+
+// newRemoveKVPrefix returns a remote KV custom prefix for the keys.
+func newRemoveKVPrefix(envs *environment, kvType string) (pref string) {
+ switch kvType {
+ case kvModeBackend, kvModeCache, kvModeConsul:
+ return fmt.Sprintf("%s:%s:", kvType, keyNamespaceCheck)
+ case kvModeRedis:
+ return fmt.Sprintf("%s:%s:", envs.RedisKeyPrefix, keyNamespaceCheck)
+ default:
+ panic(fmt.Errorf("dnscheck kv type: %w: %q", errors.ErrBadEnumValue, kvType))
+ }
+}
+
+// newConsulRemoteKV returns a new properly initialized Consul-based remote
+// key-value storage. c must be valid.
+func (c *remoteKVConfig) newConsulRemoteKV(envs *environment) (kv remotekv.Interface, err error) {
+ consulKVURL := envs.ConsulDNSCheckKVURL
+ consulSessionURL := envs.ConsulDNSCheckSessionURL
+ if consulKVURL == nil || consulSessionURL == nil {
return remotekv.Empty{}, nil
}
+ kv, err = consulkv.NewKV(&consulkv.Config{
+ URL: &consulKVURL.URL,
+ SessionURL: &consulSessionURL.URL,
+ Client: agdhttp.NewClient(&agdhttp.ClientConfig{
+ // TODO(ameshkov): Consider making configurable.
+ Timeout: 15 * time.Second,
+ }),
+ // TODO(ameshkov): Consider making configurable.
+ Limiter: rate.NewLimiter(rate.Limit(200)/60, 1),
+ TTL: c.TTL.Duration,
+ MaxRespSize: maxRespSize,
+ })
+ if err != nil {
+ return nil, fmt.Errorf("initializing consul dnscheck kv: %w", err)
+ }
+
return kv, nil
}
@@ -238,6 +296,7 @@ func validateNonNilIPs(ips []netip.Addr, fam netutil.AddrFamily) (err error) {
// DNSCheck key-value database modes.
const (
kvModeBackend = "backend"
+ kvModeCache = "cache"
kvModeConsul = "consul"
kvModeRedis = "redis"
)
@@ -246,7 +305,7 @@ const (
// checking.
type remoteKVConfig struct {
// Type defines the type of remote key-value store. Allowed values are
- // [kvModeBackend], [kvModeConsul] and [kvModeRedis].
+ // [kvModeBackend], [kvModeCache], [kvModeConsul] and [kvModeRedis].
Type string `yaml:"type"`
// TTL defines, for how long to keep the information about a single client.
@@ -269,6 +328,8 @@ func (c *remoteKVConfig) validate() (err error) {
if ttl.Duration <= 0 {
return newNotPositiveError("ttl", ttl)
}
+ case kvModeCache:
+ // Go on.
case kvModeConsul:
if ttl.Duration < consulkv.MinTTL || ttl.Duration > consulkv.MaxTTL {
return fmt.Errorf(
@@ -288,8 +349,6 @@ func (c *remoteKVConfig) validate() (err error) {
ttl,
)
}
- case "":
- return fmt.Errorf("type: %w", errors.ErrEmptyValue)
default:
return fmt.Errorf("type: %w: %q", errors.ErrBadEnumValue, c.Type)
}
diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go
index f66aba5..1feb5ab 100644
--- a/internal/cmd/cmd.go
+++ b/internal/cmd/cmd.go
@@ -94,11 +94,13 @@ func Main(plugins *plugin.Registry) {
errors.Check(b.initMsgConstructor(ctx))
+ errors.Check(b.initTLSManager(ctx))
+
errors.Check(b.initServerGroups(ctx))
errors.Check(b.startBindToDevice(ctx))
- errors.Check(b.initTLS(ctx))
+ errors.Check(b.initTicketRotator(ctx))
errors.Check(b.initGRPCMetrics(ctx))
diff --git a/internal/cmd/conncheck.go b/internal/cmd/conncheck.go
index 441aa0f..0ab6a78 100644
--- a/internal/cmd/conncheck.go
+++ b/internal/cmd/conncheck.go
@@ -48,8 +48,8 @@ func connectivityCheck(srvGrps []*agd.ServerGroup, connCheck *connCheckConfig) (
}
defer func() {
- errClose := errors.Annotate(conn4.Close(), "connectivity check: closing ipv4: %w")
- err = errors.WithDeferred(err, errClose)
+ closeErr := errors.Annotate(conn4.Close(), "connectivity check: closing ipv4: %w")
+ err = errors.WithDeferred(err, closeErr)
}()
if !requireIPv6ConnCheck(srvGrps) {
@@ -69,8 +69,8 @@ func connectivityCheck(srvGrps []*agd.ServerGroup, connCheck *connCheckConfig) (
}
defer func() {
- errClose := errors.Annotate(conn6.Close(), "connectivity check: closing ipv6: %w")
- err = errors.WithDeferred(err, errClose)
+ closeErr := errors.Annotate(conn6.Close(), "connectivity check: closing ipv6: %w")
+ err = errors.WithDeferred(err, closeErr)
}()
return nil
diff --git a/internal/cmd/dnsdb.go b/internal/cmd/dnsdb.go
index 6134d7e..80bdc0a 100644
--- a/internal/cmd/dnsdb.go
+++ b/internal/cmd/dnsdb.go
@@ -1,9 +1,12 @@
package cmd
import (
+ "log/slog"
+
"github.com/AdguardTeam/AdGuardDNS/internal/dnsdb"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/golibs/errors"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
)
// dnsDBConfig is the configuration of the DNSDB module.
@@ -34,12 +37,16 @@ func (c *dnsDBConfig) validate() (err error) {
// toInternal builds and returns an anonymous statistics collector. c must be
// valid.
-func (c *dnsDBConfig) toInternal(errColl errcoll.Interface) (d dnsdb.Interface) {
+func (c *dnsDBConfig) toInternal(
+ baseLogger *slog.Logger,
+ errColl errcoll.Interface,
+) (d dnsdb.Interface) {
if !c.Enabled {
return dnsdb.Empty{}
}
db := dnsdb.New(&dnsdb.DefaultConfig{
+ Logger: baseLogger.With(slogutil.KeyPrefix, "dnsdb"),
ErrColl: errColl,
MaxSize: c.MaxSize,
})
diff --git a/internal/cmd/env.go b/internal/cmd/env.go
index ff5f027..87ff37d 100644
--- a/internal/cmd/env.go
+++ b/internal/cmd/env.go
@@ -68,8 +68,10 @@ type environment struct {
RedisIdleTimeout timeutil.Duration `env:"REDIS_IDLE_TIMEOUT" envDefault:"30s"`
- RedisMaxActive int `env:"REDIS_MAX_ACTIVE" envDefault:"10"`
- RedisMaxIdle int `env:"REDIS_MAX_IDLE" envDefault:"3"`
+ // TODO(a.garipov): Rename to DNSCHECK_CACHE_KV_COUNT?
+ DNSCheckCacheKVSize int `env:"DNSCHECK_CACHE_KV_SIZE"`
+ RedisMaxActive int `env:"REDIS_MAX_ACTIVE" envDefault:"10"`
+ RedisMaxIdle int `env:"REDIS_MAX_IDLE" envDefault:"3"`
ListenPort uint16 `env:"LISTEN_PORT" envDefault:"8181"`
RedisPort uint16 `env:"REDIS_PORT" envDefault:"6379"`
@@ -233,10 +235,12 @@ func (envs *environment) validateFromValidConfig(conf *configuration) (err error
var errs []error
switch typ := conf.Check.RemoteKV.Type; typ {
- case kvModeRedis:
- errs = envs.validateRedis(errs)
case kvModeBackend:
errs = envs.validateBackendKV(errs)
+ case kvModeCache:
+ errs = envs.validateCache(errs)
+ case kvModeRedis:
+ errs = envs.validateRedis(errs)
default:
// Probably consul.
}
@@ -259,38 +263,51 @@ func (envs *environment) validateFromValidConfig(conf *configuration) (err error
return errors.Join(errs...)
}
+// validateCache appends validation errors to the given errs if environment
+// variables for KV Cache contain errors.
+func (envs *environment) validateCache(errs []error) (res []error) {
+ res = errs
+
+ if envs.DNSCheckCacheKVSize <= 0 {
+ err := newNotPositiveError("DNSCHECK_CACHE_KV_SIZE", envs.DNSCheckCacheKVSize)
+ res = append(res, err)
+ }
+
+ return res
+}
+
// validateRedis appends validation errors to the given errs if environment
// variables for Redis contain errors.
-func (envs *environment) validateRedis(errs []error) (withRedis []error) {
- withRedis = errs
+func (envs *environment) validateRedis(errs []error) (res []error) {
+ res = errs
if envs.RedisAddr == "" {
- err := fmt.Errorf("REDIS_ADDR: %q", errors.ErrEmptyValue)
- withRedis = append(withRedis, err)
+ err := fmt.Errorf("REDIS_ADDR: %w", errors.ErrEmptyValue)
+ res = append(res, err)
}
if envs.RedisIdleTimeout.Duration <= 0 {
err := newNotPositiveError("REDIS_IDLE_TIMEOUT", envs.RedisIdleTimeout)
- withRedis = append(withRedis, err)
+ res = append(res, err)
}
if envs.RedisMaxActive < 0 {
err := newNegativeError("REDIS_MAX_ACTIVE", envs.RedisMaxActive)
- withRedis = append(withRedis, err)
+ res = append(res, err)
}
if envs.RedisMaxIdle < 0 {
err := newNegativeError("REDIS_MAX_IDLE", envs.RedisMaxIdle)
- withRedis = append(withRedis, err)
+ res = append(res, err)
}
- return withRedis
+ return res
}
// validateBackendKV appends validation errors to the given errs if environment
// variables for a backend key-value store contain errors.
-func (envs *environment) validateBackendKV(errs []error) (withKV []error) {
- withKV = errs
+func (envs *environment) validateBackendKV(errs []error) (res []error) {
+ res = errs
var u *url.URL
if envs.DNSCheckRemoteKVURL != nil {
@@ -299,16 +316,16 @@ func (envs *environment) validateBackendKV(errs []error) (withKV []error) {
err := urlutil.ValidateGRPCURL(u)
if err != nil {
- withKV = append(withKV, fmt.Errorf("env DNSCHECK_REMOTEKV_URL: %w", err))
+ res = append(res, fmt.Errorf("env DNSCHECK_REMOTEKV_URL: %w", err))
}
- return withKV
+ return res
}
// validateProfilesURLs appends validation errors to the given errs if profiles
// URLs in environment variables are invalid.
-func (envs *environment) validateProfilesURLs(errs []error) (withURLs []error) {
- withURLs = errs
+func (envs *environment) validateProfilesURLs(errs []error) (res []error) {
+ res = errs
grpcOnlyURLs := []*urlEnvData{{
url: envs.BillStatURL,
@@ -332,11 +349,11 @@ func (envs *environment) validateProfilesURLs(errs []error) (withURLs []error) {
err := urlutil.ValidateGRPCURL(u)
if err != nil {
- withURLs = append(withURLs, fmt.Errorf("env %s: %w", urlData.name, err))
+ res = append(res, fmt.Errorf("env %s: %w", urlData.name, err))
}
}
- return withURLs
+ return res
}
// validateRateLimitURLs appends validation errors to the given errs if rate
diff --git a/internal/cmd/filter.go b/internal/cmd/filter.go
index ee27585..0596a32 100644
--- a/internal/cmd/filter.go
+++ b/internal/cmd/filter.go
@@ -2,16 +2,8 @@ package cmd
import (
"fmt"
- "log/slog"
- "net/url"
- "time"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
- "github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix"
"github.com/AdguardTeam/golibs/errors"
- "github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/timeutil"
"github.com/c2h5oh/datasize"
)
@@ -27,9 +19,13 @@ type filtersConfig struct {
// CustomFilterCacheSize is the size of the LRU cache of compiled filtering
// engines for profiles with custom filtering rules.
+ //
+ // TODO(a.garipov): Rename to "custom_filter_cache_count"?
CustomFilterCacheSize int `yaml:"custom_filter_cache_size"`
// SafeSearchCacheSize is the size of the LRU cache of safe-search results.
+ //
+ // TODO(a.garipov): Rename to "safe_search_cache_count"?
SafeSearchCacheSize int `yaml:"safe_search_cache_size"`
// ResponseTTL is the TTL to set for DNS responses to requests for filtered
@@ -66,58 +62,6 @@ type filtersConfig struct {
SDEEnabled bool `yaml:"sde_enabled"`
}
-// toInternal converts c to the filter storage configuration for the DNS server.
-// cacheDir must exist. c must be valid.
-func (c *filtersConfig) toInternal(
- logger *slog.Logger,
- errColl errcoll.Interface,
- cacheManager agdcache.Manager,
- envs *environment,
- safeBrowsing *hashprefix.Filter,
- adultBlocking *hashprefix.Filter,
- newRegDomains *hashprefix.Filter,
-) (conf *filter.DefaultStorageConfig) {
- var blockedServiceIndexURL *url.URL
- if envs.BlockedServiceEnabled {
- blockedServiceIndexURL = netutil.CloneURL(&envs.BlockedServiceIndexURL.URL)
- }
-
- var generalSafeSearchRulesURL *url.URL
- if envs.GeneralSafeSearchEnabled {
- generalSafeSearchRulesURL = netutil.CloneURL(&envs.GeneralSafeSearchURL.URL)
- }
-
- var youtubeSafeSearchRulesURL *url.URL
- if envs.YoutubeSafeSearchEnabled {
- youtubeSafeSearchRulesURL = netutil.CloneURL(&envs.YoutubeSafeSearchURL.URL)
- }
-
- return &filter.DefaultStorageConfig{
- BaseLogger: logger,
- FilterIndexURL: netutil.CloneURL(&envs.FilterIndexURL.URL),
- BlockedServiceIndexURL: blockedServiceIndexURL,
- GeneralSafeSearchRulesURL: generalSafeSearchRulesURL,
- YoutubeSafeSearchRulesURL: youtubeSafeSearchRulesURL,
- SafeBrowsing: safeBrowsing,
- AdultBlocking: adultBlocking,
- NewRegDomains: newRegDomains,
- Now: time.Now,
- ErrColl: errColl,
- CacheManager: cacheManager,
- CacheDir: envs.FilterCachePath,
- CustomFilterCacheSize: c.CustomFilterCacheSize,
- SafeSearchCacheSize: c.SafeSearchCacheSize,
- // TODO(a.garipov): Consider making this configurable.
- SafeSearchCacheTTL: 1 * time.Hour,
- RuleListCacheSize: c.RuleListCache.Size,
- RefreshIvl: c.RefreshIvl.Duration,
- IndexRefreshTimeout: c.IndexRefreshTimeout.Duration,
- RuleListRefreshTimeout: c.RuleListRefreshTimeout.Duration,
- UseRuleListCache: c.RuleListCache.Enabled,
- MaxRuleListSize: c.MaxSize,
- }
-}
-
// type check
var _ validator = (*filtersConfig)(nil)
@@ -153,6 +97,8 @@ func (c *filtersConfig) validate() (err error) {
// fltRuleListCache contains filtering rule-list cache configuration.
type fltRuleListCache struct {
// Size defines the size of the LRU cache of rule-list filtering results.
+ //
+ // TODO(a.garipov): Rename to "count"?
Size int `yaml:"size"`
// Enabled shows if the rule-list cache is enabled. If it is false, the
diff --git a/internal/cmd/filteringgroup.go b/internal/cmd/filteringgroup.go
index e920bfc..4ebc06b 100644
--- a/internal/cmd/filteringgroup.go
+++ b/internal/cmd/filteringgroup.go
@@ -24,13 +24,17 @@ type filteringGroup struct {
// ID is a filtering group ID. Must be unique.
ID string `yaml:"id"`
- // BlockPrivateRelay shows if Apple Private Relay queries are blocked for
- // requests using this filtering group.
- BlockPrivateRelay bool `yaml:"block_private_relay"`
+ // BlockChromePrefetch shows if the Chrome prefetch proxy feature should be
+ // disabled for requests using this filtering group.
+ BlockChromePrefetch bool `yaml:"block_chrome_prefetch"`
// BlockFirefoxCanary shows if Firefox canary domain is blocked for
// requests using this filtering group.
BlockFirefoxCanary bool `yaml:"block_firefox_canary"`
+
+ // BlockPrivateRelay shows if Apple Private Relay queries are blocked for
+ // requests using this filtering group.
+ BlockPrivateRelay bool `yaml:"block_private_relay"`
}
// fltGrpRuleLists contains filter rule lists configuration for a filtering
@@ -44,6 +48,15 @@ type fltGrpRuleLists struct {
Enabled bool `yaml:"enabled"`
}
+// toInternal converts c to the rule-list configuration for the filtering group.
+// c must be valid.
+func (c *fltGrpRuleLists) toInternal(ids []filter.ID) (fltConf *filter.ConfigRuleList) {
+ return &filter.ConfigRuleList{
+ IDs: ids,
+ Enabled: c.Enabled,
+ }
+}
+
// fltGrpParental contains parental protection configuration for a filtering
// group.
type fltGrpParental struct {
@@ -64,6 +77,19 @@ type fltGrpParental struct {
YoutubeSafeSearch bool `yaml:"youtube_safe_search"`
}
+// toInternal converts c to the parental-control configuration for the filtering
+// group. c must be valid.
+func (c *fltGrpParental) toInternal() (fltConf *filter.ConfigParental) {
+ return &filter.ConfigParental{
+ PauseSchedule: nil,
+ BlockedServices: nil,
+ Enabled: c.Enabled,
+ AdultBlockingEnabled: c.BlockAdult,
+ SafeSearchGeneralEnabled: c.GeneralSafeSearch,
+ SafeSearchYouTubeEnabled: c.YoutubeSafeSearch,
+ }
+}
+
// fltGrpSafeBrowsing contains general safe browsing configuration for
// a filtering group.
type fltGrpSafeBrowsing struct {
@@ -79,6 +105,16 @@ type fltGrpSafeBrowsing struct {
BlockNewlyRegisteredDomains bool `yaml:"block_newly_registered_domains"`
}
+// toInternal converts c to the safe-browsing configuration for the filtering
+// group. c must be valid.
+func (c *fltGrpSafeBrowsing) toInternal() (fltConf *filter.ConfigSafeBrowsing) {
+ return &filter.ConfigSafeBrowsing{
+ Enabled: c.Enabled,
+ DangerousDomainsEnabled: c.BlockDangerousDomains,
+ NewlyRegisteredDomainsEnabled: c.BlockNewlyRegisteredDomains,
+ }
+}
+
// type check
var _ validator = (*filteringGroup)(nil)
@@ -87,21 +123,23 @@ func (g *filteringGroup) validate() (err error) {
switch {
case g == nil:
return errors.ErrNoValue
- case g.RuleLists == nil:
- return fmt.Errorf("rule_lists: %w", errors.ErrNoValue)
- case g.ID == "":
- return fmt.Errorf("id: %w", errors.ErrEmptyValue)
case g.Parental == nil:
return fmt.Errorf("parental: %w", errors.ErrNoValue)
+ case g.RuleLists == nil:
+ return fmt.Errorf("rule_lists: %w", errors.ErrNoValue)
+ case g.SafeBrowsing == nil:
+ return fmt.Errorf("safe_browsing: %w", errors.ErrNoValue)
+ case g.ID == "":
+ return fmt.Errorf("id: %w", errors.ErrEmptyValue)
}
fltIDs := container.NewMapSet[string]()
for i, fltID := range g.RuleLists.IDs {
if fltIDs.Has(fltID) {
- return fmt.Errorf("rule_lists: at index %d: duplicate id %q", i, fltID)
+ return fmt.Errorf("rule_lists: at index %d: id: %w: %q", i, errors.ErrDuplicated, fltID)
}
- _, err = agd.NewFilterListID(fltID)
+ _, err = filter.NewID(fltID)
if err != nil {
return fmt.Errorf("rule_lists: at index %d: %w", i, err)
}
@@ -123,32 +161,29 @@ func (groups filteringGroups) toInternal(
) (fltGrps map[agd.FilteringGroupID]*agd.FilteringGroup, err error) {
fltGrps = make(map[agd.FilteringGroupID]*agd.FilteringGroup, len(groups))
for _, g := range groups {
- filterIDs := make([]agd.FilterListID, len(g.RuleLists.IDs))
+ filterIDs := make([]filter.ID, len(g.RuleLists.IDs))
for i, fltID := range g.RuleLists.IDs {
// Assume that these have already been validated in
// [filteringGroup.validate].
- id := agd.FilterListID(fltID)
+ id := filter.ID(fltID)
if !s.HasListID(id) {
return nil, fmt.Errorf("filter list id %q is not in the index", id)
}
- filterIDs[i] = agd.FilterListID(fltID)
+ filterIDs[i] = filter.ID(fltID)
}
id := agd.FilteringGroupID(g.ID)
fltGrps[id] = &agd.FilteringGroup{
- ID: id,
- RuleListsEnabled: g.RuleLists.Enabled,
- RuleListIDs: filterIDs,
- ParentalEnabled: g.Parental.Enabled,
- BlockAdult: g.Parental.BlockAdult,
- SafeBrowsingEnabled: g.SafeBrowsing.Enabled,
- BlockDangerousDomains: g.SafeBrowsing.BlockDangerousDomains,
- BlockNewlyRegisteredDomains: g.SafeBrowsing.BlockNewlyRegisteredDomains,
- GeneralSafeSearch: g.Parental.GeneralSafeSearch,
- YoutubeSafeSearch: g.Parental.YoutubeSafeSearch,
- BlockPrivateRelay: g.BlockPrivateRelay,
- BlockFirefoxCanary: g.BlockFirefoxCanary,
+ FilterConfig: &filter.ConfigGroup{
+ Parental: g.Parental.toInternal(),
+ RuleList: g.RuleLists.toInternal(filterIDs),
+ SafeBrowsing: g.SafeBrowsing.toInternal(),
+ },
+ ID: id,
+ BlockChromePrefetch: g.BlockChromePrefetch,
+ BlockFirefoxCanary: g.BlockFirefoxCanary,
+ BlockPrivateRelay: g.BlockPrivateRelay,
}
}
@@ -172,7 +207,7 @@ func (groups filteringGroups) validate() (err error) {
}
if ids.Has(string(g.ID)) {
- return fmt.Errorf("at index %d: duplicate id %q", i, g.ID)
+ return fmt.Errorf("at index %d: id: %w: %q", i, errors.ErrDuplicated, g.ID)
}
ids.Add(g.ID)
diff --git a/internal/cmd/geoip.go b/internal/cmd/geoip.go
index f9100a3..d691136 100644
--- a/internal/cmd/geoip.go
+++ b/internal/cmd/geoip.go
@@ -8,9 +8,13 @@ import (
// geoIPConfig is the GeoIP database configuration.
type geoIPConfig struct {
// HostCacheSize is the size of the hostname lookup cache, in entries.
+ //
+ // TODO(a.garipov): Rename to "host_cache_count"?
HostCacheSize int `yaml:"host_cache_size"`
// IPCacheSize is the size of the IP lookup cache, in entries.
+ //
+ // TODO(a.garipov): Rename to "ip_cache_count"?
IPCacheSize int `yaml:"ip_cache_size"`
// RefreshIvl defines how often AdGuard DNS reopens the GeoIP database
diff --git a/internal/cmd/server.go b/internal/cmd/server.go
index 11bb88a..4b512d1 100644
--- a/internal/cmd/server.go
+++ b/internal/cmd/server.go
@@ -1,11 +1,14 @@
package cmd
import (
+ "crypto/tls"
"fmt"
"net/netip"
+ "slices"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/bindtodevice"
+ "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
"github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
@@ -14,11 +17,11 @@ import (
// toInternal returns the configuration of DNS servers for a single server
// group. srvs and other parts of the configuration must be valid.
func (srvs servers) toInternal(
- mtrc tlsconfig.Metrics,
- tlsConfig *agd.TLS,
btdMgr *bindtodevice.Manager,
+ tlsMgr tlsconfig.Manager,
ratelimitConf *rateLimitConfig,
dnsConf *dnsConfig,
+ deviceDomains []string,
) (dnsSrvs []*agd.Server, err error) {
dnsSrvs = make([]*agd.Server, 0, len(srvs))
for _, srv := range srvs {
@@ -66,18 +69,7 @@ func (srvs servers) toInternal(
QUICLimitsEnabled: ratelimitConf.QUIC.Enabled,
}
- tlsConf := tlsConfig.Conf.Clone()
-
- // Attach the functions that will count TLS handshake metrics.
- tlsConf.GetConfigForClient = mtrc.BeforeHandshake(string(srv.Protocol))
- tlsConf.VerifyConnection = mtrc.AfterHandshake(
- string(srv.Protocol),
- srv.Name,
- tlsConfig.DeviceDomains,
- tlsConf.Certificates,
- )
-
- dnsSrv.TLS = tlsConf
+ dnsSrv.TLS = newTLSConfig(dnsSrv, tlsMgr, deviceDomains, srv)
}
dnsSrv.SetBindData(bindData)
@@ -88,6 +80,35 @@ func (srvs servers) toInternal(
return dnsSrvs, nil
}
+// newTLSConfig returns the TLS configuration with metrics and ALPs set.
+//
+// TODO(s.chzhen): Consider moving to agd package as soon as the import cycle
+// is resolved.
+func newTLSConfig(
+ dnsSrv *agd.Server,
+ tlsMgr tlsconfig.Manager,
+ deviceDomains []string,
+ srv *server,
+) (c *agd.TLSConfig) {
+ tlsConf := tlsMgr.CloneWithMetrics(string(srv.Protocol), srv.Name, deviceDomains)
+
+ var tlsConfH3 *tls.Config
+ switch dnsSrv.Protocol {
+ case agd.ProtoDoH:
+ tlsConfH3 = tlsMgr.CloneWithMetrics(string(srv.Protocol), srv.Name, deviceDomains)
+
+ tlsConf.NextProtos = slices.Clone(dnsserver.NextProtoDoH)
+ tlsConfH3.NextProtos = slices.Clone(dnsserver.NextProtoDoH3)
+ case agd.ProtoDoQ:
+ tlsConf.NextProtos = slices.Clone(dnsserver.NextProtoDoQ)
+ }
+
+ return &agd.TLSConfig{
+ Default: tlsConf,
+ H3: tlsConfH3,
+ }
+}
+
// servers is a slice of server settings. A valid instance of servers has no
// nil items.
type servers []*server
@@ -110,7 +131,7 @@ func (srvs servers) validate() (needsTLS bool, err error) {
}
if names.Has(s.Name) {
- return false, fmt.Errorf("at index %d: duplicate name %q", i, s.Name)
+ return false, fmt.Errorf("at index %d: name: %w: %q", i, errors.ErrDuplicated, s.Name)
}
names.Add(s.Name)
@@ -337,11 +358,11 @@ func (c *serverBindInterface) validate() (err error) {
set := container.NewMapSet[netip.Prefix]()
for i, subnet := range c.Subnets {
if !subnet.IsValid() {
- return fmt.Errorf("bad subnet at index %d", i)
+ return fmt.Errorf("subnets: at index %d: bad subnet", i)
}
if set.Has(subnet) {
- return fmt.Errorf("duplicate subnet %s at index %d", subnet, i)
+ return fmt.Errorf("subnets: at index %d: %w: %s", i, errors.ErrDuplicated, subnet)
}
set.Add(subnet)
diff --git a/internal/cmd/servergroup.go b/internal/cmd/servergroup.go
index 6c9dea4..648a282 100644
--- a/internal/cmd/servergroup.go
+++ b/internal/cmd/servergroup.go
@@ -20,9 +20,9 @@ type serverGroups []*serverGroup
// service. srvGrps and other parts of the configuration must be valid.
func (srvGrps serverGroups) toInternal(
ctx context.Context,
- mtrc tlsconfig.Metrics,
messages *dnsmsg.Constructor,
btdMgr *bindtodevice.Manager,
+ tlsMgr tlsconfig.Manager,
fltGrps map[agd.FilteringGroupID]*agd.FilteringGroup,
ratelimitConf *rateLimitConfig,
dnsConf *dnsConfig,
@@ -35,26 +35,26 @@ func (srvGrps serverGroups) toInternal(
return nil, fmt.Errorf("server group %q: unknown filtering group %q", g.Name, fltGrpID)
}
- var tlsConf *agd.TLS
- tlsConf, err = g.TLS.toInternal(ctx, mtrc)
+ var deviceDomains []string
+ deviceDomains, err = g.TLS.toInternal(ctx, tlsMgr)
if err != nil {
- return nil, fmt.Errorf("tls: %w", err)
+ return nil, fmt.Errorf("tls %q: %w", g.Name, err)
}
svcSrvGrps[i] = &agd.ServerGroup{
DDR: g.DDR.toInternal(messages),
- TLS: tlsConf,
+ DeviceDomains: deviceDomains,
Name: agd.ServerGroupName(g.Name),
FilteringGroup: fltGrpID,
ProfilesEnabled: g.ProfilesEnabled,
}
svcSrvGrps[i].Servers, err = g.Servers.toInternal(
- mtrc,
- tlsConf,
btdMgr,
+ tlsMgr,
ratelimitConf,
dnsConf,
+ deviceDomains,
)
if err != nil {
return nil, fmt.Errorf("server group %q: %w", g.Name, err)
@@ -81,7 +81,7 @@ func (srvGrps serverGroups) validate() (err error) {
}
if names.Has(g.Name) {
- return fmt.Errorf("at index %d: duplicate name %q", i, g.Name)
+ return fmt.Errorf("at index %d: name: %w: %q", i, errors.ErrDuplicated, g.Name)
}
names.Add(g.Name)
@@ -148,3 +148,16 @@ func (g *serverGroup) validate() (err error) {
return nil
}
+
+// collectSessTicketPaths returns the list of unique session ticket file paths
+// for all server groups.
+func (srvGrps serverGroups) collectSessTicketPaths() (paths []string) {
+ set := container.NewSortedSliceSet[string]()
+ for _, g := range srvGrps {
+ for _, k := range g.TLS.SessionKeys {
+ set.Add(k)
+ }
+ }
+
+ return set.Values()
+}
diff --git a/internal/cmd/tickrot.go b/internal/cmd/tickrot.go
deleted file mode 100644
index fe6431b..0000000
--- a/internal/cmd/tickrot.go
+++ /dev/null
@@ -1,121 +0,0 @@
-package cmd
-
-import (
- "context"
- "crypto/tls"
- "fmt"
- "log/slog"
- "os"
-
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
- "github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
- "github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig"
- "github.com/AdguardTeam/golibs/logutil/slogutil"
-)
-
-// ticketRotator is a refresh worker that rereads and resets TLS session
-// tickets. It should be initially refreshed before use.
-type ticketRotator struct {
- logger *slog.Logger
- errColl errcoll.Interface
- mtrc tlsconfig.Metrics
- confs map[*tls.Config][]string
-}
-
-// newTicketRotator creates a new TLS session ticket rotator that rotates
-// tickets for the TLS configurations of all servers in grps.
-//
-// grps must be valid.
-func newTicketRotator(
- logger *slog.Logger,
- errColl errcoll.Interface,
- mtrc tlsconfig.Metrics,
- grps []*agd.ServerGroup,
-) (tr *ticketRotator) {
- confs := map[*tls.Config][]string{}
-
- for _, g := range grps {
- t := g.TLS
- if t == nil || len(t.SessionKeys) == 0 {
- continue
- }
-
- for _, srv := range g.Servers {
- if srv.TLS != nil {
- confs[srv.TLS] = t.SessionKeys
- }
- }
- }
-
- return &ticketRotator{
- logger: logger.With(slogutil.KeyPrefix, "tickrot"),
- errColl: errColl,
- mtrc: mtrc,
- confs: confs,
- }
-}
-
-// sessTickLen is the length of a single TLS session ticket key in bytes.
-//
-// NOTE: Unlike Nginx, Go's crypto/tls doesn't use the random bytes from the
-// session ticket keys as-is, but instead hashes these bytes and uses the first
-// 48 bytes of the hashed data as the key name, the AES key, and the HMAC key.
-const sessTickLen = 32
-
-// type check
-var _ agdservice.Refresher = (*ticketRotator)(nil)
-
-// Refresh implements the [agdservice.Refresher] interface for *ticketRotator.
-func (r *ticketRotator) Refresh(ctx context.Context) (err error) {
- r.logger.DebugContext(ctx, "refresh started")
- defer r.logger.DebugContext(ctx, "refresh finished")
-
- defer func() {
- if err != nil {
- errcoll.Collect(ctx, r.errColl, r.logger, "ticket rotation failed", err)
- }
- }()
-
- for conf, files := range r.confs {
- keys := make([][sessTickLen]byte, 0, len(files))
-
- for _, fileName := range files {
- var key [sessTickLen]byte
- key, err = readSessionTicketKey(fileName)
- if err != nil {
- r.mtrc.SetSessionTicketRotationStatus(ctx, false)
-
- return fmt.Errorf("session ticket for srv %s: %w", conf.ServerName, err)
- }
-
- keys = append(keys, key)
- }
-
- if len(keys) == 0 {
- return fmt.Errorf("no session tickets for srv %s in %q", conf.ServerName, files)
- }
-
- conf.SetSessionTicketKeys(keys)
- }
-
- r.mtrc.SetSessionTicketRotationStatus(ctx, true)
-
- return nil
-}
-
-// readSessionTicketKey reads a single TLS session ticket key from a file.
-func readSessionTicketKey(fn string) (key [sessTickLen]byte, err error) {
- // #nosec G304 -- Trust the file paths that are given to us in the
- // configuration file.
- b, err := os.ReadFile(fn)
- if err != nil {
- return key, fmt.Errorf("reading session ticket: %w", err)
- }
-
- if len(b) < sessTickLen {
- return key, fmt.Errorf("session ticket in %s: bad len %d, want %d", fn, len(b), sessTickLen)
- }
-
- return [sessTickLen]byte(b), nil
-}
diff --git a/internal/cmd/tls.go b/internal/cmd/tls.go
index 4692140..25ef9ea 100644
--- a/internal/cmd/tls.go
+++ b/internal/cmd/tls.go
@@ -3,13 +3,9 @@ package cmd
import (
"context"
"crypto/tls"
- "crypto/x509"
"fmt"
- "os"
- "path/filepath"
"strings"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
@@ -39,27 +35,22 @@ type tlsConfig struct {
// valid.
func (c *tlsConfig) toInternal(
ctx context.Context,
- mtrc tlsconfig.Metrics,
-) (conf *agd.TLS, err error) {
+ tlsMgr tlsconfig.Manager,
+) (deviceDomains []string, err error) {
if c == nil {
return nil, nil
}
- tlsConf, err := c.Certificates.toInternal(ctx, mtrc)
+ err = c.Certificates.store(ctx, tlsMgr)
if err != nil {
return nil, fmt.Errorf("certificates: %w", err)
}
- var deviceDomains []string
for _, w := range c.DeviceIDWildcards {
deviceDomains = append(deviceDomains, strings.TrimPrefix(w, "*."))
}
- return &agd.TLS{
- Conf: tlsConf,
- DeviceDomains: deviceDomains,
- SessionKeys: c.SessionKeys,
- }, nil
+ return deviceDomains, nil
}
// validate returns an error if the TLS configuration is invalid for the given
@@ -101,9 +92,9 @@ func validateDeviceIDWildcards(wildcards []string) (err error) {
for i, w := range wildcards {
// TODO(e.burkov): Consider removing this requirement.
if !strings.HasPrefix(w, "*.") {
- return fmt.Errorf("at index %d: not a wildcard", i)
+ return fmt.Errorf("at index %d: not a wildcard: %q", i, w)
} else if s.Has(w) {
- return fmt.Errorf("at index %d: duplicated wildcard", i)
+ return fmt.Errorf("at index %d: wildcard: %w: %q", i, errors.ErrDuplicated, w)
}
s.Add(w)
@@ -125,42 +116,40 @@ type tlsConfigCert struct {
// no nil items.
type tlsConfigCerts []*tlsConfigCert
-// toInternal converts certs to a TLS configuration. certs must be valid.
+// store stores the TLS certificates in the TLS manager. certs must be valid.
+func (certs tlsConfigCerts) store(ctx context.Context, tlsMgr tlsconfig.Manager) (err error) {
+ var errs []error
+ for i, c := range certs {
+ err = tlsMgr.Add(ctx, c.Certificate, c.Key)
+ if err != nil {
+ errs = append(errs, fmt.Errorf("adding certificate at index %d: %w", i, err))
+ }
+ }
+
+ if len(errs) != 0 {
+ return errors.Join(errs...)
+ }
+
+ return nil
+}
+
+// toInternal is like [tlsConfigCerts.store] but it also returns the TLS
+// configuration. certs must be valid.
func (certs tlsConfigCerts) toInternal(
ctx context.Context,
- mtrc tlsconfig.Metrics,
+ tlsMgr tlsconfig.Manager,
) (conf *tls.Config, err error) {
if len(certs) == 0 {
return nil, nil
}
- tlsCerts := make([]tls.Certificate, len(certs))
- for i, c := range certs {
- var cert tls.Certificate
- cert, err = tls.LoadX509KeyPair(c.Certificate, c.Key)
- if err != nil {
- return nil, fmt.Errorf("certificate at index %d: %w", i, err)
- }
-
- var leaf *x509.Certificate
- leaf, err = x509.ParseCertificate(cert.Certificate[0])
- if err != nil {
- return nil, fmt.Errorf("invalid leaf, certificate at index %d: %w", i, err)
- }
-
- cert.Leaf = leaf
- tlsCerts[i] = cert
-
- authAlgo, subj := leaf.PublicKeyAlgorithm.String(), leaf.Subject.String()
-
- mtrc.SetCertificateInfo(ctx, authAlgo, subj, leaf.NotAfter)
+ err = certs.store(ctx, tlsMgr)
+ if err != nil {
+ // Don't wrap the error, because it's informative enough as is.
+ return nil, err
}
- return &tls.Config{
- Certificates: tlsCerts,
- MinVersion: tls.VersionTLS12,
- MaxVersion: tls.VersionTLS13,
- }, nil
+ return tlsMgr.Clone(), nil
}
// type check
@@ -181,24 +170,3 @@ func (certs tlsConfigCerts) validate() (err error) {
return nil
}
-
-// enableTLSKeyLogging enables TLS key logging (use for debug purposes only).
-func enableTLSKeyLogging(grps []*agd.ServerGroup, keyLogFileName string) (err error) {
- path := filepath.Clean(keyLogFileName)
-
- // TODO(a.garipov): Consider closing the file when we add SIGHUP support.
- kl, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
- if err != nil {
- return fmt.Errorf("opening SSL_KEY_LOG_FILE: %w", err)
- }
-
- for _, g := range grps {
- for _, s := range g.Servers {
- if s.TLS != nil {
- s.TLS.KeyLogWriter = kl
- }
- }
- }
-
- return nil
-}
diff --git a/internal/cmd/upstream.go b/internal/cmd/upstream.go
index 91cba46..450b4a4 100644
--- a/internal/cmd/upstream.go
+++ b/internal/cmd/upstream.go
@@ -41,7 +41,10 @@ func (c *upstreamConfig) toInternal(logger *slog.Logger) (fwdConf *forward.Handl
upsConfs := toUpstreamConfigs(upstreams)
fallbackConfs := toUpstreamConfigs(fallbacks)
- metricsListener := prometheus.NewForwardMetricsListener(metrics.Namespace(), len(upstreams)+len(fallbacks))
+ metricsListener := prometheus.NewForwardMetricsListener(
+ metrics.Namespace(),
+ len(upstreams)+len(fallbacks),
+ )
var hcInit time.Duration
if c.Healthcheck.Enabled {
diff --git a/internal/cmd/websvc.go b/internal/cmd/websvc.go
index 64b38d1..76794f6 100644
--- a/internal/cmd/websvc.go
+++ b/internal/cmd/websvc.go
@@ -68,7 +68,7 @@ func (c *webConfig) toInternal(
envs *environment,
dnsCk dnscheck.Interface,
errColl errcoll.Interface,
- mtrc tlsconfig.Metrics,
+ tlsMgr tlsconfig.Manager,
) (conf *websvc.Config, err error) {
if c == nil {
return nil, nil
@@ -87,7 +87,7 @@ func (c *webConfig) toInternal(
conf.RootRedirectURL = netutil.CloneURL(&c.RootRedirectURL.URL)
}
- conf.LinkedIP, err = c.LinkedIP.toInternal(ctx, mtrc, envs.LinkedIPTargetURL)
+ conf.LinkedIP, err = c.LinkedIP.toInternal(ctx, tlsMgr, envs.LinkedIPTargetURL)
if err != nil {
return nil, fmt.Errorf("converting linked_ip: %w", err)
}
@@ -111,7 +111,7 @@ func (c *webConfig) toInternal(
}}
for _, bp := range blockPages {
- *bp.webConfPtr, err = bp.conf.toInternal(ctx, mtrc)
+ *bp.webConfPtr, err = bp.conf.toInternal(ctx, tlsMgr)
if err != nil {
return nil, fmt.Errorf("%s: %w", bp.name, err)
}
@@ -123,7 +123,7 @@ func (c *webConfig) toInternal(
return nil, err
}
- conf.NonDoHBind, err = c.NonDoHBind.toInternal(ctx, mtrc)
+ conf.NonDoHBind, err = c.NonDoHBind.toInternal(ctx, tlsMgr)
if err != nil {
return nil, fmt.Errorf("converting non_doh_bind: %w", err)
}
@@ -230,7 +230,7 @@ type linkedIPServer struct {
// toInternal converts s to a linkedIP server configuration. s must be valid.
func (s *linkedIPServer) toInternal(
ctx context.Context,
- mtrc tlsconfig.Metrics,
+ tlsMgr tlsconfig.Manager,
targetURL *urlutil.URL,
) (srv *websvc.LinkedIPServer, err error) {
if s == nil {
@@ -238,7 +238,7 @@ func (s *linkedIPServer) toInternal(
}
srv = &websvc.LinkedIPServer{}
- srv.Bind, err = s.Bind.toInternal(ctx, mtrc)
+ srv.Bind, err = s.Bind.toInternal(ctx, tlsMgr)
if err != nil {
return nil, fmt.Errorf("converting bind: %w", err)
}
@@ -288,7 +288,7 @@ type blockPageServer struct {
// toInternal converts s to a block page server configuration. s must be valid.
func (s *blockPageServer) toInternal(
ctx context.Context,
- mtrc tlsconfig.Metrics,
+ tlsMgr tlsconfig.Manager,
) (conf *websvc.BlockPageServerConfig, err error) {
if s == nil {
return nil, nil
@@ -298,7 +298,7 @@ func (s *blockPageServer) toInternal(
ContentFilePath: s.BlockPage,
}
- conf.Bind, err = s.Bind.toInternal(ctx, mtrc)
+ conf.Bind, err = s.Bind.toInternal(ctx, tlsMgr)
if err != nil {
return nil, fmt.Errorf("converting bind: %w", err)
}
@@ -337,12 +337,12 @@ type bindData []*bindItem
// be valid.
func (bd bindData) toInternal(
ctx context.Context,
- mtrc tlsconfig.Metrics,
+ tlsMgr tlsconfig.Manager,
) (data []*websvc.BindData, err error) {
data = make([]*websvc.BindData, len(bd))
for i, d := range bd {
- data[i], err = d.toInternal(ctx, mtrc)
+ data[i], err = d.toInternal(ctx, tlsMgr)
if err != nil {
return nil, fmt.Errorf("bind data at index %d: %w", i, err)
}
@@ -383,9 +383,9 @@ type bindItem struct {
// be valid.
func (i *bindItem) toInternal(
ctx context.Context,
- mtrc tlsconfig.Metrics,
+ tlsMgr tlsconfig.Manager,
) (data *websvc.BindData, err error) {
- tlsConf, err := i.Certificates.toInternal(ctx, mtrc)
+ tlsConf, err := i.Certificates.toInternal(ctx, tlsMgr)
if err != nil {
return nil, fmt.Errorf("certificates: %w", err)
}
diff --git a/internal/connlimiter/listenconfig_test.go b/internal/connlimiter/listenconfig_test.go
index 64e32e4..ef86698 100644
--- a/internal/connlimiter/listenconfig_test.go
+++ b/internal/connlimiter/listenconfig_test.go
@@ -17,27 +17,27 @@ import (
func TestListenConfig(t *testing.T) {
pc := &fakenet.PacketConn{
- OnClose: func() (err error) { panic("not implemented") },
- OnLocalAddr: func() (laddr net.Addr) { panic("not implemented") },
- OnReadFrom: func(b []byte) (n int, addr net.Addr, err error) { panic("not implemented") },
- OnSetDeadline: func(t time.Time) (err error) { panic("not implemented") },
- OnSetReadDeadline: func(t time.Time) (err error) { panic("not implemented") },
- OnSetWriteDeadline: func(t time.Time) (err error) { panic("not implemented") },
- OnWriteTo: func(b []byte, addr net.Addr) (n int, err error) { panic("not implemented") },
+ OnClose: func() (_ error) { panic("not implemented") },
+ OnLocalAddr: func() (_ net.Addr) { panic("not implemented") },
+ OnReadFrom: func(_ []byte) (_ int, _ net.Addr, _ error) {
+ panic("not implemented")
+ },
+ OnSetDeadline: func(_ time.Time) (_ error) { panic("not implemented") },
+ OnSetReadDeadline: func(_ time.Time) (_ error) { panic("not implemented") },
+ OnSetWriteDeadline: func(_ time.Time) (_ error) { panic("not implemented") },
+ OnWriteTo: func(_ []byte, _ net.Addr) (_ int, _ error) {
+ panic("not implemented")
+ },
}
lsnr := &fakenet.Listener{
- OnAccept: func() (c net.Conn, err error) { panic("not implemented") },
- OnAddr: func() (addr net.Addr) { panic("not implemented") },
- OnClose: func() (err error) { return nil },
+ OnAccept: func() (_ net.Conn, _ error) { panic("not implemented") },
+ OnAddr: func() (_ net.Addr) { panic("not implemented") },
+ OnClose: func() (_ error) { return nil },
}
c := &agdtest.ListenConfig{
- OnListen: func(
- ctx context.Context,
- network string,
- address string,
- ) (l net.Listener, err error) {
+ OnListen: func(ctx context.Context, network, address string) (l net.Listener, err error) {
return lsnr, nil
},
OnListenPacket: func(
diff --git a/internal/debugsvc/debugsvc_test.go b/internal/debugsvc/debugsvc_test.go
index 9e8a4bf..3083ca9 100644
--- a/internal/debugsvc/debugsvc_test.go
+++ b/internal/debugsvc/debugsvc_test.go
@@ -162,7 +162,7 @@ func TestService_Start(t *testing.T) {
resp, err = client.Post(ctx, refreshURL, agdhttp.HdrValApplicationJSON, reqBody)
require.NoError(t, err)
- assert.Len(t, refreshed, 0)
+ assert.Empty(t, refreshed)
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
respBody = readRespBody(t, resp)
diff --git a/internal/dnscheck/dnscheck_test.go b/internal/dnscheck/dnscheck_test.go
index f32cf9a..ebc4547 100644
--- a/internal/dnscheck/dnscheck_test.go
+++ b/internal/dnscheck/dnscheck_test.go
@@ -2,15 +2,8 @@ package dnscheck_test
import (
"net/netip"
- "testing"
-
- "github.com/AdguardTeam/golibs/testutil"
)
-func TestMain(m *testing.M) {
- testutil.DiscardLogOutput(m)
-}
-
// Test data.
var (
testRemoteIP = netip.MustParseAddr("1.2.3.4")
diff --git a/internal/dnscheck/remotekv.go b/internal/dnscheck/remotekv.go
index 4e94aa5..3933471 100644
--- a/internal/dnscheck/remotekv.go
+++ b/internal/dnscheck/remotekv.go
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
+ "log/slog"
"net/http"
"net/netip"
"slices"
@@ -19,7 +20,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/remotekv/consulkv"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/httphdr"
- "github.com/AdguardTeam/golibs/log"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil"
"github.com/miekg/dns"
cache "github.com/patrickmn/go-cache"
@@ -28,8 +29,10 @@ import (
// RemoteKV is the RemoteKV KV based DNS checker.
type RemoteKV struct {
- // mu protects cache. Don't use an RWMutex here, since the ratio of read
- // and write access is expected to be approximately equal.
+ logger *slog.Logger
+
+ // mu protects cache. Don't use an RWMutex here, since it is expected that
+ // there are about as many reads as there are writes.
mu *sync.Mutex
cache *cache.Cache
@@ -49,6 +52,9 @@ type RemoteKV struct {
// RemoteKVConfig is the configuration structure for remote KV based DNS
// checker. All fields must be non-empty.
type RemoteKVConfig struct {
+ // Logger is used to log the operation of the DNS checker.
+ Logger *slog.Logger
+
// Messages is the message constructor used to create DNS responses with
// IPv4 and IPv6 IPs.
Messages *dnsmsg.Constructor
@@ -85,8 +91,9 @@ const (
)
// NewRemoteKV creates a new remote KV based DNS checker. c must be non-nil.
-func NewRemoteKV(c *RemoteKVConfig) (cc *RemoteKV) {
+func NewRemoteKV(c *RemoteKVConfig) (dc *RemoteKV) {
return &RemoteKV{
+ logger: c.Logger,
mu: &sync.Mutex{},
cache: cache.New(defaultCacheExp, defaultCacheGC),
kv: c.RemoteKV,
@@ -104,7 +111,7 @@ func NewRemoteKV(c *RemoteKVConfig) (cc *RemoteKV) {
var _ Interface = (*RemoteKV)(nil)
// Check implements the Interface interface for *RemoteKV.
-func (cc *RemoteKV) Check(
+func (dc *RemoteKV) Check(
ctx context.Context,
req *dns.Msg,
ri *agd.RequestInfo,
@@ -124,7 +131,7 @@ func (cc *RemoteKV) Check(
}()
var randomID string
- randomID, matched, err = randomIDFromDomain(ri.Host, cc.domains)
+ randomID, matched, err = randomIDFromDomain(ri.Host, dc.domains)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return nil, err
@@ -132,32 +139,32 @@ func (cc *RemoteKV) Check(
// Not a dnscheck domain, just ignore the request.
return nil, nil
} else if randomID == "" {
- return cc.resp(ri, req)
+ return dc.resp(ri, req)
}
- inf := cc.newInfo(ri)
+ inf := dc.newInfo(ri)
b, err := json.Marshal(inf)
if err != nil {
return nil, fmt.Errorf("encoding value for key %q for remote kv: %w", randomID, err)
}
- cc.addToCache(randomID, b)
+ dc.addToCache(randomID, b)
- err = cc.kv.Set(ctx, randomID, b)
+ err = dc.kv.Set(ctx, randomID, b)
if err != nil {
- errcoll.Collectf(ctx, cc.errColl, "dnscheck: remote kv setting: %w", err)
+ errcoll.Collect(ctx, dc.errColl, dc.logger, "remote kv setting", err)
}
- return cc.resp(ri, req)
+ return dc.resp(ri, req)
}
// addToCache adds inf into cache using randomID as key. It's safe for
// concurrent use.
-func (cc *RemoteKV) addToCache(randomID string, inf []byte) {
- cc.mu.Lock()
- defer cc.mu.Unlock()
+func (dc *RemoteKV) addToCache(randomID string, inf []byte) {
+ dc.mu.Lock()
+ defer dc.mu.Unlock()
- cc.cache.SetDefault(randomID, inf)
+ dc.cache.SetDefault(randomID, inf)
}
// serverType is a type for the enum of server types in the DNS checker HTTP
@@ -172,7 +179,7 @@ const (
// newInfo returns an information record with all available data about the
// server and the request. ri must not be nil.
-func (cc *RemoteKV) newInfo(ri *agd.RequestInfo) (inf *info) {
+func (dc *RemoteKV) newInfo(ri *agd.RequestInfo) (inf *info) {
g := ri.ServerGroup
srvType := serverTypePublic
@@ -186,8 +193,8 @@ func (cc *RemoteKV) newInfo(ri *agd.RequestInfo) (inf *info) {
ServerType: srvType,
Protocol: ri.Proto.String(),
- NodeLocation: cc.nodeLocation,
- NodeName: cc.nodeName,
+ NodeLocation: dc.nodeLocation,
+ NodeName: dc.nodeName,
ClientIP: ri.RemoteIP,
}
@@ -204,7 +211,7 @@ func (cc *RemoteKV) newInfo(ri *agd.RequestInfo) (inf *info) {
//
// TODO(e.burkov): Inspect the reason for using different message constructors
// for different DNS types, and consider using only one of them.
-func (cc *RemoteKV) resp(ri *agd.RequestInfo, req *dns.Msg) (resp *dns.Msg, err error) {
+func (dc *RemoteKV) resp(ri *agd.RequestInfo, req *dns.Msg) (resp *dns.Msg, err error) {
qt := ri.QType
if qt != dns.TypeA && qt != dns.TypeAAAA {
@@ -212,24 +219,23 @@ func (cc *RemoteKV) resp(ri *agd.RequestInfo, req *dns.Msg) (resp *dns.Msg, err
}
if qt == dns.TypeA {
- return cc.messages.NewRespIP(req, cc.ipv4...)
+ return dc.messages.NewRespIP(req, dc.ipv4...)
}
- return cc.messages.NewRespIP(req, cc.ipv6...)
+ return dc.messages.NewRespIP(req, dc.ipv6...)
}
// type check
var _ http.Handler = (*RemoteKV)(nil)
// ServeHTTP implements the http.Handler interface for *RemoteKV.
-func (cc *RemoteKV) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- m, p, raddr := r.Method, r.URL.Path, r.RemoteAddr
- log.Debug("dnscheck: http req %s %s from %s", m, p, raddr)
- defer log.Debug("dnscheck: finished http req %s %s from %s", m, p, raddr)
-
+//
+// TODO(a.garipov): Consider using the websvc logger once it switches to
+// log/slog.
+func (dc *RemoteKV) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// TODO(a.garipov): Put this into constant here and in package dnssvc.
if r.URL.Path == "/dnscheck/test" {
- cc.serveCheckTest(r.Context(), w, r)
+ dc.serveCheckTest(r.Context(), w, r)
return
}
@@ -241,48 +247,48 @@ func (cc *RemoteKV) ServeHTTP(w http.ResponseWriter, r *http.Request) {
//
// TODO(a.garipov): Refactor this and other HTTP handlers to return wrapped
// errors and centralize the error handling.
-func (cc *RemoteKV) serveCheckTest(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- raddr := r.RemoteAddr
+func (dc *RemoteKV) serveCheckTest(ctx context.Context, w http.ResponseWriter, r *http.Request) {
+ l := dc.logger.With("raddr", r.RemoteAddr)
- name, err := netutil.SplitHost(r.Host)
+ host, err := netutil.SplitHost(r.Host)
if err != nil {
- log.Debug("dnscheck: http req from %s: bad host %q: %s", raddr, r.Host, err)
+ l.DebugContext(ctx, "bad host", "hostport", r.Host, slogutil.KeyError, err)
http.NotFound(w, r)
return
}
- randomID, matched, err := randomIDFromDomain(name, cc.domains)
+ randomID, matched, err := randomIDFromDomain(host, dc.domains)
if err != nil {
- log.Debug("dnscheck: http req from %s: id: %s", raddr, err)
+ l.DebugContext(ctx, "bad request", "host", host, slogutil.KeyError, err)
http.NotFound(w, r)
return
} else if !matched || randomID == "" {
// We expect dnscheck requests to have a unique ID in the domain name.
- log.Debug("dnscheck: http req from %s: bad domain %q", raddr, name)
+ l.DebugContext(ctx, "bad domain", "host", host, slogutil.KeyError, err)
http.NotFound(w, r)
return
}
- inf, ok, err := cc.info(ctx, randomID)
+ inf, ok, err := dc.info(ctx, randomID)
// TODO(s.chzhen): Use error interface instead of error value.
if errors.Is(err, consulkv.ErrRateLimited) {
http.Error(w, err.Error(), http.StatusTooManyRequests)
return
} else if err != nil {
- log.Debug("dnscheck: http req from %s: getting info: %s", raddr, err)
+ l.DebugContext(ctx, "getting info", "random_id", randomID, slogutil.KeyError, err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
} else if !ok {
- log.Debug("dnscheck: http req from %s: no info for %q", raddr, randomID)
+ l.DebugContext(ctx, "no info", "random_id", randomID, slogutil.KeyError, err)
http.NotFound(w, r)
@@ -295,12 +301,12 @@ func (cc *RemoteKV) serveCheckTest(ctx context.Context, w http.ResponseWriter, r
_, err = w.Write(inf)
if err != nil {
- errcoll.Collectf(ctx, cc.errColl, "dnscheck: http resp write error: %w", err)
+ errcoll.Collect(ctx, dc.errColl, dc.logger, "http resp write", err)
}
}
// info returns an information record by the random request ID.
-func (cc *RemoteKV) info(ctx context.Context, randomID string) (inf []byte, ok bool, err error) {
+func (dc *RemoteKV) info(ctx context.Context, randomID string) (inf []byte, ok bool, err error) {
defer func() {
metrics.DNSCheckRequestTotal.With(prometheus.Labels{
"type": "http",
@@ -312,17 +318,17 @@ func (cc *RemoteKV) info(ctx context.Context, randomID string) (inf []byte, ok b
defer func() { err = errors.Annotate(err, "getting from remote kv: %w") }()
- cc.mu.Lock()
- defer cc.mu.Unlock()
+ dc.mu.Lock()
+ defer dc.mu.Unlock()
- infoVal, ok := cc.cache.Get(randomID)
+ infoVal, ok := dc.cache.Get(randomID)
if ok {
return infoVal.([]byte), true, nil
}
- inf, ok, err = cc.kv.Get(ctx, randomID)
+ inf, ok, err = dc.kv.Get(ctx, randomID)
if err != nil {
- errcoll.Collectf(ctx, cc.errColl, "dnscheck: remote kv getting: %w", err)
+ errcoll.Collect(ctx, dc.errColl, dc.logger, "remote kv getting", err)
// Don't wrap the error, as it will get annotated.
return nil, false, err
diff --git a/internal/dnscheck/remotekv_test.go b/internal/dnscheck/remotekv_test.go
index 700d78a..9452a73 100644
--- a/internal/dnscheck/remotekv_test.go
+++ b/internal/dnscheck/remotekv_test.go
@@ -17,6 +17,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnscheck"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/remotekv"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
@@ -46,6 +47,7 @@ func TestConsul_ServeHTTP(t *testing.T) {
}
conf := &dnscheck.RemoteKVConfig{
+ Logger: slogutil.NewDiscardLogger(),
Messages: &dnsmsg.Constructor{},
RemoteKV: remotekv.Empty{},
ErrColl: agdtest.NewErrorCollector(),
@@ -171,6 +173,7 @@ func TestConsul_Check(t *testing.T) {
msgs := agdtest.NewConstructorWithTTL(t, ttl*time.Second)
conf := &dnscheck.RemoteKVConfig{
+ Logger: slogutil.NewDiscardLogger(),
Messages: msgs,
RemoteKV: remotekv.Empty{},
Domains: []string{checkDomain},
diff --git a/internal/dnsdb/dnsdb.go b/internal/dnsdb/dnsdb.go
index 097fbf9..f798333 100644
--- a/internal/dnsdb/dnsdb.go
+++ b/internal/dnsdb/dnsdb.go
@@ -6,6 +6,7 @@ package dnsdb
import (
"context"
+ "log/slog"
"sync"
"sync/atomic"
"time"
@@ -35,6 +36,7 @@ func (Empty) Record(_ context.Context, _ *dns.Msg, _ *agd.RequestInfo) {}
// Default is the default DNSDB implementation.
type Default struct {
+ logger *slog.Logger
buffer *atomic.Pointer[buffer]
errColl errcoll.Interface
maxSize int
@@ -42,6 +44,9 @@ type Default struct {
// DefaultConfig is the default DNS database configuration structure.
type DefaultConfig struct {
+ // Logger is used to log the operation of the DNS database.
+ Logger *slog.Logger
+
// ErrColl is used to collect HTTP errors.
ErrColl errcoll.Interface
@@ -52,6 +57,7 @@ type DefaultConfig struct {
// New creates a new default DNS database. c must not be nil.
func New(c *DefaultConfig) (db *Default) {
db = &Default{
+ logger: c.Logger,
buffer: &atomic.Pointer[buffer]{},
errColl: c.ErrColl,
maxSize: c.MaxSize,
diff --git a/internal/dnsdb/dnsdb_test.go b/internal/dnsdb/dnsdb_test.go
deleted file mode 100644
index 5f4d60b..0000000
--- a/internal/dnsdb/dnsdb_test.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package dnsdb_test
-
-import (
- "testing"
-
- "github.com/AdguardTeam/golibs/testutil"
-)
-
-func TestMain(m *testing.M) {
- testutil.DiscardLogOutput(m)
-}
diff --git a/internal/dnsdb/http.go b/internal/dnsdb/http.go
index 2ca435c..a54200c 100644
--- a/internal/dnsdb/http.go
+++ b/internal/dnsdb/http.go
@@ -31,7 +31,7 @@ func (db *Default) ServeHTTP(w http.ResponseWriter, r *http.Request) {
defer func() {
if err != nil {
h.Set(httphdr.XError, err.Error())
- errcoll.Collectf(ctx, db.errColl, "dnsdb: http handler error: %w", err)
+ errcoll.Collect(ctx, db.errColl, db.logger, "handling http", err)
}
}()
diff --git a/internal/dnsdb/http_test.go b/internal/dnsdb/http_test.go
index ebe3713..9e124cb 100644
--- a/internal/dnsdb/http_test.go
+++ b/internal/dnsdb/http_test.go
@@ -18,6 +18,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/dnsdb"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
"github.com/AdguardTeam/golibs/httphdr"
+ "github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
@@ -120,6 +121,7 @@ func TestDefault_ServeHTTP(t *testing.T) {
for _, tc := range testCases {
db := dnsdb.New(&dnsdb.DefaultConfig{
+ Logger: slogutil.NewDiscardLogger(),
ErrColl: agdtest.NewErrorCollector(),
MaxSize: 100,
})
diff --git a/internal/dnsserver/cache/cache.go b/internal/dnsserver/cache/cache.go
index 85ed4d6..535c9bd 100644
--- a/internal/dnsserver/cache/cache.go
+++ b/internal/dnsserver/cache/cache.go
@@ -42,9 +42,9 @@ type MiddlewareConfig struct {
// performance metrics. If not set, EmptyMetricsListener is used.
MetricsListener MetricsListener
- // Size is the number of entities to hold in the cache. It must be greater
- // than zero.
- Size int
+ // Count is the number of entities to hold in the cache. It must be
+ // positive.
+ Count int
// MinTTL is the minimum supported TTL for cache items.
MinTTL time.Duration
@@ -64,7 +64,7 @@ func NewMiddleware(c *MiddlewareConfig) (m *Middleware) {
return &Middleware{
metrics: metrics,
- cache: gcache.New(c.Size).LRU().Build(),
+ cache: gcache.New(c.Count).LRU().Build(),
cacheMinTTL: c.MinTTL,
overrideTTL: c.OverrideTTL,
}
diff --git a/internal/dnsserver/cache/cache_test.go b/internal/dnsserver/cache/cache_test.go
index aa4b291..f7c9cd4 100644
--- a/internal/dnsserver/cache/cache_test.go
+++ b/internal/dnsserver/cache/cache_test.go
@@ -19,10 +19,10 @@ func TestMiddleware_Wrap(t *testing.T) {
const (
servFailMaxCacheTTL = 30
- reqHostname = "example.com"
- reqCname = "cname.example.com"
- reqNs1 = "ns1.example.com"
- reqNs2 = "ns2.example.com"
+ reqHost = "example.com"
+ reqCNAME = "cname.example.com"
+ reqNs1 = "ns1.example.com"
+ reqNs2 = "ns2.example.com"
defaultTTL uint32 = 3600
)
@@ -30,10 +30,10 @@ func TestMiddleware_Wrap(t *testing.T) {
reqAddr := netip.MustParseAddr("1.2.3.4")
testTTL := 60 * time.Second
- aReq := dnsservertest.NewReq(reqHostname, dns.TypeA, dns.ClassINET)
- cnameReq := dnsservertest.NewReq(reqHostname, dns.TypeCNAME, dns.ClassINET)
- cnameAns := dnsservertest.SectionAnswer{dnsservertest.NewCNAME(reqHostname, defaultTTL, reqCname)}
- soaNs := dnsservertest.SectionNs{dnsservertest.NewSOA(reqHostname, defaultTTL, reqNs1, reqNs2)}
+ aReq := dnsservertest.NewReq(reqHost, dns.TypeA, dns.ClassINET)
+ cnameReq := dnsservertest.NewReq(reqHost, dns.TypeCNAME, dns.ClassINET)
+ cnameAns := dnsservertest.SectionAnswer{dnsservertest.NewCNAME(reqHost, defaultTTL, reqCNAME)}
+ soaNs := dnsservertest.SectionNs{dnsservertest.NewSOA(reqHost, defaultTTL, reqNs1, reqNs2)}
const N = 5
testCases := []struct {
@@ -46,7 +46,7 @@ func TestMiddleware_Wrap(t *testing.T) {
}{{
req: aReq,
resp: dnsservertest.NewResp(dns.RcodeSuccess, aReq, dnsservertest.SectionAnswer{
- dnsservertest.NewA(reqHostname, defaultTTL, reqAddr),
+ dnsservertest.NewA(reqHost, defaultTTL, reqAddr),
}),
name: "simple_a",
wantNumReq: 1,
@@ -83,7 +83,7 @@ func TestMiddleware_Wrap(t *testing.T) {
}, {
req: aReq,
resp: dnsservertest.NewResp(dns.RcodeNameError, aReq, dnsservertest.SectionNs{
- dnsservertest.NewNS(reqHostname, defaultTTL, reqNs1),
+ dnsservertest.NewNS(reqHost, defaultTTL, reqNs1),
}),
name: "non_authoritative_nxdomain",
// TODO(ameshkov): Consider https://datatracker.ietf.org/doc/html/rfc2308#section-3.
@@ -107,7 +107,7 @@ func TestMiddleware_Wrap(t *testing.T) {
}, {
req: cnameReq,
resp: dnsservertest.NewResp(dns.RcodeSuccess, cnameReq, dnsservertest.SectionAnswer{
- dnsservertest.NewCNAME(reqHostname, defaultTTL, reqCname),
+ dnsservertest.NewCNAME(reqHost, defaultTTL, reqCNAME),
}),
name: "simple_cname_ans",
wantNumReq: 1,
@@ -116,7 +116,7 @@ func TestMiddleware_Wrap(t *testing.T) {
}, {
req: aReq,
resp: dnsservertest.NewResp(dns.RcodeSuccess, aReq, dnsservertest.SectionAnswer{
- dnsservertest.NewA(reqHostname, 0, reqAddr),
+ dnsservertest.NewA(reqHost, 0, reqAddr),
}),
name: "expired_one",
wantNumReq: N,
@@ -125,7 +125,7 @@ func TestMiddleware_Wrap(t *testing.T) {
}, {
req: aReq,
resp: dnsservertest.NewResp(dns.RcodeSuccess, aReq, dnsservertest.SectionAnswer{
- dnsservertest.NewA(reqHostname, 10, reqAddr),
+ dnsservertest.NewA(reqHost, 10, reqAddr),
}),
name: "override_ttl_ok",
wantNumReq: 1,
@@ -134,7 +134,7 @@ func TestMiddleware_Wrap(t *testing.T) {
}, {
req: aReq,
resp: dnsservertest.NewResp(dns.RcodeSuccess, aReq, dnsservertest.SectionAnswer{
- dnsservertest.NewA(reqHostname, 1000, reqAddr),
+ dnsservertest.NewA(reqHost, 1000, reqAddr),
}),
name: "override_ttl_max",
wantNumReq: 1,
@@ -143,7 +143,7 @@ func TestMiddleware_Wrap(t *testing.T) {
}, {
req: aReq,
resp: dnsservertest.NewResp(dns.RcodeSuccess, aReq, dnsservertest.SectionAnswer{
- dnsservertest.NewA(reqHostname, 0, reqAddr),
+ dnsservertest.NewA(reqHost, 0, reqAddr),
}),
name: "override_ttl_zero",
wantNumReq: N,
@@ -152,7 +152,7 @@ func TestMiddleware_Wrap(t *testing.T) {
}, {
req: aReq,
resp: dnsservertest.NewResp(dns.RcodeServerFailure, aReq, dnsservertest.SectionAnswer{
- dnsservertest.NewA(reqHostname, servFailMaxCacheTTL, reqAddr),
+ dnsservertest.NewA(reqHost, servFailMaxCacheTTL, reqAddr),
}),
name: "override_ttl_servfail",
wantNumReq: 1,
@@ -186,7 +186,7 @@ func TestMiddleware_Wrap(t *testing.T) {
withCache := dnsserver.WithMiddlewares(
handler,
cache.NewMiddleware(&cache.MiddlewareConfig{
- Size: 100,
+ Count: 100,
MinTTL: minTTL,
OverrideTTL: tc.minTTL != nil,
}),
diff --git a/internal/dnsserver/dnsservertest/server.go b/internal/dnsserver/dnsservertest/server.go
index 36b07b7..b89c592 100644
--- a/internal/dnsserver/dnsservertest/server.go
+++ b/internal/dnsserver/dnsservertest/server.go
@@ -158,6 +158,14 @@ func RunLocalHTTPSServer(
network = dnsserver.NetworkTCP
}
+ var tlsConfigH3 *tls.Config
+ if tlsConfig != nil {
+ tlsConfigH3 = tlsConfig.Clone()
+
+ tlsConfig.NextProtos = dnsserver.NextProtoDoH
+ tlsConfigH3.NextProtos = dnsserver.NextProtoDoH3
+ }
+
conf := dnsserver.ConfigHTTPS{
ConfigBase: dnsserver.ConfigBase{
Name: "test",
@@ -165,8 +173,9 @@ func RunLocalHTTPSServer(
Handler: h,
Network: network,
},
- TLSConfig: tlsConfig,
- NonDNSHandler: nonDNSHandler,
+ TLSConfDefault: tlsConfig,
+ TLSConfH3: tlsConfigH3,
+ NonDNSHandler: nonDNSHandler,
}
s = dnsserver.NewServerHTTPS(conf)
diff --git a/internal/dnsserver/dnsservertest/tls.go b/internal/dnsserver/dnsservertest/tls.go
index ae93dce..9c68623 100644
--- a/internal/dnsserver/dnsservertest/tls.go
+++ b/internal/dnsserver/dnsservertest/tls.go
@@ -38,20 +38,30 @@ func CreateServerTLSConfig(tlsServerName string) (tlsConfig *tls.Config) {
NotBefore: notBefore,
NotAfter: notAfter,
- KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
+ KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature |
+ x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
IsCA: true,
}
template.DNSNames = append(template.DNSNames, tlsServerName)
- derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(privateKey), privateKey)
+ derBytes, err := x509.CreateCertificate(
+ rand.Reader,
+ &template,
+ &template,
+ publicKey(privateKey),
+ privateKey,
+ )
if err != nil {
panic(fmt.Sprintf("failed to create certificate: %v", err))
}
certPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
- keyPem := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)})
+ keyPem := pem.EncodeToMemory(&pem.Block{
+ Type: "RSA PRIVATE KEY",
+ Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
+ })
cert, err := tls.X509KeyPair(certPem, keyPem)
if err != nil {
diff --git a/internal/dnsserver/go.mod b/internal/dnsserver/go.mod
index a9d8c73..c3c934a 100644
--- a/internal/dnsserver/go.mod
+++ b/internal/dnsserver/go.mod
@@ -1,9 +1,9 @@
module github.com/AdguardTeam/AdGuardDNS/internal/dnsserver
-go 1.23.2
+go 1.23.4
require (
- github.com/AdguardTeam/golibs v0.30.1
+ github.com/AdguardTeam/golibs v0.30.4
github.com/ameshkov/dnscrypt/v2 v2.3.0
github.com/ameshkov/dnsstamps v1.0.3
github.com/bluele/gcache v0.0.2
@@ -12,11 +12,11 @@ require (
github.com/panjf2000/ants/v2 v2.10.0
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible
github.com/prometheus/client_golang v1.20.5
- github.com/quic-go/quic-go v0.48.1
+ github.com/quic-go/quic-go v0.48.2
github.com/stretchr/testify v1.9.0
- golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
- golang.org/x/net v0.30.0
- golang.org/x/sys v0.26.0
+ golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
+ golang.org/x/net v0.32.0
+ golang.org/x/sys v0.28.0
)
require (
@@ -26,23 +26,23 @@ require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
- github.com/google/pprof v0.0.0-20241023014458-598669927662 // indirect
+ github.com/google/pprof v0.0.0-20241203143554-1e3fdc7de467 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
- github.com/onsi/ginkgo/v2 v2.20.2 // indirect
+ github.com/onsi/ginkgo/v2 v2.22.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
- github.com/prometheus/common v0.60.0 // indirect
+ github.com/prometheus/common v0.60.1 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
go.uber.org/mock v0.5.0 // indirect
- golang.org/x/crypto v0.28.0 // indirect
- golang.org/x/mod v0.21.0 // indirect
- golang.org/x/sync v0.8.0 // indirect
- golang.org/x/text v0.19.0 // indirect
- golang.org/x/time v0.7.0 // indirect
- golang.org/x/tools v0.26.0 // indirect
+ golang.org/x/crypto v0.30.0 // indirect
+ golang.org/x/mod v0.22.0 // indirect
+ golang.org/x/sync v0.10.0 // indirect
+ golang.org/x/text v0.21.0 // indirect
+ golang.org/x/time v0.8.0 // indirect
+ golang.org/x/tools v0.28.0 // indirect
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/internal/dnsserver/go.sum b/internal/dnsserver/go.sum
index 870bc08..a7fdf7f 100644
--- a/internal/dnsserver/go.sum
+++ b/internal/dnsserver/go.sum
@@ -1,5 +1,4 @@
-github.com/AdguardTeam/golibs v0.30.1 h1:/yv7dq2h7WXw/jTDxkE3FP9zHerRT+i03PZRHJX4fPU=
-github.com/AdguardTeam/golibs v0.30.1/go.mod h1:FkwcNQEJoGsgDGXcalrVa/4gWbE68KsmE2guXWtBQUE=
+github.com/AdguardTeam/golibs v0.30.4 h1:zfFX1v4hkOCz6BifkneiBW2PCwSK523kYNr+VwaFrIw=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
@@ -26,8 +25,7 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/pprof v0.0.0-20241023014458-598669927662 h1:SKMkD83p7FwUqKmBsPdLHF5dNyxq3jOWwu9w9UyH5vA=
-github.com/google/pprof v0.0.0-20241023014458-598669927662/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
+github.com/google/pprof v0.0.0-20241203143554-1e3fdc7de467 h1:keEZFtbLJugfE0qHn+Ge1JCE71spzkchQobDf3mzS/4=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -38,10 +36,8 @@ github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
-github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
-github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
-github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
+github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
+github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
github.com/panjf2000/ants/v2 v2.10.0 h1:zhRg1pQUtkyRiOFo2Sbqwjp0GfBNo9cUY2/Grpx1p+8=
github.com/panjf2000/ants/v2 v2.10.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I=
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible h1:IWzUvJ72xMjmrjR9q3H1PF+jwdN0uNQiR2t1BLNalyo=
@@ -52,14 +48,12 @@ github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA=
-github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
+github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
-github.com/quic-go/quic-go v0.48.1 h1:y/8xmfWI9qmGTc+lBr4jKRUWLGSlSigv847ULJ4hYXA=
-github.com/quic-go/quic-go v0.48.1/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
+github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -72,25 +66,16 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
-golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
-golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
-golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
-golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
-golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
-golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
-golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
-golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
+golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY=
+golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
+golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
+golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
-golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
-golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
-golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
-golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
-golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
-golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
-golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
-golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
+golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
+golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
+golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
+golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
+golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/internal/dnsserver/prometheus/cache_test.go b/internal/dnsserver/prometheus/cache_test.go
index 05545ac..b0aa82b 100644
--- a/internal/dnsserver/prometheus/cache_test.go
+++ b/internal/dnsserver/prometheus/cache_test.go
@@ -19,7 +19,7 @@ import (
func TestCacheMetricsListener_integration_cache(t *testing.T) {
cacheMiddleware := cache.NewMiddleware(&cache.MiddlewareConfig{
MetricsListener: prometheus.NewCacheMetricsListener(testNamespace),
- Size: 100,
+ Count: 100,
})
handlerWithMiddleware := dnsserver.WithMiddlewares(
diff --git a/internal/dnsserver/protocol.go b/internal/dnsserver/protocol.go
index 71da69b..0299afb 100644
--- a/internal/dnsserver/protocol.go
+++ b/internal/dnsserver/protocol.go
@@ -61,7 +61,7 @@ func (p Protocol) ALPN() (alpn []string) {
case ProtoDoQ:
return []string{nextProtoDoQ}
case ProtoDoH:
- return slices.Clone(nextProtoDoH3)
+ return slices.Clone(NextProtoDoH3)
default:
return nil
}
diff --git a/internal/dnsserver/serverhttps.go b/internal/dnsserver/serverhttps.go
index 848d80b..81427e2 100644
--- a/internal/dnsserver/serverhttps.go
+++ b/internal/dnsserver/serverhttps.go
@@ -43,15 +43,15 @@ const (
httpIdleTimeout = 120 * time.Second
)
-// nextProtoDoH is a list of ALPN that we would add by default to the server's
-// *tls.Config if no NextProto is specified there. Note, that with this order,
-// we prioritize HTTP/2 over HTTP/1.1.
-var nextProtoDoH = []string{http2.NextProtoTLS, "http/1.1"}
+// NextProtoDoH is a list of ALPN protocols added by default to the server's
+// *tls.Config if no NextProto is specified there. Note that with this order,
+// HTTP/2 is prioritized over HTTP/1.1.
+var NextProtoDoH = []string{http2.NextProtoTLS, "http/1.1"}
-// nextProtoDoH3 is a list of ALPN that we should add by default to the server's
+// NextProtoDoH3 is a list of ALPN protocols added by default to the server's
// *tls.Config if no NextProto is specified there and DoH3 is supposed to be
-// used.
-var nextProtoDoH3 = []string{http3.NextProtoH3, http2.NextProtoTLS, "http/1.1"}
+// used. Note that with this order, HTTP/2 is prioritized over HTTP/1.1.
+var NextProtoDoH3 = []string{http3.NextProtoH3, http2.NextProtoTLS, "http/1.1"}
// ConfigHTTPS is a struct that needs to be passed to NewServerHTTPS to
// initialize a new ServerHTTPS instance. You can choose whether HTTP/3 is
@@ -59,10 +59,14 @@ var nextProtoDoH3 = []string{http3.NextProtoH3, http2.NextProtoTLS, "http/1.1"}
// will listen to both HTTP/2 and HTTP/3, but if you set it to NetworkTCP, the
// server will only use HTTP/2 and NetworkUDP will mean HTTP/3 only.
type ConfigHTTPS struct {
- // TLSConfig is the TLS configuration for HTTPS. If not set and
- // [ConfigBase.Network] is set to NetworkTCP the server will listen to
- // plain HTTP.
- TLSConfig *tls.Config
+ // TLSConfDefault is the TLS configuration for HTTPS. If not set and
+ // [ConfigBase.Network] is set to NetworkTCP the server will listen to plain
+ // HTTP. If it is not nil, it must be set to [NextProtoDoH].
+ TLSConfDefault *tls.Config
+
+ // TLSConfH3 is the TLS configuration for DoH3. If it is not nil, it must
+ // be set to [NextProtoDoH3].
+ TLSConfH3 *tls.Config
// NonDNSHandler handles requests with the path not equal to /dns-query.
// If it is empty, the server will return 404 for requests like that.
@@ -306,7 +310,7 @@ func (s *ServerHTTPS) serveHTTPS(ctx context.Context, hs *http.Server, l net.Lis
defer s.handlePanicAndExit(ctx)
scheme := urlutil.SchemeHTTPS
- if s.conf.TLSConfig == nil {
+ if s.conf.TLSConfDefault == nil {
scheme = urlutil.SchemeHTTP
}
@@ -516,12 +520,9 @@ func (s *ServerHTTPS) listenTLS(ctx context.Context) (err error) {
}
// Prepare the TLS configuration of the server.
- tlsConf := s.conf.TLSConfig
+ tlsConf := s.conf.TLSConfDefault
if tlsConf == nil {
return nil
- } else if len(tlsConf.NextProtos) == 0 {
- tlsConf = tlsConf.Clone()
- tlsConf.NextProtos = nextProtoDoH
}
s.tcpListener = tls.NewListener(s.tcpListener, tlsConf)
@@ -532,11 +533,7 @@ func (s *ServerHTTPS) listenTLS(ctx context.Context) (err error) {
// listenQUIC starts a QUIC listener that will be used to serve HTTP/3 requests.
func (s *ServerHTTPS) listenQUIC(ctx context.Context) (err error) {
// Prepare the TLS configuration of the server.
- tlsConf := s.conf.TLSConfig
- if tlsConf != nil && len(tlsConf.NextProtos) == 0 {
- tlsConf = tlsConf.Clone()
- tlsConf.NextProtos = nextProtoDoH3
- }
+ tlsConf := s.conf.TLSConfH3
conn, err := s.listenConfig.ListenPacket(ctx, "udp", s.addr)
if err != nil {
diff --git a/internal/dnsserver/serverhttps_test.go b/internal/dnsserver/serverhttps_test.go
index 9b9c9c6..53ace00 100644
--- a/internal/dnsserver/serverhttps_test.go
+++ b/internal/dnsserver/serverhttps_test.go
@@ -307,7 +307,8 @@ func TestDNSMsgToJSONMsg(t *testing.T) {
Type: dns.TypeHTTPS,
Class: dns.ClassINET,
TTL: 100,
- Data: `0 example.com alpn="h2,h3" ech="AQI=" ipv4hint="127.0.0.1,127.0.0.2" ipv6hint="2000::,2001::"`,
+ Data: `0 example.com alpn="h2,h3" ech="AQI=" ipv4hint="127.0.0.1,127.0.0.2" ` +
+ `ipv6hint="2000::,2001::"`,
}}, jsonMsg.Answer)
require.Equal(t, []dnsserver.JSONAnswer{{
Name: "example.org",
diff --git a/internal/dnsserver/serverquic.go b/internal/dnsserver/serverquic.go
index 8865cf4..c83c7b0 100644
--- a/internal/dnsserver/serverquic.go
+++ b/internal/dnsserver/serverquic.go
@@ -62,10 +62,16 @@ const (
// compatProtoDQ are ALPNs for backwards compatibility.
var compatProtoDQ = []string{"doq-i00", "doq-i02", "doq-i03", "dq"}
+// NextProtoDoQ is a list of ALPN protocols added by default to the server's
+// *tls.Config if no NextProto is specified there and DoQ is supposed to be
+// used.
+var NextProtoDoQ = append([]string{nextProtoDoQ}, compatProtoDQ...)
+
// ConfigQUIC is a struct that needs to be passed to NewServerQUIC to
// initialize a new ServerQUIC instance.
type ConfigQUIC struct {
- // TLSConfig is the TLS configuration for QUIC.
+ // TLSConfig is the TLS configuration for QUIC. If it is not nil, it must
+ // be set to [NextProtoDoQ].
TLSConfig *tls.Config
ConfigBase
@@ -111,12 +117,6 @@ const quicBytePoolSize = dns.MaxMsgSize
// NewServerQUIC creates a new ServerQUIC instance.
func NewServerQUIC(conf ConfigQUIC) (s *ServerQUIC) {
- // Make sure DOQ ALPNs are enabled in the TLS config.
- tlsConfig := conf.TLSConfig
- if len(tlsConfig.NextProtos) == 0 {
- tlsConfig.NextProtos = append([]string{nextProtoDoQ}, compatProtoDQ...)
- }
-
if conf.ListenConfig == nil {
// Do not enable OOB here as quic-go will do that on its own.
conf.ListenConfig = netext.DefaultListenConfig(nil)
diff --git a/internal/dnsserver/serverquic_test.go b/internal/dnsserver/serverquic_test.go
index 0eadfcd..f3c1a2b 100644
--- a/internal/dnsserver/serverquic_test.go
+++ b/internal/dnsserver/serverquic_test.go
@@ -107,6 +107,7 @@ func TestServerQUIC_integration_ENDS0Padding(t *testing.T) {
func TestServerQUIC_integration_0RTT(t *testing.T) {
tlsConfig := dnsservertest.CreateServerTLSConfig("example.org")
+ tlsConfig.NextProtos = dnsserver.NextProtoDoQ
srv, addr, err := dnsservertest.RunLocalQUICServer(
dnsservertest.NewDefaultHandler(),
tlsConfig,
diff --git a/internal/dnssvc/dnssvc.go b/internal/dnssvc/dnssvc.go
index 5d8e314..66c7661 100644
--- a/internal/dnssvc/dnssvc.go
+++ b/internal/dnssvc/dnssvc.go
@@ -347,14 +347,15 @@ func NewListener(
case agd.ProtoDoH:
l = dnsserver.NewServerHTTPS(dnsserver.ConfigHTTPS{
ConfigBase: baseConf,
- TLSConfig: s.TLS,
+ TLSConfDefault: s.TLS.Default,
+ TLSConfH3: s.TLS.H3,
NonDNSHandler: nonDNS,
MaxStreamsPerPeer: quicConf.MaxStreamsPerPeer,
QUICLimitsEnabled: quicConf.QUICLimitsEnabled,
})
case agd.ProtoDoQ:
l = dnsserver.NewServerQUIC(dnsserver.ConfigQUIC{
- TLSConfig: s.TLS,
+ TLSConfig: s.TLS.Default,
ConfigBase: baseConf,
MaxStreamsPerPeer: quicConf.MaxStreamsPerPeer,
QUICLimitsEnabled: quicConf.QUICLimitsEnabled,
@@ -369,7 +370,7 @@ func NewListener(
MaxPipelineCount: tcpConf.MaxPipelineCount,
TCPIdleTimeout: tcpConf.IdleTimeout,
},
- TLSConfig: s.TLS,
+ TLSConfig: s.TLS.Default,
})
default:
return nil, fmt.Errorf("protocol: %w: %d", errors.ErrBadEnumValue, p)
diff --git a/internal/dnssvc/handler.go b/internal/dnssvc/handler.go
index 49f5f2c..0862e20 100644
--- a/internal/dnssvc/handler.go
+++ b/internal/dnssvc/handler.go
@@ -88,7 +88,7 @@ func wrapPreUpstreamMw(ctx context.Context, c *HandlersConfig) (wrapped dnsserve
cacheMw := cache.NewMiddleware(&cache.MiddlewareConfig{
// TODO(a.garipov): Do not use promauto and refactor.
MetricsListener: dnssrvprom.NewCacheMetricsListener(metrics.Namespace()),
- Size: conf.NoECSCount,
+ Count: conf.NoECSCount,
MinTTL: conf.MinTTL,
OverrideTTL: conf.OverrideCacheTTL,
})
@@ -146,7 +146,10 @@ func newMainMiddlewareMetrics(c *HandlersConfig) (mainMwMtrc MainMiddlewareMetri
// newHandlersForServers returns a handler map for each server group and each
// server.
func newHandlersForServers(c *HandlersConfig, h dnsserver.Handler) (handlers Handlers, err error) {
- rlMwMtrc, err := metrics.NewDefaultRatelimitMiddleware(c.MetricsNamespace, c.PrometheusRegisterer)
+ rlMwMtrc, err := metrics.NewDefaultRatelimitMiddleware(
+ c.MetricsNamespace,
+ c.PrometheusRegisterer,
+ )
if err != nil {
return nil, fmt.Errorf("ratelimit middleware metrics: %w", err)
}
@@ -206,6 +209,6 @@ func newDeviceFinder(c *HandlersConfig, g *agd.ServerGroup, s *agd.Server) (df a
ProfileDB: c.ProfileDB,
HumanIDParser: c.HumanIDParser,
Server: s,
- DeviceDomains: g.TLS.DeviceDomains,
+ DeviceDomains: g.DeviceDomains,
})
}
diff --git a/internal/dnssvc/handler_test.go b/internal/dnssvc/handler_test.go
index 39a7474..d7e5df4 100644
--- a/internal/dnssvc/handler_test.go
+++ b/internal/dnssvc/handler_test.go
@@ -60,19 +60,27 @@ func TestNewHandlers(t *testing.T) {
},
}
- fltGrps := map[agd.FilteringGroupID]*agd.FilteringGroup{
- dnssvctest.FilteringGroupID: {
- ID: dnssvctest.FilteringGroupID,
- RuleListIDs: []agd.FilterListID{dnssvctest.FilterListID1},
- RuleListsEnabled: true,
+ fltGrp := &agd.FilteringGroup{
+ FilterConfig: &filter.ConfigGroup{
+ Parental: &filter.ConfigParental{},
+ RuleList: &filter.ConfigRuleList{
+ IDs: []filter.ID{dnssvctest.FilterListID1},
+ Enabled: true,
+ },
+ SafeBrowsing: &filter.ConfigSafeBrowsing{},
},
+ ID: dnssvctest.FilteringGroupID,
+ }
+
+ fltGrps := map[agd.FilteringGroupID]*agd.FilteringGroup{
+ dnssvctest.FilteringGroupID: fltGrp,
}
fltStrg := &agdtest.FilterStorage{
- OnFilterFromContext: func(_ context.Context, _ *agd.RequestInfo) (f filter.Interface) {
+ OnForConfig: func(_ context.Context, _ filter.Config) (f filter.Interface) {
panic("not implemented")
},
- OnHasListID: func(_ agd.FilterListID) (ok bool) { panic("not implemented") },
+ OnHasListID: func(_ filter.ID) (ok bool) { panic("not implemented") },
}
hashMatcher := &agdtest.HashMatcher{
@@ -91,7 +99,7 @@ func TestNewHandlers(t *testing.T) {
}
ruleStat := &agdtest.RuleStat{
- OnCollect: func(_ context.Context, _ agd.FilterListID, _ agd.FilterRuleText) {
+ OnCollect: func(_ context.Context, _ filter.ID, _ filter.RuleText) {
panic("not implemented")
},
}
@@ -104,7 +112,6 @@ func TestNewHandlers(t *testing.T) {
DDR: &agd.DDR{
Enabled: true,
},
- TLS: &agd.TLS{},
Name: dnssvctest.ServerGroupName,
FilteringGroup: dnssvctest.FilteringGroupID,
Servers: []*agd.Server{srv},
diff --git a/internal/dnssvc/integration_test.go b/internal/dnssvc/integration_test.go
index dc63f64..eae2d60 100644
--- a/internal/dnssvc/integration_test.go
+++ b/internal/dnssvc/integration_test.go
@@ -57,7 +57,7 @@ func newTestService(
querylogCh chan<- *querylog.Entry,
geoIPCh chan<- string,
dnsDBCh chan<- *agd.RequestInfo,
- ruleStatCh chan<- agd.FilterRuleText,
+ ruleStatCh chan<- filter.RuleText,
) (svc *dnssvc.Service, srvAddr netip.AddrPort) {
t.Helper()
@@ -73,11 +73,19 @@ func newTestService(
}
prof := &agd.Profile{
+ FilterConfig: &filter.ConfigClient{
+ Custom: &filter.ConfigCustom{},
+ Parental: &filter.ConfigParental{},
+ RuleList: &filter.ConfigRuleList{
+ IDs: []filter.ID{dnssvctest.FilterListID1},
+ Enabled: true,
+ },
+ SafeBrowsing: &filter.ConfigSafeBrowsing{},
+ },
Access: access.EmptyProfile{},
BlockingMode: &dnsmsg.BlockingModeNullIP{},
ID: dnssvctest.ProfileID,
DeviceIDs: []agd.DeviceID{dnssvctest.DeviceID},
- RuleListIDs: []agd.FilterListID{dnssvctest.FilterListID1},
FilteredResponseTTL: agdtest.FilteredResponseTTL,
FilteringEnabled: true,
QueryLogEnabled: true,
@@ -124,10 +132,10 @@ func newTestService(
}
fltStrg := &agdtest.FilterStorage{
- OnFilterFromContext: func(_ context.Context, _ *agd.RequestInfo) (f filter.Interface) {
+ OnForConfig: func(_ context.Context, _ filter.Config) (f filter.Interface) {
return flt
},
- OnHasListID: func(_ agd.FilterListID) (ok bool) { panic("not implemented") },
+ OnHasListID: func(_ filter.ID) (ok bool) { panic("not implemented") },
}
var ql querylog.Interface = &agdtest.QueryLog{
@@ -164,7 +172,7 @@ func newTestService(
}
ruleStat := &agdtest.RuleStat{
- OnCollect: func(_ context.Context, _ agd.FilterListID, text agd.FilterRuleText) {
+ OnCollect: func(_ context.Context, _ filter.ID, text filter.RuleText) {
testutil.RequireSend(pt, ruleStatCh, text, dnssvctest.Timeout)
},
}
@@ -182,15 +190,25 @@ func newTestService(
DDR: &agd.DDR{
Enabled: true,
},
- TLS: &agd.TLS{
- DeviceDomains: []string{dnssvctest.DomainForDevices},
- },
+ DeviceDomains: []string{dnssvctest.DomainForDevices},
Name: dnssvctest.ServerGroupName,
FilteringGroup: dnssvctest.FilteringGroupID,
Servers: []*agd.Server{srv},
ProfilesEnabled: true,
}}
+ fltGrp := &agd.FilteringGroup{
+ FilterConfig: &filter.ConfigGroup{
+ Parental: &filter.ConfigParental{},
+ RuleList: &filter.ConfigRuleList{
+ IDs: []filter.ID{dnssvctest.FilterListID1},
+ Enabled: true,
+ },
+ SafeBrowsing: &filter.ConfigSafeBrowsing{},
+ },
+ ID: dnssvctest.FilteringGroupID,
+ }
+
hdlrConf := &dnssvc.HandlersConfig{
BaseLogger: slogutil.NewDiscardLogger(),
Cache: &dnssvc.CacheConfig{
@@ -227,11 +245,7 @@ func newTestService(
RuleStat: ruleStat,
MetricsNamespace: path.Base(t.Name()),
FilteringGroups: map[agd.FilteringGroupID]*agd.FilteringGroup{
- dnssvctest.FilteringGroupID: {
- ID: dnssvctest.FilteringGroupID,
- RuleListIDs: []agd.FilterListID{dnssvctest.FilterListID1},
- RuleListsEnabled: true,
- },
+ dnssvctest.FilteringGroupID: fltGrp,
},
ServerGroups: srvGrps,
EDEEnabled: true,
@@ -271,7 +285,7 @@ func TestService_Wrap(t *testing.T) {
querylogCh := make(chan *querylog.Entry, 1)
geoIPCh := make(chan string, 2)
dnsDBCh := make(chan *agd.RequestInfo, 1)
- ruleStatCh := make(chan agd.FilterRuleText, 1)
+ ruleStatCh := make(chan filter.RuleText, 1)
errCollCh := make(chan error, 1)
go func() {
@@ -291,21 +305,27 @@ func TestService_Wrap(t *testing.T) {
})
t.Run("simple_success", func(t *testing.T) {
- noMatch := func(
- _ context.Context,
- m *dns.Msg,
- _ *agd.RequestInfo,
- ) (r filter.Result, err error) {
- pt := testutil.PanicT{}
- require.NotEmpty(pt, m.Question)
- require.Equal(pt, dnssvctest.DomainFQDN, m.Question[0].Name)
-
- return nil, nil
- }
-
flt := &agdtest.Filter{
- OnFilterRequest: noMatch,
- OnFilterResponse: noMatch,
+ OnFilterRequest: func(
+ _ context.Context,
+ fltReq *filter.Request,
+ ) (r filter.Result, err error) {
+ pt := testutil.PanicT{}
+ require.NotEmpty(pt, fltReq.DNS.Question)
+ require.Equal(pt, dnssvctest.DomainFQDN, fltReq.DNS.Question[0].Name)
+
+ return nil, nil
+ },
+ OnFilterResponse: func(
+ _ context.Context,
+ fltResp *filter.Response,
+ ) (r filter.Result, err error) {
+ pt := testutil.PanicT{}
+ require.NotEmpty(pt, fltResp.DNS.Question)
+ require.Equal(pt, dnssvctest.DomainFQDN, fltResp.DNS.Question[0].Name)
+
+ return nil, nil
+ },
}
svc, srvAddr := newTestService(
@@ -346,13 +366,13 @@ func TestService_Wrap(t *testing.T) {
dnsDBReqInfo := <-dnsDBCh
assert.NotNil(t, dnsDBReqInfo)
- assert.Equal(t, agd.FilterRuleText(""), <-ruleStatCh)
+ assert.Equal(t, filter.RuleText(""), <-ruleStatCh)
})
t.Run("request_cname", func(t *testing.T) {
const (
- cname = "cname.example.org"
- cnameRule agd.FilterRuleText = "||" + dnssvctest.Domain + "^$dnsrewrite=" + cname
+ cname = "cname.example.org"
+ cnameRule filter.RuleText = "||" + dnssvctest.Domain + "^$dnsrewrite=" + cname
)
cnameFQDN := dns.Fqdn(cname)
@@ -360,11 +380,10 @@ func TestService_Wrap(t *testing.T) {
flt := &agdtest.Filter{
OnFilterRequest: func(
_ context.Context,
- m *dns.Msg,
- _ *agd.RequestInfo,
+ fltReq *filter.Request,
) (r filter.Result, err error) {
// Pretend a CNAME rewrite matched the request.
- mod := dnsmsg.Clone(m)
+ mod := dnsmsg.Clone(fltReq.DNS)
mod.Question[0].Name = cnameFQDN
return &filter.ResultModifiedRequest{
@@ -373,11 +392,7 @@ func TestService_Wrap(t *testing.T) {
Rule: cnameRule,
}, nil
},
- OnFilterResponse: func(
- _ context.Context,
- _ *dns.Msg,
- _ *agd.RequestInfo,
- ) (filter.Result, error) {
+ OnFilterResponse: func(_ context.Context, _ *filter.Response) (filter.Result, error) {
panic("not implemented")
},
}
diff --git a/internal/dnssvc/internal/devicefinder/device_test.go b/internal/dnssvc/internal/devicefinder/device_test.go
index e558de7..4de63b0 100644
--- a/internal/dnssvc/internal/devicefinder/device_test.go
+++ b/internal/dnssvc/internal/devicefinder/device_test.go
@@ -199,7 +199,8 @@ func TestDefault_Find_byHumanID(t *testing.T) {
// Use uppercase versions to make sure that the device finder recognizes the
// device-type and profile data regardless of the case.
- extIDStr := "OTR-" + strings.ToUpper(dnssvctest.ProfileIDStr) + "-" + dnssvctest.HumanIDStr + "-!!!"
+ extIDStr := "OTR-" + strings.ToUpper(dnssvctest.ProfileIDStr) + "-" +
+ dnssvctest.HumanIDStr + "-!!!"
profDB := agdtest.NewProfileDB()
profDB.OnCreateAutoDevice = func(
diff --git a/internal/dnssvc/internal/devicefinder/devicefinder_test.go b/internal/dnssvc/internal/devicefinder/devicefinder_test.go
index bab26e7..69847af 100644
--- a/internal/dnssvc/internal/devicefinder/devicefinder_test.go
+++ b/internal/dnssvc/internal/devicefinder/devicefinder_test.go
@@ -408,7 +408,12 @@ func BenchmarkDefault(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for range b.N {
- sinkDevResult = df.Find(ctx, bc.req, dnssvctest.ClientAddrPort, dnssvctest.ServerAddrPort)
+ sinkDevResult = df.Find(
+ ctx,
+ bc.req,
+ dnssvctest.ClientAddrPort,
+ dnssvctest.ServerAddrPort,
+ )
}
_ = testutil.RequireTypeAssert[*agd.DeviceResultOK](b, sinkDevResult)
diff --git a/internal/dnssvc/internal/dnssvctest/dnssvctest.go b/internal/dnssvc/internal/dnssvctest/dnssvctest.go
index 65f0cfb..b992e05 100644
--- a/internal/dnssvc/internal/dnssvctest/dnssvctest.go
+++ b/internal/dnssvc/internal/dnssvctest/dnssvctest.go
@@ -9,6 +9,7 @@ import (
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/miekg/dns"
)
@@ -36,8 +37,8 @@ const (
// Common filtering-rule list ID for tests.
const (
- FilterListID1 agd.FilterListID = FilterListID1Str
- FilterListID2 agd.FilterListID = FilterListID2Str
+ FilterListID1 filter.ID = FilterListID1Str
+ FilterListID2 filter.ID = FilterListID2Str
)
// Common domains and FQDNs for tests.
@@ -144,7 +145,9 @@ func NewServer(
if proto.IsStdEncrypted() {
// #nosec G402 -- This is a test helper.
- srv.TLS = &tls.Config{}
+ srv.TLS = &agd.TLSConfig{
+ Default: &tls.Config{},
+ }
}
switch proto {
diff --git a/internal/dnssvc/internal/initial/specialdomain.go b/internal/dnssvc/internal/initial/specialdomain.go
index d79d102..7f57960 100644
--- a/internal/dnssvc/internal/initial/specialdomain.go
+++ b/internal/dnssvc/internal/initial/specialdomain.go
@@ -29,6 +29,12 @@ const (
// Resolvers for querying the resolver with unknown or absent name.
DDRDomain = DDRLabel + "." + ResolverARPADomain
+ // ChromePrefetchHost is the hostname that Chrome uses to check if it should
+ // use the Chrome Private Prefetch Proxy feature.
+ //
+ // See https://developer.chrome.com/docs/privacy-security/private-prefetch-proxy-for-network-admins.
+ ChromePrefetchHost = "dns-tunnel-check.googlezip.net"
+
// FirefoxCanaryHost is the hostname that Firefox uses to check if it should
// use its own DNS-over-HTTPS settings.
//
@@ -251,6 +257,10 @@ func (mw *Middleware) specialDomainHandler(
if shouldBlockPrivateRelay(ri, prof) {
return mw.handlePrivateRelay, "apple_private_relay"
}
+ case ChromePrefetchHost:
+ if shouldBlockChromePrefetch(ri, prof) {
+ return mw.handleChromePrefetch, "chrome_prefetch"
+ }
case FirefoxCanaryHost:
if shouldBlockFirefoxCanary(ri, prof) {
return mw.handleFirefoxCanary, "firefox"
@@ -262,36 +272,34 @@ func (mw *Middleware) specialDomainHandler(
return nil, ""
}
-// shouldBlockPrivateRelay returns true if the query is for an Apple Private
-// Relay check domain and the request information or profile indicates that
-// Apple Private Relay should be blocked.
-func shouldBlockPrivateRelay(ri *agd.RequestInfo, prof *agd.Profile) (ok bool) {
+// shouldBlockChromePrefetch returns true request information or profile
+// indicate that the Chrome prefetch domain should be blocked.
+func shouldBlockChromePrefetch(ri *agd.RequestInfo, prof *agd.Profile) (ok bool) {
if prof != nil {
- return prof.BlockPrivateRelay
+ return prof.BlockChromePrefetch
}
- return ri.FilteringGroup.BlockPrivateRelay
+ return ri.FilteringGroup.BlockChromePrefetch
}
-// handlePrivateRelay responds to Apple Private Relay queries with an NXDOMAIN
-// response.
-func (mw *Middleware) handlePrivateRelay(
+// handleChromePrefetch responds to Chrome prefetch domain queries with an
+// NXDOMAIN response.
+func (mw *Middleware) handleChromePrefetch(
ctx context.Context,
rw dnsserver.ResponseWriter,
req *dns.Msg,
ri *agd.RequestInfo,
) (err error) {
- metrics.DNSSvcApplePrivateRelayRequestsTotal.Inc()
+ metrics.DNSSvcChromePrefetchRequestsTotal.Inc()
resp := ri.Messages.NewRespRCode(req, dns.RcodeNameError)
err = rw.WriteMsg(ctx, req, resp)
- return errors.Annotate(err, "writing private relay resp: %w")
+ return errors.Annotate(err, "writing chrome prefetch resp: %w")
}
-// shouldBlockFirefoxCanary returns true if the query is for a Firefox canary
-// domain and the request information or profile indicates that Firefox canary
-// domain should be blocked.
+// shouldBlockFirefoxCanary returns true request information or profile indicate
+// that the Firefox canary domain should be blocked.
func shouldBlockFirefoxCanary(ri *agd.RequestInfo, prof *agd.Profile) (ok bool) {
if prof != nil {
return prof.BlockFirefoxCanary
@@ -315,3 +323,29 @@ func (mw *Middleware) handleFirefoxCanary(
return errors.Annotate(err, "writing firefox canary resp: %w")
}
+
+// shouldBlockPrivateRelay returns true request information or profile indicate
+// that the Apple Private Relay domain should be blocked.
+func shouldBlockPrivateRelay(ri *agd.RequestInfo, prof *agd.Profile) (ok bool) {
+ if prof != nil {
+ return prof.BlockPrivateRelay
+ }
+
+ return ri.FilteringGroup.BlockPrivateRelay
+}
+
+// handlePrivateRelay responds to Apple Private Relay queries with an NXDOMAIN
+// response.
+func (mw *Middleware) handlePrivateRelay(
+ ctx context.Context,
+ rw dnsserver.ResponseWriter,
+ req *dns.Msg,
+ ri *agd.RequestInfo,
+) (err error) {
+ metrics.DNSSvcApplePrivateRelayRequestsTotal.Inc()
+
+ resp := ri.Messages.NewRespRCode(req, dns.RcodeNameError)
+ err = rw.WriteMsg(ctx, req, resp)
+
+ return errors.Annotate(err, "writing private relay resp: %w")
+}
diff --git a/internal/dnssvc/internal/initial/specialdomain_test.go b/internal/dnssvc/internal/initial/specialdomain_test.go
index 544f713..d37ac01 100644
--- a/internal/dnssvc/internal/initial/specialdomain_test.go
+++ b/internal/dnssvc/internal/initial/specialdomain_test.go
@@ -23,32 +23,37 @@ import (
func TestMiddleware_Wrap_specialDomain(t *testing.T) {
var (
profAllowed = &agd.Profile{
- Access: access.EmptyProfile{},
- BlockPrivateRelay: false,
- BlockFirefoxCanary: false,
+ Access: access.EmptyProfile{},
+ BlockChromePrefetch: false,
+ BlockFirefoxCanary: false,
+ BlockPrivateRelay: false,
}
profBlocked = &agd.Profile{
- Access: access.EmptyProfile{},
- BlockPrivateRelay: true,
- BlockFirefoxCanary: true,
+ Access: access.EmptyProfile{},
+ BlockChromePrefetch: true,
+ BlockFirefoxCanary: true,
+ BlockPrivateRelay: true,
}
)
var (
fltGrpAllowed = &agd.FilteringGroup{
- BlockPrivateRelay: false,
- BlockFirefoxCanary: false,
+ BlockChromePrefetch: false,
+ BlockFirefoxCanary: false,
+ BlockPrivateRelay: false,
}
fltGrpBlocked = &agd.FilteringGroup{
- BlockPrivateRelay: true,
- BlockFirefoxCanary: true,
+ BlockChromePrefetch: true,
+ BlockFirefoxCanary: true,
+ BlockPrivateRelay: true,
}
)
const (
appleHost = initial.ApplePrivateRelayMaskHost
+ chromeHost = initial.ChromePrefetchHost
firefoxHost = initial.FirefoxCanaryHost
)
@@ -100,29 +105,27 @@ func TestMiddleware_Wrap_specialDomain(t *testing.T) {
reqInfo: newSpecDomReqInfo(t, nil, fltGrpBlocked, firefoxHost, dns.TypeA),
name: "firefox_canary_blocked_by_fltgrp",
wantRCode: dns.RcodeRefused,
+ }, {
+ reqInfo: newSpecDomReqInfo(t, profAllowed, fltGrpAllowed, chromeHost, dns.TypeA),
+ name: "chrome_prefetch_allowed_by_both",
+ wantRCode: dns.RcodeSuccess,
+ }, {
+ reqInfo: newSpecDomReqInfo(t, profBlocked, fltGrpAllowed, chromeHost, dns.TypeA),
+ name: "chrome_prefetch_blocked_by_prof",
+ wantRCode: dns.RcodeNameError,
+ }, {
+ reqInfo: newSpecDomReqInfo(t, nil, fltGrpBlocked, chromeHost, dns.TypeA),
+ name: "chrome_prefetch_blocked_by_fltgrp",
+ wantRCode: dns.RcodeNameError,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- var handler dnsserver.Handler = dnsserver.HandlerFunc(func(
- ctx context.Context,
- rw dnsserver.ResponseWriter,
- req *dns.Msg,
- ) (err error) {
- if tc.wantRCode != dns.RcodeSuccess {
- return errors.Error("unexpectedly reached handler")
- }
-
- resp := (&dns.Msg{}).SetReply(req)
-
- return rw.WriteMsg(ctx, req, resp)
- })
-
mw := initial.New(&initial.Config{
Logger: slogutil.NewDiscardLogger(),
})
- h := mw.Wrap(handler)
+ h := mw.Wrap(newSpecDomHandler(tc.wantRCode == dns.RcodeSuccess))
ctx := testutil.ContextWithTimeout(t, dnssvctest.Timeout)
ctx = agd.ContextWithRequestInfo(ctx, tc.reqInfo)
@@ -185,3 +188,21 @@ func newSpecDomReqInfo(
return ri
}
+
+// newSpecDomHandler is a helper that creates a DNS handler for the
+// special-domain tests. The handler returns an error if wantReach is false.
+func newSpecDomHandler(wantReach bool) (h dnsserver.Handler) {
+ return dnsserver.HandlerFunc(func(
+ ctx context.Context,
+ rw dnsserver.ResponseWriter,
+ req *dns.Msg,
+ ) (err error) {
+ if !wantReach {
+ return errors.Error("unexpectedly reached handler")
+ }
+
+ resp := (&dns.Msg{}).SetReply(req)
+
+ return rw.WriteMsg(ctx, req, resp)
+ })
+}
diff --git a/internal/dnssvc/internal/mainmw/debug_internal_test.go b/internal/dnssvc/internal/mainmw/debug_internal_test.go
index 4a1580b..bbd2469 100644
--- a/internal/dnssvc/internal/mainmw/debug_internal_test.go
+++ b/internal/dnssvc/internal/mainmw/debug_internal_test.go
@@ -221,7 +221,10 @@ func TestMiddleware_writeDebugResponse(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- rw := dnsserver.NewNonWriterResponseWriter(dnssvctest.ServerTCPAddr, dnssvctest.ClientTCPAddr)
+ rw := dnsserver.NewNonWriterResponseWriter(
+ dnssvctest.ServerTCPAddr,
+ dnssvctest.ClientTCPAddr,
+ )
ctx := agd.ContextWithRequestInfo(context.Background(), tc.reqInfo)
diff --git a/internal/dnssvc/internal/mainmw/filter.go b/internal/dnssvc/internal/mainmw/filter.go
index 72b1a73..68f3d4d 100644
--- a/internal/dnssvc/internal/mainmw/filter.go
+++ b/internal/dnssvc/internal/mainmw/filter.go
@@ -7,6 +7,7 @@ import (
"time"
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
+ "github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/miekg/dns"
)
@@ -57,9 +58,12 @@ func (mw *Middleware) filterRequest(
) {
start := time.Now()
- reqRes, err := f.FilterRequest(ctx, fctx.originalRequest, ri)
+ fltReq := mw.reqInfoToFltReq(fctx.originalRequest, ri)
+ defer mw.putFltReq(fltReq)
+
+ reqRes, err := f.FilterRequest(ctx, fltReq)
if err != nil {
- mw.reportf(ctx, "filtering request: %w", err)
+ errcoll.Collect(ctx, mw.errColl, mw.logger, "filtering request", err)
}
if mod, ok := reqRes.(*filter.ResultModifiedRequest); ok {
@@ -70,6 +74,38 @@ func (mw *Middleware) filterRequest(
fctx.elapsed += time.Since(start)
}
+// reqInfoToFltReq converts data from a DNS request and request info into a
+// *filter.Request. The returned request data should be put back into the pool
+// by using [Middleware.putFltReq].
+func (mw *Middleware) reqInfoToFltReq(req *dns.Msg, ri *agd.RequestInfo) (fltReq *filter.Request) {
+ fltReq = mw.fltReqPool.Get()
+
+ // NOTE: Fill all fields of fltReq since it is reused from the pool.
+ fltReq.DNS = req
+ fltReq.Messages = ri.Messages
+ fltReq.RemoteIP = ri.RemoteIP
+
+ if _, d := ri.DeviceData(); d != nil {
+ fltReq.ClientName = string(d.Name)
+ } else {
+ fltReq.ClientName = ""
+ }
+
+ fltReq.Host = ri.Host
+ fltReq.QType = ri.QType
+ fltReq.QClass = ri.QClass
+
+ return fltReq
+}
+
+// putFltReq sets req.DNS to nil, to prevent the message being contained in the
+// pool, which can lead to conflicts with the cloner of the middleware, and puts
+// req back into the pool.
+func (mw *Middleware) putFltReq(req *filter.Request) {
+ req.DNS = nil
+ mw.fltReqPool.Put(req)
+}
+
// filterResponse applies f to resp and sets the result of filtering in fctx.
// If origReq has a different question name than resp, the request assumed being
// CNAME-rewritten and no filtering performed on resp, the CNAME is prepended to
@@ -95,9 +131,12 @@ func (mw *Middleware) filterResponse(
var rr dns.RR = ri.Messages.NewAnswerCNAME(origReq, modReq.Question[0].Name)
origResp.Answer = slices.Insert(origResp.Answer, 0, rr)
} else {
- respRes, err := f.FilterResponse(ctx, fctx.originalResponse, ri)
+ fltResp := mw.reqInfoToFltResp(fctx.originalResponse, ri)
+ defer mw.putFltResp(fltResp)
+
+ respRes, err := f.FilterResponse(ctx, fltResp)
if err != nil {
- mw.reportf(ctx, "filtering response: %w", err)
+ errcoll.Collect(ctx, mw.errColl, mw.logger, "filtering response", err)
}
fctx.responseResult = respRes
@@ -106,11 +145,41 @@ func (mw *Middleware) filterResponse(
fctx.elapsed += time.Since(start)
}
+// reqInfoToFltResp converts data from a DNS response and request info into a
+// *filter.Response. The returned response data should be put back into
+// the pool by using [Middleware.putFltResp].
+func (mw *Middleware) reqInfoToFltResp(
+ resp *dns.Msg,
+ ri *agd.RequestInfo,
+) (fltResp *filter.Response) {
+ fltResp = mw.fltRespPool.Get()
+
+ // NOTE: Fill all fields of fltResp since it is reused from the pool.
+ fltResp.DNS = resp
+ fltResp.RemoteIP = ri.RemoteIP
+
+ if _, d := ri.DeviceData(); d != nil {
+ fltResp.ClientName = string(d.Name)
+ } else {
+ fltResp.ClientName = ""
+ }
+
+ return fltResp
+}
+
+// putFltResp sets resp.DNS to nil, to prevent the message being contained in
+// the pool, which can lead to conflicts with the cloner of the middleware, and
+// puts resp back into the pool.
+func (mw *Middleware) putFltResp(resp *filter.Response) {
+ resp.DNS = nil
+ mw.fltRespPool.Put(resp)
+}
+
// filteringData returns the data necessary for request information recording
// from the filtering context.
func filteringData(
fctx *filteringContext,
-) (id agd.FilterListID, text agd.FilterRuleText, blocked bool) {
+) (id filter.ID, text filter.RuleText, blocked bool) {
if fctx.requestResult != nil {
return resultData(fctx.requestResult, "reqRes")
}
@@ -123,9 +192,9 @@ func filteringData(
func resultData(
res filter.Result,
argName string,
-) (id agd.FilterListID, text agd.FilterRuleText, blocked bool) {
+) (id filter.ID, text filter.RuleText, blocked bool) {
if res == nil {
- return agd.FilterListIDNone, "", false
+ return filter.IDNone, "", false
}
id, text = res.MatchedRule()
@@ -164,7 +233,13 @@ func (mw *Middleware) setFilteredResponse(
var err error
fctx.filteredResponse, err = ri.Messages.NewBlockedResp(fctx.originalRequest)
if err != nil {
- mw.reportf(ctx, "creating blocked resp for filtered req: %w", err)
+ errcoll.Collect(
+ ctx,
+ mw.errColl,
+ mw.logger,
+ "creating blocked resp for filtered req",
+ err,
+ )
fctx.filteredResponse = fctx.originalResponse
}
case *filter.ResultAllowed, *filter.ResultModifiedRequest:
@@ -201,7 +276,13 @@ func (mw *Middleware) setFilteredResponseNoReq(
var err error
fctx.filteredResponse, err = ri.Messages.NewBlockedResp(fctx.originalRequest)
if err != nil {
- mw.reportf(ctx, "creating blocked resp for filtered resp: %w", err)
+ errcoll.Collect(
+ ctx,
+ mw.errColl,
+ mw.logger,
+ "creating blocked resp for filtered resp",
+ err,
+ )
fctx.filteredResponse = fctx.originalResponse
}
default:
diff --git a/internal/dnssvc/internal/mainmw/mainmw.go b/internal/dnssvc/internal/mainmw/mainmw.go
index 1442636..380504c 100644
--- a/internal/dnssvc/internal/mainmw/mainmw.go
+++ b/internal/dnssvc/internal/mainmw/mainmw.go
@@ -25,17 +25,19 @@ import (
// Middleware is the main middleware of AdGuard DNS.
type Middleware struct {
- cloner *dnsmsg.Cloner
- fltCtxPool *syncutil.Pool[filteringContext]
- logger *slog.Logger
- messages *dnsmsg.Constructor
- billStat billstat.Recorder
- errColl errcoll.Interface
- fltStrg filter.Storage
- geoIP geoip.Interface
- metrics Metrics
- queryLog querylog.Interface
- ruleStat rulestat.Interface
+ cloner *dnsmsg.Cloner
+ fltCtxPool *syncutil.Pool[filteringContext]
+ fltReqPool *syncutil.Pool[filter.Request]
+ fltRespPool *syncutil.Pool[filter.Response]
+ logger *slog.Logger
+ messages *dnsmsg.Constructor
+ billStat billstat.Recorder
+ errColl errcoll.Interface
+ fltStrg filter.Storage
+ geoIP geoip.Interface
+ metrics Metrics
+ queryLog querylog.Interface
+ ruleStat rulestat.Interface
}
// Config is the configuration structure for the main middleware. All fields
@@ -84,6 +86,12 @@ func New(c *Config) (mw *Middleware) {
fltCtxPool: syncutil.NewPool(func() (v *filteringContext) {
return &filteringContext{}
}),
+ fltReqPool: syncutil.NewPool(func() (v *filter.Request) {
+ return &filter.Request{}
+ }),
+ fltRespPool: syncutil.NewPool(func() (v *filter.Response) {
+ return &filter.Response{}
+ }),
logger: c.Logger,
messages: c.Messages,
billStat: c.BillStat,
@@ -125,7 +133,7 @@ func (mw *Middleware) Wrap(next dnsserver.Handler) (wrapped dnsserver.Handler) {
"remote_ip", ri.RemoteIP,
)
- flt := mw.fltStrg.FilterFromContext(ctx, ri)
+ flt := mw.filter(ctx, ri)
mw.filterRequest(ctx, fctx, flt, ri)
// Check the context error here, since the context could have already
@@ -171,6 +179,20 @@ func (mw *Middleware) Wrap(next dnsserver.Handler) (wrapped dnsserver.Handler) {
return dnsserver.HandlerFunc(f)
}
+// filter returns a filter based on the request information.
+func (mw *Middleware) filter(ctx context.Context, ri *agd.RequestInfo) (f filter.Interface) {
+ p, d := ri.DeviceData()
+ if p == nil {
+ return mw.fltStrg.ForConfig(ctx, ri.FilteringGroup.FilterConfig)
+ }
+
+ if p.FilteringEnabled && d.FilteringEnabled {
+ return mw.fltStrg.ForConfig(ctx, p.FilterConfig)
+ }
+
+ return mw.fltStrg.ForConfig(ctx, nil)
+}
+
// nextParams is a helper that returns the parameters to call the next handler
// with taking the filtering context into account.
func (mw *Middleware) nextParams(
@@ -239,8 +261,3 @@ func (mw *Middleware) reportMetrics(
IsBlocked: isBlocked,
})
}
-
-// reportf is a helper method for reporting non-critical errors.
-func (mw *Middleware) reportf(ctx context.Context, format string, args ...any) {
- errcoll.Collectf(ctx, mw.errColl, "mainmw: "+format, args...)
-}
diff --git a/internal/dnssvc/internal/mainmw/mainmw_test.go b/internal/dnssvc/internal/mainmw/mainmw_test.go
index e904b8b..57726dd 100644
--- a/internal/dnssvc/internal/mainmw/mainmw_test.go
+++ b/internal/dnssvc/internal/mainmw/mainmw_test.go
@@ -34,12 +34,12 @@ const (
testRespAddr4Str = "3.4.5.6"
testRewriteAddrStr = "7.8.9.0"
- testRuleAllow agd.FilterRuleText = "@@||" + dnssvctest.DomainAllowed + "^"
- testRuleBlockReq agd.FilterRuleText = "||" + dnssvctest.DomainBlocked + "^"
- testRuleBlockResp agd.FilterRuleText = "||" + testRespAddr4Str + "^"
- testRuleRewrite agd.FilterRuleText = "||" + dnssvctest.DomainRewritten +
+ testRuleAllow filter.RuleText = "@@||" + dnssvctest.DomainAllowed + "^"
+ testRuleBlockReq filter.RuleText = "||" + dnssvctest.DomainBlocked + "^"
+ testRuleBlockResp filter.RuleText = "||" + testRespAddr4Str + "^"
+ testRuleRewrite filter.RuleText = "||" + dnssvctest.DomainRewritten +
"^$dnsrewrite=NOERROR;A;" + testRewriteAddrStr
- testRuleRewriteCNAME agd.FilterRuleText = "||" + dnssvctest.DomainRewritten +
+ testRuleRewriteCNAME filter.RuleText = "||" + dnssvctest.DomainRewritten +
"^$dnsrewrite=NOERROR;CNAME;" + dnssvctest.DomainRewrittenCNAME
)
@@ -96,25 +96,23 @@ func TestMiddleware_Wrap(t *testing.T) {
flt := &agdtest.Filter{
OnFilterRequest: func(
_ context.Context,
- _ *dns.Msg,
- _ *agd.RequestInfo,
+ _ *filter.Request,
) (r filter.Result, err error) {
return nil, nil
},
OnFilterResponse: func(
_ context.Context,
- _ *dns.Msg,
- _ *agd.RequestInfo,
+ _ *filter.Response,
) (r filter.Result, err error) {
return nil, nil
},
}
fltStrg := &agdtest.FilterStorage{
- OnFilterFromContext: func(_ context.Context, _ *agd.RequestInfo) (f filter.Interface) {
+ OnForConfig: func(_ context.Context, _ filter.Config) (f filter.Interface) {
return flt
},
- OnHasListID: func(_ agd.FilterListID) (ok bool) { panic("not implemented") },
+ OnHasListID: func(_ filter.ID) (ok bool) { panic("not implemented") },
}
geoIP := agdtest.NewGeoIP()
@@ -131,10 +129,10 @@ func TestMiddleware_Wrap(t *testing.T) {
}
ruleStat := &agdtest.RuleStat{
- OnCollect: func(_ context.Context, id agd.FilterListID, text agd.FilterRuleText) {
+ OnCollect: func(_ context.Context, id filter.ID, text filter.RuleText) {
pt := testutil.PanicT{}
- require.Equal(pt, agd.FilterListID(""), id)
- require.Equal(pt, agd.FilterRuleText(""), text)
+ require.Equal(pt, filter.ID(""), id)
+ require.Equal(pt, filter.RuleText(""), text)
},
}
@@ -238,7 +236,10 @@ func TestMiddleware_Wrap(t *testing.T) {
h := mw.Wrap(newSimpleHandler(t, tc.req, wantResp))
ctx := newContext(t, tc.device, tc.profile, reqHost, reqQType, reqStart)
- rw := dnsserver.NewNonWriterResponseWriter(dnssvctest.ServerTCPAddr, dnssvctest.ClientTCPAddr)
+ rw := dnsserver.NewNonWriterResponseWriter(
+ dnssvctest.ServerTCPAddr,
+ dnssvctest.ClientTCPAddr,
+ )
serveErr := h.ServeDNS(ctx, rw, tc.req)
testutil.AssertErrorMsg(t, tc.wantErrMsg, serveErr)
@@ -332,6 +333,19 @@ func newContext(
ctx = dnsserver.ContextWithRequestInfo(ctx, &dnsserver.RequestInfo{
StartTime: start,
})
+
+ fltConf := &filter.ConfigGroup{
+ Parental: &filter.ConfigParental{},
+ RuleList: &filter.ConfigRuleList{
+ IDs: []filter.ID{
+ dnssvctest.FilterListID1,
+ dnssvctest.FilterListID2,
+ },
+ Enabled: true,
+ },
+ SafeBrowsing: &filter.ConfigSafeBrowsing{},
+ }
+
ctx = agd.ContextWithRequestInfo(ctx, &agd.RequestInfo{
DeviceResult: &agd.DeviceResultOK{
Device: d,
@@ -342,11 +356,7 @@ func newContext(
ASN: testASN,
},
FilteringGroup: &agd.FilteringGroup{
- RuleListIDs: []agd.FilterListID{
- dnssvctest.FilterListID1,
- dnssvctest.FilterListID2,
- },
- RuleListsEnabled: true,
+ FilterConfig: fltConf,
},
Messages: agdtest.NewConstructor(tb),
RemoteIP: dnssvctest.ClientAddr,
@@ -540,7 +550,7 @@ func TestMiddleware_Wrap_filtering(t *testing.T) {
respRes filter.Result
name string
wantErrMsg string
- wantRule agd.FilterRuleText
+ wantRule filter.RuleText
}{{
req: reqAllow,
device: nil,
@@ -641,28 +651,23 @@ func TestMiddleware_Wrap_filtering(t *testing.T) {
flt := &agdtest.Filter{
OnFilterRequest: func(
_ context.Context,
- _ *dns.Msg,
- _ *agd.RequestInfo,
+ _ *filter.Request,
) (r filter.Result, err error) {
return tc.reqRes, nil
},
OnFilterResponse: func(
_ context.Context,
- _ *dns.Msg,
- _ *agd.RequestInfo,
+ _ *filter.Response,
) (r filter.Result, err error) {
return tc.respRes, nil
},
}
fltStrg := &agdtest.FilterStorage{
- OnFilterFromContext: func(
- _ context.Context,
- _ *agd.RequestInfo,
- ) (f filter.Interface) {
+ OnForConfig: func(_ context.Context, _ filter.Config) (f filter.Interface) {
return flt
},
- OnHasListID: func(_ agd.FilterListID) (ok bool) { panic("not implemented") },
+ OnHasListID: func(_ filter.ID) (ok bool) { panic("not implemented") },
}
q := tc.req.Question[0]
@@ -680,7 +685,7 @@ func TestMiddleware_Wrap_filtering(t *testing.T) {
}
ruleStat := &agdtest.RuleStat{
- OnCollect: func(_ context.Context, id agd.FilterListID, text agd.FilterRuleText) {
+ OnCollect: func(_ context.Context, id filter.ID, text filter.RuleText) {
pt := testutil.PanicT{}
require.Equal(pt, dnssvctest.FilterListID1, id)
require.Equal(pt, tc.wantRule, text)
@@ -705,7 +710,10 @@ func TestMiddleware_Wrap_filtering(t *testing.T) {
h := mw.Wrap(newSimpleHandler(t, tc.wantUpsReq, tc.upsResp))
ctx := newContext(t, tc.device, tc.profile, reqHost, reqQType, reqStart)
- rw := dnsserver.NewNonWriterResponseWriter(dnssvctest.ServerTCPAddr, dnssvctest.ClientTCPAddr)
+ rw := dnsserver.NewNonWriterResponseWriter(
+ dnssvctest.ServerTCPAddr,
+ dnssvctest.ClientTCPAddr,
+ )
serveErr := h.ServeDNS(ctx, rw, tc.req)
testutil.AssertErrorMsg(t, tc.wantErrMsg, serveErr)
diff --git a/internal/dnssvc/internal/mainmw/record.go b/internal/dnssvc/internal/mainmw/record.go
index 9065adc..247f4ed 100644
--- a/internal/dnssvc/internal/mainmw/record.go
+++ b/internal/dnssvc/internal/mainmw/record.go
@@ -11,6 +11,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdnet"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
+ "github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/AdGuardDNS/internal/optslog"
"github.com/AdguardTeam/AdGuardDNS/internal/querylog"
@@ -87,7 +88,7 @@ func (mw *Middleware) recordQueryInfo(
err := mw.queryLog.Write(ctx, e)
if err != nil {
// Consider query logging errors non-critical.
- mw.reportf(ctx, "writing query log: %w", err)
+ errcoll.Collect(ctx, mw.errColl, mw.logger, "writing query log", err)
}
}
@@ -138,7 +139,7 @@ func (mw *Middleware) responseData(
ip, err := ipFromAnswer(resp.Answer)
if err != nil {
- mw.reportf(ctx, "getting response data: %w", err)
+ errcoll.Collect(ctx, mw.errColl, mw.logger, "getting response data", err)
}
return rcode, ip, dnssec
@@ -228,7 +229,7 @@ func (mw *Middleware) country(ctx context.Context, host string, ip netip.Addr) (
l, err := mw.geoIP.Data(host, ip)
if err != nil {
// Consider GeoIP errors non-critical.
- mw.reportf(ctx, "getting geoip data: %w", err)
+ errcoll.Collect(ctx, mw.errColl, mw.logger, "getting geoip data", err)
}
if l != nil {
diff --git a/internal/dnssvc/internal/ratelimitmw/access_test.go b/internal/dnssvc/internal/ratelimitmw/access_test.go
index 51b5656..dc5e798 100644
--- a/internal/dnssvc/internal/ratelimitmw/access_test.go
+++ b/internal/dnssvc/internal/ratelimitmw/access_test.go
@@ -42,7 +42,7 @@ func TestMiddleware_Wrap_access(t *testing.T) {
blockedClient2Prefix = netip.MustParsePrefix("2001:db8::/120")
)
- accessMgr, errAccess := access.NewGlobal(
+ accessMgr, accessErr := access.NewGlobal(
[]string{
domainBlockedNormal,
domainBlockedUppercase,
@@ -53,7 +53,7 @@ func TestMiddleware_Wrap_access(t *testing.T) {
blockedClient2Prefix,
},
)
- require.NoError(t, errAccess)
+ require.NoError(t, accessErr)
geoIP := agdtest.NewGeoIP()
geoIP.OnData = func(_ string, _ netip.Addr) (l *geoip.Location, err error) {
@@ -154,9 +154,11 @@ func TestMiddleware_Wrap_access(t *testing.T) {
qtype: dns.TypeAAAA,
}}
- handler := dnsserver.HandlerFunc(func(ctx context.Context, rw dnsserver.ResponseWriter, req *dns.Msg) (_ error) {
- return rw.WriteMsg(ctx, req, dnsservertest.NewResp(dns.RcodeSuccess, req))
- })
+ handler := dnsserver.HandlerFunc(
+ func(ctx context.Context, rw dnsserver.ResponseWriter, req *dns.Msg) (err error) {
+ return rw.WriteMsg(ctx, req, dnsservertest.NewResp(dns.RcodeSuccess, req))
+ },
+ )
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
diff --git a/internal/ecscache/cache_internal_test.go b/internal/ecscache/cache_internal_test.go
index 2084b07..d1a4dc8 100644
--- a/internal/ecscache/cache_internal_test.go
+++ b/internal/ecscache/cache_internal_test.go
@@ -15,10 +15,10 @@ var msgSink *dns.Msg
func BenchmarkMiddleware_Get(b *testing.B) {
mw := &Middleware{
cache: agdcache.NewLRU[uint64, *cacheItem](&agdcache.LRUConfig{
- Size: 10,
+ Count: 10,
}),
ecsCache: agdcache.NewLRU[uint64, *cacheItem](&agdcache.LRUConfig{
- Size: 10,
+ Count: 10,
}),
}
diff --git a/internal/ecscache/ecsblocklist.go b/internal/ecscache/ecsblocklist.go
index 7f0e84f..8246cc1 100644
--- a/internal/ecscache/ecsblocklist.go
+++ b/internal/ecscache/ecsblocklist.go
@@ -7,34 +7,44 @@ 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.",
"0cf.io.",
"0cf17917-395b-4f25-91cc-db3bdd6044b0.prmutv.co.",
+ "1009384777.rsc.cdn77.org.",
"1024tera.com.",
- "1024terabox.com.",
"103.chtsite.com.",
"11-alarm-mop.meshare.com.",
- "11222.cn.",
"12-alarm-mop.meshare.com.",
"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.",
+ "1549064449.rsc.cdn77.org.",
"163.com.",
"163jiasu.com.",
"163yun.com.",
+ "1663719199.rsc.cdn77.org.",
"1688.com.",
- "173bf104.akstat.io.",
- "173bf10f.akstat.io.",
+ "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.",
- "2owo1y.n0qq3z.com.",
"2talk.com.",
"360safe.com.",
"3a6b0682-f3e1-4576-a706-5eb4101b9cc3.prmutv.co.",
@@ -43,15 +53,11 @@ var FakeECSFQDNs = container.NewMapSet(
"4.401402081.west-gcloud.codm.activision.com.",
"4.adsco.re.",
"401402081.west-gcloud.codm.activision.com.",
- "404.playrix.com.",
"4186979c18134d1eae82ec64dbfc9af2.pacloudflare.com.",
- "41ku.cn.",
- "46799.apps.zdusercontent.com.",
"46a2e5c3c5a64e218b60f2c2ee76b750.pacloudflare.com.",
- "481528.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.",
@@ -67,14 +73,12 @@ var FakeECSFQDNs = container.NewMapSet(
"a-api.anthropic.com.",
"a-cdn.anthropic.com.",
"a-m-p.xyz.",
- "a.ad.gt.cdn.cloudflare.net.",
+ "a.api.permutive.app.",
+ "a.applvn.com.",
"a.getepic.com.",
- "a.klaviyo.com.cdn.cloudflare.net.",
"a.nel.cloudflare.com.",
"a.nitropay.com.",
- "a.researchgate.net.",
- "a.vsstatic.com.",
- "a19558781057.cdn.optimizely.com.",
+ "a10681260716.cdn.optimizely.com.",
"a2321.casalemedia.com.",
"a2322.casalemedia.com.",
"a2323.casalemedia.com.",
@@ -89,17 +93,18 @@ var FakeECSFQDNs = container.NewMapSet(
"a2332.casalemedia.com.",
"a2333.casalemedia.com.",
"a2334.casalemedia.com.",
- "a2335.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.",
- "a2349.casalemedia.com.",
"a2350.casalemedia.com.",
"a2351.casalemedia.com.",
"a2352.casalemedia.com.",
@@ -107,10 +112,8 @@ var FakeECSFQDNs = container.NewMapSet(
"a2354.casalemedia.com.",
"a2355.casalemedia.com.",
"a2356.casalemedia.com.",
- "a2357.casalemedia.com.",
"a2358.casalemedia.com.",
"a2359.casalemedia.com.",
- "a2360.casalemedia.com.",
"a2361.casalemedia.com.",
"a2362.casalemedia.com.",
"a2363.casalemedia.com.",
@@ -124,13 +127,12 @@ var FakeECSFQDNs = container.NewMapSet(
"a2371.casalemedia.com.",
"a2372.casalemedia.com.",
"a2373.casalemedia.com.",
- "a2374.casalemedia.com.",
"a2375.casalemedia.com.",
- "a2376.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.",
@@ -150,15 +152,13 @@ var FakeECSFQDNs = container.NewMapSet(
"a2398.casalemedia.com.",
"a2399.casalemedia.com.",
"a2400.casalemedia.com.",
- "a2401.casalemedia.com.",
"a2402.casalemedia.com.",
"a2403.casalemedia.com.",
"a2404.casalemedia.com.",
"a2405.casalemedia.com.",
"a2406.casalemedia.com.",
+ "a2407.casalemedia.com.",
"a2408.casalemedia.com.",
- "a2409.casalemedia.com.",
- "a2410.casalemedia.com.",
"a2411.casalemedia.com.",
"a2412.casalemedia.com.",
"a2413.casalemedia.com.",
@@ -172,7 +172,6 @@ var FakeECSFQDNs = container.NewMapSet(
"a2421.casalemedia.com.",
"a2422.casalemedia.com.",
"a2423.casalemedia.com.",
- "a2424.casalemedia.com.",
"a2425.casalemedia.com.",
"a2426.casalemedia.com.",
"a2427.casalemedia.com.",
@@ -184,6 +183,7 @@ var FakeECSFQDNs = container.NewMapSet(
"a2433.casalemedia.com.",
"a2434.casalemedia.com.",
"a2435.casalemedia.com.",
+ "a2436.casalemedia.com.",
"a2437.casalemedia.com.",
"a2438.casalemedia.com.",
"a2439.casalemedia.com.",
@@ -202,21 +202,20 @@ var FakeECSFQDNs = container.NewMapSet(
"a2452.casalemedia.com.",
"a2453.casalemedia.com.",
"a2454.casalemedia.com.",
- "a2455.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.",
- "a2465.casalemedia.com.",
+ "a2466.casalemedia.com.",
"a2467.casalemedia.com.",
- "a2468.casalemedia.com.",
"a2469.casalemedia.com.",
"a2470.casalemedia.com.",
"a2471.casalemedia.com.",
- "a2472.casalemedia.com.",
"a2473.casalemedia.com.",
"a2474.casalemedia.com.",
"a2475.casalemedia.com.",
@@ -226,11 +225,10 @@ var FakeECSFQDNs = container.NewMapSet(
"a2479.casalemedia.com.",
"a2480.casalemedia.com.",
"a2481.casalemedia.com.",
- "a2482.casalemedia.com.",
"a2483.casalemedia.com.",
+ "a2484.casalemedia.com.",
"a2485.casalemedia.com.",
"a2486.casalemedia.com.",
- "a2487.casalemedia.com.",
"a2488.casalemedia.com.",
"a2489.casalemedia.com.",
"a2490.casalemedia.com.",
@@ -239,6 +237,7 @@ var FakeECSFQDNs = container.NewMapSet(
"a2494.casalemedia.com.",
"a2495.casalemedia.com.",
"a2496.casalemedia.com.",
+ "a2497.casalemedia.com.",
"a2498.casalemedia.com.",
"a2499.casalemedia.com.",
"a2500.casalemedia.com.",
@@ -248,6 +247,7 @@ var FakeECSFQDNs = container.NewMapSet(
"a2504.casalemedia.com.",
"a2505.casalemedia.com.",
"a2506.casalemedia.com.",
+ "a2507.casalemedia.com.",
"a2508.casalemedia.com.",
"a2509.casalemedia.com.",
"a2510.casalemedia.com.",
@@ -260,8 +260,8 @@ var FakeECSFQDNs = container.NewMapSet(
"a2517.casalemedia.com.",
"a2518.casalemedia.com.",
"a2519.casalemedia.com.",
- "a2520.casalemedia.com.",
"a2521.casalemedia.com.",
+ "a2522.casalemedia.com.",
"a2523.casalemedia.com.",
"a2524.casalemedia.com.",
"a2525.casalemedia.com.",
@@ -269,13 +269,14 @@ var FakeECSFQDNs = container.NewMapSet(
"a2527.casalemedia.com.",
"a2528.casalemedia.com.",
"a2529.casalemedia.com.",
- "a25758810713.cdn.optimizely.com.",
"a2681.casalemedia.com.",
"a2682.casalemedia.com.",
"a2683.casalemedia.com.",
"a2684.casalemedia.com.",
"a2685.casalemedia.com.",
"a2686.casalemedia.com.",
+ "a2687.casalemedia.com.",
+ "a2688.casalemedia.com.",
"a2689.casalemedia.com.",
"a2690.casalemedia.com.",
"a2691.casalemedia.com.",
@@ -288,7 +289,6 @@ 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.",
@@ -303,7 +303,9 @@ var FakeECSFQDNs = container.NewMapSet(
"a2713.casalemedia.com.",
"a2714.casalemedia.com.",
"a2715.casalemedia.com.",
+ "a2716.casalemedia.com.",
"a2717.casalemedia.com.",
+ "a2718.casalemedia.com.",
"a2719.casalemedia.com.",
"a2720.casalemedia.com.",
"a2721.casalemedia.com.",
@@ -320,10 +322,12 @@ var FakeECSFQDNs = container.NewMapSet(
"a2732.casalemedia.com.",
"a2733.casalemedia.com.",
"a2734.casalemedia.com.",
+ "a2735.casalemedia.com.",
"a2736.casalemedia.com.",
"a2737.casalemedia.com.",
"a2738.casalemedia.com.",
"a2739.casalemedia.com.",
+ "a2740.casalemedia.com.",
"a2741.casalemedia.com.",
"a2742.casalemedia.com.",
"a2743.casalemedia.com.",
@@ -342,7 +346,6 @@ 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.",
@@ -352,12 +355,12 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
"a2777.casalemedia.com.",
"a2778.casalemedia.com.",
@@ -365,7 +368,6 @@ 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.",
@@ -382,7 +384,6 @@ var FakeECSFQDNs = container.NewMapSet(
"a2799.casalemedia.com.",
"a2800.casalemedia.com.",
"a2a5c7f9-3fa0-4182-889a-15aa61acf59b.prmutv.co.",
- "a3.tuyacn.com.",
"a3551.casalemedia.com.",
"a3552.casalemedia.com.",
"a3553.casalemedia.com.",
@@ -402,13 +403,13 @@ 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.",
"a3575.casalemedia.com.",
+ "a3576.casalemedia.com.",
+ "a3577.casalemedia.com.",
"a3578.casalemedia.com.",
- "a3579.casalemedia.com.",
"a3580.casalemedia.com.",
"a3581.casalemedia.com.",
"a3582.casalemedia.com.",
@@ -417,8 +418,8 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
@@ -471,17 +472,17 @@ var FakeECSFQDNs = container.NewMapSet(
"a3640.casalemedia.com.",
"a3641.casalemedia.com.",
"a3642.casalemedia.com.",
+ "a3643.casalemedia.com.",
"a3644.casalemedia.com.",
- "a3645.casalemedia.com.",
"a3646.casalemedia.com.",
"a3647.casalemedia.com.",
"a3648.casalemedia.com.",
"a3649.casalemedia.com.",
- "a3650.casalemedia.com.",
"a3651.casalemedia.com.",
"a3652.casalemedia.com.",
"a3653.casalemedia.com.",
"a3654.casalemedia.com.",
+ "a3655.casalemedia.com.",
"a3656.casalemedia.com.",
"a3657.casalemedia.com.",
"a3658.casalemedia.com.",
@@ -491,6 +492,7 @@ 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.",
@@ -499,7 +501,7 @@ var FakeECSFQDNs = container.NewMapSet(
"a3671.casalemedia.com.",
"a3672.casalemedia.com.",
"a3673.casalemedia.com.",
- "a3675.casalemedia.com.",
+ "a3674.casalemedia.com.",
"a3676.casalemedia.com.",
"a3677.casalemedia.com.",
"a3678.casalemedia.com.",
@@ -520,6 +522,7 @@ var FakeECSFQDNs = container.NewMapSet(
"a3694.casalemedia.com.",
"a3695.casalemedia.com.",
"a3696.casalemedia.com.",
+ "a3697.casalemedia.com.",
"a3698.casalemedia.com.",
"a3699.casalemedia.com.",
"a3700.casalemedia.com.",
@@ -562,7 +565,6 @@ var FakeECSFQDNs = container.NewMapSet(
"a5561.casalemedia.com.",
"a5562.casalemedia.com.",
"a5563.casalemedia.com.",
- "a5564.casalemedia.com.",
"a5565.casalemedia.com.",
"a5566.casalemedia.com.",
"a5567.casalemedia.com.",
@@ -572,11 +574,13 @@ var FakeECSFQDNs = container.NewMapSet(
"a5571.casalemedia.com.",
"a5572.casalemedia.com.",
"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.",
@@ -591,6 +595,7 @@ var FakeECSFQDNs = container.NewMapSet(
"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,6 +613,7 @@ var FakeECSFQDNs = container.NewMapSet(
"a5612.casalemedia.com.",
"a5613.casalemedia.com.",
"a5614.casalemedia.com.",
+ "a5615.casalemedia.com.",
"a5616.casalemedia.com.",
"a5617.casalemedia.com.",
"a5618.casalemedia.com.",
@@ -618,7 +624,7 @@ var FakeECSFQDNs = container.NewMapSet(
"a5623.casalemedia.com.",
"a5624.casalemedia.com.",
"a5625.casalemedia.com.",
- "a5626.casalemedia.com.",
+ "a5627.casalemedia.com.",
"a5628.casalemedia.com.",
"a5629.casalemedia.com.",
"a5630.casalemedia.com.",
@@ -632,6 +638,15 @@ var FakeECSFQDNs = container.NewMapSet(
"a5638.casalemedia.com.",
"a5639.casalemedia.com.",
"a5640.casalemedia.com.",
+ "a5641.casalemedia.com.",
+ "a5643.casalemedia.com.",
+ "a5645.casalemedia.com.",
+ "a5648.casalemedia.com.",
+ "a5649.casalemedia.com.",
+ "a5656.casalemedia.com.",
+ "a5657.casalemedia.com.",
+ "a5667.casalemedia.com.",
+ "a5668.casalemedia.com.",
"a5671.casalemedia.com.",
"a5672.casalemedia.com.",
"a5673.casalemedia.com.",
@@ -640,11 +655,9 @@ var FakeECSFQDNs = container.NewMapSet(
"a5676.casalemedia.com.",
"a5677.casalemedia.com.",
"a5678.casalemedia.com.",
- "a5679.casalemedia.com.",
"a5680.casalemedia.com.",
"a5681.casalemedia.com.",
"a5682.casalemedia.com.",
- "a5683.casalemedia.com.",
"a5684.casalemedia.com.",
"a5685.casalemedia.com.",
"a5686.casalemedia.com.",
@@ -656,6 +669,7 @@ var FakeECSFQDNs = container.NewMapSet(
"a5692.casalemedia.com.",
"a5693.casalemedia.com.",
"a5694.casalemedia.com.",
+ "a5695.casalemedia.com.",
"a5696.casalemedia.com.",
"a5697.casalemedia.com.",
"a5698.casalemedia.com.",
@@ -664,12 +678,8 @@ 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.",
"a5776.casalemedia.com.",
"a5777.casalemedia.com.",
@@ -684,27 +694,25 @@ var FakeECSFQDNs = container.NewMapSet(
"a5788.casalemedia.com.",
"a5789.casalemedia.com.",
"a5790.casalemedia.com.",
- "a5792.casalemedia.com.",
+ "a5791.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.",
"a5807.casalemedia.com.",
- "a5808.casalemedia.com.",
"a5809.casalemedia.com.",
"a581.casalemedia.com.",
"a5810.casalemedia.com.",
- "a5812.casalemedia.com.",
+ "a5811.casalemedia.com.",
"a5813.casalemedia.com.",
"a5815.casalemedia.com.",
- "a582.casalemedia.com.",
"a583.casalemedia.com.",
"a584.casalemedia.com.",
"a585.casalemedia.com.",
@@ -716,24 +724,28 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
"a615.casalemedia.com.",
+ "a616.casalemedia.com.",
"a617.casalemedia.com.",
"a618.casalemedia.com.",
"a619.casalemedia.com.",
@@ -748,6 +760,7 @@ var FakeECSFQDNs = container.NewMapSet(
"a628.casalemedia.com.",
"a629.casalemedia.com.",
"a630.casalemedia.com.",
+ "a631.casalemedia.com.",
"a632.casalemedia.com.",
"a633.casalemedia.com.",
"a634.casalemedia.com.",
@@ -757,19 +770,20 @@ var FakeECSFQDNs = container.NewMapSet(
"a638.casalemedia.com.",
"a639.casalemedia.com.",
"a640.casalemedia.com.",
+ "a921.casalemedia.com.",
"a922.casalemedia.com.",
"a923.casalemedia.com.",
"a924.casalemedia.com.",
"a925.casalemedia.com.",
"a926.casalemedia.com.",
"a927.casalemedia.com.",
- "a928.casalemedia.com.",
"a929.casalemedia.com.",
"a930.casalemedia.com.",
"a931.casalemedia.com.",
"a932.casalemedia.com.",
"a933.casalemedia.com.",
"a934.casalemedia.com.",
+ "a935.casalemedia.com.",
"a936.casalemedia.com.",
"a937.casalemedia.com.",
"a938.casalemedia.com.",
@@ -783,6 +797,7 @@ var FakeECSFQDNs = container.NewMapSet(
"a946.casalemedia.com.",
"a947.casalemedia.com.",
"a948.casalemedia.com.",
+ "a949.casalemedia.com.",
"a950.casalemedia.com.",
"a951.casalemedia.com.",
"a952.casalemedia.com.",
@@ -801,6 +816,7 @@ var FakeECSFQDNs = container.NewMapSet(
"a966.casalemedia.com.",
"a967.casalemedia.com.",
"a968.casalemedia.com.",
+ "a969.casalemedia.com.",
"a9695278-4085-40b3-9f02-8d4c38a6ff01.prmutv.co.",
"a970.casalemedia.com.",
"a971.casalemedia.com.",
@@ -808,43 +824,60 @@ var FakeECSFQDNs = container.NewMapSet(
"a973.casalemedia.com.",
"a974.casalemedia.com.",
"a975.casalemedia.com.",
+ "a976.casalemedia.com.",
"a977.casalemedia.com.",
"a979.casalemedia.com.",
"a980.casalemedia.com.",
+ "a9972578858.cdn.optimizely.com.",
"aa.online-metrix.net.",
"aa.quantummetric.com.",
"ab.qq.com.",
"aba2c424-419a-4d03-9aed-2dca8a7139e4.prmutv.co.",
+ "abeacdataonrt-stsdk.vivo.com.cn.",
+ "abeacdataort-stsdk.vivo.com.cn.",
"abroad.apilocate.amap.com.",
"abtest-aws-us-east-01.saas.sensorsdata.com.",
"accela.com.",
"accentuate.io.",
"access.mp.lura.live.",
+ "access.ovid.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.",
- "achi.faceit.com.",
+ "achievements.xboxlive.com.",
"acme-v02.api.letsencrypt.org.",
- "acp.haplat.net.",
"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.",
"adcell.com.",
"adder.feeder.co.",
+ "addictpodcast.com.",
"additionfi.com.",
"adfarm1.adition.com.",
+ "adgzs.top.",
"adiam.tech.",
"adition.com.",
"aditude.io.",
@@ -860,33 +893,31 @@ var FakeECSFQDNs = container.NewMapSet(
"adtechnacity.com.",
"advantage.purpleguys.com.",
"advantage2.purpleguys.com.",
- "adventisthealthwest.sharepoint.com.",
"adview.com.",
"adx-os.bridgeoos.com.",
"adx-sg-req.anythinktech.com.",
- "adxpremium.services.",
- "aee.myisolved.com.",
+ "aepenergy.sharepoint.com.",
"aeries.com.",
+ "afd-cf.www.linkedin.com.",
"afd-lnkd.www.linkedin.com.",
- "affpa.top.",
- "afss.zhiqinyun.cn.",
- "afterpay.com.",
+ "agent-logos.storage.googleapis.com.",
"agent.marketingcloudfx.com.",
"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.",
- "alarmnet.com.",
"albany.remotepc.com.",
"album-sg01a.ocloud.heytapmobi.com.",
+ "alfasense.com.",
"alibaba-inc.com.",
"alibabachengdun.com.",
"alibabacorp.com.",
@@ -905,16 +936,20 @@ var FakeECSFQDNs = container.NewMapSet(
"amd.com.",
"amdcopen.m.taobao.com.",
"amdcopen.m.taobao.com.gds.alibabadns.com.",
- "americanexpress.com.edgekey.net.",
+ "amino01.mom.",
+ "amino03.mom.",
+ "ammarkids.online.",
"amp.permutive.com.",
"amplitudelab.usemotion.com.",
- "ams-itm-radar-testobject.citrix.com.",
"amsterdam.remotepc.com.",
"an3762.ci.managedwhitelisting.com.",
"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.",
@@ -924,21 +959,24 @@ var FakeECSFQDNs = container.NewMapSet(
"android.crashsight.wetest.net.",
"anlian.co.",
"announce.torrentsmd.com.",
- "answers.yext-pixel.com.",
+ "ant.learnscitech.com.",
"anthropic.com.",
"antstream.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-cdn.prod.life360.com.",
+ "api-auth.zztfly.com.",
+ "api-decider.vsco.co.",
"api-eu1.hubspot.com.",
+ "api-gl.store.heytapmobi.com.",
"api-glb-aaps1b.smoot.apple.com.",
"api-glb-aapse1c.smoot.apple.com.",
- "api-glb-aeun1a.smoot.apple.com.",
- "api-glb-aeun1b.smoot.apple.com.",
"api-glb-aeus2a.smoot.apple.com.",
"api-glb-aeus2b.smoot.apple.com.",
"api-glb-aeuw1b.smoot.apple.com.",
+ "api-glb-asae1b.smoot.apple.com.",
"api-glb-ause1a.smoot.apple.com.",
"api-glb-ause1b.smoot.apple.com.",
"api-glb-ause1c.smoot.apple.com.",
@@ -953,34 +991,37 @@ var FakeECSFQDNs = container.NewMapSet(
"api-pos.titank12.com.",
"api-preview.luckyorange.com.",
"api-sh.django.t.taobao.com.gds.alibabadns.com.",
- "api.adquery.io.",
+ "api-user.dalyfeds.com.",
"api.ams.gcc.teams.microsoft.com.",
+ "api.axs.com.",
"api.bobble.ai.",
+ "api.boldcommerce.com.",
"api.box.com.",
"api.btloader.com.",
- "api.bycd3.com.",
- "api.claude.ai.",
+ "api.cb-device-intelligence.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.gravitec.net.",
"api.gx.me.",
+ "api.hetangsmart.com.",
+ "api.hqt0w.com.",
"api.icalendars.app.",
- "api.inboxsdk.com.",
"api.ipgeolocation.io.",
"api.k.163.com.",
"api.kinogram.best.",
"api.lightboxcdn.com.",
"api.loyalhealth.com.",
+ "api.lytics.io.",
"api.mangacoin.net.",
- "api.matetranslate.com.",
+ "api.mida.so.",
"api.moyoung.com.",
"api.my.jbi.global.",
"api.onedrive.com.",
@@ -989,54 +1030,52 @@ var FakeECSFQDNs = container.NewMapSet(
"api.pgf-thek63.com.",
"api.playlnk.io.",
"api.popin.cc.",
- "api.pushbullet.com.",
+ "api.qingcigame.com.",
"api.queryly.com.",
- "api.radiotime.com.",
- "api.recs.sky.com.",
+ "api.qwant.com.",
+ "api.rakuten.com.",
"api.reverso.net.",
"api.ringcentral.biz.",
"api.rollbar.com.",
"api.smartdeploy.com.",
"api.snapchat.com.",
- "api.sobot.com.",
- "api.streak.com.",
"api.swishapps.ai.",
- "api.tiles.virtualearth.net.",
+ "api.tapcart.com.",
+ "api.textnow.me.",
"api.transitapp.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.userlike.com.",
"api.vieon.vn.",
- "api.voicemod.net.",
+ "api.vsco.co.",
"api.workjam.com.",
+ "api.xiaoyi.com.",
"api.y41w4.com.",
"api000.backblazeb2.com.",
"api001.backblazeb2.com.",
"api002.backblazeb2.com.",
- "api2.matetranslate.com.",
"apiblink.ru.",
"apiisgp.ezvizlife.com.",
"apiisgp.hik-connect.com.",
"apimgmttmgpxfqy6dfjiqfsk6t67i30fgsnfhah4rrjw51coy3.trafficmanager.net.",
"apis.live.net.",
- "apitm.toolmatrix.plus.",
"apk.v-mate.mobi.",
"apk.vidmate.net.",
"aplo-evnt.com.",
"apm.gotokeep.com.",
"app-atl.five9.com.",
"app-eu1.hubspot.com.",
- "app-goal.com.",
"app-scl.five9.com.",
"app-student.atitesting.com.",
"app.adoric-om.com.",
"app.box.com.",
"app.cart-bot.net.",
"app.cybba.solutions.",
- "app.divvy.co.",
- "app.docusign.com.",
"app.ee-share.com.",
"app.paces.jbi.global.",
"app.pendo.qgenda.com.",
@@ -1057,40 +1096,29 @@ var FakeECSFQDNs = container.NewMapSet(
"appstoreonrt-stsdk.vivo.com.cn.",
"appstoreort-stsdk.vivo.com.cn.",
"apro-api.collegeboard.org.",
+ "apv-static.minute.ly.",
"arenabg.com.",
"aristotleinsight.com.",
"arm-frontdoor-edge-geo.trafficmanager.net.",
"arm1.maxhost.io.",
"arms-retcode-sg.aliyuncs.com.",
- "arms.aliyuncs.com.",
- "arnoldclark1-my.sharepoint.com.",
- "arup.sharepoint.com.",
"as-api.asm.skype.com.",
"as-prod.asyncgw.teams.microsoft.com.",
"as.footballbros.io.",
"asanalytics.booking.com.",
"asheville.remotepc.com.",
- "asia-001.azure-apim.net.",
+ "ashleyfurniture.aiproxies.com.",
"asia-adlog.vivoglobal.com.",
"asia-browser.vivoglobal.com.",
- "asia-browserplat.vivoglobal.com.",
- "asia-config-appstore.vivoglobal.com.",
"asia-cota.vivoglobal.com.",
"asia-ex-adlog.vivoglobal.com.",
- "asia-exbrowser.vivoglobal.com.",
- "asia-exbrowsercrp.vivoglobal.com.",
- "asia-exmagazineunlock-proxy.vivoglobal.com.",
- "asia-gamecenter.vivoglobal.com.",
"asia-gsearch.vivoglobal.com.",
- "asia-magazineunlock.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-sms-api.vivoglobal.com.",
- "asia-southeast2-idm-corp-prd.cloudfunctions.net.",
"asia-st-romsp.vivoglobal.com.",
"asia-st-sl.vivoglobal.com.",
"asia-st-sysupgrade.vivoglobal.com.",
@@ -1103,8 +1131,8 @@ var FakeECSFQDNs = container.NewMapSet(
"asia-vnote.vivoglobal.com.",
"asia-vpushonrt-stsdk.vivoglobal.com.",
"asia-vpushort-stsdk.vivoglobal.com.",
- "asia-weather.vivoglobal.com.",
- "asia.gikken.co.",
+ "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.",
@@ -1113,13 +1141,14 @@ 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.",
- "assets-cms.thescore.com.",
- "assets-www.xbox.com.",
+ "asset.fwscripts.com.",
+ "assets.adobetarget.com.",
"assets.api.stairwell.com.",
- "assets.mayoclinic.org.",
- "assets.thdstatic.com.edgekey.net.",
+ "assets.pinterest.com.",
+ "assetshare.basspro.com.",
"assistant-dre.op.hicloud.com.",
"astemo-am.spectrum.colortokens.com.",
"async-motiondetection-us-1d.oss-us-west-1.aliyuncs.com.",
@@ -1128,39 +1157,37 @@ var FakeECSFQDNs = container.NewMapSet(
"atlanta.remotepc.com.",
"atlanta3.remotepc.com.",
"atlanta4.remotepc.com.",
+ "atlassianblog.wpengine.com.",
"atlsbc04ag1.atl.five9.com.",
+ "atzscr.itsupport247.net.",
"au-prod.asyncgw.teams.microsoft.com.",
"au.footballbros.io.",
"auc-collabrtc.officeapps.live.com.",
"auckland.remotepc.com.",
- "audio-ssl.itunes.apple.com.",
"audio.vivintsky.com.",
"audiostatlog.cc.easebar.com.",
+ "augmentation.osi.office.net.",
"australiaeast.api.cognitive.microsoft.com.",
"auth-l7.bereal.com.",
- "auth.bereal.com.",
- "auth.cengage.com.",
"auth.jbisumari.org.",
- "auth.tesla.com.",
- "autohome.com.cn.",
+ "auth.laureate.net.",
"autotrack.studyquicks.com.",
"avalanche.autotrader.co.uk.",
"aws-aus-e-rdvz.g.nssvc.net.",
"aws-bz-s-rdvz.g.nssvc.net.",
- "ayads.co.",
"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.",
- "b1-wndc1.zemanta.com.",
"b1t-nldc1.zemanta.com.",
- "b1t-wndc1.zemanta.com.",
"b2b.filesyscrm.com.",
"bablosoft.com.",
"backend-l.deepl.com.",
@@ -1175,37 +1202,37 @@ var FakeECSFQDNs = container.NewMapSet(
"bangalore3.remotepc.com.",
"bangalore4.remotepc.com.",
"bangkok.remotepc.com.",
+ "bankozarks-my.sharepoint.com.",
"barstoolsports.com.",
"bd1cec50-00d1-4ce9-9572-785857419a1e.prmutv.co.",
- "bdpnt-my.sharepoint.com.",
+ "bdfed.stitch.mlbinfra.com.",
"bea.gov.",
- "beacon-iad2.rubiconproject.com.",
"beacon.qq.com.",
"beacons4.gvt2.com.",
"beacons5.gvt2.com.",
+ "becpsn-my.sharepoint.com.",
+ "bee.tc.easebar.com.",
"beizi.biz.",
"belgium.remotepc.com.",
"belgrad.remotepc.com.",
"bend.remotepc.com.",
- "benefitfocus.com.",
"bgtfs.transitapp.com.",
"bi-tracker-global.rivergame.net.",
- "bidmyadz.com.",
+ "biddr.brealtime.com.cdn.cloudflare.net.",
"bidster.net.",
"bifrost.vivaldi.com.",
- "bigcommerce.com.cdn.cloudflare.net.",
"bigdata.talkie-ai.com.",
"bik.gov.tr.",
+ "bisdus-my.sharepoint.com.",
"bl6pap003.storage.live.com.",
"bl6pap004.storage.live.com.",
"black-cat.crypto.com.",
"blackboard.com.",
"blackbox.dropbox-dns.com.",
"block64.com.",
- "blockchain.info.",
"bloomerang.co.",
+ "bluedot.is.autonavi.com.gds.alibabadns.com.",
"bluffdale.remotepc.com.",
- "blz04pap002.storage.live.com.",
"blz04pap005.storage.live.com.",
"blz04pap006.storage.live.com.",
"bmaus.bumble.com.",
@@ -1214,7 +1241,9 @@ var FakeECSFQDNs = container.NewMapSet(
"bnz06pap002.storage.live.com.",
"bnz06pap004.storage.live.com.",
"bnz07pap001.storage.live.com.",
+ "boardgamearena.net.",
"bobble.ai.",
+ "bokep.work.",
"books-analytics-events.apple.com.",
"books-personalization-server.apple.com.",
"booksy.com.",
@@ -1229,11 +1258,13 @@ var FakeECSFQDNs = container.NewMapSet(
"brazilsouth.api.cognitive.microsoft.com.",
"breitbart.com.",
"bridge.tonapi.io.",
+ "broadsimp.site.",
"broadstreetads.com.",
"broker-ws-prod-cag-sg.vasdgame.com.",
"bscedge.com.",
"bsprings.remotepc.com.",
"bssrvc66.com.",
+ "bsw-ig.criteo.com.",
"bt.moack.co.kr.",
"btrace.qq.com.",
"bucharest.remotepc.com.",
@@ -1242,9 +1273,11 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
"c1.ttcache.com.",
@@ -1262,21 +1295,21 @@ var FakeECSFQDNs = container.NewMapSet(
"ca002.backblaze.com.",
"ca80a1adb12a4fbdac5ffcbc944e9a61.pacloudflare.com.",
"cac-collabrtc.officeapps.live.com.",
+ "cache.dciwx.com.",
"cachenetworks.com.",
"caldav.163.com.",
"california.remotepc.com.",
- "camsoda.com.",
+ "camstore.vsco.co.",
"canada.remotepc.com.",
"canadacentral.api.cognitive.microsoft.com.",
"canadaeast.api.cognitive.microsoft.com.",
"canberra.remotepc.com.",
+ "cantor-lite-api.vsco.co.",
"capetown.remotepc.com.",
- "capi.elements.video.cdn.cloudflare.net.",
"captaina.co.",
"car-cloud-cn.net.",
"cardiff.remotepc.com.",
"care.novaicare.com.",
- "carters.com.",
"cartx.cloud.",
"carystudio.com.",
"cat1.hbwrapper.com.",
@@ -1286,7 +1319,6 @@ var FakeECSFQDNs = container.NewMapSet(
"cbs.com.",
"cbsipv4.shuzilm.cn.",
"cc.easebar.com.",
- "ccf.ivalua.com.",
"cdml02.contentdm.oclc.org.",
"cdn-audio-gcp-media.getepic.com.",
"cdn-cname.pendo.io.",
@@ -1301,24 +1333,28 @@ var FakeECSFQDNs = container.NewMapSet(
"cdn.chargeafter.com.",
"cdn.conveythis.com.",
"cdn.deepintent.com.",
- "cdn.flyaa.aa.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.instamed.com.",
"cdn.instapagemetrics.com.",
- "cdn.jsdelivrs.com.",
+ "cdn.jst.ai.",
+ "cdn.mscdirect.com.",
"cdn.overleaf.com.",
+ "cdn.pandora.xiaomi.com.",
"cdn.qq.com.",
- "cdn.registerdisney.go.com.",
+ "cdn.resonate.com.",
"cdn.sierrapacificgroup.com.",
"cdn.skcrtxr.com.",
"cdn.t-bank-app.ru.",
"cdn.tapdb-dev.com.",
- "cdn.themoneytizer.fr.",
"cdn.us.minga.io.",
"cdn.uxfeedback.ru.",
+ "cdn01.boxcdn.net.",
"cdn1.wixdns.net.",
- "cdnetworks.net.",
"cdngslb.com.",
"cdntm.hsbc.co.uk.",
"cdnwidget.com.",
@@ -1327,21 +1363,30 @@ var FakeECSFQDNs = container.NewMapSet(
"cedexis-test.com.",
"cedock.com.",
"cef.com.br.",
- "cekdptonline.kpu.go.id.",
+ "center00.deltaork.com.",
+ "center01.deltaork.com.",
+ "center02.deltaork.com.",
+ "center03.deltaork.com.",
"center04.deltaork.com.",
+ "center05.deltaork.com.",
+ "center06.deltaork.com.",
"center07.deltaork.com.",
"center08.deltaork.com.",
+ "center09.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.",
"cfa.fidelity.com.",
- "cform.loyalhealth.com.",
"cgi.twns.qq.com.",
"changehealthcare.com.",
"chargeafter.com.",
- "charlotte.remotepc.com.",
+ "chat.openai.com.cdn.cloudflare.net.",
+ "check.ragpets.com.",
"check2.lloydsbank.co.uk.",
+ "check2.ragpets.com.",
+ "checkout-service.global-e.com.",
"chennai.remotepc.com.",
"chi01pap001.storage.live.com.",
"chi01pap002.storage.live.com.",
@@ -1358,53 +1403,41 @@ var FakeECSFQDNs = container.NewMapSet(
"chujingapp.com.",
"cicd-release-api.dalyfeds.com.",
"cinarra.com.",
- "cis.citrix.com.",
"cisco.app.box.com.",
- "citiretailservices.citibankonline.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.",
- "cl-gl036e4fc3.gcdn.co.",
- "cl-glffabd6cc.maps.cdn.orange.com.",
- "cl2009.com.",
"classroom6x.gitlab.io.",
"claude.ai.",
"cleveland.remotepc.com.",
- "cli.speedtest.net.",
+ "clevelandclinic-my.sharepoint.com.",
"click-eu.preclknu.com.",
- "click-v4.expdirclk.com.",
- "click.admeking.com.",
"click.pclk.name.",
"click.preclknu.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.",
- "client.perimeterx.net.edgekey.net.",
- "client.ppe.repmap.microsoft.com.",
"clientlog3.music.163.com.",
- "clientsettingscdn.roblox.com.edgekey.net.",
- "cliftonlarsonallen-my.sharepoint.com.",
"cloud-agent.policypak.com.",
"cloud-config-service.rtc.aliyuncs.com.",
"cloud-links.net.",
"cloud-rest.lenovomm.com.",
- "cloud.browser.360.cn.",
+ "cloud.huawei.ru.",
"cloud.ibm.com.",
"cloud.vmp.onezapp.com.",
- "cloud.xm-cdn.com.",
"cloud.zoom.us.",
"cloudbirds.cn.",
"clouddata.turbowarp.org.",
"cloudflareperf.com.",
"cloudlinks.cn.",
"cloudscdn.info.",
- "cloudsvc.policypak.com.",
"cm-x.mgid.com.",
"cmgate.vip.qq.com.",
"cmpassport.com.",
@@ -1412,14 +1445,12 @@ var FakeECSFQDNs = container.NewMapSet(
"cn-mib2high-mbbservices.audi-connect.cn.",
"cn-mib2plus.mbbservices-1a.audi-connect.cn.",
"cn.gcloudcs.com.",
- "cname.cordial.com.",
"cname.pendo.io.",
"cnplug.ttlock.com.",
- "cnvid.site.",
"cnzz.com.",
"co-vcode-od.vivoglobal.com.",
"codacloud.net.",
- "codepush.akulaku.net.",
+ "code.etracker.com.",
"codis-bak.ngb.haplat.net.",
"codis.ngb.haplat.net.",
"collabrtc-geo.rtc.trafficmanager.net.",
@@ -1427,36 +1458,37 @@ var FakeECSFQDNs = container.NewMapSet(
"collect.quickcep.com.",
"collect.trendyol.com.",
"collector.quillbot.com.",
- "collector.therealxh.com.",
+ "collector.vhx.tv.",
"collector.wdp.brave.com.",
"collector.xhamster.com.",
- "collector.xhamster.desi.",
+ "collector.xhwear.life.",
"columbus.remotepc.com.",
"com.yangyi19.com.",
"cometglobal.cf.t3cloud.pb.com.",
"cometservd1.pb.com.",
- "commerce.nbcuni.com.",
- "commercial.ocsp.identrust.com.",
"common-afd.fe.1drv.com.",
"common-afdrk.fe.1drv.com.",
- "common.xshareapp.com.",
- "community.spiceworks.com.cdn.cloudflare.net.",
+ "common.ltwebstatic.com.",
+ "community.thescore.com.",
"compiles.overleafusercontent.com.",
- "comserver.global.mspa.n-able.com.",
"config-security.com.",
- "config-toolbar.ducunt.com.",
"config.a-m-p.xyz.",
"config.cmpassport.com.",
+ "config.config-factory.com.",
"config.content-settings.com.",
- "config.inmobi.com.",
"config.office.net.",
"config.y5en.com.",
"config2.cmpassport.com.",
"configdl.teamviewer.com.",
+ "configuration.apple.com.edgekey.net.",
"connect.garenanow.com.",
+ "connect.garmin.com.",
+ "connectivitycheck.unisoc.com.",
"contacts.zoho.com.",
+ "contendvc.cnouyi.pizza.",
"content.assist.chromeriver.com.",
"content.bhrpendo.bamboohr.com.",
+ "content.citizensbankonline.com.",
"content.data.aleks.com.",
"content.data.mheducation.com.",
"content.discover.com.",
@@ -1469,11 +1501,15 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
"content22.bmo.com.",
+ "content22.citibankonline.com.",
"content22.citicards.com.",
"content22.online.citi.com.",
+ "contentlmsp.student.atitesting.com.",
"context.reverso.net.",
"control-out.mna.qq.com.",
"coolccloud.com.",
@@ -1482,18 +1518,18 @@ var FakeECSFQDNs = container.NewMapSet(
"cordial.com.",
"core.iprom.net.",
"core.omiapp.me.",
- "corpmdm.v.aaplimg.com.",
"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.",
"cpm.vuukle.net.",
"cpm.xrtb.io.",
"cpr-pusa01.app.blackbaud.net.",
- "cpt96125.shopvoxpopulus.com.",
"cpx-research.com.",
"crash.xiaohongshu.com.",
"crashlytics.com.",
@@ -1501,27 +1537,33 @@ var FakeECSFQDNs = container.NewMapSet(
"crashsight.wetest.net.",
"creator.pdf24.org.",
"creditmaven.com.",
- "crl.americanexpress.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.",
"cta-eu1.hubspot.com.",
"cti.roku.com.",
"ctmail.com.",
+ "cts.appmeta.store.",
+ "cummins.zoom.us.",
"customer.homedepot.com.",
"cvs.quantummetric.com.",
"cwogzftn.usw.stape.io.",
"czechrepublic.remotepc.com.",
+ "d.applvn.com.",
"d.docs.live.net.",
"d.pub.network.",
"d2fb08da-1c03-4c8a-978f-ad8a96b4c31f.partner.permutive.app.",
@@ -1533,6 +1575,7 @@ var FakeECSFQDNs = container.NewMapSet(
"da.footballbros.io.",
"da.mosspf.com.",
"da.toponadss.com.",
+ "dacdn.visualwebsiteoptimizer.com.",
"daemon.nanoleaf.me.",
"dallas.remotepc.com.",
"dallas2.remotepc.com.",
@@ -1540,10 +1583,9 @@ var FakeECSFQDNs = container.NewMapSet(
"dallas4.remotepc.com.",
"dallas5.remotepc.com.",
"dantri.com.vn.",
- "daraz.com.",
+ "dashboard.meraki.com.",
"dashi.163.com.",
"data-detect.nie.easebar.com.",
- "data-tr.rethinkad.com.",
"data.analytics.thomsonreuters.com.",
"data.analytics.ux.quickbase.com.",
"data.assist.chromeriver.com.",
@@ -1575,44 +1617,45 @@ var FakeECSFQDNs = container.NewMapSet(
"data.tracking.billtrust.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.cdn.cloudflare.net.",
+ "dc.cftls.t.co.",
"dc.di.atlas.samsung.com.",
"dc.dqa.samsung.com.",
"dc.sigmob.cn.",
"dc.wondershare.com.",
"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-idm.api.io.mi.com.",
- "de-odc.samsungapps.com.",
"de-prod.asyncgw.teams.microsoft.com.",
"de.dt.rcs.telephony.goog.",
- "de.hlth.io.mi.com.",
"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.zztfly.com.",
"deviceapi.ca1.absolute.com.",
"deviceops.hstgps.com.",
"dewu.com.",
- "dexerto.media.",
"dfaklj.tech.",
"dhs.gov.",
"dialpad.com.",
@@ -1634,7 +1677,9 @@ var FakeECSFQDNs = container.NewMapSet(
"dls-udc.dqa.samsung.com.",
"dls.di.atlas.samsung.com.",
"dm-us.hybrid.ai.",
+ "dm.wo.com.cn.",
"dmv.ca.gov.",
+ "dmwww.geo.dmcdn.net.",
"dns-e.ns4v.icu.",
"dns-tunnel-check.googlezip.net.",
"dns.rubyfish.cn.",
@@ -1646,25 +1691,23 @@ var FakeECSFQDNs = container.NewMapSet(
"docs.zoom.us.",
"doctrack.fda.gov.ph.",
"document360.io.",
- "domaincfg.vivoglobal.com.",
- "donaldson-my.sharepoint.com.",
"donewyork1.remotepc.com.",
"donewyork2.remotepc.com.",
"donewyork3.remotepc.com.",
"dosfo1.remotepc.com.",
"dosfo2.remotepc.com.",
- "dowjones-prod.actioniq.mr-in.com.",
+ "douyin.starrydyn.com.",
"download.2.401402081.west-gcloud.codm.activision.com.",
+ "download.lenovo.com.edgekey.net.",
"downloadcenter.genetec.com.",
+ "doximity-res.cloudinary.com.",
"dp.barclaysus.com.",
- "dp.im.weibo.cn.",
"dpf.authorize.net.",
"dpool.sina.com.cn.",
- "dr.netease.im.",
"dreame.tech.",
"drfdisvc.walmart.com.",
- "drop-assets.ea.com.",
"drsquatch.com.",
+ "ds-cdn.com.",
"ds.gsma.com.",
"dsa-eu.hybrid.ai.",
"dsm01pap001.storage.live.com.",
@@ -1674,19 +1717,16 @@ var FakeECSFQDNs = container.NewMapSet(
"dsm01pap008.storage.live.com.",
"dsm01pap009.storage.live.com.",
"dsm04pap002.storage.live.com.",
- "dsp-ap.eskimi.com.",
- "dsp-cookie.adfarm1.adition.com.",
"dsp-trk.eskimi.com.",
"dsp-trvm.eskimi.com.",
"dspcluster.adfarm1.adition.com.",
"dspx.tv.",
"dstillery.com.",
- "dt.netease.im.",
"dtscout.com.",
- "dualspaceapi.com.",
"dubai.remotepc.com.",
"dublin.remotepc.com.",
"dun.163yun.com.",
+ "dypnsapi.aliyuncs.com.",
"dz.cyberhaven.io.",
"dzfread.cn.",
"e-189.21cn.com.",
@@ -1774,6 +1814,8 @@ var FakeECSFQDNs = container.NewMapSet(
"e2c8.gcp.gvt2.com.",
"e2c80.gcp.gvt2.com.",
"e2c81.gcp.gvt2.com.",
+ "e2c82.gcp.gvt2.com.",
+ "e2c83.gcp.gvt2.com.",
"e2c9.gcp.gvt2.com.",
"e2cs01.gcp.gvt2.com.",
"e2cs02.gcp.gvt2.com.",
@@ -1829,25 +1871,21 @@ var FakeECSFQDNs = container.NewMapSet(
"e2cs53.gcp.gvt2.com.",
"e2cs54.gcp.gvt2.com.",
"e2cs55.gcp.gvt2.com.",
- "e3c12f53-768d-4aa2-8e31-b8d0ee6320b1.prmutv.co.",
"e43.ultipro.com.",
"e488cdb0-e7cb-4d91-9648-60d437d8e491.prmutv.co.",
"e5de3d23065c4748b155c28e6fa36f3e.pacloudflare.com.",
"ea.footballbros.io.",
"eafddirect.msedge.net.",
- "eagle.haplat.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.",
"ecs-gallatin-c2s.trafficmanager.net.",
"ed.link.",
"edevice.toshiba-solutions.com.",
- "edge.xero.com.",
"edgecdn.ru.",
"edgedl.me.gvt1.com.",
"edgelocation.ivanticloud.com.",
@@ -1860,19 +1898,18 @@ var FakeECSFQDNs = container.NewMapSet(
"efs.ultipro.com.",
"egateway.ultipro.com.",
"ei.dyn-rev.app.",
- "ejoyspace.com.",
+ "eic-ngfts.lge.com.",
+ "elaracaring.sharepoint.com.",
"elemecdn.com.",
- "elixarco.com.",
- "elonuniversity-my.sharepoint.com.",
+ "em-frontend-assets.airtrfx.com.",
"emailaptitude.com.",
- "employers.indeed.com.",
+ "empower-retirement.com.",
"endpointprotector.com.",
"engage.wixapps.net.",
"engagementapi.skype.com.",
- "engine.phn.doublepimp.com.",
"enplug.com.",
"ent.box.com.",
- "entitlements.auth.riotgames.com.",
+ "entitlement.auth.adobe.com.",
"envoy-ios-prod.getepic.com.",
"envysion.com.",
"epdg.vowifi.cspire.com.",
@@ -1890,37 +1927,42 @@ 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.",
"eu4-excel-collab.officeapps.live.com.",
"eu4-powerpoint-collab.officeapps.live.com.",
+ "euc-collabrtc-geo.rtc.trafficmanager.net.",
"euc-collabrtc.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.attentivemobile.com.",
"events.glanceapis.com.",
+ "events.polarbyte.com.",
"events.swishapps.ai.",
"ex-adreq-asia.vivoglobal.com.",
- "exacti.us.",
"exappupgrade.vivoglobal.com.",
- "exb.quicknewsfeed.online.",
- "excel-collab-geo.ocs.trafficmanager.net.",
"excel-collab.officeapps.live.com.",
- "excel-geo.wac.trafficmanager.net.",
+ "exceptionless.io.",
+ "exit.streamoptim.com.",
"exodus.desync.com.",
"exp.host.",
+ "experimentation.deepl.com.",
"exponential.com.",
+ "ext.wisesecapp.com.",
"extension.faro.speechify.dev.",
"extension.savvy.security.",
"external-ams2-1.xx.fbcdn.net.",
@@ -1939,6 +1981,7 @@ var FakeECSFQDNs = container.NewMapSet(
"external-lax3-2.xx.fbcdn.net.",
"external-lga3-1.xx.fbcdn.net.",
"external-lga3-2.xx.fbcdn.net.",
+ "external-lga3-3.xx.fbcdn.net.",
"external-lhr6-1.xx.fbcdn.net.",
"external-lhr6-2.xx.fbcdn.net.",
"external-lhr8-1.xx.fbcdn.net.",
@@ -1949,19 +1992,26 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
"f9tg.com.",
+ "fa000000074.resources.office.net.",
+ "fa000000132.resources.office.net.",
+ "fa000000137.resources.office.net.",
"faas.marktplaats.nl.",
"factor.reg.163.com.",
"factset.com.",
"fanatics.ent.box.com.",
"fanduel.quantummetric.com.",
"fanyiegg.youdao.com.",
- "fcix.net.",
+ "fastlane.rubiconproject.com.",
"fdccpaadaptor.forddirectservices.com.",
+ "fdl.zztfly.com.",
"fe.xiaohongshu.com.",
"fed.federate365.com.",
"feeder.co.",
@@ -1972,22 +2022,25 @@ var FakeECSFQDNs = container.NewMapSet(
"fef.msub06.manage.microsoft.com.",
"fef.msub07.manage.microsoft.com.",
"feikua.net.",
- "felixistderbeste.de.",
"fema.gov.",
"femaledaily.com.",
"fengkongcloud.com.",
"fgwn01.ultipro.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.",
"fireeye.com.",
- "fishdom-cdn.playrix.com.",
"five9.com.",
+ "fiverr-res.cloudinary.com.",
"flashcards.vitalsource.com.",
"flashjoin.net.",
"flip.to.",
@@ -1997,18 +2050,18 @@ var FakeECSFQDNs = container.NewMapSet(
"fn.us.ipqscdn.com.",
"fo.iemiq.com.",
"foisonad.com.",
+ "fontawesome.com.cdn.cloudflare.net.",
"forcesafesearch.google.com.",
- "forethought.ai.",
"form.jotform.com.",
"forms-eu1.hscollectedforms.net.",
"forms-eu1.hsforms.com.",
"forms-eu1.hubspot.com.",
- "forms-na1.hubspot.com.",
+ "forms.hsforms.com.",
"fortisimperious.com.",
"fortworth.remotepc.com.",
- "fotpro135alto.com.",
"foundation-ipv4.youdao.com.",
"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-ee.rcs.telephony.goog.",
@@ -2021,6 +2074,7 @@ var FakeECSFQDNs = container.NewMapSet(
"fp-us-xfinity.rcs.telephony.goog.",
"fp.ca.rogers.rcs.telephony.goog.",
"fp.de.dt.rcs.telephony.goog.",
+ "fp.marketingcloudfx.com.",
"fp.ps.easebar.com.",
"fp.us.tracfone.rcs.telephony.goog.",
"fp4-us-att.rcs.telephony.goog.",
@@ -2031,7 +2085,9 @@ 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.",
@@ -2039,22 +2095,20 @@ var FakeECSFQDNs = container.NewMapSet(
"ftkew02.ultipro.com.",
"ftkn01.ultipro.com.",
"ftkn02.ultipro.com.",
- "ftp.ext.hp.com.edgekey.net.",
- "func-fhcc-bacdnlqayn.cn-shenzhen.fcapp.run.",
- "futuretechuu.com.",
"fxltsbl.com.",
"g10498469755.co.",
+ "g1584674684.co.",
"g9hc4.cn.",
"ga.badambiz.com.",
"galaxyappstore.com.",
+ "game.zuiqiangyingyu.net.",
"gameloft.com.",
"gamemonkey.org.",
"gamepigeon.net.",
- "gamingcontext.xboxlive.com.",
"gateway.ultiproworkplace.com.",
+ "gb-vodafone.rcs.telephony.goog.",
"gb.ee.rcs.telephony.goog.",
"gb.o2.rcs.telephony.goog.",
- "gbc-powerpoint.officeapps.live.com.",
"gbc-word-edit.officeapps.live.com.",
"gccmod.ecs.office.com.",
"gcdn.co.",
@@ -2063,50 +2117,44 @@ var FakeECSFQDNs = container.NewMapSet(
"gcloudsdk.com.",
"gcs.sc-cdn.net.",
"gdc-reqs-a.ngb.haplat.net.",
- "gdc-scouccl1.haplat.net.",
- "gdc-scouccl2.haplat.net.",
"gdid.datalake.gameloft.com.",
- "gdp.haplat.net.",
"geappl.io.",
- "geappliances.sharepoint.com.",
+ "geckoterminal.com.",
"geico-sync.quantummetric.com.",
- "genetherapyhub.com.",
"geniusmonkey.com.",
"geo-dra.platform.hicloud.com.",
- "geocodeservice.costco.com.",
- "geometrygame.org.",
+ "geoip.apps.getjoy.ai.",
"geoplugin.net.",
+ "geovn0mhn4u98k.josyliving.com.",
"germanywestcentral.api.cognitive.microsoft.com.",
"getadmiral.com.",
"getbutton.io.",
- "getnitropack.com.",
+ "getjoy.ai.",
"gettopple.com.",
"getui.net.",
"gifer.com.",
"gifshow.com.",
- "git.rancher.io.",
+ "gigabyte.com.",
"gitlab.com.",
"gla.gameloft.com.",
- "gleap.io.",
"global-tokenserver-la.headline.uodoo.com.",
"global.datasite.com.",
- "globalapi.smart2pay.com.",
- "globalgme.com.",
"globalsigncdn.com.cdn.cloudflare.net.",
"globalsun.io.",
"gme.qcloud.com.",
"gnc.com.",
- "go.blcdog.com.",
+ "gnss-eph.oss-cn-hangzhou.aliyuncs.com.",
+ "go.activengage.com.",
+ "go.pdfforge.org.",
"goaffpro.com.",
- "gohighlevel.com.",
"gonines.com.",
- "good-loop.com.",
"google.org.",
"googledomains.com.",
"googlesync.permutive.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.",
@@ -2115,17 +2163,16 @@ var FakeECSFQDNs = container.NewMapSet(
"grow.me.",
"grpc.vivintsky.com.",
"gslb.xiaohongshu.com.",
- "gsp85-ssl.ls.apple.com.",
- "gtimg.cn.",
+ "gspe19-ssl.ls.apple.com.",
"gtm.deepl.com.",
"gtm.vividseats.com.",
- "guid.tpns.sgp.tencent.com.",
"gw5.push.mcp.weibo.cn.",
"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.",
@@ -2137,6 +2184,7 @@ var FakeECSFQDNs = container.NewMapSet(
"h-walmart.online-metrix.net.",
"h.app.wdesk.com.",
"h.online-metrix.net.",
+ "h30-deploy.obviyo.net.",
"h64.online-metrix.net.",
"hamina.remotepc.com.",
"hanoi.remotepc.com.",
@@ -2146,35 +2194,33 @@ var FakeECSFQDNs = container.NewMapSet(
"hawaii.remotepc.com.",
"hbopenbid-apac-v2.pubmnet.com.",
"hbwrapper.com.",
- "hcaptcha.com.",
+ "hds.ksc.kaspersky.com.",
"hecheck.bitmyanmar.info.",
"hello.idocdn.com.",
"helloid.com.",
- "hermes-us.inspi-dsp.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.",
"highwire.org.",
- "hillsboroughcounty-my.sharepoint.com.",
+ "hiram.iad-03.braze.com.",
"hisearch-dra.dt.dbankcloud.com.",
"hismarttv.com.",
"hits.getelevar.com.",
"hk.gcloudcs.com.",
"hk.ntp.org.cn.",
"hk.wechat.com.",
- "holid.io.",
"holykjvbible.com.",
"home.highwire.org.",
+ "homedepot.quantummetric.com.",
"honeywell.com.",
- "hongkong.remotepc.com.",
"hornetsecurity.com.",
- "hotapi16-normal-ie.tiktokv.eu.",
- "hotukdeals.com.",
"hpfal.deepl.com.",
"hpkaj.deepl.com.",
"hpplay.cn.",
- "hrsa.gov.",
"hstgps.com.",
"html.it.",
"html5.qq.com.",
@@ -2182,16 +2228,16 @@ var FakeECSFQDNs = container.NewMapSet(
"httpdns.y5en.com.",
"huan.tv.",
"huaweicloud.com.",
- "huaweicloudwaf.com.",
- "hub.gibraltarsoftware.com.",
"hubble.netease.com.",
"hubcloud.com.cn.",
"hubspotemail.net.",
"huion.cn.",
+ "hulkapps-wishlist.nyc3.digitaloceanspaces.com.",
"huorong.cn.",
- "hw.118114.net.",
"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.",
@@ -2199,28 +2245,25 @@ var FakeECSFQDNs = container.NewMapSet(
"i.magazine.heytapmobi.com.",
"i.one-bid.com.",
"i.qchannel03.cn.",
- "i.voe.sx.",
"i6-vn.weather.oppomobile.com.",
- "iaas.jdcloud.com.",
+ "ialicdn.com.",
"iappscontent.courts.state.ny.us.",
"ibm.account.box.com.",
"ibm.box.com.",
"ibsrv.net.",
"icalendars.app.",
+ "icanhazip.tacticalrmm.io.",
"icare.infranetgroup.com.",
"ichano.cn.",
"iconmonstr.com.",
- "id-ooredoo.rcs.telephony.goog.",
"id-telkom.rcs.telephony.goog.",
"id-timer-appstore.vivoglobal.com.",
- "id.therealxh.com.",
+ "id.xhwear.life.",
"idahofalls.remotepc.com.",
- "idchicago1.remotepc.com.",
"iddallas1.remotepc.com.",
"iddenver.remotepc.com.",
- "iddetroit.remotepc.com.",
+ "ideafyi.oss-us-west-1.aliyuncs.com.",
"idlondon.remotepc.com.",
- "idmadrid.remotepc.com.",
"idnewyork1.remotepc.com.",
"idocdn.com.",
"idr.cdnwidget.com.",
@@ -2231,15 +2274,15 @@ var FakeECSFQDNs = container.NewMapSet(
"ikki.youdao.com.",
"illuminate.zendesk.com.",
"ilog-sea-aliyun.alipayplus.com.",
+ "image-sc.richrelevance.com.",
+ "image-service.onefootball.com.",
"image.myqcloud.com.",
"image.online.adp.com.",
- "images.emailaptitude.com.",
- "images.gopuff.com.",
- "imagetolink.com.",
"imagetrendelite.com.",
- "imap.163.com.",
- "imap.earthlink.net.",
- "img-3.kwcdn.com.",
+ "imeclient.openspeech.cn.",
+ "img.cdn4dd.com.",
+ "img.onesignal.com.",
+ "img.shields.io.",
"img2021.navyfederal.org.",
"img9.target.com.",
"imghst-de.com.",
@@ -2249,7 +2292,6 @@ var FakeECSFQDNs = container.NewMapSet(
"imou-sg3-ali-online-paas-private-picture.oss-ap-southeast-1.aliyuncs.com.",
"imoulife.com.",
"imp.admeking.com.",
- "imp.adx.roockmobile.com.",
"impactify.media.",
"imptrk.siteplug.com.",
"in-api.asm.skype.com.",
@@ -2261,12 +2303,9 @@ var FakeECSFQDNs = container.NewMapSet(
"inc-collabrtc.officeapps.live.com.",
"indianapolis.remotepc.com.",
"inf.miui.com.",
- "ingov.sharepoint.com.",
- "init.clmbosean.space.",
"inneraudioms.cc.easebar.com.",
"innity.com.",
"innity.net.",
- "ino.qq.com.",
"ins-tgrfs7t3.ias.tencent-cloud.net.",
"inskinad.com.",
"inspi-dsp.com.",
@@ -2277,26 +2316,23 @@ var FakeECSFQDNs = container.NewMapSet(
"instantmessaging-pa-jms-preprod-us.googleapis.com.",
"instantmessaging-pa-jms-us.googleapis.com.",
"instatus.com.",
- "int.dpool.sina.com.cn.",
"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.remotepc.com.",
"ip-api.com.",
- "ip.istatmenus.app.",
"ipfs.io.",
- "iphonesubmissions.apple.com.",
- "ipm.atm.youku.com.",
+ "ipmeta.io.",
"iprofiles.apple.com.",
"iprom.net.",
"ipv4.cadc.absolute.com.",
"ipv4.geojs.io.",
"ipv4.sdiptest.com.",
"ipv4.tracker.harry.lu.",
- "ipv6-4.sdiptest.com.",
+ "ipv6-3.sdiptest.com.",
"iq.com.",
"iscorp.com.",
"istanbul.remotepc.com.",
@@ -2308,19 +2344,27 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
+ "jdadelivers.com.",
"jdcloud.com.",
"jhahosted.com.",
"jishiyuchat.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.",
@@ -2328,19 +2372,16 @@ var FakeECSFQDNs = container.NewMapSet(
"jpost.com.",
"jpush.cn.",
"jpush.io.",
- "jqueryui.com.",
"js-eu1.hs-analytics.net.",
"js-eu1.hs-banner.com.",
"js-eu1.hs-scripts.com.",
"js-eu1.hsadspixel.net.",
"js-eu1.hscollectedforms.net.",
"js-eu1.hsforms.net.",
+ "js-eu1.hsleadflows.net.",
"js-eu1.hubspot.com.",
- "js.aiservice.vn.",
"js.eruptr.io.",
- "js.hs-analytics.net.",
"js.volumental.com.",
- "jsdelivr.net.",
"jsonatm.broker.tplay.qq.com.",
"jss.starbucks.com.",
"jssprod-starbucks.trafficmanager.net.",
@@ -2349,6 +2390,8 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
@@ -2358,20 +2401,20 @@ var FakeECSFQDNs = container.NewMapSet(
"k8s1-la-ext-haproxy.lb.indexww.com.",
"k8s1-ny-ext-haproxy.lb.indexww.com.",
"k8s1-sj-ext-haproxy.lb.indexww.com.",
- "k8s1-va-ext-haproxy.lb.indexww.com.",
"kajicam.com.",
- "karmanow.com.",
- "kelvin.education.",
"kiev.remotepc.com.",
"kirkland.zoom.us.",
+ "kitchenaid.com.",
"kiwisizing.com.",
"klagenfurt.remotepc.com.",
"knightlab.com.",
"knoxville.remotepc.com.",
"komect.com.",
"komiku.id.",
+ "kopipait.store.",
"koreacentral.api.cognitive.microsoft.com.",
"kstatic.googleusercontent.com.",
+ "kumcams.com.",
"kunlunaq.com.",
"kunlunar.com.",
"kunlunca.com.",
@@ -2384,6 +2427,7 @@ var FakeECSFQDNs = container.NewMapSet(
"kurogame.xyz.",
"kwimgs.com.",
"kzhi.tech.",
+ "l.deepintent.com.",
"la.remotepc.com.",
"la1.chat.si.riotgames.com.",
"la10.remotepc.com.",
@@ -2392,9 +2436,9 @@ var FakeECSFQDNs = container.NewMapSet(
"la2.remotepc.com.",
"la3.remotepc.com.",
"la4.remotepc.com.",
+ "la4lbg.uae2grp.ucweb.com.",
"la8.remotepc.com.",
"la9.remotepc.com.",
- "labtech.corcystems.com.",
"labtech.myitpros.com.",
"lahuashanbx.com.",
"lalapush.com.",
@@ -2402,22 +2446,21 @@ var FakeECSFQDNs = container.NewMapSet(
"lansing.remotepc.com.",
"lansweeper.com.",
"larksuite.com.",
- "launcherios.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.",
- "ldy.ieypg.com.",
"leadmanagerfx.com.",
"leihuo.netease.com.",
"leiniao.com.",
"levect.com.",
"level10gc.com.",
"leveldata.poki.io.",
- "lexicon.33across.com.",
"lianmeng.360.cn.",
+ "liblynx.com.",
"libretexts.org.",
"license.gonative.io.",
"license.litespeedtech.com.",
@@ -2431,49 +2474,51 @@ var FakeECSFQDNs = container.NewMapSet(
"linguee.com.",
"link-ga-hz-azure.yunxinfw.com.",
"link-vision-picture-sg-v2.oss-ap-southeast-1.aliyuncs.com.",
- "links.officedepot.com.mx.",
"lisbon.remotepc.com.",
"lissabon.remotepc.com.",
"list.tronlink.org.",
- "lists.theepochtimes.com.",
"litedev.sgp.hik-connect.com.",
"litespeedtech.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.",
- "livedmpsk12ia.sharepoint.com.",
+ "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.",
"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.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.newrelic.com.",
+ "login.ringcentral.com.",
"login.teamviewer.com.",
"login.vivaldi.net.",
- "logs.impactify.media.",
- "logs2.sportslocalmedia.com.",
- "logu.hpplay.cn.",
"logx.optimizely.com.",
+ "lokalise.co.",
"lol.sw.game.qq.com.",
"london.remotepc.com.",
"london2.remotepc.com.",
@@ -2495,21 +2540,18 @@ var FakeECSFQDNs = container.NewMapSet(
"m1.ubianet.com.",
"m110601-fcdn.mp.lura.live.",
"m2.ubianet.com.",
+ "m3.d.meituan.net.",
"m3.ubianet.com.",
"m4.ubianet.com.",
"m5.ubianet.com.",
"m6.ubianet.com.",
- "ma.officedepot.com.",
- "macarne.com.",
"macclog-as.rj.link.",
"madrid.remotepc.com.",
"maers.adrs.org.cn.",
- "magic.link.",
"magichue.net.",
"maidenhead.remotepc.com.",
"mail.superhuman.com.",
"mailinblue.com.",
- "main.clmbosean.space.",
"maintenanceconnection.com.",
"malware-filter.gitlab.io.",
"mam.manage.microsoft.us.",
@@ -2519,7 +2561,7 @@ var FakeECSFQDNs = container.NewMapSet(
"management.privatelink.azure.com.",
"manassas.remotepc.com.",
"manchester.remotepc.com.",
- "marketingassets.staples.com.",
+ "marketplace.canva.com.",
"marmot-cloud.com.",
"marseille.remotepc.com.",
"master1.teamviewer.com.",
@@ -2539,17 +2581,10 @@ var FakeECSFQDNs = container.NewMapSet(
"master8.teamviewer.com.",
"master9.teamviewer.com.",
"match.adfarm1.adition.com.",
- "matetranslate.com.",
- "matrix.netease.com.",
"max-l.mediav.com.",
"mbboauth-1c.prd.cn.vwg-connect.cn.",
"mcallen.remotepc.com.",
- "mcds.dalyfeds.com.",
"mcount.easebar.com.",
- "mdap.tngdigital.com.my.",
- "mdiasrv.com.cdn.cloudflare.net.",
- "mdlg.pb13bonnie.com.",
- "mdp-upgrade-cn.heytapmobi.com.",
"meari-oss-us.oss-us-west-1.aliyuncs.com.",
"meari-us.oss-us-west-1.aliyuncs.com.",
"medellin.remotepc.com.",
@@ -2559,6 +2594,7 @@ var FakeECSFQDNs = container.NewMapSet(
"media-atl3-1.cdn.whatsapp.net.",
"media-atl3-2.cdn.whatsapp.net.",
"media-atl3-3.cdn.whatsapp.net.",
+ "media-ber1-1.cdn.whatsapp.net.",
"media-bog2-1.cdn.whatsapp.net.",
"media-bog2-2.cdn.whatsapp.net.",
"media-bos5-1.cdn.whatsapp.net.",
@@ -2577,7 +2613,6 @@ var FakeECSFQDNs = container.NewMapSet(
"media-fra3-2.cdn.whatsapp.net.",
"media-fra5-1.cdn.whatsapp.net.",
"media-fra5-2.cdn.whatsapp.net.",
- "media-gig4-1.cdn.whatsapp.net.",
"media-gru1-1.cdn.whatsapp.net.",
"media-gru1-2.cdn.whatsapp.net.",
"media-gru2-1.cdn.whatsapp.net.",
@@ -2593,6 +2628,7 @@ 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.",
@@ -2600,11 +2636,13 @@ var FakeECSFQDNs = container.NewMapSet(
"media-lax3-2.cdn.whatsapp.net.",
"media-lga3-1.cdn.whatsapp.net.",
"media-lga3-2.cdn.whatsapp.net.",
+ "media-lga3-3.cdn.whatsapp.net.",
"media-lhr6-1.cdn.whatsapp.net.",
"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.",
@@ -2615,31 +2653,38 @@ var FakeECSFQDNs = container.NewMapSet(
"media-muc2-1.cdn.whatsapp.net.",
"media-ord5-1.cdn.whatsapp.net.",
"media-ord5-2.cdn.whatsapp.net.",
+ "media-ord5-3.cdn.whatsapp.net.",
"media-otp1-1.cdn.whatsapp.net.",
"media-phx1-1.cdn.whatsapp.net.",
"media-qro1-1.cdn.whatsapp.net.",
"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.",
+ "media-sin2-2.cdn.whatsapp.net.",
"media-sin6-1.cdn.whatsapp.net.",
"media-sin6-2.cdn.whatsapp.net.",
"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-xsp1-1.cdn.whatsapp.net.",
- "media-xsp1-2.cdn.whatsapp.net.",
- "media-xsp1-3.cdn.whatsapp.net.",
- "media.expedia.com.",
+ "media.defense.gov.",
+ "media.pearsoncmg.com.",
"media.ringcentral.com.",
"media.superhuman.com.",
"media.tinkoff.ru.",
- "mediafuse.com.",
+ "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.",
@@ -2647,12 +2692,6 @@ var FakeECSFQDNs = container.NewMapSet(
"metrics5.data.hicloud.com.",
"mexicocity.remotepc.com.",
"mgbsdknaeast.matrix.easebar.com.",
- "mgspabst.prismray.io.",
- "mgspfacts.prismray.io.",
- "mgsphdr1.prismray.io.",
- "mgsppres.prismray.io.",
- "mgsppros1.prismray.io.",
- "mgsptele.prismray.io.",
"mgtv.com.",
"miami.remotepc.com.",
"miami2.remotepc.com.",
@@ -2660,33 +2699,34 @@ var FakeECSFQDNs = container.NewMapSet(
"miami4.remotepc.com.",
"mib2clu8.car-cloud-cn.net.",
"microad.jp.",
+ "microfun.cn.",
"microvirt.com.",
"mid4.linkedin.com.",
+ "mida.so.",
"migu.cn.",
"milan.remotepc.com.",
"mimir2.vivaldi.com.",
"min-api.cryptocompare.com.",
"mini.browser.360.cn.",
- "minigame.vip.",
"mintkeyboard.com.",
+ "mirror5.internetdownloadmanager.com.",
"mixi.media.",
- "mlmannouncements.pearson.com.",
- "mmods.site.",
+ "mobavenue.com.",
+ "mobile-api.opentable.com.",
"mobile-bank.cdn-tinkoff.ru.",
"mobile-collector.newrelic.com.cdn.cloudflare.net.",
"mobile-l7.bereal.com.",
"mobile-protect-api.securetheorem.com.",
- "mobile.bereal.com.",
"mobile.shuzilm.cn.",
+ "mobileapi.us.afterpay.com.",
"mobiledataplan-pa.googleapis.com.",
"mobilemaps-pa-gz.googleapis.com.",
"mobilemaps.googleapis.com.",
- "mobilesecuritycore-cdn.norton.com.edgekey.net.",
"modelportrait.xiaohongshu.com.",
"modesto.remotepc.com.",
"moni-onrt-stsdk.vivo.com.cn.",
+ "monitor.fraudblocker.com.",
"monitoring.getelevar.com.",
- "monitoring.worksighted.com.",
"montage-updates.displaynote.com.",
"monticello.remotepc.com.",
"montreal.remotepc.com.",
@@ -2700,53 +2740,53 @@ var FakeECSFQDNs = container.NewMapSet(
"mrisoftware.com.",
"ms.applvn.com.",
"ms1app.pb.com.",
+ "ms4.applvn.com.",
"msdl.microsoft.com.",
"msf.3g.qq.com.",
"msg-img-hk.oss-cn-hongkong.aliyuncs.com.",
- "msg.cmpassport.com.",
+ "mssdk22-normal-useast1a.tiktokv.com.",
+ "mssdk22-normal-useast2a.tiktokv.com.",
"mtrace.qq.com.",
"mumbai.remotepc.com.",
"munich.remotepc.com.",
- "music.163.com.",
+ "muscache.cn.",
"music.163.com.163jiasu.com.",
- "musical.ly.",
"musicps.p2p.qq.com.",
"musicpunch.p2p.qq.com.",
"musicstylingonline.com.",
"mw.footballbros.io.",
- "mw.mw2.global.",
- "mweb-hb.presage.io.",
"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.microsoftpersonalcontent.com.",
"my.nalpeiron.com.",
- "myim3banner.kloc.co.",
"myisolved.com.",
"myporn.club.",
"myqcloud.com.",
"mystery-game-tile.poki.io.",
- "myvscloud.com.",
"myworkdaycdn.com.cn.",
+ "n-1-ashx.ad-m.net.",
+ "n-2-ashx.ad-m.net.",
+ "n-7-ashx.ad-m.net.",
"n33.ultipro.com.",
"n35.ultipro.com.",
- "na120.epm.cyberark.com.",
+ "na113.epm.cyberark.com.",
"na2.chat.si.riotgames.com.",
"nab.com.au.",
"najva.com.",
+ "nam.veta.naver.com.",
"namequery.com.",
- "nanoleaf.me.",
"naperville.remotepc.com.",
"nashville.remotepc.com.",
+ "nationalmap.gov.",
"nationalreview.com.",
"nativecos.com.",
"nc-pod1-smp-device.apple.com.",
- "nc-pod5-smp-device.apple.com.",
"nc.com.",
"ncentral.centrexit.com.",
"nearme.com.cn.",
@@ -2758,13 +2798,13 @@ var FakeECSFQDNs = container.NewMapSet(
"netpop.app.",
"netpresenter.com.",
"netsolssl.com.",
+ "network-check.sybo.net.",
"newcontinuum.net.",
"neworleans.remotepc.com.",
- "news-abroad.vivo.com.",
+ "news-af-2.feednews.com.",
"news-af.feednews.com.",
"news-client.apple.com.",
"news-events.apple.com.",
- "news-global.cloud.",
"news-nar-aud.apple.com.",
"news-sports-events.apple.com.",
"newsletter-edge.apple.com.",
@@ -2780,6 +2820,7 @@ var FakeECSFQDNs = container.NewMapSet(
"ngb.haplat.net.",
"nice-team.net.",
"nie.netease.com.",
+ "ninjakiwi.com.",
"nist.gov.",
"nitroapps.co.",
"nitropay.com.",
@@ -2787,8 +2828,8 @@ var FakeECSFQDNs = container.NewMapSet(
"node.setupad.com.",
"nokia.com.",
"nordcurrent.com.",
- "norma-external-collect.meizu.com.",
"northcentralus.api.cognitive.microsoft.com.",
+ "northerntool.com.",
"northeurope.api.cognitive.microsoft.com.",
"norwayeast.api.cognitive.microsoft.com.",
"notes-analytics-events.apple.com.",
@@ -2796,7 +2837,6 @@ var FakeECSFQDNs = container.NewMapSet(
"notification889.com.",
"notify.music.163.com.",
"novaicare.com.",
- "npmjs.com.",
"nps.gov.",
"ns-cloud-a1.googledomains.com.",
"ns-cloud-a2.googledomains.com.",
@@ -2822,7 +2862,6 @@ var FakeECSFQDNs = container.NewMapSet(
"ns.identrust.com.",
"ns0105.secondary.cloudflare.com.",
"ns0160.secondary.cloudflare.com.",
- "ns1.a0.impervasecuredns.net.",
"ns1.cloudflare.net.",
"ns1.g.aaplimg.com.",
"ns1.google.com.",
@@ -2840,7 +2879,6 @@ var FakeECSFQDNs = container.NewMapSet(
"ns4.google.com.",
"ns5.cloudflare.net.",
"nsa.nalpeiron.com.",
- "nsatc.net.",
"ntes53.netease.com.",
"ntp.aliyun.com.",
"ntp.arlo.com.",
@@ -2862,45 +2900,40 @@ var FakeECSFQDNs = container.NewMapSet(
"nwsalert.onelouder.com.",
"nycourts.gov.",
"nycrt.marphezis.com.",
- "o.quizlet.com.",
- "oauth.ws.sonos.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.",
- "octossp.com.",
"odrs.fda.gov.ph.",
"odw7bf.dood.video.",
"oec22-normal-alisg.tokopediax.com.",
- "oec22-normal-useast2a.tiktokv.com.",
"office.microsoft.com.",
- "officeathand.att.com.",
+ "officepreviewredir.microsoft.com.",
+ "offline-pkg-api.dalyfeds.com.",
"ogma-l7.bereal.com.",
- "ogma.bereal.com.",
+ "ogsvc.pgoriginad.com.",
"ojp.gov.",
"okko.tv.",
"ollama.com.",
- "omaha.formlabs.com.",
"omiapp.me.",
"omitech.site.",
- "omnibus.gm.com.",
- "omt.garmin.com.",
"on-hwapps-o.api.leiniao.com.",
"one.newrelic.com.",
"onekey1.cmpassport.com.",
"oneplus.net.",
"onethingpcs.com.",
"onezapp.com.",
- "online-stopwatch.com.",
+ "online-store-web.shopifyapps.com.",
+ "online.kugou.com.",
"onlinewebfonts.com.",
"op.mykonf.com.",
"opamarketplace.com.",
@@ -2908,7 +2941,6 @@ var FakeECSFQDNs = container.NewMapSet(
"open.oppomobile.com.",
"opencmp.net.",
"opendsp.ru.",
- "openrice.com.",
"opex-service-cn.allawntech.com.",
"oppo.com.",
"oppomobile.com.",
@@ -2916,41 +2948,38 @@ var FakeECSFQDNs = container.NewMapSet(
"optimize.urekamedia.com.",
"optimizely.com.",
"orangehire.com.au.",
+ "orderdeadline.com.",
"oregon.remotepc.com.",
"origin.fe-image-cache-ttp.useast8.byteglb.com.",
+ "originplatform.com.",
"orlando.remotepc.com.",
- "ort.stsdk.vivo.com.cn.",
"os7lm.6kvses.com.",
"osaka.remotepc.com.",
"oss-ap-southeast-1.aliyuncs.com.",
"oss-ap-southeast-5.aliyuncs.com.",
- "oss-cn-hangzhou.aliyuncs.com.",
"oss-cn-hongkong.aliyuncs.com.",
"oss-cn-shenzhen.aliyuncs.com.",
"oss-eu-central-1.aliyuncs.com.",
"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.",
- "ovidsp.ovid.com.",
"p.adlooxtracking.com.",
- "p.teads.tv.",
+ "p.vsco.co.",
"p0-pu-private-useast8.tiktokv.us.",
"p107609.cedexis-test.com.",
- "p109522.cedexis-test.com.",
- "p11-buy.itunes.apple.com.",
- "p12-buy.itunes.apple.com.",
- "p13.zdusercontent.com.",
- "p2-buy.itunes.apple.com.",
+ "p18-buy.itunes.apple.com.",
"p20304.cedexis-test.com.",
"p20305.cedexis-test.com.",
"p20306.cedexis-test.com.",
@@ -2960,17 +2989,17 @@ var FakeECSFQDNs = container.NewMapSet(
"p20311.cedexis-test.com.",
"p20314.cedexis-test.com.",
"p20315.cedexis-test.com.",
- "p26-buy.itunes.apple.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.",
"p2p3.cloudbirds.cn.",
"p2pm-ali.reolink.com.",
- "p3-buy.itunes.apple.com.",
"p30605.cedexis-test.com.",
"p33231.cedexis-test.com.",
"p33236.cedexis-test.com.",
@@ -2987,9 +3016,10 @@ 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.",
- "p40-buy.itunes.apple.com.",
"p40255.cedexis-test.com.",
"p40259.cedexis-test.com.",
"p40264.cedexis-test.com.",
@@ -3008,9 +3038,9 @@ var FakeECSFQDNs = container.NewMapSet(
"p48436.cedexis-test.com.",
"p4p.arenabg.com.",
"p52066.cedexis-test.com.",
+ "p56-buy.itunes.apple.com.",
"p56745.cedexis-test.com.",
- "p70-buy.itunes.apple.com.",
- "p71-buy.itunes.apple.com.",
+ "p7-buy.itunes.apple.com.",
"p76593.cedexis-test.com.",
"p86075.cedexis-test.com.",
"p86077.cedexis-test.com.",
@@ -3019,6 +3049,7 @@ var FakeECSFQDNs = container.NewMapSet(
"p95708.cedexis-test.com.",
"p95711.cedexis-test.com.",
"p95722.cedexis-test.com.",
+ "pa.authenticator.beyondidentity.com.",
"pai.googlezip.net.",
"palermo.remotepc.com.",
"palm.tech.",
@@ -3027,13 +3058,13 @@ var FakeECSFQDNs = container.NewMapSet(
"partnerboost.com.",
"pasadena.remotepc.com.",
"passportalmsp.com.",
- "pat-issuer.cloudflare.com.",
+ "patagonia-us.attn.tv.",
+ "pay.datatrans.com.",
"payment.api.speechify.com.",
"payment.omiapp.me.",
"pbdlsp1.pb.com.",
"pbe1.chat.si.riotgames.com.",
"pbs.btloader.com.",
- "pc-store-lb.lenovomm.cn.",
"pc-store.lenovomm.cn.",
"pc.crashsight.wetest.net.",
"pc.perfsight.wetest.net.",
@@ -3041,23 +3072,20 @@ var FakeECSFQDNs = container.NewMapSet(
"pd.cdnwidget.com.",
"pdf24.org.",
"pdfforge.org.",
- "peloton.netlifyglobalcdn.com.",
- "penngaming-my.sharepoint.com.",
+ "peakpx.com.",
"peopleadmin.com.",
"perf-eu1.hsforms.com.",
"perfsight.qq.com.",
"perfsight.wetest.net.",
"perkspot-api.perkspot.com.",
- "permutive.arstechnica.com.",
"permutive.businessinsider.com.",
- "permutive.newyorker.com.",
+ "permutive.wired.com.",
"perr.brightvpn.com.",
"pf.intuit.com.",
"pharos.studyquicks.com.",
"phoenix.remotepc.com.",
"phoenix2.remotepc.com.",
"phonebridge.zoho.com.",
- "phonepower.com.",
"photoroom.com.",
"phx02pap002.storage.live.com.",
"phx02pap003.storage.live.com.",
@@ -3067,7 +3095,6 @@ var FakeECSFQDNs = container.NewMapSet(
"phx02pap008.storage.live.com.",
"pi2850.ci.managedwhitelisting.com.",
"pic.rutubelist.ru.",
- "picodiglobal.com.",
"piicmgvmss.polaris.com.",
"pikabu.ru.",
"pin.apiblink.ru.",
@@ -3079,51 +3106,44 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
"pla-prod-scu-apim-01.azure-api.net.",
- "planner.cloud.microsoft.",
- "platform-alib.linkedin.cn.",
"platform-alib.linkedin.cn.w.kunlunaq.com.",
- "platform.foxitcloud.com.",
- "playercdn.jivox.com.cdn.cloudflare.net.",
"playstream.media.",
+ "plotline.so.",
"plrm.zone.",
+ "plt-gw-us.xiaoyi.com.",
"pm.geniusmonkey.com.",
- "podcastswaves.com.",
+ "pods.officeapps.live.com.",
"poizon.com.",
"polandcentral.api.cognitive.microsoft.com.",
"polling.zoom.us.",
"polymarket.com.",
- "poopstream.co.",
"pop-convert.com.",
+ "popmenucloud.com.",
"popt.in.",
"portal.us.ubianet.com.",
"portals.mobi.",
"portland.remotepc.com.",
"posthog.com.",
- "potatovpn.com.",
"pov.spectrum.net.",
"powerpoint-collab.officeapps.live.com.",
- "pp.ringcentral.biz.",
"ppgames.net.",
"ppos.com.",
- "prebid-la.casalemedia.com.",
- "prebid-ny.casalemedia.com.",
"prebid-va.casalemedia.com.",
"prebid.trustedstack.com.",
"prebidserver.pixfuture.com.",
- "premierhealth-my.sharepoint.com.",
"premium.xvpn.io.",
"printaudit.com.",
"printfriendly.com.",
- "prism.app-us1.com.",
"privy.io.",
"pro-glswish-aks-tm.trafficmanager.net.",
"pro-swishapps-aks-tm.trafficmanager.net.",
"procore.com.",
- "prod-catalog-product-api.dickssportinggoods.com.",
"prod-client-api.v.aaplimg.com.",
"prod-default.lb.logrocket.network.",
"prod-event-relay-api.v.aaplimg.com.",
@@ -3132,13 +3152,15 @@ var FakeECSFQDNs = container.NewMapSet(
"prod-event-relay-sports-api.v.aaplimg.com.",
"prod-event-relay-stocks-api.v.aaplimg.com.",
"prod-event-relay-weather-api.v.aaplimg.com.",
- "prod-frs.content.classy.org.",
"prod-newsletter-edge.v.aaplimg.com.",
"prod-tasks.trafficmanager.net.",
"prod-weather-widget-event-gateway.v.aaplimg.com.",
"prod.api.letsencrypt.org.",
"production.kabutoservices.com.",
- "prolearning.nwea.org.",
+ "profiler-collector.dalyfeds.com.",
+ "project-limelight.com.",
+ "projects.gitlab.io.",
+ "promotions.newegg.com.",
"proquest.com.",
"protonvpn.com.",
"provaltech.com.",
@@ -3151,11 +3173,17 @@ var FakeECSFQDNs = container.NewMapSet(
"publicfaas.vasdgame.com.",
"publictracker.xyz.",
"puffer.6.401402081.west-gcloud.codm.activision.com.",
- "pull-flv-f58-tt03.fcdn.eu.tiktokcdn.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.",
"punch.p2p.qq.com.",
"push-ads-cn.heytapmobi.com.",
"push-row.zui.com.",
- "push.bitdefender.net.",
+ "push-rtmp-l95.douyincdn.com.ctdns.cn.",
"push.omiapp.me.",
"pushcrew.com.",
"pushimg.com.",
@@ -3166,6 +3194,7 @@ var FakeECSFQDNs = container.NewMapSet(
"pwa.zoom.us.",
"px.ads.linkedin.com.",
"px4.ads.linkedin.com.",
+ "pxl.stripchat.com.",
"qagpublic.qg1.apps.qualys.ca.",
"qagpublic.qg1.apps.qualys.co.uk.",
"qagpublic.qg1.apps.qualys.com.",
@@ -3180,27 +3209,24 @@ var FakeECSFQDNs = container.NewMapSet(
"qcloud.com.",
"qfp.intuit.com.",
"qiezibenpao.com.",
- "qinglong.me.",
+ "qikify.com.",
"qiniudns.com.",
"qiniup.com.",
"qiyukf.com.",
"qookkagames.com.",
- "qortex.ai.",
- "qpic.cn.",
+ "qq.com.cn.",
"qualcomm.cn.",
"qualcomm.com.",
"qualys.ca.",
"qualys.com.",
"qualys.eu.",
"qualys.in.",
+ "quantamagazine.org.",
"quantil.com.",
"quantummetric.com.",
- "quebeccity.remotepc.com.",
- "questionai.com.",
"quicinc.com.",
"quickcep.com.",
"qxwz.com.",
- "r.akulaku.net.",
"r.ingest-lr.com.",
"r.intake-lr.com.",
"r.logr-ingest.com.",
@@ -3211,70 +3237,59 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
+ "r1---sn-a5msener.c.2mdn.net.",
"r1---sn-a5msenle.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-ab5l6nrk.c.2mdn.net.",
"r1---sn-ab5l6nrl.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-ab5sznzr.c.2mdn.net.",
"r1---sn-ab5sznzs.c.2mdn.net.",
"r1---sn-ab5sznzy.c.2mdn.net.",
- "r1---sn-ab5sznzz.c.2mdn.net.",
- "r1---sn-ajab55-55.c.2mdn.net.",
- "r1---sn-jxopj-nh4e.googlevideo.com.",
- "r1---sn-nh5gujvh-h4xe.googlevideo.com.",
- "r1---sn-o097znsr.c.2mdn.net.",
- "r1---sn-p5qddn76.c.2mdn.net.",
- "r1---sn-p5qddn7d.c.2mdn.net.",
"r1---sn-p5qddn7r.c.2mdn.net.",
- "r1---sn-p5qlsn6l.c.2mdn.net.",
- "r1---sn-p5qlsn7l.c.2mdn.net.",
- "r1---sn-p5qlsnrl.c.2mdn.net.",
- "r1---sn-p5qlsnrr.c.2mdn.net.",
+ "r1---sn-p5qs7n6d.c.2mdn.net.",
"r1---sn-p5qs7nsk.c.2mdn.net.",
"r1---sn-p5qs7nzr.c.2mdn.net.",
- "r1---sn-q4fl6n6d.c.2mdn.net.",
+ "r1---sn-q4fl6n6y.c.2mdn.net.",
+ "r1---sn-q4fl6n6z.c.2mdn.net.",
"r1---sn-q4fl6nd7.c.2mdn.net.",
- "r1---sn-q4fl6nsk.c.2mdn.net.",
- "r1---sn-q4flrne7.c.2mdn.net.",
- "r1---sn-q4flrner.c.2mdn.net.",
+ "r1---sn-q4flrney.c.2mdn.net.",
"r1---sn-q4flrnez.c.2mdn.net.",
- "r1---sn-q4flrnlz.c.2mdn.net.",
- "r1---sn-q4flrnsl.c.2mdn.net.",
- "r1---sn-q4fzene7.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-vgqsknzl.c.2mdn.net.",
"r1---sn-vgqsknzs.c.2mdn.net.",
- "r1---sn-vgqsknzy.c.2mdn.net.",
"r1---sn-vgqsrn6e.c.2mdn.net.",
- "r1---sn-vgqsrn6l.c.2mdn.net.",
"r1---sn-vgqsrne6.c.2mdn.net.",
- "r1---sn-vgqsrnez.c.2mdn.net.",
"r1---sn-vgqsrnl6.c.2mdn.net.",
"r1---sn-vgqsrnld.c.2mdn.net.",
- "r1---sn-vgqsrnsd.c.2mdn.net.",
- "r1---sn-vgqsrnz7.c.2mdn.net.",
+ "r1---sn-vgqsrnls.c.2mdn.net.",
+ "r1---sn-vgqsrnlz.c.2mdn.net.",
+ "r1---sn-vgqsrnsy.c.2mdn.net.",
"r1---sn-vgqsrnzd.c.2mdn.net.",
+ "r1---sn-vgqsrnzr.c.2mdn.net.",
"r1---sn-vgqsrnzs.c.2mdn.net.",
"r1---sn-vgqsrnzz.c.2mdn.net.",
- "r1---sn-voxoxu-v3jl.googlevideo.com.",
- "r1---sn-voxoxu-v3js.googlevideo.com.",
- "r2---sn-2napbiu-p5ie.gvt1.com.",
- "r2---sn-a5mekn6d.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.",
@@ -3286,169 +3301,150 @@ var FakeECSFQDNs = container.NewMapSet(
"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-jxopj-nh4e.googlevideo.com.",
- "r2---sn-nh5gujvh-h4xe.googlevideo.com.",
- "r2---sn-npoldn7z.c.2mdn.net.",
- "r2---sn-p5qddn7z.c.2mdn.net.",
- "r2---sn-p5qlsn7s.c.2mdn.net.",
+ "r2---sn-n2uxaxjvh-j5xs.gvt1.com.",
+ "r2---sn-p5qddn7r.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-q4fl6n66.c.2mdn.net.",
- "r2---sn-q4fl6n6d.c.2mdn.net.",
- "r2---sn-q4fl6nsk.c.2mdn.net.",
- "r2---sn-q4fl6nsr.c.2mdn.net.",
- "r2---sn-q4flrnlz.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-q4flrnsl.c.2mdn.net.",
- "r2---sn-q4fzen7l.c.2mdn.net.",
+ "r2---sn-q4fzen7e.c.2mdn.net.",
"r2---sn-q4fzene7.c.2mdn.net.",
"r2---sn-q4fzenee.c.2mdn.net.",
+ "r2---sn-vgqskn66.c.2mdn.net.",
"r2---sn-vgqskn6s.c.2mdn.net.",
"r2---sn-vgqskn6z.c.2mdn.net.",
- "r2---sn-vgqsknld.c.2mdn.net.",
- "r2---sn-vgqsknlz.c.2mdn.net.",
- "r2---sn-vgqsknsk.c.2mdn.net.",
- "r2---sn-vgqsknze.c.2mdn.net.",
- "r2---sn-vgqsknzl.c.2mdn.net.",
- "r2---sn-vgqsknzz.c.2mdn.net.",
+ "r2---sn-vgqsknek.c.2mdn.net.",
+ "r2---sn-vgqsknz7.c.2mdn.net.",
+ "r2---sn-vgqsrn66.c.2mdn.net.",
"r2---sn-vgqsrn67.c.2mdn.net.",
- "r2---sn-vgqsrn6e.c.2mdn.net.",
- "r2---sn-vgqsrnld.c.2mdn.net.",
+ "r2---sn-vgqsrn6z.c.2mdn.net.",
+ "r2---sn-vgqsrne6.c.2mdn.net.",
+ "r2---sn-vgqsrnls.c.2mdn.net.",
"r2---sn-vgqsrnlz.c.2mdn.net.",
- "r2---sn-vgqsrnsr.c.2mdn.net.",
- "r2---sn-vgqsrnsy.c.2mdn.net.",
"r2---sn-vgqsrnz7.c.2mdn.net.",
"r2---sn-vgqsrnzd.c.2mdn.net.",
- "r2---sn-vgqsrnzk.c.2mdn.net.",
- "r2---sn-vgqsrnzr.c.2mdn.net.",
- "r2---sn-voxoxu-v3jl.googlevideo.com.",
- "r2---sn-voxoxu-v3js.googlevideo.com.",
- "r2-t.trackedlink.net.",
- "r3---sn-2napbiu-p5ie.gvt1.com.",
- "r3---sn-a5mekn6r.c.2mdn.net.",
- "r3---sn-a5meknzl.c.2mdn.net.",
+ "r2---sn-vgqsrnzs.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-ab5sznzy.c.2mdn.net.",
"r3---sn-ab5sznzz.c.2mdn.net.",
- "r3---sn-jxopj-nh4e.googlevideo.com.",
- "r3---sn-p5qddn76.c.2mdn.net.",
- "r3---sn-p5qlsn6l.c.2mdn.net.",
- "r3---sn-p5qlsn76.c.2mdn.net.",
+ "r3---sn-hp57kndk.c.2mdn.net.",
+ "r3---sn-p5qddn7d.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-p5qs7nzy.c.2mdn.net.",
- "r3---sn-q4fl6n66.c.2mdn.net.",
- "r3---sn-q4fl6n6d.c.2mdn.net.",
- "r3---sn-q4fl6ndl.c.2mdn.net.",
- "r3---sn-q4fl6ndz.c.2mdn.net.",
- "r3---sn-q4fl6nss.c.2mdn.net.",
- "r3---sn-q4flrne6.c.2mdn.net.",
- "r3---sn-q4flrnsk.c.2mdn.net.",
+ "r3---sn-q4fl6ns6.c.2mdn.net.",
+ "r3---sn-q4flrnel.c.2mdn.net.",
+ "r3---sn-q4flrnld.c.2mdn.net.",
+ "r3---sn-q4flrnlz.c.2mdn.net.",
"r3---sn-q4flrnsl.c.2mdn.net.",
- "r3---sn-q4fzen7s.c.2mdn.net.",
+ "r3---sn-q4flrnss.c.2mdn.net.",
+ "r3---sn-q4fzen7l.c.2mdn.net.",
"r3---sn-vgqskn66.c.2mdn.net.",
- "r3---sn-vgqskn6d.c.2mdn.net.",
+ "r3---sn-vgqskn67.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-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-vgqsrn6e.c.2mdn.net.",
"r3---sn-vgqsrn6l.c.2mdn.net.",
- "r3---sn-vgqsrnes.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-vgqsrnsr.c.2mdn.net.",
- "r3---sn-vgqsrnsy.c.2mdn.net.",
- "r3---sn-vgqsrnz7.c.2mdn.net.",
+ "r3---sn-vgqsrns6.c.2mdn.net.",
"r3---sn-vgqsrnzd.c.2mdn.net.",
- "r3---sn-vgqsrnzs.c.2mdn.net.",
- "r3---sn-vgqsrnzy.c.2mdn.net.",
"r3---sn-vgqsrnzz.c.2mdn.net.",
- "r4---sn-ab5l6ndr.c.2mdn.net.",
+ "r4---sn-a5mekndl.c.2mdn.net.",
+ "r4---sn-ab5l6ndy.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-ab5l6nrs.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-ab5sznzs.c.2mdn.net.",
"r4---sn-ab5sznzy.c.2mdn.net.",
"r4---sn-ab5sznzz.c.2mdn.net.",
- "r4---sn-jxopj-nh4e.googlevideo.com.",
- "r4---sn-npoe7nsy.c.2mdn.net.",
- "r4---sn-npoldn7y.c.2mdn.net.",
"r4---sn-p5qddn76.c.2mdn.net.",
- "r4---sn-p5qddn7r.c.2mdn.net.",
+ "r4---sn-p5qddn7z.c.2mdn.net.",
"r4---sn-p5qlsn6l.c.2mdn.net.",
- "r4---sn-p5qlsn7d.c.2mdn.net.",
"r4---sn-p5qlsnrr.c.2mdn.net.",
"r4---sn-p5qs7n6d.c.2mdn.net.",
- "r4---sn-p5qs7nsk.c.2mdn.net.",
- "r4---sn-p5qs7nsr.c.2mdn.net.",
- "r4---sn-p5qs7nzk.c.2mdn.net.",
- "r4---sn-p5qs7nzy.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-q4fl6nss.c.2mdn.net.",
- "r4---sn-q4fl6nsy.c.2mdn.net.",
+ "r4---sn-q4fl6nz7.c.2mdn.net.",
"r4---sn-q4flrn7k.c.2mdn.net.",
- "r4---sn-q4flrnez.c.2mdn.net.",
- "r4---sn-q4flrnsd.c.2mdn.net.",
- "r4---sn-q4flrnsk.c.2mdn.net.",
- "r4---sn-q4fzen7l.c.2mdn.net.",
- "r4---sn-q4fzen7r.c.2mdn.net.",
+ "r4---sn-q4flrnld.c.2mdn.net.",
+ "r4---sn-q4flrnss.c.2mdn.net.",
"r4---sn-vgqskn66.c.2mdn.net.",
- "r4---sn-vgqsknld.c.2mdn.net.",
- "r4---sn-vgqsknlk.c.2mdn.net.",
- "r4---sn-vgqsknly.c.2mdn.net.",
- "r4---sn-vgqsknlz.c.2mdn.net.",
+ "r4---sn-vgqskn6d.c.2mdn.net.",
+ "r4---sn-vgqskn6s.c.2mdn.net.",
"r4---sn-vgqskns7.c.2mdn.net.",
- "r4---sn-vgqsknz6.c.2mdn.net.",
- "r4---sn-vgqsknzd.c.2mdn.net.",
+ "r4---sn-vgqsknz7.c.2mdn.net.",
+ "r4---sn-vgqsknze.c.2mdn.net.",
"r4---sn-vgqsknzk.c.2mdn.net.",
"r4---sn-vgqsknzs.c.2mdn.net.",
- "r4---sn-vgqsknzy.c.2mdn.net.",
- "r4---sn-vgqsrn6l.c.2mdn.net.",
- "r4---sn-vgqsrnez.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-vgqsrnlz.c.2mdn.net.",
"r4---sn-vgqsrns6.c.2mdn.net.",
- "r4---sn-vgqsrnsr.c.2mdn.net.",
- "r4---sn-vgqsrnsy.c.2mdn.net.",
- "r4---sn-vgqsrnz6.c.2mdn.net.",
+ "r4---sn-vgqsrnzd.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-ab5l6nk6.c.2mdn.net.",
"r5---sn-ab5l6nkd.c.2mdn.net.",
"r5---sn-ab5l6nr6.c.2mdn.net.",
@@ -3464,108 +3460,109 @@ var FakeECSFQDNs = container.NewMapSet(
"r5---sn-ab5sznzk.c.2mdn.net.",
"r5---sn-ab5sznzl.c.2mdn.net.",
"r5---sn-ab5sznzr.c.2mdn.net.",
- "r5---sn-ab5sznzy.c.2mdn.net.",
"r5---sn-ajab55-55.c.2mdn.net.",
- "r5---sn-jxopj-nh4e.googlevideo.com.",
- "r5---sn-nx57ynsl.c.2mdn.net.",
- "r5---sn-p5qddn76.c.2mdn.net.",
+ "r5---sn-p5qddn7d.c.2mdn.net.",
"r5---sn-p5qddn7k.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-p5qlsndk.c.2mdn.net.",
"r5---sn-p5qlsnrr.c.2mdn.net.",
"r5---sn-p5qlsny6.c.2mdn.net.",
- "r5---sn-p5qs7nsr.c.2mdn.net.",
+ "r5---sn-p5qs7nzk.c.2mdn.net.",
"r5---sn-p5qs7nzy.c.2mdn.net.",
- "r5---sn-q4fl6nsk.c.2mdn.net.",
- "r5---sn-q4fl6nz6.c.2mdn.net.",
- "r5---sn-q4fl6nzy.c.2mdn.net.",
- "r5---sn-q4flrn7k.c.2mdn.net.",
- "r5---sn-q4flrnlz.c.2mdn.net.",
+ "r5---sn-q4fl6nss.c.2mdn.net.",
"r5---sn-q4flrnsd.c.2mdn.net.",
"r5---sn-q4flrnsl.c.2mdn.net.",
"r5---sn-q4fzen7l.c.2mdn.net.",
"r5---sn-qjp5q5-55.c.2mdn.net.",
"r5---sn-vgqskn67.c.2mdn.net.",
"r5---sn-vgqskn6d.c.2mdn.net.",
- "r5---sn-vgqskn6z.c.2mdn.net.",
- "r5---sn-vgqsknlr.c.2mdn.net.",
- "r5---sn-vgqsknz6.c.2mdn.net.",
- "r5---sn-vgqsknze.c.2mdn.net.",
+ "r5---sn-vgqskn6s.c.2mdn.net.",
+ "r5---sn-vgqsknz7.c.2mdn.net.",
"r5---sn-vgqsknzs.c.2mdn.net.",
- "r5---sn-vgqsknzy.c.2mdn.net.",
- "r5---sn-vgqsknzz.c.2mdn.net.",
"r5---sn-vgqsrn66.c.2mdn.net.",
"r5---sn-vgqsrn67.c.2mdn.net.",
- "r5---sn-vgqsrn6l.c.2mdn.net.",
+ "r5---sn-vgqsrn6z.c.2mdn.net.",
"r5---sn-vgqsrne6.c.2mdn.net.",
- "r5---sn-vgqsrnek.c.2mdn.net.",
+ "r5---sn-vgqsrned.c.2mdn.net.",
"r5---sn-vgqsrnez.c.2mdn.net.",
"r5---sn-vgqsrnl6.c.2mdn.net.",
- "r5---sn-vgqsrnlk.c.2mdn.net.",
- "r5---sn-vgqsrnz7.c.2mdn.net.",
- "r6---sn-jxopj-nh4e.googlevideo.com.",
+ "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.visualwebsiteoptimizer.com.",
+ "r6.visualwebsiteoptimizer.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.",
"rcs-acs-mcc311.jibe.google.com.",
"rcs-acs-mcc510.jibe.google.com.",
"rcs-acs-tmo-us.jibe.google.com.",
+ "rcs-copper-optimized-us.googleapis.com.",
"rcs-copper-us.googleapis.com.",
"rcs.telephony.goog.",
+ "rctiplus.id.",
"rd.com.",
+ "readingplus.com.",
"realtime-data-api.transitapp.com.",
"realtime.luckyorange.com.",
"realtime.services.box.net.",
"recombee.com.",
"recruiting.ultipro.com.",
"recruiting2.ultipro.com.",
- "referralrock.com.",
"reflector.makerbot.com.",
"refpaucqkl.top.",
"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.",
"resideo.com.",
"resource.digitalinsight.com.",
- "resources.office.net.edgekey.net.",
- "rest.iad-01.braze.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.",
+ "ripamatic.com.",
"rivergame.net.",
"riverside.remotepc.com.",
"rl.progressive.com.",
"rl.quantummetric.com.",
"rlm.haokan.mobi.",
- "rmm.aunalytics.com.",
- "rmm2.jmark.com.",
"rms-dra.platform.dbankcloud.com.",
"rn-resource-app.xiaohongshu.com.",
+ "roaming-eu.officeapps.live.com.",
"roborock.com.",
"rockylinux.org.",
- "roockmobile.com.",
"router.teamviewer.com.",
"roxy.azurefd.net.",
"rpt.cedexis.com.",
@@ -3573,13 +3570,12 @@ var FakeECSFQDNs = container.NewMapSet(
"rq.wh.cmcm.com.",
"rr1---sn-0nnpbo5a-bggl.googlevideo.com.",
"rr1---sn-0op8v4h5pox-cbgl.googlevideo.com.",
- "rr1---sn-2aqu-hoaly.googlevideo.com.",
"rr1---sn-2imern76.googlevideo.com.",
"rr1---sn-2imern7d.googlevideo.com.",
"rr1---sn-2imeyn7k.googlevideo.com.",
"rr1---sn-2napbiu-p5ie.googlevideo.com.",
"rr1---sn-2napbiu-p5ie.gvt1.com.",
- "rr1---sn-2pmxapm0n-gpje.googlevideo.com.",
+ "rr1---sn-2oaig5-55.googlevideo.com.",
"rr1---sn-2pmxapm0n-gpjl.googlevideo.com.",
"rr1---sn-30a7rne6.googlevideo.com.",
"rr1---sn-30a7rned.googlevideo.com.",
@@ -3590,7 +3586,6 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
@@ -3614,7 +3609,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-4g5edndl.googlevideo.com.",
"rr1---sn-4g5edndr.googlevideo.com.",
"rr1---sn-4g5ednds.googlevideo.com.",
- "rr1---sn-4g5ednds.gvt1.com.",
"rr1---sn-4g5edndy.googlevideo.com.",
"rr1---sn-4g5edndz.googlevideo.com.",
"rr1---sn-4g5ednkl.googlevideo.com.",
@@ -3636,18 +3630,16 @@ 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-5abxgpxuxaxjvh-9n4e.googlevideo.com.",
- "rr1---sn-5abxgpxuxaxjvh-9n4l.googlevideo.com.",
- "rr1---sn-5abxgpxuxaxjvh-9n4s.googlevideo.com.",
- "rr1---sn-5abxgpxuxaxjvh-9n4z.googlevideo.com.",
+ "rr1---sn-4pgnuapbiu-hiul.googlevideo.com.",
"rr1---sn-5axnug5-hxm6.googlevideo.com.",
+ "rr1---sn-5gxo-in8l.googlevideo.com.",
"rr1---sn-5gxo-in8s.googlevideo.com.",
"rr1---sn-5hne6n6e.googlevideo.com.",
"rr1---sn-5hne6n6l.googlevideo.com.",
@@ -3660,8 +3652,8 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-5hne6nsz.googlevideo.com.",
"rr1---sn-5hne6nz6.googlevideo.com.",
"rr1---sn-5hne6nzd.googlevideo.com.",
- "rr1---sn-5hne6nzd.gvt1.com.",
"rr1---sn-5hne6nzk.googlevideo.com.",
+ "rr1---sn-5hne6nzk.gvt1.com.",
"rr1---sn-5hne6nzy.googlevideo.com.",
"rr1---sn-5hnednss.googlevideo.com.",
"rr1---sn-5hnednsz.googlevideo.com.",
@@ -3673,6 +3665,8 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-5hneknee.googlevideo.com.",
"rr1---sn-5hneknek.googlevideo.com.",
"rr1---sn-5hneknes.googlevideo.com.",
+ "rr1---sn-5jn5a5n35-5ojs.googlevideo.com.",
+ "rr1---sn-5pgnugx5h-hn2z.googlevideo.com.",
"rr1---sn-5uaezndd.googlevideo.com.",
"rr1---sn-5uaezne6.googlevideo.com.",
"rr1---sn-5uaezned.googlevideo.com.",
@@ -3680,12 +3674,15 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-5uaeznes.googlevideo.com.",
"rr1---sn-5uaeznez.googlevideo.com.",
"rr1---sn-5uaeznl6.googlevideo.com.",
+ "rr1---sn-5uaeznld.googlevideo.com.",
+ "rr1---sn-5uaeznls.googlevideo.com.",
"rr1---sn-5uaeznlz.googlevideo.com.",
"rr1---sn-5uaezns7.googlevideo.com.",
"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.",
"rr1---sn-5ualdnll.googlevideo.com.",
@@ -3703,11 +3700,9 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-5ualdnsz.googlevideo.com.",
"rr1---sn-5ualdnz7.googlevideo.com.",
"rr1---sn-5ualdnze.googlevideo.com.",
- "rr1---sn-8pxuuxa-nbo6s.googlevideo.com.",
"rr1---sn-8qj-nbo66.googlevideo.com.",
"rr1---sn-8qj-nbo6y.googlevideo.com.",
"rr1---sn-8xgp1vo-2iae7.googlevideo.com.",
- "rr1---sn-8xgp1vo-a5me.googlevideo.com.",
"rr1---sn-8xgp1vo-a5ml.googlevideo.com.",
"rr1---sn-8xgp1vo-ab56.googlevideo.com.",
"rr1---sn-8xgp1vo-ab5d.googlevideo.com.",
@@ -3716,10 +3711,13 @@ var FakeECSFQDNs = container.NewMapSet(
"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-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.",
@@ -3729,57 +3727,52 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-9gv7zn7r.googlevideo.com.",
"rr1---sn-9gv7zn7y.googlevideo.com.",
"rr1---sn-a5m7lnl6.googlevideo.com.",
- "rr1---sn-a5m7lnl6.gvt1.com.",
"rr1---sn-a5m7lnld.googlevideo.com.",
+ "rr1---sn-a5m7lnld.gvt1.com.",
"rr1---sn-a5mekn6d.googlevideo.com.",
- "rr1---sn-a5mekn6d.gvt1.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-a5meknd6.gvt1.com.",
"rr1---sn-a5meknde.googlevideo.com.",
- "rr1---sn-a5meknde.gvt1.com.",
"rr1---sn-a5mekndl.googlevideo.com.",
"rr1---sn-a5mekndl.gvt1.com.",
"rr1---sn-a5meknds.googlevideo.com.",
"rr1---sn-a5mekndz.googlevideo.com.",
- "rr1---sn-a5mekndz.gvt1.com.",
"rr1---sn-a5meknsd.googlevideo.com.",
"rr1---sn-a5meknsy.googlevideo.com.",
- "rr1---sn-a5meknzk.googlevideo.com.",
- "rr1---sn-a5meknzk.gvt1.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-a5mlrnl6.gvt1.com.",
"rr1---sn-a5mlrnll.googlevideo.com.",
"rr1---sn-a5mlrnls.googlevideo.com.",
- "rr1---sn-a5mlrnls.gvt1.com.",
"rr1---sn-a5mlrnlz.googlevideo.com.",
"rr1---sn-a5mlrnlz.gvt1.com.",
"rr1---sn-a5msen76.googlevideo.com.",
- "rr1---sn-a5msen76.gvt1.com.",
"rr1---sn-a5msen7l.googlevideo.com.",
"rr1---sn-a5msen7s.googlevideo.com.",
"rr1---sn-a5msen7z.googlevideo.com.",
"rr1---sn-a5msenek.googlevideo.com.",
- "rr1---sn-a5msenek.gvt1.com.",
"rr1---sn-a5msener.googlevideo.com.",
"rr1---sn-a5msenes.googlevideo.com.",
- "rr1---sn-a5msenes.gvt1.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.",
@@ -3789,33 +3782,29 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-ab5l6nrz.googlevideo.com.",
"rr1---sn-ab5sznld.googlevideo.com.",
"rr1---sn-ab5sznly.googlevideo.com.",
- "rr1---sn-ab5sznly.gvt1.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-ab5sznzr.googlevideo.com.",
- "rr1---sn-ab5sznzr.gvt1.com.",
"rr1---sn-ab5sznzs.googlevideo.com.",
"rr1---sn-ab5sznzy.googlevideo.com.",
"rr1---sn-ab5sznzz.googlevideo.com.",
"rr1---sn-aigl6n6s.googlevideo.com.",
"rr1---sn-aigl6ned.googlevideo.com.",
"rr1---sn-aigl6nek.googlevideo.com.",
- "rr1---sn-aigl6nek.gvt1.com.",
"rr1---sn-aigl6ner.googlevideo.com.",
"rr1---sn-aigl6ney.googlevideo.com.",
"rr1---sn-aigl6nl7.googlevideo.com.",
"rr1---sn-aigl6ns6.googlevideo.com.",
"rr1---sn-aigl6nsd.googlevideo.com.",
- "rr1---sn-aigl6nsd.gvt1.com.",
"rr1---sn-aigl6nsk.googlevideo.com.",
"rr1---sn-aigl6nsr.googlevideo.com.",
- "rr1---sn-aigl6nsr.gvt1.com.",
"rr1---sn-aigl6nz7.googlevideo.com.",
"rr1---sn-aigl6nze.googlevideo.com.",
- "rr1---sn-aigl6nze.gvt1.com.",
"rr1---sn-aigl6nzk.googlevideo.com.",
"rr1---sn-aigl6nzl.googlevideo.com.",
"rr1---sn-aigl6nzl.gvt1.com.",
@@ -3839,6 +3828,12 @@ var FakeECSFQDNs = container.NewMapSet(
"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-cvb7lne7.googlevideo.com.",
"rr1---sn-cvb7lnee.googlevideo.com.",
"rr1---sn-cvb7lnls.googlevideo.com.",
@@ -3849,7 +3844,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-gpuuxg-hxhl.googlevideo.com.",
"rr1---sn-gpuuxg-hxhs.googlevideo.com.",
"rr1---sn-gpuuxg-hxhz.googlevideo.com.",
- "rr1---sn-gpuuxg-hxhz.gvt1.com.",
"rr1---sn-hjoj-jaul.googlevideo.com.",
"rr1---sn-hjoj-poul.googlevideo.com.",
"rr1---sn-hoa7kn76.googlevideo.com.",
@@ -3857,29 +3851,39 @@ var FakeECSFQDNs = container.NewMapSet(
"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-hp57yns7.gvt1.com.",
"rr1---sn-hp57ynse.googlevideo.com.",
+ "rr1---sn-hp57ynse.gvt1.com.",
"rr1---sn-hp57ynsl.googlevideo.com.",
"rr1---sn-hp57ynss.googlevideo.com.",
- "rr1---sn-hp57ynss.gvt1.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-hxgpu-qufs.googlevideo.com.",
"rr1---sn-i3b7kn6s.googlevideo.com.",
"rr1---sn-i3b7knld.googlevideo.com.",
"rr1---sn-i3b7knlk.googlevideo.com.",
@@ -3897,19 +3901,21 @@ var FakeECSFQDNs = container.NewMapSet(
"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-2ias.googlevideo.com.",
"rr1---sn-jvhj5nu-nh4e.googlevideo.com.",
- "rr1---sn-jvhj5nu-nh4l.googlevideo.com.",
"rr1---sn-jvhj5nu-nh4s.googlevideo.com.",
- "rr1---sn-jvhj5nu-nh4z.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.",
"rr1---sn-n2uxaxjvh-j5xl.googlevideo.com.",
"rr1---sn-n2uxaxjvh-j5xl.gvt1.com.",
"rr1---sn-n2uxaxjvh-j5xs.googlevideo.com.",
@@ -3927,6 +3933,7 @@ 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.",
@@ -3938,10 +3945,8 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-npoe7nl6.googlevideo.com.",
"rr1---sn-npoe7nlz.googlevideo.com.",
"rr1---sn-npoe7ns6.googlevideo.com.",
- "rr1---sn-npoe7ns6.gvt1.com.",
"rr1---sn-npoe7ns7.googlevideo.com.",
"rr1---sn-npoe7nsd.googlevideo.com.",
- "rr1---sn-npoe7nsd.gvt1.com.",
"rr1---sn-npoe7nsk.googlevideo.com.",
"rr1---sn-npoe7nsl.googlevideo.com.",
"rr1---sn-npoe7nsr.googlevideo.com.",
@@ -3953,14 +3958,12 @@ 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.",
"rr1---sn-npoeenlk.googlevideo.com.",
"rr1---sn-npoeenll.googlevideo.com.",
"rr1---sn-npoeenly.googlevideo.com.",
- "rr1---sn-npoeenly.gvt1.com.",
"rr1---sn-npoeens7.googlevideo.com.",
"rr1---sn-npoldn76.googlevideo.com.",
"rr1---sn-npoldn7d.googlevideo.com.",
@@ -3971,35 +3974,26 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-npoldn7y.googlevideo.com.",
"rr1---sn-npoldn7z.googlevideo.com.",
"rr1---sn-npoldne7.googlevideo.com.",
- "rr1---sn-ntqe6nee.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-nx57ynsd.gvt1.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-nx57ynsz.gvt1.com.",
"rr1---sn-nx5s7n76.googlevideo.com.",
"rr1---sn-nx5s7n7d.googlevideo.com.",
- "rr1---sn-nx5s7n7s.googlevideo.com.",
"rr1---sn-nx5s7n7y.googlevideo.com.",
- "rr1---sn-nx5s7nee.googlevideo.com.",
"rr1---sn-nx5s7nel.googlevideo.com.",
"rr1---sn-o097znsd.googlevideo.com.",
"rr1---sn-o097znse.googlevideo.com.",
"rr1---sn-o097znsk.googlevideo.com.",
- "rr1---sn-o097znsk.gvt1.com.",
"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-o097znz7.gvt1.com.",
"rr1---sn-o097znzd.googlevideo.com.",
"rr1---sn-o097znze.googlevideo.com.",
"rr1---sn-o097znzk.googlevideo.com.",
@@ -4025,17 +4019,19 @@ 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-pjnpu-5hfe.googlevideo.com.",
- "rr1---sn-pjx-nwv6.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-q4fl6n6s.googlevideo.com.",
"rr1---sn-q4fl6n6s.gvt1.com.",
@@ -4044,65 +4040,64 @@ var FakeECSFQDNs = container.NewMapSet(
"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-q4fl6ndz.gvt1.com.",
"rr1---sn-q4fl6nlz.googlevideo.com.",
- "rr1---sn-q4fl6nlz.gvt1.com.",
"rr1---sn-q4fl6ns6.googlevideo.com.",
"rr1---sn-q4fl6ns6.gvt1.com.",
"rr1---sn-q4fl6ns7.googlevideo.com.",
- "rr1---sn-q4fl6ns7.gvt1.com.",
"rr1---sn-q4fl6nsd.googlevideo.com.",
- "rr1---sn-q4fl6nsd.gvt1.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-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-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-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-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-q4flrnss.gvt1.com.",
"rr1---sn-q4fzen7e.googlevideo.com.",
- "rr1---sn-q4fzen7e.gvt1.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-q4fzen7y.gvt1.com.",
"rr1---sn-q4fzene7.googlevideo.com.",
- "rr1---sn-q4fzene7.gvt1.com.",
"rr1---sn-q4fzenee.googlevideo.com.",
"rr1---sn-qjp5q5-55.googlevideo.com.",
"rr1---sn-qxo7rn7k.googlevideo.com.",
@@ -4111,37 +4106,37 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-qxoedn7k.googlevideo.com.",
"rr1---sn-qxoedne7.googlevideo.com.",
"rr1---sn-qxoednee.googlevideo.com.",
- "rr1---sn-u1hp55-5c.googlevideo.com.",
- "rr1---sn-uqnuxaxjvh-hnoe.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-vgqskn66.googlevideo.com.",
"rr1---sn-vgqskn67.googlevideo.com.",
- "rr1---sn-vgqskn67.gvt1.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.",
"rr1---sn-vgqsknek.googlevideo.com.",
- "rr1---sn-vgqsknek.gvt1.com.",
"rr1---sn-vgqsknes.googlevideo.com.",
"rr1---sn-vgqsknez.googlevideo.com.",
"rr1---sn-vgqsknld.googlevideo.com.",
"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-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-vgqsknzd.gvt1.com.",
"rr1---sn-vgqsknze.googlevideo.com.",
"rr1---sn-vgqsknzk.googlevideo.com.",
"rr1---sn-vgqsknzl.googlevideo.com.",
@@ -4149,23 +4144,23 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-vgqsknzs.googlevideo.com.",
"rr1---sn-vgqsknzy.googlevideo.com.",
"rr1---sn-vgqsknzz.googlevideo.com.",
- "rr1---sn-vgqsknzz.gvt1.com.",
"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-vgqsrne6.gvt1.com.",
"rr1---sn-vgqsrned.googlevideo.com.",
"rr1---sn-vgqsrnek.googlevideo.com.",
"rr1---sn-vgqsrnes.googlevideo.com.",
"rr1---sn-vgqsrnez.googlevideo.com.",
"rr1---sn-vgqsrnl6.googlevideo.com.",
- "rr1---sn-vgqsrnl6.gvt1.com.",
"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.",
@@ -4173,6 +4168,7 @@ 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.",
@@ -4184,21 +4180,21 @@ var FakeECSFQDNs = container.NewMapSet(
"rr1---sn-vnix5o-28ql.googlevideo.com.",
"rr1---sn-voxoxu-v3jl.googlevideo.com.",
"rr1---sn-voxoxu-v3js.googlevideo.com.",
- "rr1.sn-q4fl6nd7.googlevideo.com.",
- "rr1.sn-q4fl6ns7.googlevideo.com.",
- "rr1.sn-q4fl6nsd.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-q4flrnlz.googlevideo.com.",
+ "rr1.sn-q4flrnl7.googlevideo.com.",
"rr1.sn-q4flrnsl.googlevideo.com.",
- "rr1.sn-q4flrnss.googlevideo.com.",
"rr2---sn-0nnpbo5a-bggl.googlevideo.com.",
"rr2---sn-0op8v4h5pox-cbgl.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-2pmxapm0n-gpje.googlevideo.com.",
"rr2---sn-2pmxapm0n-gpjl.googlevideo.com.",
"rr2---sn-30a7rne6.googlevideo.com.",
"rr2---sn-30a7rner.googlevideo.com.",
@@ -4207,12 +4203,12 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-30a7yney.googlevideo.com.",
"rr2---sn-30a7ynl7.googlevideo.com.",
"rr2---sn-3jpm-hjpe.googlevideo.com.",
- "rr2---sn-3jpm-hjpe.gvt1.com.",
"rr2---sn-4g5e6ns6.googlevideo.com.",
"rr2---sn-4g5e6ns7.googlevideo.com.",
"rr2---sn-4g5e6nsd.googlevideo.com.",
"rr2---sn-4g5e6nsk.googlevideo.com.",
"rr2---sn-4g5e6nsr.googlevideo.com.",
+ "rr2---sn-4g5e6nss.googlevideo.com.",
"rr2---sn-4g5e6nsy.googlevideo.com.",
"rr2---sn-4g5e6nsz.googlevideo.com.",
"rr2---sn-4g5e6nz7.googlevideo.com.",
@@ -4258,15 +4254,12 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-4g5lznle.googlevideo.com.",
"rr2---sn-4g5lznls.googlevideo.com.",
"rr2---sn-4g5lznlz.googlevideo.com.",
- "rr2---sn-5abxgpxuxaxjvh-9n4l.googlevideo.com.",
- "rr2---sn-5abxgpxuxaxjvh-9n4s.googlevideo.com.",
- "rr2---sn-5abxgpxuxaxjvh-9n4z.googlevideo.com.",
+ "rr2---sn-4pgnuapbiu-hiul.googlevideo.com.",
"rr2---sn-5axnug5-hxm6.googlevideo.com.",
+ "rr2---sn-5gxo-in8l.googlevideo.com.",
"rr2---sn-5gxo-in8s.googlevideo.com.",
"rr2---sn-5hne6n6e.googlevideo.com.",
- "rr2---sn-5hne6n6e.gvt1.com.",
"rr2---sn-5hne6n6l.googlevideo.com.",
- "rr2---sn-5hne6n6l.gvt1.com.",
"rr2---sn-5hne6ns6.googlevideo.com.",
"rr2---sn-5hne6nsd.googlevideo.com.",
"rr2---sn-5hne6nsk.googlevideo.com.",
@@ -4277,19 +4270,20 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-5hne6nz6.gvt1.com.",
"rr2---sn-5hne6nzd.googlevideo.com.",
"rr2---sn-5hne6nzk.googlevideo.com.",
- "rr2---sn-5hne6nzs.googlevideo.com.",
+ "rr2---sn-5hne6nzk.gvt1.com.",
"rr2---sn-5hne6nzy.googlevideo.com.",
"rr2---sn-5hnednss.googlevideo.com.",
"rr2---sn-5hnednsz.googlevideo.com.",
- "rr2---sn-5hnednsz.gvt1.com.",
"rr2---sn-5hnekn76.googlevideo.com.",
"rr2---sn-5hnekn7d.googlevideo.com.",
"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.",
"rr2---sn-5uaezned.googlevideo.com.",
@@ -4298,7 +4292,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-5uaeznez.googlevideo.com.",
"rr2---sn-5uaeznl6.googlevideo.com.",
"rr2---sn-5uaeznld.googlevideo.com.",
- "rr2---sn-5uaeznlz.googlevideo.com.",
+ "rr2---sn-5uaeznls.googlevideo.com.",
"rr2---sn-5uaezns7.googlevideo.com.",
"rr2---sn-5uaeznse.googlevideo.com.",
"rr2---sn-5uaeznsl.googlevideo.com.",
@@ -4322,7 +4316,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-5ualdnsz.googlevideo.com.",
"rr2---sn-5ualdnz7.googlevideo.com.",
"rr2---sn-5ualdnze.googlevideo.com.",
- "rr2---sn-8pxuuxa-nbo6l.googlevideo.com.",
"rr2---sn-8qj-nbo66.googlevideo.com.",
"rr2---sn-8qj-nbo6y.googlevideo.com.",
"rr2---sn-8xgp1vo-2iae7.googlevideo.com.",
@@ -4334,11 +4327,13 @@ var FakeECSFQDNs = container.NewMapSet(
"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-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.",
@@ -4362,13 +4357,11 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-a5mekn6z.gvt1.com.",
"rr2---sn-a5meknd6.googlevideo.com.",
"rr2---sn-a5meknde.googlevideo.com.",
- "rr2---sn-a5meknde.gvt1.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-a5mekndz.gvt1.com.",
"rr2---sn-a5meknsd.googlevideo.com.",
"rr2---sn-a5meknsy.googlevideo.com.",
"rr2---sn-a5meknzk.googlevideo.com.",
@@ -4376,23 +4369,21 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
- "rr2---sn-a5mlrnl6.gvt1.com.",
"rr2---sn-a5mlrnll.googlevideo.com.",
"rr2---sn-a5mlrnls.googlevideo.com.",
"rr2---sn-a5mlrnlz.googlevideo.com.",
"rr2---sn-a5msen76.googlevideo.com.",
"rr2---sn-a5msen7l.googlevideo.com.",
- "rr2---sn-a5msen7l.gvt1.com.",
"rr2---sn-a5msen7s.googlevideo.com.",
"rr2---sn-a5msen7z.googlevideo.com.",
"rr2---sn-a5msenek.googlevideo.com.",
"rr2---sn-a5msenek.gvt1.com.",
"rr2---sn-a5msener.googlevideo.com.",
"rr2---sn-a5msenes.googlevideo.com.",
- "rr2---sn-a5msenes.gvt1.com.",
"rr2---sn-a5msenl7.googlevideo.com.",
- "rr2---sn-a5msenl7.gvt1.com.",
"rr2---sn-a5msenle.googlevideo.com.",
"rr2---sn-a5msenle.gvt1.com.",
"rr2---sn-a5msenll.googlevideo.com.",
@@ -4401,12 +4392,10 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-ab5l6nk6.googlevideo.com.",
"rr2---sn-ab5l6nkd.googlevideo.com.",
"rr2---sn-ab5l6nr6.googlevideo.com.",
- "rr2---sn-ab5l6nr6.gvt1.com.",
"rr2---sn-ab5l6nrd.googlevideo.com.",
"rr2---sn-ab5l6nrk.googlevideo.com.",
"rr2---sn-ab5l6nrl.googlevideo.com.",
"rr2---sn-ab5l6nrr.googlevideo.com.",
- "rr2---sn-ab5l6nrr.gvt1.com.",
"rr2---sn-ab5l6nrs.googlevideo.com.",
"rr2---sn-ab5l6nrz.googlevideo.com.",
"rr2---sn-ab5sznld.googlevideo.com.",
@@ -4414,24 +4403,25 @@ 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-ab5sznzl.gvt1.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-aigl6nsd.googlevideo.com.",
"rr2---sn-aigl6nsk.googlevideo.com.",
"rr2---sn-aigl6nsr.googlevideo.com.",
- "rr2---sn-aigl6nsr.gvt1.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.",
@@ -4455,6 +4445,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-cvb7lne7.googlevideo.com.",
"rr2---sn-cvb7lnee.googlevideo.com.",
"rr2---sn-cvb7lnls.googlevideo.com.",
@@ -4475,29 +4472,37 @@ var FakeECSFQDNs = container.NewMapSet(
"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-hp57kndk.gvt1.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-hp57ynsl.gvt1.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-hxgpu-qufs.googlevideo.com.",
"rr2---sn-i3b7kn6s.googlevideo.com.",
"rr2---sn-i3b7knld.googlevideo.com.",
@@ -4508,7 +4513,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-i3b7knzl.googlevideo.com.",
"rr2---sn-i3b7knzs.googlevideo.com.",
"rr2---sn-i3belne6.googlevideo.com.",
- "rr2---sn-i3belney.googlevideo.com.",
"rr2---sn-i3belnl6.googlevideo.com.",
"rr2---sn-i3belnl7.googlevideo.com.",
"rr2---sn-i3belnll.googlevideo.com.",
@@ -4517,19 +4521,19 @@ var FakeECSFQDNs = container.NewMapSet(
"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-nh4l.googlevideo.com.",
"rr2---sn-jvhj5nu-nh4s.googlevideo.com.",
- "rr2---sn-jvhj5nu-nh4z.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.",
@@ -4573,6 +4577,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-npoeener.googlevideo.com.",
"rr2---sn-npoeeney.googlevideo.com.",
"rr2---sn-npoeenez.googlevideo.com.",
+ "rr2---sn-npoeenl7.googlevideo.com.",
"rr2---sn-npoeenle.googlevideo.com.",
"rr2---sn-npoeenlk.googlevideo.com.",
"rr2---sn-npoeenll.googlevideo.com.",
@@ -4583,30 +4588,26 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-npoldn7e.googlevideo.com.",
"rr2---sn-npoldn7l.googlevideo.com.",
"rr2---sn-npoldn7s.googlevideo.com.",
- "rr2---sn-npoldn7s.gvt1.com.",
"rr2---sn-npoldn7y.googlevideo.com.",
"rr2---sn-npoldn7z.googlevideo.com.",
"rr2---sn-npoldne7.googlevideo.com.",
- "rr2---sn-ntq7yned.googlevideo.com.",
"rr2---sn-nuagpm-nuae.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-nx57ynsd.gvt1.com.",
"rr2---sn-nx57ynse.googlevideo.com.",
+ "rr2---sn-nx57ynse.gvt1.com.",
"rr2---sn-nx57ynsk.googlevideo.com.",
"rr2---sn-nx57ynsk.gvt1.com.",
"rr2---sn-nx57ynsl.googlevideo.com.",
"rr2---sn-nx57ynss.googlevideo.com.",
- "rr2---sn-nx57ynss.gvt1.com.",
"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.",
- "rr2---sn-nx5s7nee.gvt1.com.",
"rr2---sn-o097znsd.googlevideo.com.",
"rr2---sn-o097znse.googlevideo.com.",
"rr2---sn-o097znsk.googlevideo.com.",
@@ -4615,23 +4616,22 @@ 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.",
"rr2---sn-oj5hn5-55.googlevideo.com.",
+ "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.",
"rr2---sn-p5qlsn76.googlevideo.com.",
"rr2---sn-p5qlsn7d.googlevideo.com.",
- "rr2---sn-p5qlsn7d.gvt1.com.",
"rr2---sn-p5qlsn7l.googlevideo.com.",
"rr2---sn-p5qlsn7s.googlevideo.com.",
"rr2---sn-p5qlsnd6.googlevideo.com.",
+ "rr2---sn-p5qlsndk.googlevideo.com.",
"rr2---sn-p5qlsndr.googlevideo.com.",
"rr2---sn-p5qlsndz.googlevideo.com.",
"rr2---sn-p5qlsnrl.googlevideo.com.",
@@ -4654,10 +4654,9 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-q4fl6n6r.googlevideo.com.",
"rr2---sn-q4fl6n6r.gvt1.com.",
"rr2---sn-q4fl6n6s.googlevideo.com.",
- "rr2---sn-q4fl6n6s.gvt1.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.",
@@ -4666,65 +4665,62 @@ var FakeECSFQDNs = container.NewMapSet(
"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-q4fl6nsl.gvt1.com.",
"rr2---sn-q4fl6nsr.googlevideo.com.",
- "rr2---sn-q4fl6nsr.gvt1.com.",
"rr2---sn-q4fl6nss.googlevideo.com.",
"rr2---sn-q4fl6nsy.googlevideo.com.",
"rr2---sn-q4fl6nz6.googlevideo.com.",
"rr2---sn-q4fl6nz6.gvt1.com.",
"rr2---sn-q4fl6nz7.googlevideo.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-q4flrne7.googlevideo.com.",
- "rr2---sn-q4flrne7.gvt1.com.",
"rr2---sn-q4flrnee.googlevideo.com.",
"rr2---sn-q4flrnee.gvt1.com.",
"rr2---sn-q4flrnek.googlevideo.com.",
- "rr2---sn-q4flrnek.gvt1.com.",
"rr2---sn-q4flrnel.googlevideo.com.",
"rr2---sn-q4flrnel.gvt1.com.",
"rr2---sn-q4flrner.googlevideo.com.",
- "rr2---sn-q4flrner.gvt1.com.",
"rr2---sn-q4flrnes.googlevideo.com.",
- "rr2---sn-q4flrnes.gvt1.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-q4flrnld.gvt1.com.",
"rr2---sn-q4flrnle.googlevideo.com.",
"rr2---sn-q4flrnlz.googlevideo.com.",
"rr2---sn-q4flrnsd.googlevideo.com.",
- "rr2---sn-q4flrnsd.gvt1.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-q4fzen7l.googlevideo.com.",
- "rr2---sn-q4fzen7l.gvt1.com.",
"rr2---sn-q4fzen7r.googlevideo.com.",
"rr2---sn-q4fzen7r.gvt1.com.",
"rr2---sn-q4fzen7s.googlevideo.com.",
- "rr2---sn-q4fzen7s.gvt1.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-q4fzenee.gvt1.com.",
"rr2---sn-qjp5q5-55.googlevideo.com.",
"rr2---sn-qxo7rn7k.googlevideo.com.",
"rr2---sn-qxo7rn7r.googlevideo.com.",
@@ -4733,16 +4729,20 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-qxoedne7.googlevideo.com.",
"rr2---sn-qxoednee.googlevideo.com.",
"rr2---sn-u1hp55-5c.googlevideo.com.",
- "rr2---sn-uqnuxaxjvh-hnoe.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-vgqskn66.googlevideo.com.",
"rr2---sn-vgqskn67.googlevideo.com.",
- "rr2---sn-vgqskn67.gvt1.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.",
@@ -4754,46 +4754,51 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-vgqsknlr.googlevideo.com.",
"rr2---sn-vgqsknls.googlevideo.com.",
"rr2---sn-vgqsknly.googlevideo.com.",
- "rr2---sn-vgqsknly.gvt1.com.",
"rr2---sn-vgqsknlz.googlevideo.com.",
"rr2---sn-vgqskns7.googlevideo.com.",
- "rr2---sn-vgqsknsk.googlevideo.com.",
+ "rr2---sn-vgqsknse.googlevideo.com.",
"rr2---sn-vgqsknz6.googlevideo.com.",
+ "rr2---sn-vgqsknz6.gvt1.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-vgqsknzr.gvt1.com.",
"rr2---sn-vgqsknzs.googlevideo.com.",
"rr2---sn-vgqsknzy.googlevideo.com.",
- "rr2---sn-vgqsknzz.googlevideo.com.",
+ "rr2---sn-vgqsknzy.gvt1.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-vgqsrn6z.gvt1.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-vgqsrnz7.gvt1.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.",
@@ -4803,16 +4808,21 @@ var FakeECSFQDNs = container.NewMapSet(
"rr2---sn-vnix5o-28ql.googlevideo.com.",
"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-hp57knds.googlevideo.com.",
- "rr2.sn-ntq7yned.googlevideo.com.",
- "rr2.sn-q4fl6nsk.googlevideo.com.",
- "rr2.sn-q4fl6nsr.googlevideo.com.",
+ "rr2.sn-q4flrnek.googlevideo.com.",
+ "rr2.sn-q4flrnsk.googlevideo.com.",
"rr3---sn-0nnpbo5a-bggl.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-30a7rne6.googlevideo.com.",
"rr3---sn-30a7rnek.googlevideo.com.",
"rr3---sn-30a7rner.googlevideo.com.",
@@ -4821,7 +4831,6 @@ 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.",
@@ -4841,6 +4850,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-4g5ednd7.googlevideo.com.",
"rr3---sn-4g5edndd.googlevideo.com.",
"rr3---sn-4g5ednde.googlevideo.com.",
+ "rr3---sn-4g5edndk.googlevideo.com.",
"rr3---sn-4g5edndl.googlevideo.com.",
"rr3---sn-4g5edndr.googlevideo.com.",
"rr3---sn-4g5ednds.googlevideo.com.",
@@ -4857,6 +4867,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-4g5ednsl.googlevideo.com.",
"rr3---sn-4g5ednsr.googlevideo.com.",
"rr3---sn-4g5ednss.googlevideo.com.",
+ "rr3---sn-4g5ednsy.googlevideo.com.",
"rr3---sn-4g5ednsz.googlevideo.com.",
"rr3---sn-4g5ednz7.googlevideo.com.",
"rr3---sn-4g5lzne6.googlevideo.com.",
@@ -4871,9 +4882,6 @@ 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-5hne6n6e.googlevideo.com.",
"rr3---sn-5hne6n6l.googlevideo.com.",
"rr3---sn-5hne6ns6.googlevideo.com.",
@@ -4897,14 +4905,15 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-5hneknee.googlevideo.com.",
"rr3---sn-5hneknek.googlevideo.com.",
"rr3---sn-5hneknes.googlevideo.com.",
+ "rr3---sn-5pgnugx5h-hn2z.googlevideo.com.",
"rr3---sn-5uaezndd.googlevideo.com.",
"rr3---sn-5uaezne6.googlevideo.com.",
"rr3---sn-5uaezned.googlevideo.com.",
"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.",
"rr3---sn-5uaezns7.googlevideo.com.",
"rr3---sn-5uaeznse.googlevideo.com.",
@@ -4931,7 +4940,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-5ualdnze.googlevideo.com.",
"rr3---sn-8qj-nbo66.googlevideo.com.",
"rr3---sn-8xgp1vo-2iae7.googlevideo.com.",
- "rr3---sn-8xgp1vo-a5me.googlevideo.com.",
"rr3---sn-8xgp1vo-ab56.googlevideo.com.",
"rr3---sn-8xgp1vo-ab5d.googlevideo.com.",
"rr3---sn-8xgp1vo-ab5e.googlevideo.com.",
@@ -4944,6 +4952,7 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
@@ -4956,10 +4965,9 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-a5m7lnld.googlevideo.com.",
"rr3---sn-a5mekn6d.googlevideo.com.",
"rr3---sn-a5mekn6d.gvt1.com.",
- "rr3---sn-a5mekn6k.googlevideo.com.",
- "rr3---sn-a5mekn6k.gvt1.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.",
@@ -4967,7 +4975,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-a5meknd6.gvt1.com.",
"rr3---sn-a5meknde.googlevideo.com.",
"rr3---sn-a5mekndl.googlevideo.com.",
- "rr3---sn-a5mekndl.gvt1.com.",
"rr3---sn-a5meknds.googlevideo.com.",
"rr3---sn-a5mekndz.googlevideo.com.",
"rr3---sn-a5mekndz.gvt1.com.",
@@ -4976,38 +4983,40 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-a5meknzk.googlevideo.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-a5msen76.gvt1.com.",
"rr3---sn-a5msen7l.googlevideo.com.",
"rr3---sn-a5msen7s.googlevideo.com.",
- "rr3---sn-a5msen7s.gvt1.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-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-ab5l6nk6.gvt1.com.",
"rr3---sn-ab5l6nkd.googlevideo.com.",
- "rr3---sn-ab5l6nkd.gvt1.com.",
"rr3---sn-ab5l6nr6.googlevideo.com.",
+ "rr3---sn-ab5l6nr6.gvt1.com.",
"rr3---sn-ab5l6nrd.googlevideo.com.",
- "rr3---sn-ab5l6nrd.gvt1.com.",
"rr3---sn-ab5l6nrk.googlevideo.com.",
"rr3---sn-ab5l6nrl.googlevideo.com.",
- "rr3---sn-ab5l6nrl.gvt1.com.",
"rr3---sn-ab5l6nrr.googlevideo.com.",
"rr3---sn-ab5l6nrs.googlevideo.com.",
"rr3---sn-ab5l6nrz.googlevideo.com.",
@@ -5015,6 +5024,7 @@ 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.",
@@ -5036,7 +5046,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-aigl6nze.googlevideo.com.",
"rr3---sn-aigl6nze.gvt1.com.",
"rr3---sn-aigl6nzk.googlevideo.com.",
- "rr3---sn-aigl6nzr.googlevideo.com.",
+ "rr3---sn-aigl6nzl.googlevideo.com.",
"rr3---sn-aigl6nzs.googlevideo.com.",
"rr3---sn-aigzrn76.googlevideo.com.",
"rr3---sn-aigzrn7d.googlevideo.com.",
@@ -5053,6 +5063,12 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-aigzrnsz.googlevideo.com.",
"rr3---sn-aigzrnz7.googlevideo.com.",
"rr3---sn-aigzrnze.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-cvb7lne7.googlevideo.com.",
"rr3---sn-cvb7lnlz.googlevideo.com.",
"rr3---sn-cvb7sn7r.googlevideo.com.",
@@ -5069,27 +5085,35 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-hp57kn6y.googlevideo.com.",
"rr3---sn-hp57kn6y.gvt1.com.",
"rr3---sn-hp57knd6.googlevideo.com.",
- "rr3---sn-hp57knd6.gvt1.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-hp57ynly.gvt1.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.",
@@ -5100,19 +5124,20 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
"rr3---sn-i3belnls.googlevideo.com.",
"rr3---sn-i3belnlz.googlevideo.com.",
"rr3---sn-i3bssn7e.googlevideo.com.",
"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-nh4l.googlevideo.com.",
"rr3---sn-jvhj5nu-nh4s.googlevideo.com.",
- "rr3---sn-jvhj5nu-nh4z.googlevideo.com.",
"rr3---sn-jvhj5nu-qufe.googlevideo.com.",
"rr3---sn-jvhj5nu-qufl.googlevideo.com.",
"rr3---sn-jvhj5nu-qufs.googlevideo.com.",
@@ -5127,7 +5152,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-n4v7snlr.googlevideo.com.",
"rr3---sn-n4v7snls.googlevideo.com.",
"rr3---sn-n4v7snly.googlevideo.com.",
- "rr3---sn-n4v7snly.gvt1.com.",
"rr3---sn-n4v7sns7.googlevideo.com.",
"rr3---sn-n4v7snse.googlevideo.com.",
"rr3---sn-npoe7ndl.googlevideo.com.",
@@ -5144,6 +5168,7 @@ 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.",
@@ -5174,20 +5199,18 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-npoldne7.googlevideo.com.",
"rr3---sn-nx57ynlk.googlevideo.com.",
"rr3---sn-nx57ynsd.googlevideo.com.",
- "rr3---sn-nx57ynsd.gvt1.com.",
+ "rr3---sn-nx57ynse.googlevideo.com.",
"rr3---sn-nx57ynsk.googlevideo.com.",
"rr3---sn-nx57ynsl.googlevideo.com.",
"rr3---sn-nx57ynss.googlevideo.com.",
- "rr3---sn-nx57ynss.gvt1.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-o097znsd.googlevideo.com.",
+ "rr3---sn-nx5s7nel.gvt1.com.",
"rr3---sn-o097znse.googlevideo.com.",
"rr3---sn-o097znsk.googlevideo.com.",
"rr3---sn-o097znsl.googlevideo.com.",
@@ -5208,6 +5231,7 @@ 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.",
@@ -5220,7 +5244,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-p5qs7n6d.googlevideo.com.",
"rr3---sn-p5qs7nsk.googlevideo.com.",
"rr3---sn-p5qs7nsr.googlevideo.com.",
- "rr3---sn-p5qs7nsr.gvt1.com.",
"rr3---sn-p5qs7nzk.googlevideo.com.",
"rr3---sn-p5qs7nzr.googlevideo.com.",
"rr3---sn-p5qs7nzy.googlevideo.com.",
@@ -5228,71 +5251,78 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-q4fl6n66.googlevideo.com.",
"rr3---sn-q4fl6n66.gvt1.com.",
"rr3---sn-q4fl6n6d.googlevideo.com.",
- "rr3---sn-q4fl6n6d.gvt1.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-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-q4fl6nsd.gvt1.com.",
"rr3---sn-q4fl6nsk.googlevideo.com.",
- "rr3---sn-q4fl6nsk.gvt1.com.",
"rr3---sn-q4fl6nsl.googlevideo.com.",
"rr3---sn-q4fl6nsr.googlevideo.com.",
+ "rr3---sn-q4fl6nsr.gvt1.com.",
"rr3---sn-q4fl6nss.googlevideo.com.",
"rr3---sn-q4fl6nsy.googlevideo.com.",
- "rr3---sn-q4fl6nsy.gvt1.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-q4flrn7k.gvt1.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.",
"rr3---sn-q4flrnee.googlevideo.com.",
"rr3---sn-q4flrnek.googlevideo.com.",
- "rr3---sn-q4flrnek.gvt1.com.",
"rr3---sn-q4flrnel.googlevideo.com.",
"rr3---sn-q4flrner.googlevideo.com.",
"rr3---sn-q4flrnes.googlevideo.com.",
"rr3---sn-q4flrney.googlevideo.com.",
- "rr3---sn-q4flrney.gvt1.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-q4flrnl7.gvt1.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.",
"rr3---sn-q4flrnsd.googlevideo.com.",
- "rr3---sn-q4flrnsd.gvt1.com.",
"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-q4fzen7l.googlevideo.com.",
- "rr3---sn-q4fzen7l.gvt1.com.",
"rr3---sn-q4fzen7r.googlevideo.com.",
+ "rr3---sn-q4fzen7r.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.",
"rr3---sn-qxo7rn7k.googlevideo.com.",
"rr3---sn-qxo7rn7r.googlevideo.com.",
@@ -5301,17 +5331,15 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
"rr3---sn-vgqskn6d.googlevideo.com.",
- "rr3---sn-vgqskn6d.gvt1.com.",
"rr3---sn-vgqskn6s.googlevideo.com.",
- "rr3---sn-vgqskn6s.gvt1.com.",
"rr3---sn-vgqskn6z.googlevideo.com.",
"rr3---sn-vgqskne6.googlevideo.com.",
"rr3---sn-vgqskned.googlevideo.com.",
- "rr3---sn-vgqskned.gvt1.com.",
"rr3---sn-vgqsknek.googlevideo.com.",
"rr3---sn-vgqsknes.googlevideo.com.",
"rr3---sn-vgqsknez.googlevideo.com.",
@@ -5319,14 +5347,15 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-vgqsknlk.googlevideo.com.",
"rr3---sn-vgqsknll.googlevideo.com.",
"rr3---sn-vgqsknlr.googlevideo.com.",
- "rr3---sn-vgqsknly.googlevideo.com.",
+ "rr3---sn-vgqsknlr.gvt1.com.",
"rr3---sn-vgqsknlz.googlevideo.com.",
"rr3---sn-vgqskns7.googlevideo.com.",
+ "rr3---sn-vgqskns7.gvt1.com.",
"rr3---sn-vgqsknse.googlevideo.com.",
- "rr3---sn-vgqsknse.gvt1.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.",
@@ -5338,25 +5367,27 @@ var FakeECSFQDNs = container.NewMapSet(
"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-vgqsrn6z.gvt1.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.",
@@ -5367,22 +5398,25 @@ var FakeECSFQDNs = container.NewMapSet(
"rr3---sn-vgqsrnzs.googlevideo.com.",
"rr3---sn-vgqsrnzy.googlevideo.com.",
"rr3---sn-vgqsrnzz.googlevideo.com.",
- "rr3.sn-5hnednss.googlevideo.com.",
- "rr3.sn-5hneknee.googlevideo.com.",
- "rr3.sn-q4fl6nd7.googlevideo.com.",
- "rr3.sn-q4fl6nsy.googlevideo.com.",
- "rr3.sn-q4flrn7k.googlevideo.com.",
- "rr3.sn-q4fzen7l.googlevideo.com.",
- "rr3.sn-q4fzenee.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.",
"rr4---sn-0nnpbo5a-bggl.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-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.",
@@ -5435,10 +5469,6 @@ 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-5hne6n6e.googlevideo.com.",
"rr4---sn-5hne6n6l.googlevideo.com.",
"rr4---sn-5hne6ns6.googlevideo.com.",
@@ -5448,8 +5478,8 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-5hne6nsy.googlevideo.com.",
"rr4---sn-5hne6nsz.googlevideo.com.",
"rr4---sn-5hne6nz6.googlevideo.com.",
- "rr4---sn-5hne6nz6.gvt1.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.",
@@ -5462,7 +5492,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.",
"rr4---sn-5uaezned.googlevideo.com.",
@@ -5471,6 +5501,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-5uaeznez.googlevideo.com.",
"rr4---sn-5uaeznl6.googlevideo.com.",
"rr4---sn-5uaeznld.googlevideo.com.",
+ "rr4---sn-5uaeznls.googlevideo.com.",
"rr4---sn-5uaeznlz.googlevideo.com.",
"rr4---sn-5uaezns7.googlevideo.com.",
"rr4---sn-5uaeznse.googlevideo.com.",
@@ -5495,7 +5526,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-5ualdnsz.googlevideo.com.",
"rr4---sn-5ualdnz7.googlevideo.com.",
"rr4---sn-5ualdnze.googlevideo.com.",
- "rr4---sn-8pxuuxa-nbo6s.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.",
@@ -5507,6 +5538,7 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
@@ -5516,70 +5548,79 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-9gv7zn7r.googlevideo.com.",
"rr4---sn-9gv7zn7y.googlevideo.com.",
"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-a5meknd6.gvt1.com.",
"rr4---sn-a5meknde.googlevideo.com.",
"rr4---sn-a5meknde.gvt1.com.",
"rr4---sn-a5mekndl.googlevideo.com.",
"rr4---sn-a5meknds.googlevideo.com.",
- "rr4---sn-a5meknds.gvt1.com.",
"rr4---sn-a5mekndz.googlevideo.com.",
"rr4---sn-a5meknsd.googlevideo.com.",
"rr4---sn-a5meknsy.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-a5msen7l.gvt1.com.",
"rr4---sn-a5msen7s.googlevideo.com.",
"rr4---sn-a5msen7z.googlevideo.com.",
- "rr4---sn-a5msenek.googlevideo.com.",
- "rr4---sn-a5msenek.gvt1.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-a5msenll.gvt1.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-ab5l6nrs.googlevideo.com.",
- "rr4---sn-ab5l6nrs.gvt1.com.",
"rr4---sn-ab5l6nrz.googlevideo.com.",
- "rr4---sn-ab5l6nrz.gvt1.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-aigl6nek.googlevideo.com.",
"rr4---sn-aigl6ner.googlevideo.com.",
@@ -5588,6 +5629,7 @@ 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.",
@@ -5610,9 +5652,14 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-aigzrnsz.googlevideo.com.",
"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-cvb7lne7.googlevideo.com.",
"rr4---sn-cvb7lnee.googlevideo.com.",
- "rr4---sn-cvb7lnls.googlevideo.com.",
"rr4---sn-cvb7lnlz.googlevideo.com.",
"rr4---sn-cvb7sn7k.googlevideo.com.",
"rr4---sn-cvb7sn7r.googlevideo.com.",
@@ -5622,9 +5669,10 @@ var FakeECSFQDNs = container.NewMapSet(
"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-hp57kn6y.gvt1.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.",
@@ -5633,19 +5681,22 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
@@ -5656,10 +5707,10 @@ 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-i3belnls.googlevideo.com.",
"rr4---sn-i3belnlz.googlevideo.com.",
"rr4---sn-i3bssn7e.googlevideo.com.",
"rr4---sn-jn2pgx4pcxg-w5os.googlevideo.com.",
@@ -5667,10 +5718,10 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-jvhj5nu-2ial.googlevideo.com.",
"rr4---sn-jvhj5nu-2ias.googlevideo.com.",
"rr4---sn-jvhj5nu-nh4e.googlevideo.com.",
- "rr4---sn-jvhj5nu-nh4l.googlevideo.com.",
"rr4---sn-jvhj5nu-nh4s.googlevideo.com.",
- "rr4---sn-jvhj5nu-nh4z.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.",
@@ -5684,8 +5735,8 @@ 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-npoe7ndl.gvt1.com.",
"rr4---sn-npoe7nds.googlevideo.com.",
"rr4---sn-npoe7ne7.googlevideo.com.",
"rr4---sn-npoe7ned.googlevideo.com.",
@@ -5697,8 +5748,8 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-npoe7nl6.googlevideo.com.",
"rr4---sn-npoe7nlz.googlevideo.com.",
"rr4---sn-npoe7ns6.googlevideo.com.",
- "rr4---sn-npoe7ns6.gvt1.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.",
@@ -5719,7 +5770,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-npoeenll.googlevideo.com.",
"rr4---sn-npoeenly.googlevideo.com.",
"rr4---sn-npoeens7.googlevideo.com.",
- "rr4---sn-npoeens7.gvt1.com.",
"rr4---sn-npoldn76.googlevideo.com.",
"rr4---sn-npoldn7d.googlevideo.com.",
"rr4---sn-npoldn7e.googlevideo.com.",
@@ -5728,14 +5778,15 @@ 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-nx57ynlk.googlevideo.com.",
"rr4---sn-nx57ynlk.gvt1.com.",
"rr4---sn-nx57ynsd.googlevideo.com.",
- "rr4---sn-nx57ynsd.gvt1.com.",
"rr4---sn-nx57ynse.googlevideo.com.",
"rr4---sn-nx57ynsk.googlevideo.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.",
@@ -5743,8 +5794,8 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
@@ -5761,16 +5812,20 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-p5qddn7d.googlevideo.com.",
"rr4---sn-p5qddn7k.googlevideo.com.",
"rr4---sn-p5qddn7r.googlevideo.com.",
+ "rr4---sn-p5qddn7z.googlevideo.com.",
"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.",
"rr4---sn-p5qlsndk.googlevideo.com.",
+ "rr4---sn-p5qlsndr.googlevideo.com.",
"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.",
@@ -5779,17 +5834,21 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-p5qs7nzr.googlevideo.com.",
"rr4---sn-p5qs7nzy.googlevideo.com.",
"rr4---sn-q4fl6n66.googlevideo.com.",
- "rr4---sn-q4fl6n66.gvt1.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.",
@@ -5804,32 +5863,35 @@ var FakeECSFQDNs = container.NewMapSet(
"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-q4fl6nzy.gvt1.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-q4flrne7.gvt1.com.",
"rr4---sn-q4flrnee.googlevideo.com.",
- "rr4---sn-q4flrnee.gvt1.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-q4flrnld.googlevideo.com.",
+ "rr4---sn-q4flrnld.gvt1.com.",
"rr4---sn-q4flrnle.googlevideo.com.",
- "rr4---sn-q4flrnlz.googlevideo.com.",
"rr4---sn-q4flrnsd.googlevideo.com.",
"rr4---sn-q4flrnsk.googlevideo.com.",
"rr4---sn-q4flrnsl.googlevideo.com.",
@@ -5840,12 +5902,14 @@ var FakeECSFQDNs = container.NewMapSet(
"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-q4fzen7y.googlevideo.com.",
+ "rr4---sn-q4fzen7y.gvt1.com.",
"rr4---sn-q4fzene7.googlevideo.com.",
- "rr4---sn-q4fzene7.gvt1.com.",
"rr4---sn-q4fzenee.googlevideo.com.",
"rr4---sn-q4fzenee.gvt1.com.",
+ "rr4---sn-qjp5q5-55.googlevideo.com.",
"rr4---sn-qxo7rn7k.googlevideo.com.",
"rr4---sn-qxo7rn7r.googlevideo.com.",
"rr4---sn-qxo7rn7y.googlevideo.com.",
@@ -5858,7 +5922,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-vgqskn66.googlevideo.com.",
"rr4---sn-vgqskn66.gvt1.com.",
"rr4---sn-vgqskn67.googlevideo.com.",
- "rr4---sn-vgqskn67.gvt1.com.",
"rr4---sn-vgqskn6d.googlevideo.com.",
"rr4---sn-vgqskn6d.gvt1.com.",
"rr4---sn-vgqskn6s.googlevideo.com.",
@@ -5870,74 +5933,76 @@ var FakeECSFQDNs = container.NewMapSet(
"rr4---sn-vgqsknez.googlevideo.com.",
"rr4---sn-vgqsknld.googlevideo.com.",
"rr4---sn-vgqsknlk.googlevideo.com.",
- "rr4---sn-vgqsknlk.gvt1.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-vgqskns7.gvt1.com.",
"rr4---sn-vgqsknse.googlevideo.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-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-vgqsrn6z.gvt1.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-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-vgqsrnzy.gvt1.com.",
"rr4---sn-vgqsrnzz.googlevideo.com.",
- "rr4.sn-5hne6n6l.googlevideo.com.",
- "rr4.sn-5hne6nsd.googlevideo.com.",
- "rr4.sn-q4fl6n66.googlevideo.com.",
- "rr4.sn-q4fl6ndl.googlevideo.com.",
- "rr4.sn-q4fl6nsk.googlevideo.com.",
- "rr4.sn-q4flrner.googlevideo.com.",
- "rr4.sn-q4flrnes.googlevideo.com.",
- "rr4.sn-q4flrnld.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.",
"rr5---sn-0nnpbo5a-bggl.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-30a7yner.googlevideo.com.",
"rr5---sn-30a7yney.googlevideo.com.",
"rr5---sn-30a7ynl7.googlevideo.com.",
"rr5---sn-4g5e6ns6.googlevideo.com.",
@@ -5949,6 +6014,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-4g5e6nsy.googlevideo.com.",
"rr5---sn-4g5e6nsz.googlevideo.com.",
"rr5---sn-4g5e6nz7.googlevideo.com.",
+ "rr5---sn-4g5e6nze.googlevideo.com.",
"rr5---sn-4g5e6nzl.googlevideo.com.",
"rr5---sn-4g5e6nzs.googlevideo.com.",
"rr5---sn-4g5e6nzz.googlevideo.com.",
@@ -5960,11 +6026,9 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-4g5ednde.googlevideo.com.",
"rr5---sn-4g5edndk.googlevideo.com.",
"rr5---sn-4g5edndl.googlevideo.com.",
- "rr5---sn-4g5edndl.gvt1.com.",
"rr5---sn-4g5edndr.googlevideo.com.",
"rr5---sn-4g5ednds.googlevideo.com.",
"rr5---sn-4g5edndy.googlevideo.com.",
- "rr5---sn-4g5edndy.gvt1.com.",
"rr5---sn-4g5edndz.googlevideo.com.",
"rr5---sn-4g5ednkl.googlevideo.com.",
"rr5---sn-4g5ednld.googlevideo.com.",
@@ -5992,10 +6056,7 @@ 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-5hne6n6e.googlevideo.com.",
- "rr5---sn-5hne6n6e.gvt1.com.",
"rr5---sn-5hne6n6l.googlevideo.com.",
"rr5---sn-5hne6ns6.googlevideo.com.",
"rr5---sn-5hne6nsk.googlevideo.com.",
@@ -6003,9 +6064,11 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
@@ -6016,16 +6079,17 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-5hnekn7z.googlevideo.com.",
"rr5---sn-5hneknee.googlevideo.com.",
"rr5---sn-5hneknek.googlevideo.com.",
- "rr5---sn-5hneknek.gvt1.com.",
"rr5---sn-5hneknes.googlevideo.com.",
- "rr5---sn-5hneknes.gvt1.com.",
+ "rr5---sn-5pgnugx5h-hn2z.googlevideo.com.",
"rr5---sn-5uaezndd.googlevideo.com.",
"rr5---sn-5uaezne6.googlevideo.com.",
"rr5---sn-5uaezned.googlevideo.com.",
+ "rr5---sn-5uaeznel.googlevideo.com.",
"rr5---sn-5uaeznes.googlevideo.com.",
"rr5---sn-5uaeznez.googlevideo.com.",
"rr5---sn-5uaeznl6.googlevideo.com.",
"rr5---sn-5uaeznld.googlevideo.com.",
+ "rr5---sn-5uaeznls.googlevideo.com.",
"rr5---sn-5uaeznlz.googlevideo.com.",
"rr5---sn-5uaezns7.googlevideo.com.",
"rr5---sn-5uaeznse.googlevideo.com.",
@@ -6050,7 +6114,6 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-5ualdnsz.googlevideo.com.",
"rr5---sn-5ualdnz7.googlevideo.com.",
"rr5---sn-5ualdnze.googlevideo.com.",
- "rr5---sn-8pxuuxa-nbo6l.googlevideo.com.",
"rr5---sn-8qj-nbo66.googlevideo.com.",
"rr5---sn-8xgp1vo-2iae7.googlevideo.com.",
"rr5---sn-8xgp1vo-a5me.googlevideo.com.",
@@ -6063,11 +6126,12 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-8xgp1vo-vgqe.googlevideo.com.",
"rr5---sn-8xgp1vo-xfge.googlevideo.com.",
"rr5---sn-8xgp1vo-xfgl.googlevideo.com.",
- "rr5---sn-9gv76n7e.googlevideo.com.",
+ "rr5---sn-9gv76n7l.googlevideo.com.",
"rr5---sn-9gv76n7s.googlevideo.com.",
"rr5---sn-9gv76n7z.googlevideo.com.",
"rr5---sn-9gv7ene6.googlevideo.com.",
"rr5---sn-9gv7ened.googlevideo.com.",
+ "rr5---sn-9gv7zn76.googlevideo.com.",
"rr5---sn-9gv7zn7e.googlevideo.com.",
"rr5---sn-9gv7zn7r.googlevideo.com.",
"rr5---sn-9gv7zn7y.googlevideo.com.",
@@ -6077,32 +6141,32 @@ var FakeECSFQDNs = container.NewMapSet(
"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-a5mekndl.gvt1.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-a5meknsy.gvt1.com.",
"rr5---sn-a5meknzk.googlevideo.com.",
+ "rr5---sn-a5meknzk.gvt1.com.",
"rr5---sn-a5meknzr.googlevideo.com.",
"rr5---sn-a5meknzs.googlevideo.com.",
"rr5---sn-a5meknzs.gvt1.com.",
"rr5---sn-a5mlrnek.googlevideo.com.",
- "rr5---sn-a5mlrnek.gvt1.com.",
"rr5---sn-a5mlrnl6.googlevideo.com.",
"rr5---sn-a5mlrnl6.gvt1.com.",
"rr5---sn-a5mlrnll.googlevideo.com.",
- "rr5---sn-a5mlrnll.gvt1.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.",
@@ -6119,37 +6183,34 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-ab5l6nk6.googlevideo.com.",
"rr5---sn-ab5l6nkd.googlevideo.com.",
"rr5---sn-ab5l6nr6.googlevideo.com.",
- "rr5---sn-ab5l6nr6.gvt1.com.",
"rr5---sn-ab5l6nrd.googlevideo.com.",
"rr5---sn-ab5l6nrk.googlevideo.com.",
- "rr5---sn-ab5l6nrk.gvt1.com.",
"rr5---sn-ab5l6nrl.googlevideo.com.",
"rr5---sn-ab5l6nrr.googlevideo.com.",
+ "rr5---sn-ab5l6nrr.gvt1.com.",
"rr5---sn-ab5l6nrs.googlevideo.com.",
- "rr5---sn-ab5l6nrs.gvt1.com.",
"rr5---sn-ab5l6nrz.googlevideo.com.",
- "rr5---sn-ab5l6nrz.gvt1.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-ab5sznzk.gvt1.com.",
"rr5---sn-ab5sznzl.googlevideo.com.",
"rr5---sn-ab5sznzr.googlevideo.com.",
- "rr5---sn-ab5sznzr.gvt1.com.",
"rr5---sn-ab5sznzs.googlevideo.com.",
"rr5---sn-ab5sznzy.googlevideo.com.",
- "rr5---sn-ab5sznzy.gvt1.com.",
"rr5---sn-ab5sznzz.googlevideo.com.",
"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-aigl6ner.gvt1.com.",
+ "rr5---sn-aigl6ney.googlevideo.com.",
"rr5---sn-aigl6nl7.googlevideo.com.",
+ "rr5---sn-aigl6ns6.googlevideo.com.",
"rr5---sn-aigl6nsd.googlevideo.com.",
"rr5---sn-aigl6nsk.googlevideo.com.",
"rr5---sn-aigl6nsr.googlevideo.com.",
@@ -6159,6 +6220,7 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
@@ -6176,36 +6238,47 @@ 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-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-hp57ynse.googlevideo.com.",
- "rr5---sn-hp57ynse.gvt1.com.",
"rr5---sn-hp57ynsl.googlevideo.com.",
"rr5---sn-hp57ynss.googlevideo.com.",
"rr5---sn-i3b7kn6s.googlevideo.com.",
@@ -6225,20 +6298,23 @@ var FakeECSFQDNs = container.NewMapSet(
"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-nh4z.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.",
+ "rr5---sn-muxa-2iae.googlevideo.com.",
"rr5---sn-n4v7snee.googlevideo.com.",
"rr5---sn-n4v7sney.googlevideo.com.",
"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.",
@@ -6248,6 +6324,7 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-npoe7ne7.googlevideo.com.",
"rr5---sn-npoe7ned.googlevideo.com.",
"rr5---sn-npoe7nek.googlevideo.com.",
+ "rr5---sn-npoe7ner.googlevideo.com.",
"rr5---sn-npoe7nes.googlevideo.com.",
"rr5---sn-npoe7ney.googlevideo.com.",
"rr5---sn-npoe7nez.googlevideo.com.",
@@ -6255,14 +6332,12 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-npoe7nlz.googlevideo.com.",
"rr5---sn-npoe7ns6.googlevideo.com.",
"rr5---sn-npoe7ns7.googlevideo.com.",
- "rr5---sn-npoe7ns7.gvt1.com.",
"rr5---sn-npoe7nsd.googlevideo.com.",
"rr5---sn-npoe7nsk.googlevideo.com.",
"rr5---sn-npoe7nsl.googlevideo.com.",
"rr5---sn-npoe7nsr.googlevideo.com.",
"rr5---sn-npoe7nss.googlevideo.com.",
"rr5---sn-npoe7nsy.googlevideo.com.",
- "rr5---sn-npoe7nsy.gvt1.com.",
"rr5---sn-npoe7nz7.googlevideo.com.",
"rr5---sn-npoeene6.googlevideo.com.",
"rr5---sn-npoeened.googlevideo.com.",
@@ -6272,40 +6347,35 @@ 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-npoldn76.gvt1.com.",
"rr5---sn-npoldn7d.googlevideo.com.",
+ "rr5---sn-npoldn7d.gvt1.com.",
"rr5---sn-npoldn7e.googlevideo.com.",
"rr5---sn-npoldn7l.googlevideo.com.",
- "rr5---sn-npoldn7l.gvt1.com.",
"rr5---sn-npoldn7s.googlevideo.com.",
"rr5---sn-npoldn7y.googlevideo.com.",
- "rr5---sn-npoldn7z.googlevideo.com.",
"rr5---sn-npoldne7.googlevideo.com.",
- "rr5---sn-ntq7yns7.googlevideo.com.",
- "rr5---sn-ntqe6n76.googlevideo.com.",
- "rr5---sn-ntqe6n7r.googlevideo.com.",
- "rr5---sn-ntqe6nee.googlevideo.com.",
"rr5---sn-nx57ynlk.googlevideo.com.",
"rr5---sn-nx57ynsd.googlevideo.com.",
- "rr5---sn-nx57ynse.googlevideo.com.",
+ "rr5---sn-nx57ynsd.gvt1.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-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.",
@@ -6317,7 +6387,9 @@ 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.",
@@ -6328,6 +6400,7 @@ 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.",
@@ -6335,73 +6408,77 @@ 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-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-q4fl6n6d.gvt1.com.",
"rr5---sn-q4fl6n6r.googlevideo.com.",
"rr5---sn-q4fl6n6s.googlevideo.com.",
"rr5---sn-q4fl6n6y.googlevideo.com.",
+ "rr5---sn-q4fl6n6y.gvt1.com.",
"rr5---sn-q4fl6n6z.googlevideo.com.",
"rr5---sn-q4fl6n6z.gvt1.com.",
"rr5---sn-q4fl6nd7.googlevideo.com.",
- "rr5---sn-q4fl6nd7.gvt1.com.",
"rr5---sn-q4fl6nde.googlevideo.com.",
+ "rr5---sn-q4fl6nde.gvt1.com.",
"rr5---sn-q4fl6ndl.googlevideo.com.",
- "rr5---sn-q4fl6ndl.gvt1.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-q4fl6ns6.gvt1.com.",
+ "rr5---sn-q4fl6ns7.googlevideo.com.",
"rr5---sn-q4fl6nsd.googlevideo.com.",
- "rr5---sn-q4fl6nsd.gvt1.com.",
"rr5---sn-q4fl6nsk.googlevideo.com.",
"rr5---sn-q4fl6nsl.googlevideo.com.",
+ "rr5---sn-q4fl6nsl.gvt1.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-q4flrn7k.googlevideo.com.",
- "rr5---sn-q4flrn7k.gvt1.com.",
+ "rr5---sn-q4fl6nzy.gvt1.com.",
"rr5---sn-q4flrn7r.googlevideo.com.",
+ "rr5---sn-q4flrn7r.gvt1.com.",
"rr5---sn-q4flrn7y.googlevideo.com.",
- "rr5---sn-q4flrne6.googlevideo.com.",
- "rr5---sn-q4flrne6.gvt1.com.",
"rr5---sn-q4flrne7.googlevideo.com.",
- "rr5---sn-q4flrnee.googlevideo.com.",
+ "rr5---sn-q4flrne7.gvt1.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-q4flrnle.googlevideo.com.",
"rr5---sn-q4flrnlz.googlevideo.com.",
+ "rr5---sn-q4flrnlz.gvt1.com.",
"rr5---sn-q4flrnsd.googlevideo.com.",
- "rr5---sn-q4flrnsd.gvt1.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-q4fzen7l.gvt1.com.",
"rr5---sn-q4fzen7r.googlevideo.com.",
- "rr5---sn-q4fzen7r.gvt1.com.",
"rr5---sn-q4fzen7s.googlevideo.com.",
"rr5---sn-q4fzen7s.gvt1.com.",
"rr5---sn-q4fzen7y.googlevideo.com.",
@@ -6414,57 +6491,62 @@ var FakeECSFQDNs = container.NewMapSet(
"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-vgqsknlk.gvt1.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.",
+ "rr5---sn-vgqsknse.gvt1.com.",
"rr5---sn-vgqsknsk.googlevideo.com.",
"rr5---sn-vgqsknz6.googlevideo.com.",
"rr5---sn-vgqsknz7.googlevideo.com.",
"rr5---sn-vgqsknzd.googlevideo.com.",
- "rr5---sn-vgqsknze.googlevideo.com.",
+ "rr5---sn-vgqsknzd.gvt1.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.",
- "rr5---sn-vgqsrn6e.gvt1.com.",
"rr5---sn-vgqsrn6l.googlevideo.com.",
"rr5---sn-vgqsrn6z.googlevideo.com.",
"rr5---sn-vgqsrne6.googlevideo.com.",
"rr5---sn-vgqsrned.googlevideo.com.",
"rr5---sn-vgqsrnek.googlevideo.com.",
"rr5---sn-vgqsrnes.googlevideo.com.",
- "rr5---sn-vgqsrnes.gvt1.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.",
@@ -6473,30 +6555,25 @@ var FakeECSFQDNs = container.NewMapSet(
"rr5---sn-vgqsrnsd.googlevideo.com.",
"rr5---sn-vgqsrnsr.googlevideo.com.",
"rr5---sn-vgqsrnsy.googlevideo.com.",
- "rr5---sn-vgqsrnsy.gvt1.com.",
"rr5---sn-vgqsrnz6.googlevideo.com.",
- "rr5---sn-vgqsrnz6.gvt1.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-5hneknes.googlevideo.com.",
- "rr5.sn-hp57knds.googlevideo.com.",
- "rr5.sn-ntq7yns7.googlevideo.com.",
- "rr5.sn-ntqe6n7r.googlevideo.com.",
- "rr5.sn-ntqe6nee.googlevideo.com.",
+ "rr5.sn-4g5ednsy.googlevideo.com.",
+ "rr5.sn-hgn7rn7r.googlevideo.com.",
+ "rr5.sn-q4fl6n66.googlevideo.com.",
+ "rr5.sn-q4fl6nde.googlevideo.com.",
"rr5.sn-q4fl6ndl.googlevideo.com.",
- "rr5.sn-q4flrne6.googlevideo.com.",
- "rr5.sn-q4flrne7.googlevideo.com.",
- "rr5.sn-q4flrnld.googlevideo.com.",
- "rr6---sn-5abxgpxuxaxjvh-9n4e.googlevideo.com.",
- "rr6---sn-5abxgpxuxaxjvh-9n4l.googlevideo.com.",
- "rr6---sn-8pxuuxa-nbo6l.googlevideo.com.",
+ "rr5.sn-q4fl6nsk.googlevideo.com.",
+ "rr5.sn-q4flrnss.googlevideo.com.",
+ "rr5.sn-q4fzen7s.googlevideo.com.",
+ "rr6---sn-5pgnugx5h-hn2z.googlevideo.com.",
"rr6---sn-8qj-nbo66.googlevideo.com.",
- "rr6---sn-8xgp1vo-a5me.googlevideo.com.",
"rr6---sn-8xgp1vo-ab56.googlevideo.com.",
"rr6---sn-8xgp1vo-ab5d.googlevideo.com.",
"rr6---sn-8xgp1vo-ab5e.googlevideo.com.",
@@ -6508,42 +6585,36 @@ var FakeECSFQDNs = container.NewMapSet(
"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-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-nh4l.googlevideo.com.",
"rr6---sn-jvhj5nu-nh4s.googlevideo.com.",
- "rr6---sn-jvhj5nu-nh4z.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-5abxgpxuxaxjvh-9n4e.googlevideo.com.",
- "rr7---sn-5abxgpxuxaxjvh-9n4l.googlevideo.com.",
- "rr7---sn-8pxuuxa-nbo6l.googlevideo.com.",
"rr7---sn-8qj-nbo66.googlevideo.com.",
- "rr7---sn-8xgp1vo-a5me.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-vgqe.googlevideo.com.",
"rr7---sn-8xgp1vo-xfge.googlevideo.com.",
"rr7---sn-jvhj5nu-nh4e.googlevideo.com.",
- "rr7---sn-jvhj5nu-nh4l.googlevideo.com.",
"rr7---sn-jvhj5nu-nh4s.googlevideo.com.",
- "rr7---sn-jvhj5nu-nh4z.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-5abxgpxuxaxjvh-9n4e.googlevideo.com.",
- "rr8---sn-5abxgpxuxaxjvh-9n4l.googlevideo.com.",
- "rr8---sn-8pxuuxa-nbo6l.googlevideo.com.",
"rr8---sn-8xgp1vo-ab56.googlevideo.com.",
"rr8---sn-8xgp1vo-ab5d.googlevideo.com.",
"rr8---sn-8xgp1vo-ab5e.googlevideo.com.",
@@ -6560,13 +6631,14 @@ var FakeECSFQDNs = container.NewMapSet(
"rsx.afterpay.com.",
"rt.applvn.com.",
"rt.teramind.co.",
- "rta.www.wrike.com.",
+ "rtb-apac.appocean.media.",
"rtb-apac.rtbserve.io.",
"rtb-eu.rtbserve.io.",
"rtb-useast-v4.qortex.ai.",
"rtb-useast.creativedot.net.",
"rtb-useast.rtbserve.io.",
"rtb-uswest-v4.qortex.ai.",
+ "rtb2-apac.xaprio.net.",
"rtb2-eu.xaprio.net.",
"rtb2-useast.xaprio.net.",
"rtbsuperhub.com.",
@@ -6578,33 +6650,31 @@ var FakeECSFQDNs = container.NewMapSet(
"ruijienetworks.com.",
"rule34video.com.",
"rum.perfops.cdb.cdn.orange.com.",
- "rum.perfops.mdb.cdn.orange.com.",
- "rumble.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-light.tiket.photos.",
+ "s-pinimg-com-cdn-cloudflare-net.pinimg.com.",
"s.deepl.com.",
- "s.q.easebar.com.",
"s.seedtag.com.",
"s2-a.time.mci1.us.rozint.net.",
"s2-b.time.mci1.us.rozint.net.",
- "s3-advertising.zalopay.vn.",
"s9.gifyu.com.",
"sa1.chat.si.riotgames.com.",
"sa2.chat.si.riotgames.com.",
"sa3.chat.si.riotgames.com.",
"saas.sensorsdata.com.",
"sach.gopsahome.info.",
- "sadmin.brightcove.com.",
"saintasaph.remotepc.com.",
- "salesbridge-my.sharepoint.com.",
+ "sales.ai.dynamics.com.",
+ "salidzini.lv.",
"saltlakecity.remotepc.com.",
"samsclub.quantummetric.com.",
"sanantonio.remotepc.com.",
- "sandbox.wdesk.com.",
"sandboxclient.retinavue.net.",
"sandboxregister.retinavue.net.",
"sandiego.remotepc.com.",
@@ -6620,7 +6690,6 @@ var FakeECSFQDNs = container.NewMapSet(
"sat02pap003.storage.live.com.",
"sat02pap004.storage.live.com.",
"sc.zoom.us.",
- "scheduler.eagle.haplat.net.",
"schneidercorp.com.",
"sciener.cn.",
"scm.haplat.net.",
@@ -6636,6 +6705,7 @@ var FakeECSFQDNs = container.NewMapSet(
"scontent-atl3-2.xx.fbcdn.net.",
"scontent-atl3-3.cdninstagram.com.",
"scontent-atl3-3.xx.fbcdn.net.",
+ "scontent-ber1-1.cdninstagram.com.",
"scontent-bkk1-1.xx.fbcdn.net.",
"scontent-bkk1-2.xx.fbcdn.net.",
"scontent-bog2-1.cdninstagram.com.",
@@ -6666,6 +6736,7 @@ var FakeECSFQDNs = container.NewMapSet(
"scontent-dfw5-2.xx.fbcdn.net.",
"scontent-dus1-1.cdninstagram.com.",
"scontent-dus1-1.xx.fbcdn.net.",
+ "scontent-fml1-1.cdninstagram.com.",
"scontent-fra3-1.cdninstagram.com.",
"scontent-fra3-1.xx.fbcdn.net.",
"scontent-fra3-2.cdninstagram.com.",
@@ -6674,7 +6745,6 @@ 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.",
@@ -6709,6 +6779,8 @@ var FakeECSFQDNs = container.NewMapSet(
"scontent-lga3-1.xx.fbcdn.net.",
"scontent-lga3-2.cdninstagram.com.",
"scontent-lga3-2.xx.fbcdn.net.",
+ "scontent-lga3-3.cdninstagram.com.",
+ "scontent-lga3-3.xx.fbcdn.net.",
"scontent-lhr6-1.cdninstagram.com.",
"scontent-lhr6-1.xx.fbcdn.net.",
"scontent-lhr6-2.cdninstagram.com.",
@@ -6717,6 +6789,7 @@ 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.",
@@ -6735,6 +6808,8 @@ var FakeECSFQDNs = container.NewMapSet(
"scontent-ord5-1.xx.fbcdn.net.",
"scontent-ord5-2.cdninstagram.com.",
"scontent-ord5-2.xx.fbcdn.net.",
+ "scontent-ord5-3.cdninstagram.com.",
+ "scontent-ord5-3.xx.fbcdn.net.",
"scontent-phx1-1.cdninstagram.com.",
"scontent-phx1-1.xx.fbcdn.net.",
"scontent-qro1-1.cdninstagram.com.",
@@ -6745,6 +6820,10 @@ var FakeECSFQDNs = container.NewMapSet(
"scontent-sea1-1.xx.fbcdn.net.",
"scontent-sin11-1.cdninstagram.com.",
"scontent-sin11-1.xx.fbcdn.net.",
+ "scontent-sin11-2.cdninstagram.com.",
+ "scontent-sin11-2.xx.fbcdn.net.",
+ "scontent-sin2-1.cdninstagram.com.",
+ "scontent-sin2-1.xx.fbcdn.net.",
"scontent-sin6-1.cdninstagram.com.",
"scontent-sin6-1.xx.fbcdn.net.",
"scontent-sin6-2.cdninstagram.com.",
@@ -6756,33 +6835,28 @@ 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.",
"scontent-waw2-1.xx.fbcdn.net.",
"scontent-waw2-2.cdninstagram.com.",
"scontent-waw2-2.xx.fbcdn.net.",
- "scontent-xsp1-1.cdninstagram.com.",
- "scontent-xsp1-1.xx.fbcdn.net.",
- "scontent-xsp1-2.cdninstagram.com.",
- "scontent-xsp1-2.xx.fbcdn.net.",
- "scontent-xsp1-3.cdninstagram.com.",
- "scontent-xsp1-3.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.",
- "sdgframeonyc01.frameo.net.",
- "sdgframeonyc02.frameo.net.",
+ "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.iad-01.braze.com.cdn.cloudflare.net.",
- "sdk.iad-07.braze.com.cdn.cloudflare.net.",
"sdk.qcloud.com.",
"sdkgate.pushv3.easebar.com.",
"sdktmp.hubcloud.com.cn.",
- "sdn.lxdns.com.",
- "sdn.qtlcname.com.",
"seabroadnet.com.",
"seagate.com.",
"seagullscientific.com.",
@@ -6793,70 +6867,70 @@ var FakeECSFQDNs = container.NewMapSet(
"searchserverapi.com.",
"seattle.remotepc.com.",
"secaucus.remotepc.com.",
- "secure-oldnavy.gap.com.",
- "secure-signals.permutive.app.",
"secure.accurint.com.",
+ "secure.internetdownloadmanager.com.",
"secure.syndetics.com.",
"securelink.med.usc.edu.",
+ "secureprivacy.ai.",
"securetheorem.com.",
"securityapi.d3-pr-tm.com.",
"seedtag.com.",
"send.microad.jp.",
- "sendspace.com.",
"sensorsdata.cn.",
"sensorsdata.com.",
"sensorsgateway.com.",
- "sentry-pro.bssrvc66.com.",
"sentry-webapp.quillbot.com.",
"sentry.appodeal.com.",
- "sentry.quillbot.com.",
+ "sentry.archive.org.",
"sentry.radyushin.com.",
- "sentry.voicemod.net.",
+ "sentry.wmt.dev.",
"sentry.wrike.com.",
- "seoul.remotepc.com.",
+ "seo.apps.avada.io.",
+ "serraview.com.",
"served-by.pixfuture.com.",
- "service-light.gamesafe.qq.com.",
- "service.toonblast.net.",
+ "servedby.flashtalking.com-v1.edgekey.net.",
+ "service.maxymiser.net.",
+ "service.wemass.com.",
"service2.ultipro.com.",
+ "servicebus102.myconnectsecure.com.",
"servicebus1021.myconnectsecure.com.",
"servicebus1022.myconnectsecure.com.",
"servicebus1023.myconnectsecure.com.",
"servicebus1024.myconnectsecure.com.",
"servicebus1041.myconnectsecure.com.",
- "services.gradle.org.",
+ "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.lego.com.",
- "services.listrak.com.cdn.cloudflare.net.",
- "services2.risevision.com.",
"servicetitan.com.",
"servx.opamarketplace.com.",
"servx.playstream.media.",
"settings.luckyorange.com.",
- "setupvpn.com.",
"sevenrooms.com.",
- "sewjn80htn-3.algolianet.com.",
"sg-o-s3.smartcloudcon.com.",
"sg.api.translator.voice.gcloudsdk.com.",
"sg.business.smartcamera.api.io.mi.com.",
- "sg.hlth.io.mi.com.",
"sg1a-excel-collab.officeapps.live.com.",
"sg1a-powerpoint-collab.officeapps.live.com.",
"sgfp.tongdun.net.",
"sgmbocast.com.",
"sgpcas.ezvizlife.com.",
- "share.fcgame.net.",
"sharpschool.com.",
"shc6.y.qq.com.",
- "shop.roadster.com.",
"shopcircle.co.",
"shopify-gtm-suite.getelevar.com.",
+ "shopifynetwork.com.",
"shortpixel.ai.",
- "show-sb.com.",
"shp.ee.",
+ "shredder-eu.osi.office.net.",
"shrinetheme.com.",
"shuzilm.cn.",
- "sigma-performance-h73na.proxima.nie.easebar.com.",
"signin.ultipro.com.",
- "signup.live.com.",
"sina.com.",
"sinaimg.cn.",
"sip-backup.phonepower.com.",
@@ -6868,24 +6942,29 @@ var FakeECSFQDNs = container.NewMapSet(
"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-1211.ringcentral.com.",
+ "sip121-1221.ringcentral.com.",
"sip123-1121.ringcentral.com.",
"sip123-1131.ringcentral.com.",
"sip123-1141.ringcentral.com.",
+ "sip123-1211.ringcentral.com.",
+ "sip123-1221.ringcentral.com.",
"sip131-1121.ringcentral.com.",
"sip131-1131.ringcentral.com.",
"sip131-1141.ringcentral.com.",
+ "sip131-1241.ringcentral.com.",
"sip132-1121.ringcentral.com.",
- "sip132-1131.ringcentral.com.",
"sip132-1141.ringcentral.com.",
"sip421-121.ringcentral.biz.",
"site-config.com.",
"sjc.zoom.us.",
"sjc04pap001.storage.live.com.",
"sjc04pap002.storage.live.com.",
+ "skeepers.io.",
+ "skims.com.",
"skyapi.onedrive.live.com.",
"skyapi.policies.live.net.",
"skydrivesync.policies.live.net.",
@@ -6912,11 +6991,12 @@ var FakeECSFQDNs = container.NewMapSet(
"smoot-searchv2-ause2c.v.aaplimg.com.",
"smoot-searchv2-ausw2b.v.aaplimg.com.",
"smoot-searchv2-ausw2c.v.aaplimg.com.",
- "smrcy.sharepoint.com.",
"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.",
@@ -6925,8 +7005,9 @@ var FakeECSFQDNs = container.NewMapSet(
"sofia.remotepc.com.",
"sogoucdn.com.",
"sohu.com.",
- "sohucs.com.",
+ "solarmanpv.com.",
"solid.preyproject.com.",
+ "somplo.com.",
"sonar-akl1-1.xx.fbcdn.net.",
"sonar-ams2-1.xx.fbcdn.net.",
"sonar-ams4-1.xx.fbcdn.net.",
@@ -6962,8 +7043,6 @@ var FakeECSFQDNs = container.NewMapSet(
"sonar-eze1-1.xx.fbcdn.net.",
"sonar-fco2-1.xx.fbcdn.net.",
"sonar-fml1-1.xx.fbcdn.net.",
- "sonar-fml20-1.xx.fbcdn.net.",
- "sonar-for1-1.xx.fbcdn.net.",
"sonar-for2-1.xx.fbcdn.net.",
"sonar-for2-2.xx.fbcdn.net.",
"sonar-fra3-1.xx.fbcdn.net.",
@@ -7003,6 +7082,7 @@ var FakeECSFQDNs = container.NewMapSet(
"sonar-lax3-2.xx.fbcdn.net.",
"sonar-lga3-1.xx.fbcdn.net.",
"sonar-lga3-2.xx.fbcdn.net.",
+ "sonar-lga3-3.xx.fbcdn.net.",
"sonar-lhr6-1.xx.fbcdn.net.",
"sonar-lhr6-2.xx.fbcdn.net.",
"sonar-lhr8-1.xx.fbcdn.net.",
@@ -7015,7 +7095,7 @@ var FakeECSFQDNs = container.NewMapSet(
"sonar-mad1-1.xx.fbcdn.net.",
"sonar-mad2-1.xx.fbcdn.net.",
"sonar-man2-1.xx.fbcdn.net.",
- "sonar-mba1-1.xx.fbcdn.net.",
+ "sonar-mba2-1.xx.fbcdn.net.",
"sonar-mct1-1.xx.fbcdn.net.",
"sonar-mct1-2.xx.fbcdn.net.",
"sonar-mia3-1.xx.fbcdn.net.",
@@ -7034,6 +7114,7 @@ var FakeECSFQDNs = container.NewMapSet(
"sonar-nrt1-2.xx.fbcdn.net.",
"sonar-ord5-1.xx.fbcdn.net.",
"sonar-ord5-2.xx.fbcdn.net.",
+ "sonar-ord5-3.xx.fbcdn.net.",
"sonar-otp1-1.xx.fbcdn.net.",
"sonar-phx1-1.xx.fbcdn.net.",
"sonar-pmo1-1.xx.fbcdn.net.",
@@ -7045,6 +7126,9 @@ var FakeECSFQDNs = container.NewMapSet(
"sonar-scl2-1.xx.fbcdn.net.",
"sonar-sea1-1.xx.fbcdn.net.",
"sonar-sin11-1.xx.fbcdn.net.",
+ "sonar-sin11-2.xx.fbcdn.net.",
+ "sonar-sin2-1.xx.fbcdn.net.",
+ "sonar-sin2-2.xx.fbcdn.net.",
"sonar-sin6-1.xx.fbcdn.net.",
"sonar-sin6-2.xx.fbcdn.net.",
"sonar-sin6-3.xx.fbcdn.net.",
@@ -7056,52 +7140,50 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
"sonar-waw2-2.xx.fbcdn.net.",
- "sonar-xsp1-1.xx.fbcdn.net.",
- "sonar-xsp1-2.xx.fbcdn.net.",
- "sonar-xsp1-3.xx.fbcdn.net.",
"sonar-yyz1-1.xx.fbcdn.net.",
"sonar-zrh1-1.xx.fbcdn.net.",
"sonar.viously.com.",
- "soulapp.cn.",
"southafricanorth.api.cognitive.microsoft.com.",
"southcarolina.remotepc.com.",
"southeastasia.api.cognitive.microsoft.com.",
"southindia.api.cognitive.microsoft.com.",
"spadsync.com.",
"sparteo.com.",
- "speedtest.sv5.macarne.com.",
+ "speedtest.us-sc.kamatera.com.",
"spiny.ai.",
"spion.savvy.security.",
- "split-tool.com.",
"sq.bls.mdt.qq.com.",
"src.ebay-us.com.",
- "srv.stalker.to.",
"srv2.maxhost.io.",
"ssafp.samsclub.com.",
"ssctech.com.",
- "ssl-avd.innity.net.",
"ssl.geoplugin.net.",
+ "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.",
- "stags.bluekai.com.",
- "stalker.to.",
- "standwithcrypto.org.",
+ "starbucks-wfmr.jdadelivers.com.",
"stardustgod.com.",
"starrydyn.com.",
"startssl.com.",
+ "stash.webstaurantstore.com.",
"stat.pdfforge.org.",
"statad.ru.",
"static-atl3-1.xx.fbcdn.net.",
"static-atl3-2.xx.fbcdn.net.",
+ "static-atl3-3.xx.fbcdn.net.",
"static-bos5-1.xx.fbcdn.net.",
"static-den2-1.xx.fbcdn.net.",
"static-dfw5-1.xx.fbcdn.net.",
@@ -7113,41 +7195,49 @@ 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-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.atgsvcs.com.",
"static.avito.ru.",
"static.getliner.com.",
- "static.mytonwallet.io.",
- "static.sharepointonline.com.",
- "static1.teacherspayteachers.com.",
- "static5.mixi.media.",
+ "static.kwcdn.com.",
+ "static2.mixi.media.",
"static6.mixi.media.",
"static8.mixi.media.",
- "staticview.msn.com.edgekey.net.",
"stats.aeries.com.",
"stats.rip.",
"stats.transitapp.com.",
"statsig.anthropic.com.",
+ "stevemadden.com.",
"stg-data-in.ads.heytapmobile.com.",
"stg-data.ads.heytapmobi.com.",
"stockholm.remotepc.com.",
"stocks-analytics-events.apple.com.",
- "store-dra.hispace.dbankcloud.cn.",
+ "storage.procore.com.",
"store.qq.com.",
+ "store.vsco.co.",
+ "storeedgefd.dsx.mp.microsoft.com.",
+ "storymagic.co.",
+ "streamhub.tech.",
"streetviewpixels-pa.googleapis.com.",
- "stripchat.ooo.",
+ "stripchat.com.",
"stse02.ultipro.com.",
"stsew02.ultipro.com.",
"stsn02.ultipro.com.",
"student.atitesting.com.",
+ "studentaid.gov.",
"studyquicks.com.",
"stun.acrobits.cz.",
"stun.cdnbye.com.",
@@ -7159,43 +7249,49 @@ var FakeECSFQDNs = container.NewMapSet(
"stun2.ringcentral.com.",
"stun3.l.google.com.",
"stun4.l.google.com.",
+ "stvinc-my.sharepoint.com.",
"su6786.ci.managedwhitelisting.com.",
- "submittals-ui-service.pages.procore.com.",
"sumari-prod-1.srv.jbisumari.org.",
"sumologic.com.",
- "sun.hac.lp1.d4c.nintendo.net.",
"sunmi.com.",
"supl.qxwz.com.",
- "support.oracle.com.",
"support.powerschool.com.",
+ "sv-ookla.geolinks.com.",
"sv8.cyberhaven.io.",
"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.inmobi.com.",
"sync.oraki.io.",
"sync.videowalldirect.com.",
+ "syncfusion.com.",
"syndetics.com.",
"systemreportservices.genetec.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.",
- "tags.bluekai.com.edgekey.net.",
+ "tagcommander.com.",
"tags.natwest.com.",
- "taipei.remotepc.com.",
- "talon-service-prod.ecosec.on.epicgames.com.",
"tampa.remotepc.com.",
"tapecontent.net.",
"tapsell.ir.",
+ "tara.ns.cloudflare.com.",
"target.digitalaudience.io.",
"tasks.office.com.",
"tasteofhome.com.",
@@ -7207,21 +7303,23 @@ 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.",
"teamviewer.com.",
- "techcommunity.microsoft.com.",
+ "teamviewer.com.cdn.cloudflare.net.",
"teddymobile.cn.",
- "telecom.shuzilm.cn.",
+ "telegraph-sync.quantummetric.com.",
"telemetry-sdk-inmobi-comtm.trafficmanager.net.",
"telemetry.savvy.security.",
"telemetry.sdk.inmobi.com.",
"telephony.goog.",
"teleport.media.",
- "template.net.",
"tencent-cloud.com.",
"tencent-cloud.net.",
+ "tencent.com.",
"tencentmusic.com.",
"tenda.com.cn.",
"tenpay.com.",
@@ -7229,16 +7327,17 @@ var FakeECSFQDNs = container.NewMapSet(
"terabox.com.",
"terabox1024.com.",
"terms3.hicloud.com.",
+ "test3.dantri.com.vn.",
"tgp.qq.com.",
"tgpa.qq.com.",
"thanhnien.vn.",
"theoks.net.",
- "therealxh.com.",
"thetracker.org.",
"thinkific.com.",
"thm.visa.com.",
"thm12.visa.com.",
- "thumb-v0.xhpingcdn.com.",
+ "thumb-v1.xhpingcdn.com.",
+ "thumb-v4.xhpingcdn.com.",
"time-a-b.nist.gov.",
"time-a-g.nist.gov.",
"time-a.nist.gov.",
@@ -7255,24 +7354,23 @@ var FakeECSFQDNs = container.NewMapSet(
"time.ecansol.net.",
"time.lmtlabs.com.",
"time.nest.com.",
- "time.tritan.gg.",
+ "time.tritan.host.",
"time.walb.tech.",
"time1.aliyun.com.",
"time1.google.com.",
"time2.aliyun.com.",
"time2.google.com.",
- "time2.tritan.gg.",
+ "time2.tritan.host.",
"time3.aliyun.com.",
"time3.google.com.",
"time4.google.com.",
+ "timejs.game.easebar.com.",
"timi-esports.qq.com.",
- "tinnhiemmang.vn.",
- "titank12.com.",
"tk.bridgeoos.com.",
"tk.mosspf.com.",
"tk.mossru.com.",
- "tkda.mossru.com.",
"tkx.mp.lura.live.",
+ "tl.upwork.com.",
"tlivecdn.com.",
"tlivesource.com.",
"tls12.eu01.nr-data.net.cdn.cloudflare.net.",
@@ -7281,6 +7379,7 @@ 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.",
@@ -7292,11 +7391,9 @@ var FakeECSFQDNs = container.NewMapSet(
"tngdigital.com.my.",
"tokyo.remotepc.com.",
"tongdun.net.",
- "topgadgetlife.com.",
"toronto.remotepc.com.",
"toshiba-solutions.com.",
"touch-us.xiaoyi.com.",
- "township-cdn.playrix.com.",
"tplay.qq.com.",
"tpns.sgp.tencent.com.",
"tpns.sh.tencent.com.",
@@ -7338,34 +7435,35 @@ var FakeECSFQDNs = container.NewMapSet(
"tra-ved-ru.best82.com.",
"trace.qq.com.",
"track-eu1.hubspot.com.",
- "track.easeus.com.",
"track.sendlane.com.",
+ "track.yourrtb.com.",
"trackedlink.net.",
"tracker-udp.gbitt.info.",
"tracker.auctor.tv.",
"tracker.best61.com.",
- "tracker.ccp.ovh.",
"tracker.files.fm.",
"tracker.grepler.com.",
"tracker.hownetwork.xyz.",
"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.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.",
- "trustlist.adobe.com.",
"ts1.qq.com.",
"ts2.qq.com.",
"tse1.explicit.bing.net.",
@@ -7378,38 +7476,43 @@ var FakeECSFQDNs = container.NewMapSet(
"tuoitre.vn.",
"turn.cloudflare.com.",
"tw.ntp.org.cn.",
- "twcgov-my.sharepoint.com.",
"twcloudgz.ucbj.net.",
"twcloudgz1.ucbj.net.",
+ "twvideogz31.ucbj.net.",
+ "twvideohf41.ucbj.net.",
+ "twvideohf42.ucbj.net.",
+ "twvideohf43.ucbj.net.",
"tydevice.com.",
+ "typenetwork.com.",
"u.4dex.io.",
"uaenorth.api.cognitive.microsoft.com.",
+ "uam1.dexcom.com.",
"uapi.mp.360.cn.",
- "ubeethiesemo.com.",
"uber.zoom.us.",
"uc.cn.",
"ucweb.com.",
"udemycdn.com.",
"ue.lenovomm.cn.",
"uhabo.com.",
+ "uhf.microsoft.com.",
"uk-api.asm.skype.com.",
- "uk-odc.samsungapps.com.",
"uk-prod.asyncgw.teams.microsoft.com.",
"uk3-excel-collab.officeapps.live.com.",
"uk3-powerpoint-collab.officeapps.live.com.",
"uk5-excel-collab.officeapps.live.com.",
+ "ukc-collabrtc-geo.rtc.trafficmanager.net.",
+ "ukc-collabrtc.officeapps.live.com.",
"ukc-excel-collab.officeapps.live.com.",
"ukg.com.",
- "ukyuh.tech.",
"ulikecam.com.",
"ulinq.asia.",
+ "ulta.quantummetric.com.",
"ultipro.com.",
"ultiprotime.com.",
"ultiproworkplace.com.",
"umami.is.",
"umeng.com.",
"unicom.shuzilm.cn.",
- "uniconsent.com.",
"unified-inbox-1-gw.ultipro.com.",
"unified-inbox-2-gw.ultipro.com.",
"unipay.qq.com.",
@@ -7421,13 +7524,13 @@ var FakeECSFQDNs = container.NewMapSet(
"update.ee-share.com.",
"update.huorong.cn.",
"update.kingsoftstore.com.",
+ "update.pdfforge.org.",
"update.vivaldi.com.",
"update.yealink.com.",
"updatechannel.sharegate.com.",
- "updater.prntscr.com.",
- "updater.skillbrains.com.",
"updaterservices.genetec.com.",
"updatesnl.macrium.com.",
+ "upload1.systemmonitor.co.uk.",
"upravel.com.",
"upremium.asia.",
"urekamedia.com.",
@@ -7438,10 +7541,10 @@ var FakeECSFQDNs = container.NewMapSet(
"us-05.ws-api.ringcentral.com.",
"us-06.ws-api.ringcentral.com.",
"us-api.asm.skype.com.",
- "us-atl-anx-r001.router.teamviewer.com.",
- "us-atl-anx-r002.router.teamviewer.com.",
- "us-atl-anx-r005.router.teamviewer.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.",
"us-central1-aeo-datasci-microsrv-pr-afe8.cloudfunctions.net.",
"us-central1-amp-error-reporting.cloudfunctions.net.",
@@ -7450,11 +7553,11 @@ var FakeECSFQDNs = container.NewMapSet(
"us-central1-darden-main.cloudfunctions.net.",
"us-central1-digitalproducts-gabbo.cloudfunctions.net.",
"us-central1-ds-specials-dev.cloudfunctions.net.",
+ "us-central1-firepolls-d5258.cloudfunctions.net.",
"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-launchpad-169908.cloudfunctions.net.",
"us-central1-live-prod-1-1.cloudfunctions.net.",
"us-central1-locket-4252a.cloudfunctions.net.",
"us-central1-mikmak-microservices.cloudfunctions.net.",
@@ -7463,52 +7566,36 @@ var FakeECSFQDNs = container.NewMapSet(
"us-central1-royal-match-prod-cce6d.cloudfunctions.net.",
"us-central1-shopify-instrumentat-ff788286.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-dal-anx-r001.router.teamviewer.com.",
- "us-dal-anx-r007.router.teamviewer.com.",
- "us-dal-anx-r009.router.teamviewer.com.",
- "us-dal-anx-r010.router.teamviewer.com.",
- "us-dal-gcp-r002.router.teamviewer.com.",
- "us-den-anx-r001.router.teamviewer.com.",
- "us-den-anx-r005.router.teamviewer.com.",
+ "us-chi-anx-r003.router.teamviewer.com.",
+ "us-den-anx-r002.router.teamviewer.com.",
+ "us-den-anx-r008.router.teamviewer.com.",
"us-device-scheduler.ymcs.yealink.com.",
"us-device.ymcs.yealink.com.",
- "us-east-1.console.aws.amazon.com.",
"us-east4-chkp-gcp-rnd-threat-hunt-box.cloudfunctions.net.",
- "us-lax-anx-r007.router.teamviewer.com.",
- "us-lax-anx-r008.router.teamviewer.com.",
- "us-lax-anx-r009.router.teamviewer.com.",
- "us-lax-anx-r011.router.teamviewer.com.",
- "us-lax-anx-r012.router.teamviewer.com.",
- "us-lax-anx-r014.router.teamviewer.com.",
- "us-lax-anx-r015.router.teamviewer.com.",
- "us-lax-gcp-r002.router.teamviewer.com.",
- "us-lax-gcp-r004.router.teamviewer.com.",
- "us-mia-anx-r003.router.teamviewer.com.",
+ "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-r009.router.teamviewer.com.",
- "us-mia-anx-r013.router.teamviewer.com.",
- "us-mks-gcp-r003.router.teamviewer.com.",
- "us-njc-anx-r005.router.teamviewer.com.",
- "us-njc-anx-r008.router.teamviewer.com.",
- "us-njc-anx-r015.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-prod.asyncgw.teams.microsoft.com.",
- "us-sea-anx-r005.router.teamviewer.com.",
- "us-sea-anx-r007.router.teamviewer.com.",
- "us-slc-gcp-r001.router.teamviewer.com.",
- "us-slc-gcp-r002.router.teamviewer.com.",
- "us-slc-gcp-r003.router.teamviewer.com.",
+ "us-sea-anx-r008.router.teamviewer.com.",
+ "us-service.cartsee-from.cartx.cloud.",
"us-spectrum.rcs.telephony.goog.",
- "us-was-anx-r011.router.teamviewer.com.",
- "us-was-anx-r014.router.teamviewer.com.",
+ "us-was-anx-r012.router.teamviewer.com.",
"us.att.rcs.telephony.goog.",
"us.evidation.com.",
- "us.hdtvcloud.com.",
"us.hlth.io.mi.com.",
- "us.questionai.com.",
+ "us.micardapi.micloud.xiaomi.net.",
"us.tmobile.rcs.telephony.goog.",
"us.tracfone.rcs.telephony.goog.",
"us.ubianet.com.",
@@ -7516,13 +7603,14 @@ var FakeECSFQDNs = container.NewMapSet(
"us.xfinity.rcs.telephony.goog.",
"us01docs.zoom.us.",
"us02log.zoom.us.",
+ "us02nws.zoom.us.",
"us02polling.zoom.us.",
"us02web.zoom.us.",
"us02www3.zoom.us.",
"us04asyncim.zoom.us.",
"us04nws-platform.zoom.us.",
"us04nws.zoom.us.",
- "us04st3.zoom.us.",
+ "us04st2.zoom.us.",
"us04web.zoom.us.",
"us04www3.zoom.us.",
"us05nws-platform.zoom.us.",
@@ -7530,18 +7618,12 @@ var FakeECSFQDNs = container.NewMapSet(
"us05web.zoom.us.",
"us05www3.zoom.us.",
"us06log.zoom.us.",
+ "us06nws-platform.zoom.us.",
+ "us06nws.zoom.us.",
"us06polling.zoom.us.",
"us06web.zoom.us.",
"us06www3.zoom.us.",
- "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.",
- "us4a-excel-collab.officeapps.live.com.",
- "us4a-powerpoint-collab.officeapps.live.com.",
- "us4a-word-collab.officeapps.live.com.",
"us4b-excel-collab.ocs.trafficmanager.net.",
"us4b-excel-collab.officeapps.live.com.",
"us4b-powerpoint-collab.ocs.trafficmanager.net.",
@@ -7554,8 +7636,11 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
"us8-excel-collab.ocs.trafficmanager.net.",
"us8-excel-collab.officeapps.live.com.",
@@ -7572,11 +7657,8 @@ var FakeECSFQDNs = container.NewMapSet(
"usc.edu.",
"usc.pods.officeapps.live.com.",
"uscis.gov.",
- "usdoj.gov.",
- "use3-assets.a-mo.net.",
- "use4.s.seedtag.com.",
"useast.quantumdex.io.",
- "usefulcontentsites.com.",
+ "userlike.com.",
"usgs.gov.",
"ussav.cynet.com.",
"usserver.serverapi.org.",
@@ -7586,14 +7668,14 @@ var FakeECSFQDNs = container.NewMapSet(
"ut.hzshudian.com.",
"uu.qq.com.",
"uuidksinc.net.",
- "uwcu.cyberhaven.io.",
- "uworld.com.",
"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-id.gts.byteoversea.net.",
+ "v39-mx.tiktokcdn.com.",
"v39-row.gts.byteoversea.net.",
"v39-row.tiktokcdn.com.",
"v39-us.gts.byteoversea.net.",
@@ -7605,18 +7687,18 @@ var FakeECSFQDNs = container.NewMapSet(
"v8.analytics.pinsightmedia.com.",
"v8engine.pinsightmedia.com.",
"vador.com.",
- "valleychildrensorg-my.sharepoint.com.",
"vbw.vivoglobal.com.",
- "vcsa.vmware.com.cdn.cloudflare.net.",
+ "veh-dms.na.ultifi.gm.com.",
"venafi.com.",
"verification.fda.gov.ph.",
"verticals.wix.com.",
"vfa.hpplay.cn.",
- "vgorigin.hakunaymatata.com.",
- "vi.vipr.ebaydesc.com.",
+ "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.",
@@ -7624,6 +7706,7 @@ var FakeECSFQDNs = container.NewMapSet(
"video-den2-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.",
@@ -7631,6 +7714,7 @@ var FakeECSFQDNs = container.NewMapSet(
"video-lax3-2.xx.fbcdn.net.",
"video-lga3-1.xx.fbcdn.net.",
"video-lga3-2.xx.fbcdn.net.",
+ "video-lga3-3.xx.fbcdn.net.",
"video-lhr6-1.xx.fbcdn.net.",
"video-lhr6-2.xx.fbcdn.net.",
"video-lhr8-1.xx.fbcdn.net.",
@@ -7640,25 +7724,27 @@ var FakeECSFQDNs = container.NewMapSet(
"video-msp1-1.xx.fbcdn.net.",
"video-ord5-1.xx.fbcdn.net.",
"video-ord5-2.xx.fbcdn.net.",
+ "video-ord5-3.xx.fbcdn.net.",
"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.",
- "vik-game.moonactive.net.",
+ "viki.com.",
+ "vinted.fr.",
"viously.com.",
"vipads.live.",
"virgul.com.",
"visaforchina.cn.",
"visitors.live.",
+ "visualwebsiteoptimizer.com.",
"vitality.io.",
"vividseats.com.",
"vivoglobal.com.",
- "vn-viettel.rcs.telephony.goog.",
- "vn2-red.lol.sgp.pvp.net.",
"vntsnotificationservice.visa.com.cdn.cloudflare.net.",
"vod.ngb.haplat.net.",
"vod2.ngb.haplat.net.",
@@ -7669,40 +7755,34 @@ var FakeECSFQDNs = container.NewMapSet(
"voe.sx.",
"voice.gcloudcs.com.",
"voice.telephony.goog.",
- "vote.donaldjtrump.com.",
+ "voxox.com.",
"voya.com.",
"vpn1.ocso.com.",
- "vx9dle.n0qq3z.com.",
+ "vsco.co.",
+ "vx323.com.",
"vzuu.com.",
"w.deepl.com.",
- "w25.cf.2ksports.com.",
"w3.mp.lura.live.",
"wahapanih.xyz.",
- "wap.cmpassport.com.",
"warsaw.remotepc.com.",
"wayfinding-hub-gateway-atl.ultipro.com.",
"wayfinding-hub-gateway-plas1.ultipro.com.",
- "wb.qq.com.",
- "wcsdpaorg-my.sharepoint.com.",
- "wd5-impl.workdaycdn.com.",
"we.footballbros.io.",
"weather-analytics-events.apple.com.",
"weather-server-sg.allawnos.com.",
"weather-server.allawntech.com.",
"weather-widget-events.apple.com.",
"weather.swishapps.ai.",
- "weather.transsion-os.com.",
"weathercn.com.",
- "web-assets.stockx.com.",
- "web-assets.stylitics.com.cdn.cloudflare.net.",
"web-static.mindbox.ru.",
- "web.archive.org.",
"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.",
@@ -7714,10 +7794,14 @@ var FakeECSFQDNs = container.NewMapSet(
"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.",
+ "widget-api.uxfeedback.ru.",
+ "widgets.sociablekit.com.",
"wifispot.io.",
- "wild.ww.np.dl.playstation.net.edgekey.net.",
"windows.policies.live.net.",
"winscp.net.",
"wkhpe.com.",
@@ -7725,101 +7809,103 @@ var FakeECSFQDNs = container.NewMapSet(
"word-collab.officeapps.live.com.",
"wordreference.com.",
"workdaycdn.com.cn.",
+ "worldguessr.com.",
"worldnic.com.",
"worldtimeserver.com.",
"wosign.com.",
- "wpsw.listrak.com.",
"wr.moyoung.com.",
"wr.pvp.net.",
+ "write-free.www.deepl.com.",
"ws.gleap.io.",
"ws.mybib.com.",
"ws.thales.monumetric.com.",
"ws.tildacdn.com.",
- "wsdcc.haplat.net.",
+ "wsms.haplat.net.",
"wsoversea.com.",
"wss-web.freshchat.com.",
- "www.481528.com.",
+ "wtecdn.net.",
+ "wtzw.com.",
"www.aigconnect.aig.",
- "www.americanexpress.com.edgekey.net.",
+ "www.att.com.edgekey.net.",
"www.automizely-analytics.com.",
- "www.belkin.com.",
"www.breitbart.com.",
- "www.brilliantearth.com.",
- "www.census.gov.",
+ "www.cabelas.com.",
"www.chrome.com.",
"www.claudeusercontent.com.",
"www.cmpassport.com.",
+ "www.columbia.com.",
+ "www.crazygames.com.",
"www.ctmail.com.",
- "www.ebay.com.",
- "www.education.com.",
- "www.epicgames.com.",
- "www.fda.gov.",
- "www.fiverr.com.",
- "www.garmin.com.",
"www.geappliances.com.",
"www.geoplugin.net.",
"www.google.org.",
+ "www.handmadewithjoann.com.",
+ "www.hopkinsmedicine.org.",
+ "www.hottopic.com.",
+ "www.hpsmart.com.",
"www.in.gov.",
- "www.internetdownloadmanager.com.",
+ "www.intel.com.",
"www.jimmyjohns.com.",
+ "www.jotform.com.",
+ "www.justice.gov.",
+ "www.kqzyfj.com.",
+ "www.linkedin.com.cdn.cloudflare.net.",
"www.maintenanceconnection.com.",
- "www.mcmaster.com.",
+ "www.masterclass.com.",
"www.nativecos.com.",
- "www.originplatform.com.",
"www.overleaf.com.",
"www.pingler.com.",
- "www.potterybarn.com.",
"www.printfriendly.com.",
- "www.questionai.com.",
+ "www.rbcroyalbank.com.",
"www.regions.com.",
- "www.rockchip.com.",
- "www.se.com.",
+ "www.rentcafe.com.",
+ "www.riotgames.com.",
"www.sevenrooms.com.",
- "www.southwest.com.",
+ "www.snokido.com.",
"www.startssl.com.",
- "www.stickermule.com.",
+ "www.teacherspayteachers.com.",
"www.terabox.com.",
- "www.trivago.com.",
"www.users.storage.live.com.",
"www.vipads.live.",
- "www.virustotal.com.",
"www.visaforchina.cn.",
+ "www.vividseats.com.",
"www.whitehouse.gov.",
"www.wifispot.io.",
"www.wix.com.",
"www.worldtimeserver.com.",
- "www.zenaps.com.",
"www.zoom.com.",
"www.zoom.us.",
+ "www.zoominfo.com.",
+ "www.zscaler.com.",
"www1.remotepc.com.",
- "www14.wellsfargomedia.com.",
"www2.deepl.com.",
+ "www28.pointclickcare.com.",
"www3.zoom.us.",
- "wx.huion.cn.",
+ "www30.pointclickcare.com.",
+ "www31.pointclickcare.com.",
"wxqcloud.qq.com.",
"wxqcloud.qq.com.cn.",
"wyze.com.",
"x-flow.app.",
- "xedo.me.",
+ "xbox-guide-public.rec.mp.microsoft.com.",
+ "xbox.ipv6.microsoft.com.",
"xhpingcdn.com.",
+ "xhwear.life.",
"xiaoyi.com.",
"xinqiucc.com.",
"xintaicz.cn.",
"xmcsrv.net.",
"xml-eu-v4.ezmob.com.",
+ "xml-v4.ezmob.com.",
"xml.acertb.com.",
"xml.adservtday.com.",
- "xml.cachegorilla.com.",
- "xml.ezmob.com.",
"xml.kunvertads.com.",
- "xml.popmonetizer.net.",
"xml.responseservez.com.",
"xml.revrtb.net.",
"xml.servsserverz.com.",
"xml.xmlking.com.",
- "xml.xmlwolf.com.",
"xml.zeusadx.com.",
- "xmlking.com.",
+ "xmppapi.zoom.us.",
"xmt.paze.com.",
"xp001.itsupport247.net.",
"xp002.itsupport247.net.",
@@ -7837,24 +7923,24 @@ var FakeECSFQDNs = container.NewMapSet(
"xp019.itsupport247.net.",
"yalla.live.",
"yealink.com.",
- "yh.yardiaspire.com.",
"ymmobi.com.",
"yomedia.vn.",
"yomeno.xyz.",
"yosmart.com.",
+ "youlesp.com.",
"yunxindns.com.",
"yunxinfw.com.",
- "z-long.cn.",
"z.cdn.adpool.bet.",
"z.cdn.adtarget.market.",
- "z.cdn.xbeat.space.",
- "zdx.uber.com.",
+ "z.cdn.ftd.agency.",
"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 d426db4..d78f2fc 100644
--- a/internal/ecscache/ecscache.go
+++ b/internal/ecscache/ecscache.go
@@ -91,10 +91,10 @@ type MiddlewareConfig struct {
// manager. c must not be nil.
func NewMiddleware(c *MiddlewareConfig) (m *Middleware) {
cache := agdcache.NewLRU[uint64, *cacheItem](&agdcache.LRUConfig{
- Size: c.NoECSCount,
+ Count: c.NoECSCount,
})
ecsCache := agdcache.NewLRU[uint64, *cacheItem](&agdcache.LRUConfig{
- Size: c.ECSCount,
+ Count: c.ECSCount,
})
c.CacheManager.Add(cacheIDNoECS, cache)
diff --git a/internal/filter/config.go b/internal/filter/config.go
new file mode 100644
index 0000000..b71aad6
--- /dev/null
+++ b/internal/filter/config.go
@@ -0,0 +1,118 @@
+package filter
+
+import "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
+
+// Config is the sum type of [Storage.ForConfig] configurations.
+//
+// Acceptable implementations are:
+// - nil
+// - [*ConfigClient]
+// - [*ConfigGroup]
+type Config interface {
+ isConfig()
+}
+
+// ConfigClient is a [Config] for a client.
+type ConfigClient struct {
+ // Custom is the configuration for identification or construction of a
+ // custom filter for a client. It must not be nil.
+ Custom *ConfigCustom
+
+ // Parental is the configuration for parental-control filtering. It must
+ // not be nil.
+ Parental *ConfigParental
+
+ // RuleList is the configuration for rule-list based filtering. It must not
+ // be nil.
+ RuleList *ConfigRuleList
+
+ // SafeBrowsing is the configuration for safe-browsing filtering. It must
+ // not be nil.
+ SafeBrowsing *ConfigSafeBrowsing
+}
+
+// type check
+var _ Config = (*ConfigClient)(nil)
+
+// isConfig implements the [Config] interface for *ConfigClient.
+func (*ConfigClient) isConfig() {}
+
+// ConfigCustom is the configuration for identification or construction of a
+// custom filter for a client.
+type ConfigCustom = internal.ConfigCustom
+
+// ConfigParental is the configuration for parental-control filtering.
+type ConfigParental struct {
+ // PauseSchedule is the schedule for the pausing of the parental-control
+ // filtering. If it is nil, the parental-control filtering is never paused.
+ // It is ignored if [ConfigParental.Enabled] is false.
+ PauseSchedule *ConfigSchedule
+
+ // BlockedServices are the IDs of the services blocked for this
+ // parental-control configuration. It is ignored if
+ // [ConfigParental.Enabled] is false.
+ BlockedServices []BlockedServiceID
+
+ // Enabled shows whether the parental-control feature is enabled.
+ Enabled bool
+
+ // AdultBlockingEnabled shows whether the adult-blocking filtering should be
+ // enforced. It is ignored if [ConfigParental.Enabled] is false.
+ AdultBlockingEnabled bool
+
+ // SafeSearchGeneralEnabled shows whether the general safe-search filtering
+ // should be enforced. It is ignored if [ConfigParental.Enabled] is false.
+ SafeSearchGeneralEnabled bool
+
+ // SafeSearchYouTubeEnabled shows whether the YouTube safe-search filtering
+ // should be enforced. It is ignored if [ConfigParental.Enabled] is false.
+ SafeSearchYouTubeEnabled bool
+}
+
+// ConfigRuleList is the configuration for rule-list based filtering.
+type ConfigRuleList struct {
+ // IDs are the IDs of the filtering rule lists used for this filtering
+ // configuration. They are ignored if [ConfigRuleList.Enabled] is false.
+ IDs []ID
+
+ // Enabled shows whether the rule-list based filtering is enabled.
+ Enabled bool
+}
+
+// ConfigSafeBrowsing is the configuration for safe-browsing filtering.
+type ConfigSafeBrowsing struct {
+ // Enabled shows whether the safe-browsing hashprefix-based filtering should
+ // is enabled.
+ Enabled bool
+
+ // DangerousDomainsEnabled shows whether the dangerous-domains safe-browsing
+ // filtering should be enforced. It is ignored if
+ // [ConfigSafeBrowsing.Enabled] is false.
+ DangerousDomainsEnabled bool
+
+ // NewlyRegisteredDomainsEnabled shows whether the newly-registered domains
+ // safe-browsing filtering should be enforced. It is ignored if
+ // [ConfigSafeBrowsing.Enabled] is false.
+ NewlyRegisteredDomainsEnabled bool
+}
+
+// ConfigGroup is a [Config] for a filtering group.
+type ConfigGroup struct {
+ // Parental is the configuration for parental-control filtering. It must
+ // not be nil.
+ Parental *ConfigParental
+
+ // RuleList is the configuration for rule-list based filtering. It must not
+ // be nil.
+ RuleList *ConfigRuleList
+
+ // SafeBrowsing is the configuration for safe-browsing filtering. It must
+ // not be nil.
+ SafeBrowsing *ConfigSafeBrowsing
+}
+
+// type check
+var _ Config = (*ConfigGroup)(nil)
+
+// isConfig implements the [Config] interface for *ConfigGroup.
+func (*ConfigGroup) isConfig() {}
diff --git a/internal/filter/filter.go b/internal/filter/filter.go
index d567777..1e1848d 100644
--- a/internal/filter/filter.go
+++ b/internal/filter/filter.go
@@ -12,7 +12,14 @@ import (
// Interface is the DNS request and response filter interface.
type Interface = internal.Interface
-// Filtering result aliases
+// Empty is an [Interface] implementation that always returns nil.
+type Empty = internal.Empty
+
+// Request contains information about a request being filtered.
+type Request = internal.Request
+
+// Response contains information about a response being filtered.
+type Response = internal.Response
// Result is a sum type of all possible filtering actions. See the following
// types as implementations:
@@ -39,7 +46,53 @@ type ResultModifiedResponse = internal.ResultModifiedResponse
// within the given filter list.
type ResultModifiedRequest = internal.ResultModifiedRequest
-// Hash matching for safe-browsing and adult-content blocking
+// 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)
+}
// HashMatcher is the interface for a safe-browsing and adult-blocking hash
// matcher, which is used to respond to a TXT query based on the domain name.
@@ -52,3 +105,10 @@ 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/filter_test.go b/internal/filter/filter_test.go
deleted file mode 100644
index 063d5ba..0000000
--- a/internal/filter/filter_test.go
+++ /dev/null
@@ -1,226 +0,0 @@
-package filter_test
-
-import (
- "fmt"
- "net"
- "net/http"
- "net/http/httptest"
- "net/netip"
- "net/url"
- "os"
- "path/filepath"
- "testing"
- "time"
-
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
- "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/filtertest"
- "github.com/AdguardTeam/golibs/logutil/slogutil"
- "github.com/AdguardTeam/golibs/testutil"
- "github.com/stretchr/testify/require"
-)
-
-// testFilterID is the standard ID of the filter for tests.
-const testFilterID agd.FilterListID = "filter"
-
-// testDeviceName is the standard device name for tests. Keep in sync with
-// ./testdata/filter.
-const testDeviceName agd.DeviceName = "My Device"
-
-// testSvcID is the standard ID of the blocked service for tests.
-const testSvcID agd.BlockedServiceID = "service"
-
-// Common constants. Keep in sync with ./testdata/filter and
-// ./safesearchhost.csv.
-const (
- blockedHost = "blocked.example.com"
- blockedFQDN = blockedHost + "."
-
- allowedHost = "allowed.example.com"
- allowedFQDN = allowedHost + "."
-
- blockedClientHost = "blocked-client.example.com"
- blockedClientFQDN = blockedClientHost + "."
-
- allowedClientHost = "allowed-client.example.com"
- allowedClientFQDN = allowedClientHost + "."
-
- blockedDeviceHost = "blocked-device.example.com"
- blockedDeviceFQDN = blockedDeviceHost + "."
-
- allowedDeviceHost = "allowed-device.example.com"
- allowedDeviceFQDN = allowedDeviceHost + "."
-
- otherNetHost = "example.net"
- otherNetFQDN = otherNetHost + "."
- otherOrgFQDN = "example.org."
-
- customHost = "custom.example.com"
- customFQDN = customHost + "."
- customRule = "||" + customHost + "^"
-
- safeSearchHost = "duckduckgo.com"
- safeSearchRespHost = "safe.duckduckgo.com"
-
- safeSearchIPv4Host = "www.yandex.by"
- safeSearchIPv6Host = "www.google.com"
-
- safeBrowsingHost = "scam.example.net"
- safeBrowsingSubHost = "subsub.sub." + safeBrowsingHost
- safeBrowsingSubFQDN = safeBrowsingSubHost + "."
- safeBrowsingReplHost = "safe.dns.example.net"
- safeBrowsingReplFQDN = safeBrowsingReplHost + "."
-)
-
-// Common immutable values. Keep in sync with ./testdata/ files.
-var (
- blockedIP4 = net.IP{6, 6, 6, 13}
- allowedIP4 = net.IP{7, 7, 7, 42}
-
- safeSearchIPRespIP4 = netip.MustParseAddr("213.180.193.56")
- safeSearchIPRespIP6 = netip.MustParseAddr("2001:4860:4802:32::78")
-)
-
-// Common clients. Keep in sync with ./testdata/filter.
-var (
- clientIP = netip.MustParseAddr("1.2.3.4")
- deviceIP = netip.MustParseAddr("5.6.7.8")
-)
-
-// testDataFiltersTmpl is the template for the data returned from the filter
-// index.
-const testDataFiltersTmpl = `{
- "filters": [
- {
- "filterKey": %q,
- "downloadUrl": "http://example.com"
- }
- ]
-}
-`
-
-// testDataFiltersTmpl is the template for the data returned from the blocked
-// service index.
-const testDataServicesTmpl = `{
- "blocked_services": [
- {
- "id": %q,
- "rules": [
- "||service.example.com^"
- ]
- }
- ]
-}
-`
-
-// prepareIndex is a helper that makes a new temporary directory and places the
-// testdata filter file into it as well as launches a test server with an index
-// pointing to that filter, as well as serving as the blocked service index and
-// safe search rules.
-func prepareIndex(t testing.TB) (flts, svcs, ss *url.URL, dir string) {
- t.Helper()
-
- b, err := os.ReadFile(filepath.Join("testdata", string(testFilterID)))
- require.NoError(t, err)
-
- var ssData []byte
- ssData, err = os.ReadFile(filepath.Join("testdata", string(agd.FilterListIDGeneralSafeSearch)))
- require.NoError(t, err)
-
- dir = t.TempDir()
- err = os.WriteFile(filepath.Join(dir, string(testFilterID)), b, 0o644)
- require.NoError(t, err)
-
- srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- pt := testutil.PanicT{}
-
- var werr error
- switch p := r.URL.Path; p {
- case "/services":
- _, werr = fmt.Fprintf(w, testDataServicesTmpl, testSvcID)
- case "/filters":
- _, werr = fmt.Fprintf(w, testDataFiltersTmpl, testFilterID)
- case "/safesearch":
- _, werr = w.Write(ssData)
- default:
- werr = fmt.Errorf("unexpected path %s in url", p)
- }
- require.NoError(pt, werr)
- }))
- t.Cleanup(srv.Close)
-
- var u *url.URL
- u, err = agdhttp.ParseHTTPURL(srv.URL)
- require.NoError(t, err)
-
- flts, svcs, ss = u.JoinPath("filters"), u.JoinPath("services"), u.JoinPath("safesearch")
-
- return flts, svcs, ss, dir
-}
-
-// prepareConf calls [prepareIndex] and returns a new storage config with fields
-// initialized to those from the results as well as some common test values.
-func prepareConf(t testing.TB) (c *filter.DefaultStorageConfig) {
- fltsURL, svcsURL, ssURL, cacheDir := prepareIndex(t)
-
- return &filter.DefaultStorageConfig{
- BaseLogger: slogutil.NewDiscardLogger(),
- BlockedServiceIndexURL: svcsURL,
- FilterIndexURL: fltsURL,
- GeneralSafeSearchRulesURL: ssURL,
- YoutubeSafeSearchRulesURL: ssURL,
- SafeBrowsing: &hashprefix.Filter{},
- AdultBlocking: &hashprefix.Filter{},
- NewRegDomains: &hashprefix.Filter{},
- Now: time.Now,
- ErrColl: nil,
- CacheManager: agdcache.EmptyManager{},
- CacheDir: cacheDir,
- CustomFilterCacheSize: 100,
- SafeSearchCacheSize: 100,
- SafeSearchCacheTTL: filtertest.CacheTTL,
- RuleListCacheSize: 100,
- RefreshIvl: filtertest.Staleness,
- UseRuleListCache: false,
- MaxRuleListSize: filtertest.FilterMaxSize,
- }
-}
-
-// newReqInfo returns a new request information structure with the given data.
-func newReqInfo(
- tb testing.TB,
- g *agd.FilteringGroup,
- p *agd.Profile,
- host string,
- ip netip.Addr,
- qt dnsmsg.RRType,
-) (ri *agd.RequestInfo) {
- tb.Helper()
-
- var d *agd.Device
- if p != nil {
- d = &agd.Device{
- Name: testDeviceName,
- FilteringEnabled: true,
- }
- }
-
- ri = &agd.RequestInfo{
- DeviceResult: &agd.DeviceResultOK{
- Device: d,
- Profile: p,
- },
- FilteringGroup: g,
- Messages: agdtest.NewConstructor(tb),
- Host: host,
- RemoteIP: ip,
- QType: qt,
- }
-
- return ri
-}
diff --git a/internal/filter/filterstorage/config.go b/internal/filter/filterstorage/config.go
new file mode 100644
index 0000000..72173ba
--- /dev/null
+++ b/internal/filter/filterstorage/config.go
@@ -0,0 +1,205 @@
+package filterstorage
+
+import (
+ "log/slog"
+ "net/url"
+ "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/c2h5oh/datasize"
+)
+
+// Config contains configuration for a default filter storage.
+type Config struct {
+ // BaseLogger is used to create loggers with custom prefixes for filters.
+ // It must not be nil.
+ BaseLogger *slog.Logger
+
+ // Logger is used for logging the operation of the storage. It must not be
+ // nil.
+ Logger *slog.Logger
+
+ // BlockedServices is the configuration of a blocked-service filter for a
+ // default filter storage. It must not be nil
+ BlockedServices *ConfigBlockedServices
+
+ // Custom is the configuration of a custom filters storage for a default
+ // filter storage. It must not be nil
+ Custom *ConfigCustom
+
+ // HashPrefix is the hashprefix-filter configuration for a default filter
+ // storage. It must not be nil
+ HashPrefix *ConfigHashPrefix
+
+ // RuleLists is the rule-list configuration for a default filter storage.
+ // It must not be nil.
+ RuleLists *ConfigRuleLists
+
+ // SafeSearchGeneral is the general safe-search configuration for a default
+ // filter storage. It must not be nil.
+ SafeSearchGeneral *ConfigSafeSearch
+
+ // SafeSearchYouTube is the YouTube safe-search configuration for a default
+ // filter storage. It must not be nil.
+ SafeSearchYouTube *ConfigSafeSearch
+
+ // CacheManager is the global cache manager. It must not be nil.
+ CacheManager agdcache.Manager
+
+ // Clock is used for time-related operations, such as schedule checking.
+ // It must not be nil.
+ Clock agdtime.Clock
+
+ // ErrColl is used to collect non-critical and rare errors as well as
+ // refresh errors. It must not be nil.
+ ErrColl errcoll.Interface
+
+ // Metrics are the metrics for the filters in the storage.
+ Metrics filter.Metrics
+
+ // CacheDir is the path to the directory where the cached filter files are
+ // put. It must not be empty and the directory must exist.
+ CacheDir string
+}
+
+// ConfigBlockedServices is the blocked-service filter configuration for a
+// default filter storage.
+type ConfigBlockedServices struct {
+ // IndexURL is the URL of the blocked-service filter index. It must not be
+ // modified after calling [New]. It must not be nil. It is ignored if
+ // [ConfigBlockedServices.Enabled] is false.
+ IndexURL *url.URL
+
+ // IndexMaxSize is the maximum size of the downloadable blocked-service
+ // index content. It must be positive. It is ignored if
+ // [ConfigBlockedServices.Enabled] is false.
+ IndexMaxSize datasize.ByteSize
+
+ // IndexRefreshTimeout is the timeout for the update of the blocked-service
+ // index. It must be positive. It is ignored if
+ // [ConfigBlockedServices.Enabled] is false.
+ IndexRefreshTimeout time.Duration
+
+ // IndexStaleness is the time after which the cached index file is
+ // considered stale. It must be positive. It is ignored if
+ // [ConfigBlockedServices.Enabled] is false.
+ IndexStaleness time.Duration
+
+ // ResultCacheCount is the count of items to keep in the LRU result cache of
+ // the blocked-service filters. It must be greater than zero. It is
+ // ignored if [ConfigBlockedServices.Enabled] is false.
+ ResultCacheCount int
+
+ // ResultCacheEnabled enables caching of results of the blocked-service
+ // filters. It is ignored if [ConfigBlockedServices.Enabled] is false.
+ ResultCacheEnabled bool
+
+ // Enabled shows whether the blocked-service filtering is enabled.
+ Enabled bool
+}
+
+// ConfigCustom is the configuration of a custom filters storage for a default
+// filter storage.
+type ConfigCustom struct {
+ // CacheCount is the count of items to keep in the LRU cache of custom
+ // filters. It must be greater than zero.
+ CacheCount int
+}
+
+// ConfigHashPrefix is the hashprefix-filter configuration for a default filter
+// storage.
+type ConfigHashPrefix struct {
+ // Adult is the optional hashprefix filter for adult content. If nil, no
+ // adult-content filtering is performed.
+ Adult *hashprefix.Filter
+
+ // Dangerous is the optional hashprefix filter for dangerous domains. If
+ // nil, no dangerous-domains filtering is performed.
+ Dangerous *hashprefix.Filter
+
+ // NewlyRegistered is the optional hashprefix filter for newly-registered
+ // domains. If nil, no filter of newly-registered domains is performed.
+ NewlyRegistered *hashprefix.Filter
+}
+
+// ConfigRuleLists is the rule-list configuration for a default filter storage.
+type ConfigRuleLists struct {
+ // IndexURL is the URL of the rule-list filter index. It must not be
+ // modified after calling [New]. It must not be nil.
+ IndexURL *url.URL
+
+ // IndexMaxSize is the maximum size of the downloadable filter-index
+ // content. It must be positive.
+ IndexMaxSize datasize.ByteSize
+
+ // MaxSize is the maximum size of the content of a single rule-list filter.
+ // It must be positive.
+ MaxSize datasize.ByteSize
+
+ // IndexRefreshTimeout is the timeout for the update of the rule-list filter
+ // index. It must be positive.
+ IndexRefreshTimeout time.Duration
+
+ // IndexStaleness is the time after which the cached index file is
+ // considered stale. It must be positive.
+ IndexStaleness time.Duration
+
+ // RefreshTimeout is the timeout for the update of a single rule-list
+ // filter. It must be positive.
+ RefreshTimeout time.Duration
+
+ // Staleness is the time after which the cached filter files are considered
+ // stale. It must be positive.
+ Staleness time.Duration
+
+ // ResultCacheCount is the count of items to keep in the LRU result cache of
+ // a single rule-list filter. It must be greater than zero.
+ ResultCacheCount int
+
+ // ResultCacheEnabled enables caching of results of the rule-list filters.
+ ResultCacheEnabled bool
+}
+
+// ConfigSafeSearch is the single safe-search configuration for a default filter
+// storage.
+type ConfigSafeSearch struct {
+ // URL is the HTTP(S) URL of the safe-search rules list. It must not be
+ // modified after calling [New]. It must not be nil. It is ignored if
+ // [ConfigSafeSearch.Enabled] is false.
+ URL *url.URL
+
+ // ID is the identifier of this safe-search filter. It must not be empty.
+ // It is ignored if [ConfigSafeSearch.Enabled] is false.
+ ID filter.ID
+
+ // MaxSize is the maximum size of the downloadable filter content. It must
+ // be positive. It is ignored if [ConfigSafeSearch.Enabled] is false.
+ MaxSize datasize.ByteSize
+
+ // ResultCacheTTL is the time to live of the items in the result cache. It
+ // must be positive. It is ignored if [ConfigSafeSearch.Enabled] is false.
+ //
+ // TODO(a.garipov): Currently unused. See AGDNS-398.
+ ResultCacheTTL time.Duration
+
+ // RefreshTimeout is the timeout for the update of a safe-search filter. It
+ // must be positive. It is ignored if [ConfigSafeSearch.Enabled] is false.
+ RefreshTimeout time.Duration
+
+ // Staleness is the time after which the cached filter files are considered
+ // stale. It must be positive. It is ignored if [ConfigSafeSearch.Enabled]
+ // is false.
+ Staleness time.Duration
+
+ // ResultCacheCount is the count of items to keep in the LRU result cache of
+ // a safe-search filter. It must be positive. It is ignored if
+ // [ConfigSafeSearch.Enabled] is false.
+ ResultCacheCount int
+
+ // Enabled shows whether this safe-search filter is enabled.
+ Enabled bool
+}
diff --git a/internal/filter/filterstorage/default.go b/internal/filter/filterstorage/default.go
new file mode 100644
index 0000000..e7a5acc
--- /dev/null
+++ b/internal/filter/filterstorage/default.go
@@ -0,0 +1,393 @@
+package filterstorage
+
+import (
+ "context"
+ "fmt"
+ "log/slog"
+ "path"
+ "path/filepath"
+ "sync"
+ "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/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].
+type Default struct {
+ baseLogger *slog.Logger
+ logger *slog.Logger
+
+ services *serviceblock.Filter
+
+ custom *custom.Filters
+
+ adult *hashprefix.Filter
+ dangerous *hashprefix.Filter
+ newlyRegistered *hashprefix.Filter
+
+ safeSearchGeneral *safesearch.Filter
+ safeSearchYouTube *safesearch.Filter
+
+ // ruleListsMu protects [Default.ruleLists].
+ ruleListsMu *sync.RWMutex
+ ruleLists ruleLists
+
+ ruleListIdxRefr *refreshable.Refreshable
+
+ cacheManager agdcache.Manager
+ clock agdtime.Clock
+ errColl errcoll.Interface
+ metrics filter.Metrics
+
+ cacheDir string
+
+ ruleListStaleness time.Duration
+ ruleListRefreshTimeout time.Duration
+
+ ruleListMaxSize datasize.ByteSize
+
+ ruleListResCacheCount int
+ serviceResCacheCount int
+
+ ruleListCacheEnabled bool
+ serviceResCacheEnabled bool
+}
+
+// ruleLists is convenient alias for an ID to filter mapping.
+type ruleLists = map[filter.ID]*rulelist.Refreshable
+
+// New returns a new default filter storage ready for initial refresh with
+// [Default.RefreshInitial]. c must not be nil.
+func New(c *Config) (s *Default, err error) {
+ // NOTE: Since this is a large and complex structure, enumerate all fields
+ // explicitly here to make it easier to recheck changes later.
+ s = &Default{
+ baseLogger: c.BaseLogger,
+ logger: c.Logger,
+
+ // Initialized in [Default.initBlockedServices].
+ services: nil,
+
+ // Initialized in [Default.initCustom].
+ custom: nil,
+
+ adult: c.HashPrefix.Adult,
+ dangerous: c.HashPrefix.Dangerous,
+ newlyRegistered: c.HashPrefix.NewlyRegistered,
+
+ // Initialized in [Default.initSafeSearch].
+ safeSearchGeneral: nil,
+ safeSearchYouTube: nil,
+
+ ruleListsMu: &sync.RWMutex{},
+
+ // Initialized in [Default.RefreshInitial].
+ ruleLists: nil,
+
+ // Initialized in [Default.initRuleListRefr].
+ ruleListIdxRefr: nil,
+
+ cacheManager: c.CacheManager,
+ clock: c.Clock,
+ errColl: c.ErrColl,
+ metrics: c.Metrics,
+
+ cacheDir: c.CacheDir,
+
+ ruleListStaleness: c.RuleLists.Staleness,
+ ruleListRefreshTimeout: c.RuleLists.RefreshTimeout,
+
+ ruleListMaxSize: c.RuleLists.MaxSize,
+
+ ruleListResCacheCount: c.RuleLists.ResultCacheCount,
+ serviceResCacheCount: c.BlockedServices.ResultCacheCount,
+
+ ruleListCacheEnabled: c.RuleLists.ResultCacheEnabled,
+ serviceResCacheEnabled: c.BlockedServices.ResultCacheEnabled,
+ }
+
+ err = s.init(c)
+ if err != nil {
+ // Don't wrap the error, because it's informative enough as is.
+ return nil, err
+ }
+
+ return s, nil
+}
+
+// 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 {
+ // Don't wrap the error, because it's informative enough as is.
+ errs = append(errs, err)
+ }
+
+ err = s.initSafeSearch(c.SafeSearchGeneral, c.SafeSearchYouTube)
+ if err != nil {
+ // Don't wrap the error, because it's informative enough as is.
+ errs = append(errs, err)
+ }
+
+ err = s.initRuleListRefr(c.RuleLists)
+ if err != nil {
+ // Don't wrap the error, because it's informative enough as is.
+ errs = append(errs, err)
+ }
+
+ 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) {
+ if !c.Enabled {
+ return nil
+ }
+
+ refrConf := &refreshable.Config{
+ Logger: s.baseLogger.With(
+ slogutil.KeyPrefix, path.Join("filters", string(FilterIDBlockedServiceIndex)),
+ ),
+ URL: c.IndexURL,
+ ID: filter.ID(FilterIDBlockedServiceIndex),
+ CachePath: filepath.Join(s.cacheDir, indexFileNameBlockedServices),
+ Staleness: c.IndexStaleness,
+ Timeout: c.IndexRefreshTimeout,
+ MaxSize: c.IndexMaxSize,
+ }
+
+ s.services, err = serviceblock.New(&serviceblock.Config{
+ Refreshable: refrConf,
+ ErrColl: s.errColl,
+ Metrics: s.metrics,
+ })
+ if err != nil {
+ return fmt.Errorf("blocked-service filter: %w", err)
+ }
+
+ return nil
+}
+
+// initSafeSearch initializes the safe-search filters in s. gen and yt must not
+// be nil.
+func (s *Default) initSafeSearch(gen, yt *ConfigSafeSearch) (err error) {
+ s.safeSearchGeneral, err = newSafeSearch(s.baseLogger, gen, s.cacheManager, s.cacheDir)
+ if err != nil {
+ return fmt.Errorf("general safe search: %w", err)
+ }
+
+ s.safeSearchYouTube, err = newSafeSearch(s.baseLogger, yt, s.cacheManager, s.cacheDir)
+ if err != nil {
+ return fmt.Errorf("youtube safe search: %w", err)
+ }
+
+ return nil
+}
+
+// newSafeSearch returns a new safe-search filter for the storage. All
+// arguments must not be empty.
+func newSafeSearch(
+ baseLogger *slog.Logger,
+ c *ConfigSafeSearch,
+ cacheMgr agdcache.Manager,
+ cacheDir string,
+) (f *safesearch.Filter, err error) {
+ if !c.Enabled {
+ return nil, nil
+ }
+
+ fltIDStr := string(c.ID)
+ cacheID := path.Join(cachePrefixSafeSearch, fltIDStr)
+ cache := rulelist.NewManagedResultCache(cacheMgr, cacheID, c.ResultCacheCount, true)
+
+ return safesearch.New(
+ &safesearch.Config{
+ Refreshable: &refreshable.Config{
+ Logger: baseLogger.With(slogutil.KeyPrefix, cacheID),
+ URL: c.URL,
+ ID: c.ID,
+ CachePath: filepath.Join(cacheDir, fltIDStr),
+ Staleness: c.Staleness,
+ Timeout: c.RefreshTimeout,
+ MaxSize: c.MaxSize,
+ },
+ CacheTTL: c.ResultCacheTTL,
+ },
+ cache,
+ )
+}
+
+// initRuleListRefr initializes the rule-list refresher in s. c must not be
+// nil.
+func (s *Default) initRuleListRefr(c *ConfigRuleLists) (err error) {
+ s.ruleListIdxRefr, err = refreshable.New(&refreshable.Config{
+ Logger: s.baseLogger.With(
+ slogutil.KeyPrefix, path.Join("filters", string(FilterIDRuleListIndex)),
+ ),
+ URL: c.IndexURL,
+ ID: filter.ID(FilterIDRuleListIndex),
+ CachePath: filepath.Join(s.cacheDir, indexFileNameRuleLists),
+ Staleness: c.IndexStaleness,
+ Timeout: c.IndexRefreshTimeout,
+ MaxSize: c.IndexMaxSize,
+ })
+ if err != nil {
+ return fmt.Errorf("rule-list index: %w", err)
+ }
+
+ return nil
+}
+
+// type check
+var _ filter.Storage = (*Default)(nil)
+
+// ForConfig implements the [filter.Storage] interface for *Default.
+func (s *Default) ForConfig(ctx context.Context, c filter.Config) (f filter.Interface) {
+ switch c := c.(type) {
+ case nil:
+ return filter.Empty{}
+ case *filter.ConfigClient:
+ return s.forClient(ctx, c)
+ case *filter.ConfigGroup:
+ return s.forGroup(ctx, c)
+ default:
+ panic(fmt.Errorf("filter config: %w: %T(%[2]v)", errors.ErrBadEnumValue, c))
+ }
+}
+
+// forClient returns a new filter based on a client configuration. c must not
+// be nil.
+func (s *Default) forClient(ctx context.Context, c *filter.ConfigClient) (f filter.Interface) {
+ compConf := &composite.Config{}
+
+ s.setParental(ctx, compConf, c.Parental)
+ s.setRuleLists(compConf, c.RuleList)
+ s.setSafeBrowsing(compConf, c.SafeBrowsing)
+
+ compConf.Custom = s.custom.Get(ctx, c.Custom)
+
+ return composite.New(compConf)
+}
+
+// setParental sets the parental-control filters in compConf from c. c must not
+// be nil.
+func (s *Default) setParental(
+ ctx context.Context,
+ compConf *composite.Config,
+ c *filter.ConfigParental,
+) {
+ if !c.Enabled {
+ return
+ }
+
+ pause := c.PauseSchedule
+ if pause != nil && pause.Contains(s.clock.Now()) {
+ return
+ }
+
+ if c.AdultBlockingEnabled {
+ compConf.AdultBlocking = s.adult
+ }
+
+ if c.SafeSearchGeneralEnabled {
+ compConf.GeneralSafeSearch = s.safeSearchGeneral
+ }
+
+ if c.SafeSearchYouTubeEnabled {
+ compConf.YouTubeSafeSearch = s.safeSearchYouTube
+ }
+
+ if len(c.BlockedServices) > 0 && s.services != nil {
+ compConf.ServiceLists = s.services.RuleLists(ctx, c.BlockedServices)
+ }
+}
+
+// setRuleLists sets the rule-list filters in compConf from c. c must not be
+// nil.
+func (s *Default) setRuleLists(compConf *composite.Config, c *filter.ConfigRuleList) {
+ if !c.Enabled || len(c.IDs) == 0 {
+ return
+ }
+
+ s.ruleListsMu.RLock()
+ defer s.ruleListsMu.RUnlock()
+
+ for _, id := range c.IDs {
+ rl := s.ruleLists[id]
+ if rl != nil {
+ compConf.RuleLists = append(compConf.RuleLists, rl)
+ }
+ }
+}
+
+// setSafeBrowsing sets the safe-browsing filters in compConf from c. c must
+// not be nil.
+func (s *Default) setSafeBrowsing(compConf *composite.Config, c *filter.ConfigSafeBrowsing) {
+ if !c.Enabled {
+ return
+ }
+
+ if c.DangerousDomainsEnabled {
+ compConf.SafeBrowsing = s.dangerous
+ }
+
+ if c.NewlyRegisteredDomainsEnabled {
+ compConf.NewRegisteredDomains = s.newlyRegistered
+ }
+}
+
+// forGroup returns a new filter based on a group configuration. c must not be
+// nil.
+func (s *Default) forGroup(ctx context.Context, c *filter.ConfigGroup) (f filter.Interface) {
+ compConf := &composite.Config{}
+
+ s.setParental(ctx, compConf, c.Parental)
+ s.setRuleLists(compConf, c.RuleList)
+ s.setSafeBrowsing(compConf, c.SafeBrowsing)
+
+ return composite.New(compConf)
+}
+
+// HasListID implements the [filter.Storage] interface for *Default.
+func (s *Default) HasListID(id filter.ID) (ok bool) {
+ s.ruleListsMu.RLock()
+ defer s.ruleListsMu.RUnlock()
+
+ _, ok = s.ruleLists[id]
+
+ return ok
+}
diff --git a/internal/filter/filterstorage/default_test.go b/internal/filter/filterstorage/default_test.go
new file mode 100644
index 0000000..384e06b
--- /dev/null
+++ b/internal/filter/filterstorage/default_test.go
@@ -0,0 +1,422 @@
+package filterstorage_test
+
+import (
+ "net/url"
+ "testing"
+ "time"
+
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
+ "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/container"
+ "github.com/AdguardTeam/golibs/netutil/urlutil"
+ "github.com/AdguardTeam/golibs/testutil"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestNew(t *testing.T) {
+ t.Parallel()
+
+ indexURL := &url.URL{
+ Scheme: urlutil.SchemeHTTP,
+ Host: "index.example",
+ }
+
+ servicesDisabled := &filterstorage.ConfigBlockedServices{
+ Enabled: false,
+ }
+
+ safeSearchGeneralDisabled := &filterstorage.ConfigSafeSearch{
+ ID: filter.IDGeneralSafeSearch,
+ Enabled: false,
+ }
+
+ safeSearchYouTubeDisabled := &filterstorage.ConfigSafeSearch{
+ ID: filter.IDYoutubeSafeSearch,
+ Enabled: false,
+ }
+
+ testCases := []struct {
+ services *filterstorage.ConfigBlockedServices
+ safeSearchGen *filterstorage.ConfigSafeSearch
+ safeSearchYT *filterstorage.ConfigSafeSearch
+ name string
+ }{{
+ services: servicesDisabled,
+ safeSearchGen: safeSearchGeneralDisabled,
+ safeSearchYT: safeSearchYouTubeDisabled,
+ name: "empty",
+ }, {
+ services: newConfigBlockedServices(indexURL),
+ safeSearchGen: safeSearchGeneralDisabled,
+ safeSearchYT: safeSearchYouTubeDisabled,
+ name: "blocked_services",
+ }, {
+ services: servicesDisabled,
+ safeSearchGen: newConfigSafeSearch(indexURL, filter.IDGeneralSafeSearch),
+ safeSearchYT: safeSearchYouTubeDisabled,
+ name: "safe_search_general",
+ }, {
+ services: servicesDisabled,
+ safeSearchGen: safeSearchGeneralDisabled,
+ safeSearchYT: newConfigSafeSearch(indexURL, filter.IDYoutubeSafeSearch),
+ name: "safe_search_youtube",
+ }}
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+
+ c := newDisabledConfig(t, newConfigRuleLists(indexURL))
+ c.BlockedServices = tc.services
+ c.RuleLists = newConfigRuleLists(indexURL)
+ c.SafeSearchGeneral = tc.safeSearchGen
+ c.SafeSearchYouTube = tc.safeSearchYT
+ s, err := filterstorage.New(c)
+
+ assert.NotNil(t, s)
+ assert.NoError(t, err)
+ })
+ }
+}
+
+func TestDefault_ForConfig_nil(t *testing.T) {
+ t.Parallel()
+
+ ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
+ s := newDefault(t)
+ f := s.ForConfig(ctx, nil)
+ require.NotNil(t, f)
+
+ assert.IsType(t, filter.Empty{}, f)
+}
+
+func TestDefault_ForConfig_client(t *testing.T) {
+ t.Parallel()
+
+ s := newDefault(t)
+
+ require.True(t, t.Run("custom", func(t *testing.T) {
+ conf := newFltConfigCli(
+ newFltConfigParental(false, false, false, false),
+ newFltConfigRuleList(false),
+ newFltConfigSafeBrowsing(false, false),
+ )
+
+ conf.Custom.ID = "1234"
+ conf.Custom.UpdateTime = time.Now()
+ conf.Custom.Rules = []filter.RuleText{
+ filtertest.RuleBlock,
+ }
+ conf.Custom.Enabled = true
+
+ ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
+ f := s.ForConfig(ctx, conf)
+ require.NotNil(t, f)
+
+ ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
+ r, err := f.FilterRequest(ctx, filtertest.NewARequest(t, filtertest.HostBlocked))
+ require.NoError(t, err)
+
+ wantRes := &filter.ResultBlocked{
+ List: filter.IDCustom,
+ Rule: filtertest.RuleBlock,
+ }
+
+ filtertest.AssertEqualResult(t, wantRes, r)
+ }))
+
+ require.True(t, t.Run("schedule", func(t *testing.T) {
+ conf := newFltConfigCli(
+ newFltConfigParental(false, true, false, false),
+ newFltConfigRuleList(false),
+ newFltConfigSafeBrowsing(false, false),
+ )
+
+ now := time.Now()
+
+ // Use a slice, because array indexes must be constant.
+ week := make([]*filter.DayInterval, 7)
+ week[now.Weekday()] = &filter.DayInterval{
+ Start: 0,
+ End: filter.MaxDayIntervalEndMinutes,
+ }
+
+ conf.Parental.PauseSchedule = &filter.ConfigSchedule{
+ Week: (*filter.WeeklySchedule)(week),
+ TimeZone: agdtime.UTC(),
+ }
+
+ ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
+ f := s.ForConfig(ctx, conf)
+ require.NotNil(t, f)
+
+ ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
+ r, err := f.FilterRequest(ctx, filtertest.NewARequest(t, filtertest.HostAdultContent))
+ require.NoError(t, err)
+
+ assert.Nil(t, r)
+ }))
+}
+
+func TestDefault_ForConfig_common(t *testing.T) {
+ t.Parallel()
+
+ testCases := []struct {
+ parental *filter.ConfigParental
+ ruleList *filter.ConfigRuleList
+ safeBrowsing *filter.ConfigSafeBrowsing
+ name string
+ }{{
+ parental: newFltConfigParental(false, false, false, false),
+ ruleList: newFltConfigRuleList(false),
+ safeBrowsing: newFltConfigSafeBrowsing(false, false),
+ name: "empty",
+ }, {
+ parental: newFltConfigParental(true, false, false, false),
+ ruleList: newFltConfigRuleList(false),
+ safeBrowsing: newFltConfigSafeBrowsing(false, false),
+ name: "adult_content",
+ }, {
+ parental: newFltConfigParental(false, true, false, false),
+ ruleList: newFltConfigRuleList(false),
+ safeBrowsing: newFltConfigSafeBrowsing(false, false),
+ name: "blocked_service",
+ }, {
+ parental: newFltConfigParental(false, false, false, false),
+ ruleList: newFltConfigRuleList(false),
+ safeBrowsing: newFltConfigSafeBrowsing(true, false),
+ name: "dangerous",
+ }, {
+ parental: newFltConfigParental(false, false, false, false),
+ ruleList: newFltConfigRuleList(false),
+ safeBrowsing: newFltConfigSafeBrowsing(false, true),
+ name: "newly_registered",
+ }, {
+ parental: newFltConfigParental(false, false, false, false),
+ ruleList: newFltConfigRuleList(true),
+ safeBrowsing: newFltConfigSafeBrowsing(false, false),
+ name: "rule_list_blocked",
+ }, {
+ parental: newFltConfigParental(false, false, true, false),
+ ruleList: newFltConfigRuleList(false),
+ safeBrowsing: newFltConfigSafeBrowsing(false, false),
+ name: "safe_search_general",
+ }, {
+ parental: newFltConfigParental(false, false, false, true),
+ ruleList: newFltConfigRuleList(false),
+ safeBrowsing: newFltConfigSafeBrowsing(false, false),
+ name: "safe_search_youtube",
+ }, {
+ parental: newFltConfigParental(true, true, true, true),
+ ruleList: newFltConfigRuleList(true),
+ safeBrowsing: newFltConfigSafeBrowsing(true, false),
+ name: "all",
+ }}
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+
+ s := newDefault(t)
+
+ ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
+ cliFlt := s.ForConfig(ctx, newFltConfigCli(tc.parental, tc.ruleList, tc.safeBrowsing))
+ require.NotNil(t, cliFlt)
+
+ ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
+ grpFlt := s.ForConfig(ctx, &filter.ConfigGroup{
+ Parental: tc.parental,
+ RuleList: tc.ruleList,
+ SafeBrowsing: tc.safeBrowsing,
+ })
+ require.NotNil(t, grpFlt)
+
+ t.Run("client", func(t *testing.T) {
+ assertFilterResults(t, cliFlt, tc.parental, tc.ruleList, tc.safeBrowsing)
+ })
+
+ t.Run("group", func(t *testing.T) {
+ assertFilterResults(t, grpFlt, tc.parental, tc.ruleList, tc.safeBrowsing)
+ })
+ })
+ }
+}
+
+// newFltConfigParental returns a *filter.FilterConfigParental with the
+// features properly enabled or disabled.
+func newFltConfigParental(hpAdult, svc, ssGen, ssYT bool) (c *filter.ConfigParental) {
+ c = &filter.ConfigParental{
+ Enabled: svc || hpAdult || ssGen || ssYT,
+ AdultBlockingEnabled: hpAdult,
+ SafeSearchGeneralEnabled: ssGen,
+ SafeSearchYouTubeEnabled: ssYT,
+ }
+
+ if svc {
+ c.BlockedServices = []filter.BlockedServiceID{
+ filtertest.BlockedServiceID1,
+ }
+ }
+
+ return c
+}
+
+// newFltConfigRuleList returns a *filter.FilterConfigRuleList that is
+// either enabled with one rule-list filter or is disabled.
+func newFltConfigRuleList(enabled bool) (c *filter.ConfigRuleList) {
+ c = &filter.ConfigRuleList{
+ Enabled: enabled,
+ }
+
+ if enabled {
+ c.IDs = []filter.ID{
+ filtertest.RuleListID1,
+ }
+ }
+
+ return c
+}
+
+// newFltConfigSafeBrowsing returns a *filter.FilterConfigSafeBrowsing
+// with the features properly enabled or disabled.
+func newFltConfigSafeBrowsing(hpDanger, hpNew bool) (c *filter.ConfigSafeBrowsing) {
+ return &filter.ConfigSafeBrowsing{
+ Enabled: hpDanger || hpNew,
+ DangerousDomainsEnabled: hpDanger,
+ NewlyRegisteredDomainsEnabled: hpNew,
+ }
+}
+
+// newFltConfigCli returns a *filter.FilterConfigClient with the given
+// configs as well as the additional necessary for a client's filter
+// configuration.
+func newFltConfigCli(
+ pConf *filter.ConfigParental,
+ rlConf *filter.ConfigRuleList,
+ sbConf *filter.ConfigSafeBrowsing,
+) (c *filter.ConfigClient) {
+ return &filter.ConfigClient{
+ Custom: &filter.ConfigCustom{
+ Enabled: false,
+ },
+ Parental: pConf,
+ RuleList: rlConf,
+ SafeBrowsing: sbConf,
+ }
+}
+
+// assertFilterResults is a test helper for asserting a filter's results based
+// on the configuration.
+func assertFilterResults(
+ tb testing.TB,
+ flt filter.Interface,
+ pConf *filter.ConfigParental,
+ rlConf *filter.ConfigRuleList,
+ sbConf *filter.ConfigSafeBrowsing,
+) {
+ tb.Helper()
+
+ assertFilterResultsParental(tb, flt, pConf)
+ assertFilterResultsRuleList(tb, flt, rlConf)
+ assertFilterResultsSafeBrowsing(tb, flt, sbConf)
+}
+
+// assertFilterResultsParental is a test helper for asserting a filter's results
+// based on parental-protection configuration.
+func assertFilterResultsParental(tb testing.TB, f filter.Interface, c *filter.ConfigParental) {
+ tb.Helper()
+
+ var wantResAdult, wantResSSGen, wantResSSYT, wantResSvc filter.Result
+ if c.Enabled {
+ if c.AdultBlockingEnabled {
+ wantResAdult = resultAdult
+ }
+
+ if c.SafeSearchGeneralEnabled {
+ wantResSSGen = resultSafeSearchGen
+ }
+
+ if c.SafeSearchYouTubeEnabled {
+ wantResSSYT = resultSafeSearchYT
+ }
+
+ if len(c.BlockedServices) > 0 {
+ wantResSvc = resultBlockedSvc
+ }
+ }
+
+ checks := container.KeyValues[string, filter.Result]{{
+ Key: filtertest.HostAdultContent,
+ Value: wantResAdult,
+ }, {
+ Key: filtertest.HostSafeSearchGeneral,
+ Value: wantResSSGen,
+ }, {
+ Key: filtertest.HostSafeSearchYouTube,
+ Value: wantResSSYT,
+ }, {
+ Key: filtertest.HostBlockedService1,
+ Value: wantResSvc,
+ }}
+
+ for _, c := range checks {
+ ctx := testutil.ContextWithTimeout(tb, filtertest.Timeout)
+ r, err := f.FilterRequest(ctx, filtertest.NewARequest(tb, c.Key))
+ require.NoError(tb, err)
+
+ filtertest.AssertEqualResult(tb, c.Value, r)
+ }
+}
+
+// assertFilterResultsRuleList is a test helper for asserting a filter's results
+// based on rule-list configuration.
+func assertFilterResultsRuleList(tb testing.TB, f filter.Interface, c *filter.ConfigRuleList) {
+ tb.Helper()
+
+ ctx := testutil.ContextWithTimeout(tb, filtertest.Timeout)
+ r, err := f.FilterRequest(ctx, filtertest.NewARequest(tb, filtertest.HostBlocked))
+ require.NoError(tb, err)
+
+ var wantRes filter.Result
+ if c.Enabled {
+ wantRes = resultRuleList
+ }
+
+ filtertest.AssertEqualResult(tb, wantRes, r)
+}
+
+// assertFilterResultsSafeBrowsing is a test helper for asserting a filter's
+// results based on safe-browsing configuration.
+func assertFilterResultsSafeBrowsing(
+ tb testing.TB,
+ f filter.Interface,
+ c *filter.ConfigSafeBrowsing,
+) {
+ tb.Helper()
+
+ var wantResDanger, wantResNewReg filter.Result
+ if c.Enabled {
+ if c.DangerousDomainsEnabled {
+ wantResDanger = resultDanger
+ }
+
+ if c.NewlyRegisteredDomainsEnabled {
+ wantResNewReg = resultNewReg
+ }
+ }
+
+ ctx := testutil.ContextWithTimeout(tb, filtertest.Timeout)
+ r, err := f.FilterRequest(ctx, filtertest.NewARequest(tb, filtertest.HostDangerous))
+ require.NoError(tb, err)
+
+ filtertest.AssertEqualResult(tb, wantResDanger, r)
+
+ ctx = testutil.ContextWithTimeout(tb, filtertest.Timeout)
+ r, err = f.FilterRequest(ctx, filtertest.NewARequest(tb, filtertest.HostNewlyRegistered))
+ require.NoError(tb, err)
+
+ filtertest.AssertEqualResult(tb, wantResNewReg, r)
+}
diff --git a/internal/filter/filterstorage/filterstorage.go b/internal/filter/filterstorage/filterstorage.go
new file mode 100644
index 0000000..0d057fe
--- /dev/null
+++ b/internal/filter/filterstorage/filterstorage.go
@@ -0,0 +1,30 @@
+// Package filterstorage defines an interface for a storage of filters as well
+// as the default implementation and the filter configuration.
+package filterstorage
+
+import (
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
+)
+
+// Additional synthetic filter IDs for refreshable indexes.
+//
+// TODO(a.garipov): Consider using a separate type.
+const (
+ FilterIDBlockedServiceIndex filter.ID = "blocked_service_index"
+ FilterIDRuleListIndex filter.ID = "rule_list_index"
+)
+
+// Filenames for filter indexes.
+const (
+ indexFileNameBlockedServices = "services.json"
+ indexFileNameRuleLists = "filters.json"
+)
+
+// Constants that define cache identifiers for the cache manager.
+const (
+ // cachePrefixSafeSearch is used as a cache prefix for safe-search filters.
+ cachePrefixSafeSearch = "filters/safe_search"
+
+ // cachePrefixRuleList is used a cache prefix for rule-list filters.
+ cachePrefixRuleList = "filters/rulelist"
+)
diff --git a/internal/filter/filterstorage/filterstorage_test.go b/internal/filter/filterstorage/filterstorage_test.go
new file mode 100644
index 0000000..b204ded
--- /dev/null
+++ b/internal/filter/filterstorage/filterstorage_test.go
@@ -0,0 +1,218 @@
+package filterstorage_test
+
+import (
+ "net/http"
+ "net/url"
+ "testing"
+
+ "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/miekg/dns"
+ "github.com/stretchr/testify/require"
+)
+
+// unit is a convenient alias for struct{}.
+type unit = struct{}
+
+// Common filtering results.
+var (
+ resultAdult = &filter.ResultModifiedRequest{
+ Msg: dnsservertest.NewReq(filtertest.FQDNAdultContentRepl, dns.TypeA, dns.ClassINET),
+ List: filter.IDAdultBlocking,
+ Rule: filter.RuleText(filtertest.HostAdultContent),
+ }
+
+ resultBlockedSvc = &filter.ResultBlocked{
+ List: filter.IDBlockedService,
+ Rule: filter.RuleText(filtertest.BlockedServiceID1),
+ }
+
+ resultDanger = &filter.ResultModifiedRequest{
+ Msg: dnsservertest.NewReq(filtertest.FQDNDangerousRepl, dns.TypeA, dns.ClassINET),
+ List: filter.IDSafeBrowsing,
+ Rule: filter.RuleText(filtertest.HostDangerous),
+ }
+
+ resultNewReg = &filter.ResultModifiedRequest{
+ Msg: dnsservertest.NewReq(filtertest.FQDNNewlyRegisteredRepl, dns.TypeA, dns.ClassINET),
+ List: filter.IDNewRegDomains,
+ Rule: filter.RuleText(filtertest.HostNewlyRegistered),
+ }
+
+ resultRuleList = &filter.ResultBlocked{
+ List: filtertest.RuleListID1,
+ Rule: filtertest.RuleBlock,
+ }
+
+ resultSafeSearchGen = &filter.ResultModifiedRequest{
+ Msg: dnsservertest.NewReq(filtertest.FQDNSafeSearchGeneralRepl, dns.TypeA, dns.ClassINET),
+ List: filter.IDGeneralSafeSearch,
+ Rule: filter.RuleText(filtertest.HostSafeSearchGeneral),
+ }
+
+ resultSafeSearchYT = &filter.ResultModifiedRequest{
+ Msg: dnsservertest.NewReq(filtertest.FQDNSafeSearchYouTubeRepl, dns.TypeA, dns.ClassINET),
+ List: filter.IDYoutubeSafeSearch,
+ Rule: filter.RuleText(filtertest.HostSafeSearchYouTube),
+ }
+)
+
+// newDefault returns a fully ready and initially refreshed
+// [*filterstorage.Default] for tests. It has the following filters:
+//
+// - A rule-list index with one filter with ID [filtertest.RuleListID1] and a
+// rule to block [filtertest.HostBlocked].
+// - Safe-search filters, both general and YouTube, with rules for
+// [filtertest.HostSafeSearchGeneral] and
+// [filtertest.HostSafeSearchYouTube].
+// - A blocked-service index with one service with ID
+// [filtertest.BlockedServiceID1] blocking [filtertest.HostBlockedService1].
+// - All hash-prefix filters, which block [filtertest.HostAdultContent],
+// [filtertest.HostDangerous], and [filtertest.HostNewlyRegistered].
+func newDefault(tb testing.TB) (s *filterstorage.Default) {
+ const (
+ blockData = filtertest.RuleBlockStr + "\n"
+ ssGenData = filtertest.RuleSafeSearchGeneralHostStr + "\n"
+ ssYTData = filtertest.RuleSafeSearchYouTubeStr + "\n"
+ )
+
+ rlCh := make(chan unit, 1)
+ _, ruleListURL := filtertest.PrepareRefreshable(tb, rlCh, blockData, http.StatusOK)
+ rlIdxData := filtertest.NewRuleListIndex(ruleListURL.String())
+
+ rlIdxCh := make(chan unit, 1)
+ _, ruleListIdxURL := filtertest.PrepareRefreshable(
+ tb,
+ rlIdxCh,
+ string(rlIdxData),
+ http.StatusOK,
+ )
+
+ ssGenCh, ssYTCh := make(chan unit, 1), make(chan unit, 1)
+ _, safeSearchGenURL := filtertest.PrepareRefreshable(tb, ssGenCh, ssGenData, http.StatusOK)
+ _, safeSearchYTURL := filtertest.PrepareRefreshable(tb, ssYTCh, ssYTData, http.StatusOK)
+
+ svcIdxCh := make(chan unit, 1)
+ _, svcIdxURL := filtertest.PrepareRefreshable(
+ tb,
+ svcIdxCh,
+ filtertest.BlockedServiceIndex,
+ http.StatusOK,
+ )
+
+ c := newDisabledConfig(tb, newConfigRuleLists(ruleListIdxURL))
+ c.BlockedServices = newConfigBlockedServices(svcIdxURL)
+ c.HashPrefix = &filterstorage.ConfigHashPrefix{
+ Adult: filtertest.NewHashprefixFilter(tb, filter.IDAdultBlocking),
+ Dangerous: filtertest.NewHashprefixFilter(tb, filter.IDSafeBrowsing),
+ NewlyRegistered: filtertest.NewHashprefixFilter(tb, filter.IDNewRegDomains),
+ }
+ c.SafeSearchGeneral = newConfigSafeSearch(safeSearchGenURL, filter.IDGeneralSafeSearch)
+ c.SafeSearchYouTube = newConfigSafeSearch(safeSearchYTURL, filter.IDYoutubeSafeSearch)
+
+ s, err := filterstorage.New(c)
+ require.NoError(tb, err)
+
+ ctx := testutil.ContextWithTimeout(tb, filtertest.Timeout)
+ err = s.RefreshInitial(ctx)
+ require.NoError(tb, err)
+
+ testutil.RequireReceive(tb, rlCh, filtertest.Timeout)
+ testutil.RequireReceive(tb, rlIdxCh, filtertest.Timeout)
+ testutil.RequireReceive(tb, ssGenCh, filtertest.Timeout)
+ testutil.RequireReceive(tb, ssYTCh, filtertest.Timeout)
+ testutil.RequireReceive(tb, svcIdxCh, filtertest.Timeout)
+
+ return s
+}
+
+// newDisabledConfig returns a new [*filterstorage.Config] with fields related
+// to filters set to disabled (if possible) and others, to the default test
+// entities.
+func newDisabledConfig(
+ tb testing.TB,
+ rlConf *filterstorage.ConfigRuleLists,
+) (c *filterstorage.Config) {
+ tb.Helper()
+
+ return &filterstorage.Config{
+ BaseLogger: slogutil.NewDiscardLogger(),
+ Logger: slogutil.NewDiscardLogger(),
+ BlockedServices: &filterstorage.ConfigBlockedServices{
+ Enabled: false,
+ },
+ Custom: &filterstorage.ConfigCustom{
+ CacheCount: filtertest.CacheCount,
+ },
+ HashPrefix: &filterstorage.ConfigHashPrefix{},
+ RuleLists: rlConf,
+ SafeSearchGeneral: &filterstorage.ConfigSafeSearch{
+ ID: filter.IDGeneralSafeSearch,
+ Enabled: false,
+ },
+ SafeSearchYouTube: &filterstorage.ConfigSafeSearch{
+ ID: filter.IDYoutubeSafeSearch,
+ Enabled: false,
+ },
+ CacheManager: agdcache.EmptyManager{},
+ Clock: agdtime.SystemClock{},
+ ErrColl: agdtest.NewErrorCollector(),
+ Metrics: filter.EmptyMetrics{},
+ CacheDir: tb.TempDir(),
+ }
+}
+
+// newConfigBlockedServices is a test helper that returns a new enabled
+// *ConfigBlockedServices with the given index URL. The rest of the fields are
+// set to the corresponding [filtertest] values.
+func newConfigBlockedServices(indexURL *url.URL) (c *filterstorage.ConfigBlockedServices) {
+ return &filterstorage.ConfigBlockedServices{
+ IndexURL: indexURL,
+ IndexMaxSize: filtertest.FilterMaxSize,
+ IndexRefreshTimeout: filtertest.Timeout,
+ IndexStaleness: filtertest.Staleness,
+ ResultCacheCount: filtertest.CacheCount,
+ ResultCacheEnabled: true,
+ Enabled: true,
+ }
+}
+
+// newConfigRuleLists is a test helper that returns a new *ConfigRuleLists with
+// the given index URL. The rest of the fields are set to the corresponding
+// [filtertest] values.
+func newConfigRuleLists(indexURL *url.URL) (c *filterstorage.ConfigRuleLists) {
+ return &filterstorage.ConfigRuleLists{
+ IndexURL: indexURL,
+ IndexMaxSize: filtertest.FilterMaxSize,
+ MaxSize: filtertest.FilterMaxSize,
+ IndexRefreshTimeout: filtertest.Timeout,
+ IndexStaleness: filtertest.Staleness,
+ RefreshTimeout: filtertest.Timeout,
+ Staleness: filtertest.Staleness,
+ ResultCacheCount: filtertest.CacheCount,
+ ResultCacheEnabled: true,
+ }
+}
+
+// newConfigSafeSearch is a test helper that returns a new enabled
+// *ConfigSafeSearch with the given filter URL and ID. The rest of the fields
+// are set to the corresponding [filtertest] values.
+func newConfigSafeSearch(u *url.URL, id filter.ID) (c *filterstorage.ConfigSafeSearch) {
+ return &filterstorage.ConfigSafeSearch{
+ URL: u,
+ ID: id,
+ MaxSize: filtertest.FilterMaxSize,
+ ResultCacheTTL: filtertest.CacheTTL,
+ RefreshTimeout: filtertest.Timeout,
+ Staleness: filtertest.Staleness,
+ ResultCacheCount: filtertest.CacheCount,
+ Enabled: true,
+ }
+}
diff --git a/internal/filter/index.go b/internal/filter/filterstorage/index.go
similarity index 84%
rename from internal/filter/index.go
rename to internal/filter/filterstorage/index.go
index f962d6a..ec0feb7 100644
--- a/internal/filter/index.go
+++ b/internal/filter/filterstorage/index.go
@@ -1,4 +1,4 @@
-package filter
+package filterstorage
import (
"cmp"
@@ -7,15 +7,15 @@ import (
"log/slog"
"net/url"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
"github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/golibs/errors"
)
// indexResp is the struct for the JSON response from a filter index API.
//
-// TODO(a.garipov): Consider validating uniqueness of the keys.
+// TODO(a.garipov): Consider exporting for tests?
type indexResp struct {
Filters []*indexRespFilter `json:"filters"`
}
@@ -62,8 +62,8 @@ func (f *indexRespFilter) validate() (err error) {
errs = append(errs, fmt.Errorf("downloadUrl: %w", errors.ErrEmptyValue))
}
- if f.Key == "" {
- errs = append(errs, fmt.Errorf("filterKey: %w", errors.ErrEmptyValue))
+ if _, err = filter.NewID(f.Key); err != nil {
+ errs = append(errs, fmt.Errorf("filterKey: %w", err))
}
return errors.Join(errs...)
@@ -72,7 +72,7 @@ func (f *indexRespFilter) validate() (err error) {
// indexData is the data of a single item in the filtering-rule index response.
type indexData struct {
url *url.URL
- id agd.FilterListID
+ id filter.ID
}
// toInternal converts the filters from the index to []*indexData. All errors
@@ -92,14 +92,6 @@ func (r *indexResp) toInternal(
continue
}
- id, err := agd.NewFilterListID(rf.Key)
- if err != nil {
- err = fmt.Errorf("validating id/key: %w", err)
- errcoll.Collect(ctx, errColl, logger, "index response ids", err)
-
- continue
- }
-
u, err := agdhttp.ParseHTTPURL(rf.DownloadURL)
if err != nil {
err = fmt.Errorf("validating url: %w", err)
@@ -110,7 +102,9 @@ func (r *indexResp) toInternal(
fls = append(fls, &indexData{
url: u,
- id: id,
+ // Use a simple conversion, since [*indexRespFilter.validate] has
+ // already made sure that the ID is valid.
+ id: filter.ID(rf.Key),
})
}
diff --git a/internal/filter/index_internal_test.go b/internal/filter/filterstorage/index_internal_test.go
similarity index 95%
rename from internal/filter/index_internal_test.go
rename to internal/filter/filterstorage/index_internal_test.go
index d40f9b5..087a4d1 100644
--- a/internal/filter/index_internal_test.go
+++ b/internal/filter/filterstorage/index_internal_test.go
@@ -1,4 +1,4 @@
-package filter
+package filterstorage
import (
"slices"
diff --git a/internal/filter/filterstorage/refresh.go b/internal/filter/filterstorage/refresh.go
new file mode 100644
index 0000000..322dbd9
--- /dev/null
+++ b/internal/filter/filterstorage/refresh.go
@@ -0,0 +1,240 @@
+package filterstorage
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "path"
+ "path/filepath"
+ "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"
+)
+
+// type check
+var _ agdservice.Refresher = (*Default)(nil)
+
+// Refresh implements the [agdservice.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")
+
+ err = s.refresh(ctx, false)
+ if err != nil {
+ errcoll.Collect(ctx, s.errColl, s.logger, "refresh", err)
+ }
+
+ return err
+}
+
+// refresh refreshes the rule-list, blocked-service, and safe-search filters.
+// If acceptStale is true, the cache files are used regardless of their
+// staleness.
+func (s *Default) refresh(ctx context.Context, acceptStale bool) (err error) {
+ resp, err := s.loadIndex(ctx, acceptStale)
+ if err != nil {
+ // Don't wrap the error, because it's informative enough as is.
+ return err
+ }
+
+ s.logger.InfoContext(ctx, "loaded index", "num_filters", len(resp.Filters))
+
+ fls := resp.toInternal(ctx, s.logger, s.errColl)
+
+ s.logger.InfoContext(ctx, "validated lists", "num_lists", len(fls))
+
+ newRuleLists := make(ruleLists, len(resp.Filters))
+ for _, fl := range fls {
+ s.addRuleList(ctx, newRuleLists, fl, acceptStale)
+
+ if ctxErr := ctx.Err(); ctxErr != nil {
+ // If the context has already been canceled, no need to continue, as
+ // the other refreshes won't be able to finish either way.
+ s.logger.ErrorContext(ctx, "after refreshing lists", slogutil.KeyError, ctxErr)
+
+ return fmt.Errorf("after refreshing rule lists: %w", ctxErr)
+ }
+ }
+
+ s.logger.InfoContext(ctx, "compiled lists", "num_lists", len(newRuleLists))
+
+ err = s.refreshServices(ctx, acceptStale)
+ if err != nil {
+ // Don't wrap the error, because it's informative enough as is.
+ return err
+ }
+
+ err = s.refreshSafeSearch(ctx, acceptStale)
+ if err != nil {
+ // Don't wrap the error, because it's informative enough as is.
+ return err
+ }
+
+ s.resetRuleLists(newRuleLists)
+
+ return nil
+}
+
+// loadIndex fetches, decodes, and returns the filter list index data of the
+// storage. resp.Filters are sorted.
+func (s *Default) loadIndex(
+ ctx context.Context,
+ acceptStale bool,
+) (resp *indexResp, err error) {
+ text, err := s.ruleListIdxRefr.Refresh(ctx, acceptStale)
+ if err != nil {
+ return nil, fmt.Errorf("loading index: %w", err)
+ }
+
+ resp = &indexResp{}
+ err = json.NewDecoder(strings.NewReader(text)).Decode(resp)
+ if err != nil {
+ return nil, fmt.Errorf("decoding: %w", err)
+ }
+
+ slices.SortStableFunc(resp.Filters, (*indexRespFilter).compare)
+
+ return resp, nil
+}
+
+// addRuleList adds the data from fl to newRuleLists and handles validations and
+// errors. It also adds the cache to the cache manager.
+func (s *Default) addRuleList(
+ ctx context.Context,
+ newRuleLists ruleLists,
+ fl *indexData,
+ acceptStale bool,
+) {
+ if _, ok := newRuleLists[fl.id]; ok {
+ err := fmt.Errorf("rule-list id: %w: %q", errors.ErrDuplicated, fl.id)
+ errcoll.Collect(ctx, s.errColl, s.logger, "adding rule list", err)
+
+ return
+ }
+
+ fltIDStr := string(fl.id)
+ cacheID := path.Join(cachePrefixRuleList, fltIDStr)
+ cache := rulelist.NewManagedResultCache(
+ s.cacheManager,
+ cacheID,
+ s.ruleListResCacheCount,
+ s.ruleListCacheEnabled,
+ )
+
+ rl, err := rulelist.NewRefreshable(
+ &refreshable.Config{
+ Logger: s.baseLogger.With(slogutil.KeyPrefix, cacheID),
+ URL: fl.url,
+ ID: fl.id,
+ CachePath: filepath.Join(s.cacheDir, fltIDStr),
+ Staleness: s.ruleListStaleness,
+ Timeout: s.ruleListRefreshTimeout,
+ MaxSize: s.ruleListMaxSize,
+ },
+ cache,
+ )
+ if err != nil {
+ s.reportRuleListError(ctx, fl, fmt.Errorf("creating rulelist: %w", err))
+ s.setPrevRuleList(newRuleLists, fl.id)
+
+ return
+ }
+
+ err = rl.Refresh(ctx, acceptStale)
+ if err != nil {
+ s.reportRuleListError(ctx, fl, fmt.Errorf("refreshing rulelist: %w", err))
+ s.setPrevRuleList(newRuleLists, fl.id)
+
+ return
+ }
+
+ newRuleLists[fl.id] = rl
+
+ s.metrics.SetFilterStatus(ctx, fltIDStr, s.clock.Now(), rl.RulesCount(), nil)
+}
+
+// reportRuleListError reports the error encountered when refreshing a rule-list
+// filter.
+func (s *Default) reportRuleListError(ctx context.Context, fl *indexData, err error) {
+ errcoll.Collect(ctx, s.errColl, s.logger, "rule-list error", err)
+ s.metrics.SetFilterStatus(ctx, string(fl.id), s.clock.Now(), 0, err)
+}
+
+// setPrevRuleList adds the previous version of the filter to newRuleLists, if
+// there is one.
+func (s *Default) setPrevRuleList(newRuleLists ruleLists, id filter.ID) {
+ s.ruleListsMu.RLock()
+ defer s.ruleListsMu.RUnlock()
+
+ if rl, ok := s.ruleLists[id]; ok {
+ newRuleLists[id] = rl
+ }
+}
+
+// refreshServices refreshes the blocked-service filter, if necessary.
+func (s *Default) refreshServices(ctx context.Context, acceptStale bool) (err error) {
+ if s.services == nil {
+ return nil
+ }
+
+ err = s.services.Refresh(
+ ctx,
+ s.cacheManager,
+ s.serviceResCacheCount,
+ s.serviceResCacheEnabled,
+ acceptStale,
+ )
+ if err != nil {
+ return fmt.Errorf("refreshing blocked services: %w", err)
+ }
+
+ return nil
+}
+
+// refreshSafeSearch refreshes the safe-search filters, if necessary.
+func (s *Default) refreshSafeSearch(ctx context.Context, acceptStale bool) (err error) {
+ if s.safeSearchGeneral != nil {
+ err = s.safeSearchGeneral.Refresh(ctx, acceptStale)
+ }
+ if err != nil {
+ return fmt.Errorf("refreshing general safe search: %w", err)
+ }
+
+ if s.safeSearchYouTube != nil {
+ err = s.safeSearchYouTube.Refresh(ctx, acceptStale)
+ }
+ if err != nil {
+ return fmt.Errorf("refreshing youtube safe search: %w", err)
+ }
+
+ return nil
+}
+
+// resetRuleLists replaces the storage's rule lists.
+func (s *Default) resetRuleLists(rls ruleLists) {
+ s.ruleListsMu.Lock()
+ defer s.ruleListsMu.Unlock()
+
+ s.ruleLists = rls
+}
+
+// RefreshInitial loads the content of the storage, using cached files if any,
+// regardless of their staleness.
+func (s *Default) RefreshInitial(ctx context.Context) (err error) {
+ s.logger.InfoContext(ctx, "initial refresh started")
+ defer s.logger.InfoContext(ctx, "initial refresh finished")
+
+ err = s.refresh(ctx, true)
+ if err != nil {
+ return fmt.Errorf("refreshing filter storage initially: %w", err)
+ }
+
+ return nil
+}
diff --git a/internal/filter/filterstorage/refresh_test.go b/internal/filter/filterstorage/refresh_test.go
new file mode 100644
index 0000000..9d9c47f
--- /dev/null
+++ b/internal/filter/filterstorage/refresh_test.go
@@ -0,0 +1,182 @@
+package filterstorage_test
+
+import (
+ "context"
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "testing"
+ "time"
+
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
+ "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/httphdr"
+ "github.com/AdguardTeam/golibs/testutil"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestDefault_Refresh(t *testing.T) {
+ // TODO(a.garipov): Consider ways to DRY this code with [newDefault].
+ const (
+ blockRule = filtertest.RuleBlockStr + "\n"
+ ssGenRule = filtertest.RuleSafeSearchGeneralHostStr + "\n"
+ ssYTRule = filtertest.RuleSafeSearchYouTubeStr + "\n"
+ )
+
+ rlCh := make(chan unit, 1)
+ _, ruleListURL := filtertest.PrepareRefreshable(t, rlCh, blockRule, http.StatusOK)
+ rlIdxData := filtertest.NewRuleListIndex(ruleListURL.String())
+
+ rlIdxCh := make(chan unit, 1)
+ _, ruleListIdxURL := filtertest.PrepareRefreshable(t, rlIdxCh, string(rlIdxData), http.StatusOK)
+
+ ssGenCh, ssYTCh := make(chan unit, 1), make(chan unit, 1)
+ _, safeSearchGenURL := filtertest.PrepareRefreshable(t, ssGenCh, ssGenRule, http.StatusOK)
+ _, safeSearchYTURL := filtertest.PrepareRefreshable(t, ssYTCh, ssYTRule, http.StatusOK)
+
+ svcIdxCh := make(chan unit, 1)
+ _, svcIdxURL := filtertest.PrepareRefreshable(
+ t,
+ svcIdxCh,
+ filtertest.BlockedServiceIndex,
+ http.StatusOK,
+ )
+
+ c := newDisabledConfig(t, newConfigRuleLists(ruleListIdxURL))
+ c.BlockedServices = newConfigBlockedServices(svcIdxURL)
+ c.SafeSearchGeneral = newConfigSafeSearch(safeSearchGenURL, filter.IDGeneralSafeSearch)
+ c.SafeSearchYouTube = newConfigSafeSearch(safeSearchYTURL, filter.IDYoutubeSafeSearch)
+
+ s, err := filterstorage.New(c)
+ require.NoError(t, err)
+
+ ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
+ err = s.RefreshInitial(ctx)
+ require.NoError(t, err)
+
+ testutil.RequireReceive(t, rlCh, filtertest.Timeout)
+ testutil.RequireReceive(t, rlIdxCh, filtertest.Timeout)
+ testutil.RequireReceive(t, ssGenCh, filtertest.Timeout)
+ testutil.RequireReceive(t, ssYTCh, filtertest.Timeout)
+ testutil.RequireReceive(t, svcIdxCh, filtertest.Timeout)
+
+ assert.True(t, s.HasListID(filtertest.RuleListID1))
+
+ ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
+ err = s.Refresh(ctx)
+ require.NoError(t, err)
+
+ // Make sure that the servers weren't called the second time.
+ require.Empty(t, rlCh)
+ require.Empty(t, rlIdxCh)
+ require.Empty(t, ssGenCh)
+ require.Empty(t, ssYTCh)
+ require.Empty(t, svcIdxCh)
+
+ assert.True(t, s.HasListID(filtertest.RuleListID1))
+}
+
+func TestDefault_Refresh_usePrevious(t *testing.T) {
+ const (
+ blockRule = filtertest.RuleBlockStr + "\n"
+ )
+
+ codeCh := make(chan int, 2)
+ codeCh <- http.StatusOK
+ codeCh <- http.StatusNotFound
+ ruleListURL := newCodeServer(t, blockRule, codeCh)
+
+ rlIdxData := filtertest.NewRuleListIndex(ruleListURL.String())
+ _, ruleListIdxURL := filtertest.PrepareRefreshable(t, nil, string(rlIdxData), http.StatusOK)
+
+ // Use a smaller staleness value to make sure that the filter is refreshed.
+ ruleListsConf := newConfigRuleLists(ruleListIdxURL)
+ ruleListsConf.Staleness = 1 * time.Microsecond
+
+ c := newDisabledConfig(t, ruleListsConf)
+ c.RuleLists = ruleListsConf
+ c.ErrColl = &agdtest.ErrorCollector{
+ OnCollect: func(_ context.Context, err error) {
+ errStatus := &agdhttp.StatusError{}
+ assert.ErrorAs(t, err, &errStatus)
+ assert.Equal(t, errStatus.Expected, http.StatusOK)
+ assert.Equal(t, errStatus.Got, http.StatusNotFound)
+ assert.Equal(t, errStatus.ServerName, filtertest.ServerName)
+ },
+ }
+
+ s, err := filterstorage.New(c)
+ require.NoError(t, err)
+
+ // The first refresh, success.
+ ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
+ err = s.RefreshInitial(ctx)
+ require.NoError(t, err)
+ require.True(t, s.HasListID(filtertest.RuleListID1))
+
+ fltConf := &filter.ConfigClient{
+ Custom: &filter.ConfigCustom{},
+ Parental: &filter.ConfigParental{},
+ RuleList: &filter.ConfigRuleList{
+ IDs: []filter.ID{filtertest.RuleListID1},
+ Enabled: true,
+ },
+ SafeBrowsing: &filter.ConfigSafeBrowsing{},
+ }
+
+ f := s.ForConfig(ctx, fltConf)
+ require.NotNil(t, f)
+
+ req := filtertest.NewARequest(t, filtertest.HostBlocked)
+ r, err := f.FilterRequest(ctx, req)
+ require.NoError(t, err)
+
+ filtertest.AssertEqualResult(t, resultRuleList, r)
+
+ // The second refresh, not found. The older version of the rule-list filter
+ // must still be used.
+ err = s.Refresh(ctx)
+ require.NoError(t, err)
+ require.True(t, s.HasListID(filtertest.RuleListID1))
+
+ f = s.ForConfig(ctx, fltConf)
+ require.NotNil(t, f)
+
+ r, err = f.FilterRequest(ctx, req)
+ require.NotNil(t, r)
+ require.NoError(t, err)
+
+ filtertest.AssertEqualResult(t, resultRuleList, r)
+}
+
+// newCodeServer is a helper that creates a server responding with text and
+// response-code values sent over codeCh.
+func newCodeServer(tb testing.TB, text string, codeCh <-chan int) (srvURL *url.URL) {
+ tb.Helper()
+
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ pt := testutil.PanicT{}
+
+ w.Header().Set(httphdr.Server, filtertest.ServerName)
+
+ code, ok := testutil.RequireReceive(tb, codeCh, filtertest.Timeout)
+ require.True(pt, ok)
+
+ w.WriteHeader(code)
+
+ _, writeErr := io.WriteString(w, text)
+ require.NoError(pt, writeErr)
+ }))
+
+ tb.Cleanup(srv.Close)
+
+ srvURL, err := agdhttp.ParseHTTPURL(srv.URL)
+ require.NoError(tb, err)
+
+ return srvURL
+}
diff --git a/internal/filter/hashprefix/filter.go b/internal/filter/hashprefix/filter.go
index a5baca7..dfc28b4 100644
--- a/internal/filter/hashprefix/filter.go
+++ b/internal/filter/hashprefix/filter.go
@@ -10,12 +10,12 @@ import (
"strings"
"time"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"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/internal/refreshable"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/golibs/netutil"
"github.com/c2h5oh/datasize"
@@ -44,8 +44,14 @@ type FilterConfig struct {
// ErrColl is used to collect non-critical and rare errors.
ErrColl errcoll.Interface
+ // 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
+
// ID is the ID of this hash storage for logging and error reporting.
- ID agd.FilterListID
+ ID internal.ID
// CachePath is the path to the file containing the cached filtered
// hostnames, one per line.
@@ -69,8 +75,8 @@ type FilterConfig struct {
// RefreshTimeout is the timeout for the filter update operation.
RefreshTimeout time.Duration
- // CacheSize is the size of the filter's result cache.
- CacheSize int
+ // CacheCount is the count of the elements in the filter's result cache.
+ CacheCount int
// MaxSize is the maximum size of the downloadable rule-list.
MaxSize datasize.ByteSize
@@ -86,38 +92,17 @@ type cacheItem struct {
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 (f *Filter) itemFromCache(
- ctx context.Context,
- key internal.CacheKey,
- host string,
-) (item *cacheItem, ok bool) {
- item, ok = f.resCache.Get(key)
- if !ok {
- return nil, false
- }
-
- if item.host != host {
- f.logger.WarnContext(ctx, "collision: bad cache item", "item", item, "host", host)
-
- return nil, false
- }
-
- return item, true
-}
-
// 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 *internal.Refreshable
- resCache agdcache.Interface[internal.CacheKey, *cacheItem]
+ refr *refreshable.Refreshable
errColl errcoll.Interface
- id agd.FilterListID
+ metrics internal.Metrics
+ resCache agdcache.Interface[internal.CacheKey, *cacheItem]
+ id internal.ID
repIP netip.Addr
repFQDN string
}
@@ -135,7 +120,7 @@ func NewFilter(c *FilterConfig) (f *Filter, err error) {
id := c.ID
resCache := agdcache.NewLRU[internal.CacheKey, *cacheItem](&agdcache.LRUConfig{
- Size: c.CacheSize,
+ Count: c.CacheCount,
})
c.CacheManager.Add(path.Join(IDPrefix, string(id)), resCache)
@@ -144,8 +129,9 @@ func NewFilter(c *FilterConfig) (f *Filter, err error) {
logger: c.Logger,
cloner: c.Cloner,
hashes: c.Hashes,
- resCache: resCache,
errColl: c.ErrColl,
+ metrics: c.Metrics,
+ resCache: resCache,
id: id,
}
@@ -162,7 +148,7 @@ func NewFilter(c *FilterConfig) (f *Filter, err error) {
f.repIP = ip
}
- f.refr, err = internal.NewRefreshable(&internal.RefreshableConfig{
+ f.refr, err = refreshable.New(&refreshable.Config{
Logger: f.logger,
URL: c.URL,
ID: id,
@@ -181,19 +167,19 @@ func NewFilter(c *FilterConfig) (f *Filter, err error) {
// type check
var _ internal.RequestFilter = (*Filter)(nil)
-// FilterRequest implements the [internal.RequestFilter] interface for
-// *Filter. It modifies the request or response if host matches f.
+// FilterRequest implements the [internal.RequestFilter] interface for *Filter.
+// It modifies the request or response if host matches f.
func (f *Filter) FilterRequest(
ctx context.Context,
- req *dns.Msg,
- ri *agd.RequestInfo,
+ req *internal.Request,
) (r internal.Result, err error) {
- host, qt, cl := ri.Host, ri.QType, ri.QClass
+ host, qt, cl := req.Host, req.QType, req.QClass
+
cacheKey := internal.NewCacheKey(host, qt, cl, false)
item, ok := f.itemFromCache(ctx, cacheKey, host)
f.updateCacheLookupsMetrics(ok)
if ok {
- return f.clonedResult(req, item.res), nil
+ return f.clonedResult(req.DNS, item.res), nil
}
fam, ok := isFilterable(qt)
@@ -220,7 +206,7 @@ func (f *Filter) FilterRequest(
return nil, nil
}
- r, err = f.filteredResult(req, matched, ri, fam)
+ r, err = f.filteredResult(req, matched, fam)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return nil, err
@@ -233,8 +219,30 @@ func (f *Filter) FilterRequest(
return r, nil
}
+// 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 (f *Filter) itemFromCache(
+ ctx context.Context,
+ key internal.CacheKey,
+ host string,
+) (item *cacheItem, ok bool) {
+ item, ok = f.resCache.Get(key)
+ if !ok {
+ return nil, false
+ }
+
+ if item.host != host {
+ f.logger.WarnContext(ctx, "collision: bad cache item", "item", item, "host", host)
+
+ return nil, false
+ }
+
+ return item, true
+}
+
// ID implements the [internal.RequestFilter] interface for *Filter.
-func (f *Filter) ID() (id agd.FilterListID) {
+func (f *Filter) ID() (id internal.ID) {
return f.id
}
@@ -268,24 +276,23 @@ 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 *dns.Msg,
+ req *internal.Request,
matched string,
- ri *agd.RequestInfo,
fam netutil.AddrFamily,
) (r internal.Result, err error) {
if f.repFQDN != "" {
// Assume that the repFQDN is a valid domain name then.
- req = f.cloner.Clone(req)
- req.Question[0].Name = dns.Fqdn(f.repFQDN)
+ modReq := f.cloner.Clone(req.DNS)
+ modReq.Question[0].Name = dns.Fqdn(f.repFQDN)
return &internal.ResultModifiedRequest{
- Msg: req,
+ Msg: modReq,
List: f.id,
- Rule: agd.FilterRuleText(matched),
+ Rule: internal.RuleText(matched),
}, nil
}
- resp, err := f.respForFamily(req, ri, fam)
+ resp, err := f.respForFamily(req, fam)
if err != nil {
return nil, fmt.Errorf("filter %s: creating modified result: %w", f.id, err)
}
@@ -293,15 +300,14 @@ func (f *Filter) filteredResult(
return &internal.ResultModifiedResponse{
Msg: resp,
List: f.id,
- Rule: agd.FilterRuleText(matched),
+ Rule: internal.RuleText(matched),
}, nil
}
// respForFamily returns a filtered response in accordance with the protocol
// family and question type.
func (f *Filter) respForFamily(
- req *dns.Msg,
- ri *agd.RequestInfo,
+ req *internal.Request,
fam netutil.AddrFamily,
) (resp *dns.Msg, err error) {
if fam == netutil.AddrFamilyNone {
@@ -310,21 +316,21 @@ func (f *Filter) respForFamily(
//
// TODO(ameshkov): Consider putting the resolved IP addresses into hints
// to show the blocked page here as well?
- return ri.Messages.NewBlockedResp(req)
+ return req.Messages.NewBlockedResp(req.DNS)
}
ip := f.repIP
switch {
case ip.Is4() && fam == netutil.AddrFamilyIPv4:
- return ri.Messages.NewBlockedRespIP(req, ip)
+ return req.Messages.NewBlockedRespIP(req.DNS, ip)
case ip.Is6() && fam == netutil.AddrFamilyIPv6:
- return ri.Messages.NewBlockedRespIP(req, ip)
+ return req.Messages.NewBlockedRespIP(req.DNS, ip)
default:
// TODO(e.burkov): Use [dnsmsg.Constructor.NewBlockedRespRCode] when it
// adds SOA records.
- resp = ri.Messages.NewRespRCode(req, dns.RcodeSuccess)
- ri.Messages.AddEDE(req, resp, dns.ExtendedErrorCodeFiltered)
+ resp = req.Messages.NewRespRCode(req.DNS, dns.RcodeSuccess)
+ req.Messages.AddEDE(req.DNS, resp, dns.ExtendedErrorCodeFiltered)
return resp, nil
}
@@ -356,11 +362,11 @@ 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 agd.FilterListIDSafeBrowsing:
+ case internal.IDSafeBrowsing:
metrics.HashPrefixFilterSafeBrowsingCacheSize.Set(float64(size))
- case agd.FilterListIDAdultBlocking:
+ case internal.IDAdultBlocking:
metrics.HashPrefixFilterAdultBlockingCacheSize.Set(float64(size))
- case agd.FilterListIDNewRegDomains:
+ case internal.IDNewRegDomains:
metrics.HashPrefixFilterNewRegDomainsCacheSize.Set(float64(size))
default:
panic(fmt.Errorf("unsupported FilterListID %s", id))
@@ -371,13 +377,13 @@ func (f *Filter) updateCacheSizeMetrics(size int) {
func (f *Filter) updateCacheLookupsMetrics(hit bool) {
var hitsMetric, missesMetric prometheus.Counter
switch id := f.id; id {
- case agd.FilterListIDSafeBrowsing:
+ case internal.IDSafeBrowsing:
hitsMetric = metrics.HashPrefixFilterCacheSafeBrowsingHits
missesMetric = metrics.HashPrefixFilterCacheSafeBrowsingMisses
- case agd.FilterListIDAdultBlocking:
+ case internal.IDAdultBlocking:
hitsMetric = metrics.HashPrefixFilterCacheAdultBlockingHits
missesMetric = metrics.HashPrefixFilterCacheAdultBlockingMisses
- case agd.FilterListIDNewRegDomains:
+ case internal.IDNewRegDomains:
hitsMetric = metrics.HashPrefixFilterCacheNewRegDomainsHits
missesMetric = metrics.HashPrefixFilterCacheNewRegDomainsMisses
default:
@@ -421,25 +427,26 @@ func (f *Filter) RefreshInitial(ctx context.Context) (err error) {
// not try to load the list from its URL when there is already a file in the
// cache directory, regardless of its staleness.
func (f *Filter) refresh(ctx context.Context, acceptStale bool) (err error) {
+ var count int
+ defer func() {
+ // TODO(a.garipov): Consider using [agdtime.Clock].
+ f.metrics.SetFilterStatus(ctx, string(f.id), time.Now(), count, err)
+ }()
+
text, err := f.refr.Refresh(ctx, acceptStale)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
- n, err := f.hashes.Reset(text)
- fltIDStr := string(f.id)
- metrics.SetStatusGauge(metrics.FilterUpdatedStatus.WithLabelValues(fltIDStr), err)
+ count, err = f.hashes.Reset(text)
if err != nil {
return fmt.Errorf("%s: resetting: %w", f.id, err)
}
f.resCache.Clear()
- metrics.FilterUpdatedTime.WithLabelValues(fltIDStr).SetToCurrentTime()
- metrics.FilterRulesTotal.WithLabelValues(fltIDStr).Set(float64(n))
-
- f.logger.InfoContext(ctx, "reset hosts", "num", n)
+ f.logger.InfoContext(ctx, "reset hosts", "num", count)
return nil
}
diff --git a/internal/filter/hashprefix/filter_test.go b/internal/filter/hashprefix/filter_test.go
index 3322495..1b4e6e5 100644
--- a/internal/filter/hashprefix/filter_test.go
+++ b/internal/filter/hashprefix/filter_test.go
@@ -7,11 +7,11 @@ import (
"testing"
"time"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
"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"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/filtertest"
@@ -22,72 +22,71 @@ import (
"github.com/stretchr/testify/require"
)
-// testTimeout is the common timeout for tests and contexts.
-const testTimeout = 10 * time.Second
-
func TestFilter_FilterRequest_host(t *testing.T) {
+ t.Parallel()
+
msgs := agdtest.NewConstructor(t)
testCases := []struct {
name string
host string
replHost string
- wantRule agd.FilterRuleText
+ wantRule internal.RuleText
qType dnsmsg.RRType
wantResult bool
}{{
name: "host_not_a_or_aaaa",
- host: testHost,
- replHost: testReplHost,
+ host: filtertest.HostAdultContent,
+ replHost: filtertest.HostAdultContentRepl,
wantRule: "",
qType: dns.TypeTXT,
wantResult: false,
}, {
name: "host_success",
- host: testHost,
- replHost: testReplHost,
- wantRule: testHost,
+ host: filtertest.HostAdultContent,
+ replHost: filtertest.HostAdultContentRepl,
+ wantRule: filtertest.HostAdultContent,
qType: dns.TypeA,
wantResult: true,
}, {
name: "host_success_subdomain",
- host: "a.b.c." + testHost,
- replHost: testReplHost,
- wantRule: testHost,
+ host: filtertest.HostAdultContentSub,
+ replHost: filtertest.HostAdultContentRepl,
+ wantRule: filtertest.HostAdultContent,
qType: dns.TypeA,
wantResult: true,
}, {
name: "host_no_match",
- host: testOtherHost,
- replHost: testReplHost,
+ host: filtertest.Host,
+ replHost: filtertest.HostAdultContentRepl,
wantRule: "",
qType: dns.TypeA,
wantResult: false,
}, {
name: "ip_not_a_or_aaaa",
- host: testHost,
- replHost: filtertest.SafeBrowsingReplIPv4Str,
+ host: filtertest.HostAdultContent,
+ replHost: filtertest.IPv4AdultContentReplStr,
wantRule: "",
qType: dns.TypeTXT,
wantResult: false,
}, {
name: "ip_success",
- host: testHost,
- replHost: filtertest.SafeBrowsingReplIPv4Str,
- wantRule: testHost,
+ host: filtertest.HostAdultContent,
+ replHost: filtertest.IPv4AdultContentReplStr,
+ wantRule: filtertest.HostAdultContent,
qType: dns.TypeA,
wantResult: true,
}, {
name: "ip_success_subdomain",
- host: "a.b.c." + testHost,
- replHost: filtertest.SafeBrowsingReplIPv4Str,
- wantRule: testHost,
+ host: filtertest.HostAdultContentSub,
+ replHost: filtertest.IPv4AdultContentReplStr,
+ wantRule: filtertest.HostAdultContent,
qType: dns.TypeA,
wantResult: true,
}, {
name: "ip_no_match",
- replHost: filtertest.SafeBrowsingReplIPv4Str,
- host: testOtherHost,
+ replHost: filtertest.IPv4AdultContentReplStr,
+ host: filtertest.Host,
wantRule: "",
qType: dns.TypeA,
wantResult: false,
@@ -95,124 +94,101 @@ func TestFilter_FilterRequest_host(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- f := newFilter(t, tc.replHost)
+ t.Parallel()
- req := dnsservertest.NewReq(
- dns.Fqdn(tc.host),
- tc.qType,
- dns.ClassINET,
- )
- ri := &agd.RequestInfo{
+ f := filtertest.NewHashprefixFilterWithRepl(t, filter.IDAdultBlocking, tc.replHost)
+
+ ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
+ req := dnsservertest.NewReq(dns.Fqdn(tc.host), tc.qType, dns.ClassINET)
+
+ r, err := f.FilterRequest(ctx, &internal.Request{
+ DNS: req,
Messages: msgs,
Host: tc.host,
QType: tc.qType,
- }
-
- ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
-
- r, err := f.FilterRequest(ctx, req, ri)
+ })
require.NoError(t, err)
var wantRes internal.Result
if tc.wantResult {
- if tc.replHost == testReplHost {
+ if tc.replHost == filtertest.HostAdultContentRepl {
wantRes = newModReqResult(req, tc.wantRule)
} else {
- wantRes = newModRespResult(t, req, ri.Messages, filtertest.SafeBrowsingReplIPv4)
+ wantRes = newModRespResult(t, req, msgs, filtertest.IPv4AdultContentRepl)
}
}
- assert.Equal(t, wantRes, r)
+ filtertest.AssertEqualResult(t, wantRes, r)
})
}
require.True(t, t.Run("cached_success", func(t *testing.T) {
- f := newFilter(t, testReplHost)
+ f := filtertest.NewHashprefixFilter(t, internal.IDAdultBlocking)
- req := dnsservertest.NewReq(
- dns.Fqdn(testHost),
- dns.TypeA,
- dns.ClassINET,
- )
- ri := &agd.RequestInfo{
- Messages: msgs,
- Host: testHost,
- QType: dns.TypeA,
- }
+ req := filtertest.NewARequest(t, filtertest.HostAdultContent)
ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
-
- original, err := f.FilterRequest(ctx, req, ri)
+ original, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
- cached, err := f.FilterRequest(ctx, req, ri)
+ cached, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
- // Do not check the ID as it is new for every clone.
- originalRM := testutil.RequireTypeAssert[*internal.ResultModifiedRequest](t, original)
- cachedRM := testutil.RequireTypeAssert[*internal.ResultModifiedRequest](t, cached)
- cachedRM.Msg.Id = originalRM.Msg.Id
-
- assert.Equal(t, cached, original)
+ filtertest.AssertEqualResult(t, cached, original)
}))
require.True(t, t.Run("cached_no_match", func(t *testing.T) {
- f := newFilter(t, testReplHost)
+ f := filtertest.NewHashprefixFilter(t, internal.IDAdultBlocking)
- req := dnsservertest.NewReq(
- dns.Fqdn(testOtherHost),
- dns.TypeA,
- dns.ClassINET,
- )
- ri := &agd.RequestInfo{
- Messages: msgs,
- Host: testOtherHost,
- QType: dns.TypeA,
- }
+ req := filtertest.NewARequest(t, filtertest.Host)
ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
-
- r, err := f.FilterRequest(ctx, req, ri)
+ r, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
- cached, err := f.FilterRequest(ctx, req, ri)
+ cached, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
- assert.Equal(t, cached, r)
+ filtertest.AssertEqualResult(t, cached, r)
}))
require.True(t, t.Run("https", func(t *testing.T) {
- f := newFilter(t, testReplHost)
+ f := filtertest.NewHashprefixFilter(t, internal.IDAdultBlocking)
- req := dnsservertest.NewReq(dns.Fqdn(testHost), dns.TypeHTTPS, dns.ClassINET)
- ri := &agd.RequestInfo{
- Messages: msgs,
- Host: testHost,
- QType: dns.TypeHTTPS,
- }
+ req := filtertest.NewRequest(
+ t,
+ "",
+ filtertest.HostAdultContent,
+ filtertest.IPv4Client,
+ dns.TypeHTTPS,
+ )
ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
-
- r, err := f.FilterRequest(ctx, req, ri)
+ r, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
require.NotNil(t, r)
- assert.Equal(t, newModReqResult(req, testHost), r)
+ wantRes := newModReqResult(req.DNS, filtertest.HostAdultContent)
+ filtertest.AssertEqualResult(t, wantRes, r)
}))
require.True(t, t.Run("https_ip", func(t *testing.T) {
- f := newFilter(t, filtertest.SafeBrowsingReplIPv4Str)
+ f := filtertest.NewHashprefixFilterWithRepl(
+ t,
+ internal.IDAdultBlocking,
+ filtertest.IPv4AdultContentReplStr,
+ )
- req := dnsservertest.NewReq(dns.Fqdn(testHost), dns.TypeHTTPS, dns.ClassINET)
- ri := &agd.RequestInfo{
- Messages: msgs,
- Host: testHost,
- QType: dns.TypeHTTPS,
- }
+ req := filtertest.NewRequest(
+ t,
+ "",
+ filtertest.HostAdultContent,
+ filtertest.IPv4Client,
+ dns.TypeHTTPS,
+ )
ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
-
- r, err := f.FilterRequest(ctx, req, ri)
+ r, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
require.NotNil(t, r)
@@ -221,7 +197,7 @@ func TestFilter_FilterRequest_host(t *testing.T) {
require.Len(t, m.Msg.Question, 1)
assert.Equal(t, m.Msg.Question[0].Qtype, dns.TypeHTTPS)
- assert.Len(t, m.Msg.Answer, 0)
+ assert.Empty(t, m.Msg.Answer)
}))
}
@@ -239,61 +215,28 @@ func newModRespResult(
return &internal.ResultModifiedResponse{
Msg: resp,
- List: testFltListID,
- Rule: testHost,
+ List: filter.IDAdultBlocking,
+ Rule: filtertest.HostAdultContent,
}
}
// newModReqResult is a helper for creating modified results for tests.
-func newModReqResult(
- req *dns.Msg,
- rule agd.FilterRuleText,
-) (r *internal.ResultModifiedRequest) {
+func newModReqResult(req *dns.Msg, rule internal.RuleText) (r *internal.ResultModifiedRequest) {
req = dnsmsg.Clone(req)
- req.Question[0].Name = dns.Fqdn(testReplHost)
+ req.Question[0].Name = filtertest.FQDNAdultContentRepl
return &internal.ResultModifiedRequest{
Msg: req,
- List: testFltListID,
+ List: filter.IDAdultBlocking,
Rule: rule,
}
}
-// newFilter is a helper constructor for tests.
-func newFilter(tb testing.TB, replHost string) (f *hashprefix.Filter) {
- tb.Helper()
-
- cachePath, srvURL := filtertest.PrepareRefreshable(tb, nil, testHost, http.StatusOK)
-
- strg, err := hashprefix.NewStorage("")
- require.NoError(tb, err)
-
- f, err = hashprefix.NewFilter(&hashprefix.FilterConfig{
- Logger: slogutil.NewDiscardLogger(),
- Cloner: agdtest.NewCloner(),
- CacheManager: agdcache.EmptyManager{},
- Hashes: strg,
- URL: srvURL,
- ErrColl: agdtest.NewErrorCollector(),
- ID: agd.FilterListIDAdultBlocking,
- CachePath: cachePath,
- ReplacementHost: replHost,
- Staleness: filtertest.Staleness,
- CacheTTL: filtertest.CacheTTL,
- CacheSize: 1,
- MaxSize: filtertest.FilterMaxSize,
- })
- require.NoError(tb, err)
-
- ctx := testutil.ContextWithTimeout(tb, testTimeout)
- require.NoError(tb, f.RefreshInitial(ctx))
-
- return f
-}
-
func TestFilter_Refresh(t *testing.T) {
- reqCh := make(chan struct{}, 1)
- cachePath, srvURL := filtertest.PrepareRefreshable(t, reqCh, testHost, http.StatusOK)
+ t.Parallel()
+
+ refrCh := make(chan struct{}, 1)
+ cachePath, srvURL := filtertest.PrepareRefreshable(t, refrCh, testHashes, http.StatusOK)
strg, err := hashprefix.NewStorage("")
require.NoError(t, err)
@@ -305,37 +248,41 @@ func TestFilter_Refresh(t *testing.T) {
Hashes: strg,
URL: srvURL,
ErrColl: agdtest.NewErrorCollector(),
- ID: agd.FilterListIDAdultBlocking,
+ Metrics: filter.EmptyMetrics{},
+ ID: internal.IDAdultBlocking,
CachePath: cachePath,
- ReplacementHost: testReplHost,
+ ReplacementHost: filtertest.HostAdultContentRepl,
Staleness: filtertest.Staleness,
CacheTTL: filtertest.CacheTTL,
- CacheSize: 1,
+ CacheCount: filtertest.CacheCount,
MaxSize: filtertest.FilterMaxSize,
})
require.NoError(t, err)
- ctx := testutil.ContextWithTimeout(t, testTimeout)
+ ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
require.NoError(t, f.RefreshInitial(ctx))
- ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
+ testutil.RequireReceive(t, refrCh, filtertest.Timeout)
+ ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
err = f.Refresh(ctx)
assert.NoError(t, err)
- testutil.RequireReceive(t, reqCh, filtertest.Timeout)
+ assert.Empty(t, refrCh)
}
func TestFilter_FilterRequest_staleCache(t *testing.T) {
+ t.Parallel()
+
refrCh := make(chan struct{}, 1)
- cachePath, srvURL := filtertest.PrepareRefreshable(t, refrCh, testHost, http.StatusOK)
+ cachePath, srvURL := filtertest.PrepareRefreshable(t, refrCh, testHashes, http.StatusOK)
// Put some initial data into the cache to avoid the first refresh.
cf, err := os.OpenFile(cachePath, os.O_WRONLY|os.O_APPEND, os.ModeAppend)
require.NoError(t, err)
- _, err = cf.WriteString(testOtherHost)
+ _, err = cf.WriteString(filtertest.Host + "\n")
require.NoError(t, err)
require.NoError(t, cf.Close())
@@ -353,28 +300,22 @@ func TestFilter_FilterRequest_staleCache(t *testing.T) {
Hashes: strg,
URL: srvURL,
ErrColl: agdtest.NewErrorCollector(),
- ID: agd.FilterListIDAdultBlocking,
+ Metrics: filter.EmptyMetrics{},
+ ID: internal.IDAdultBlocking,
CachePath: cachePath,
- ReplacementHost: testReplHost,
+ ReplacementHost: filtertest.HostAdultContentRepl,
Staleness: filtertest.Staleness,
CacheTTL: filtertest.CacheTTL,
- CacheSize: 1,
+ CacheCount: filtertest.CacheCount,
MaxSize: filtertest.FilterMaxSize,
}
f, err := hashprefix.NewFilter(fconf)
require.NoError(t, err)
- ctx := testutil.ContextWithTimeout(t, testTimeout)
+ ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
require.NoError(t, f.RefreshInitial(ctx))
- msgs, err := dnsmsg.NewConstructor(&dnsmsg.ConstructorConfig{
- Cloner: cloner,
- BlockingMode: &dnsmsg.BlockingModeNullIP{},
- StructuredErrors: agdtest.NewSDEConfig(true),
- FilteredResponseTTL: agdtest.FilteredResponseTTL,
- EDEEnabled: true,
- })
- require.NoError(t, err)
+ assert.Empty(t, refrCh)
// Test the following:
//
@@ -383,20 +324,18 @@ func TestFilter_FilterRequest_staleCache(t *testing.T) {
// 3. Ensure the result cache is cleared.
// 4. Ensure the stale rules aren't used.
- testHostReq := dnsservertest.NewReq(dns.Fqdn(testHost), dns.TypeA, dns.ClassINET)
- testReqInfo := &agd.RequestInfo{Messages: msgs, Host: testHost, QType: dns.TypeA}
-
- testOtherHostReq := dnsservertest.NewReq(dns.Fqdn(testOtherHost), dns.TypeA, dns.ClassINET)
- testOtherReqInfo := &agd.RequestInfo{Messages: msgs, Host: testOtherHost, QType: dns.TypeA}
+ hostReq := filtertest.NewARequest(t, filtertest.HostAdultContent)
+ otherHostReq := filtertest.NewARequest(t, filtertest.Host)
require.True(t, t.Run("hit_cached_host", func(t *testing.T) {
ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
var r internal.Result
- r, err = f.FilterRequest(ctx, testOtherHostReq, testOtherReqInfo)
+ r, err = f.FilterRequest(ctx, otherHostReq)
require.NoError(t, err)
- assert.Equal(t, newModReqResult(testOtherHostReq, testOtherHost), r)
+ wantRes := newModReqResult(otherHostReq.DNS, filtertest.Host)
+ filtertest.AssertEqualResult(t, wantRes, r)
}))
require.True(t, t.Run("refresh", func(t *testing.T) {
@@ -417,7 +356,7 @@ func TestFilter_FilterRequest_staleCache(t *testing.T) {
ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
var r internal.Result
- r, err = f.FilterRequest(ctx, testOtherHostReq, testOtherReqInfo)
+ r, err = f.FilterRequest(ctx, otherHostReq)
require.NoError(t, err)
assert.Nil(t, r)
@@ -427,10 +366,10 @@ func TestFilter_FilterRequest_staleCache(t *testing.T) {
ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
var r internal.Result
- r, err = f.FilterRequest(ctx, testHostReq, testReqInfo)
+ r, err = f.FilterRequest(ctx, hostReq)
require.NoError(t, err)
- wantRes := newModReqResult(testHostReq, testHost)
- assert.Equal(t, wantRes, r)
+ wantRes := newModReqResult(hostReq.DNS, filtertest.HostAdultContent)
+ filtertest.AssertEqualResult(t, wantRes, r)
}))
}
diff --git a/internal/filter/hashprefix/hashprefix_test.go b/internal/filter/hashprefix/hashprefix_test.go
index d411326..15c1f01 100644
--- a/internal/filter/hashprefix/hashprefix_test.go
+++ b/internal/filter/hashprefix/hashprefix_test.go
@@ -1,15 +1,8 @@
package hashprefix_test
import (
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/filtertest"
)
-// testFltListID is the common filtering-list for tests.
-const testFltListID = agd.FilterListIDAdultBlocking
-
-// Common hostnames for tests.
-const (
- testHost = "porn.example"
- testReplHost = "repl.example"
- testOtherHost = "otherporn.example"
-)
+// testHashes is the host data for tests.
+const testHashes = filtertest.HostAdultContent + "\n"
diff --git a/internal/filter/hashprefix/storage_test.go b/internal/filter/hashprefix/storage_test.go
index c57bb2a..a32bfe2 100644
--- a/internal/filter/hashprefix/storage_test.go
+++ b/internal/filter/hashprefix/storage_test.go
@@ -9,15 +9,16 @@ import (
"testing"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/filtertest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestStorage_Hashes(t *testing.T) {
- s, err := hashprefix.NewStorage(testHost)
+ s, err := hashprefix.NewStorage(testHashes)
require.NoError(t, err)
- h := sha256.Sum256([]byte(testHost))
+ h := sha256.Sum256([]byte(filtertest.HostAdultContent))
want := []string{hex.EncodeToString(h[:])}
p := hashprefix.Prefix{h[0], h[1]}
@@ -29,47 +30,49 @@ func TestStorage_Hashes(t *testing.T) {
}
func TestStorage_Matches(t *testing.T) {
- s, err := hashprefix.NewStorage(testHost)
+ s, err := hashprefix.NewStorage(testHashes)
require.NoError(t, err)
- got := s.Matches(testHost)
+ got := s.Matches(filtertest.HostAdultContent)
assert.True(t, got)
- got = s.Matches(testOtherHost)
+ got = s.Matches(filtertest.Host)
assert.False(t, got)
}
func TestStorage_Reset(t *testing.T) {
- s, err := hashprefix.NewStorage(testHost)
+ s, err := hashprefix.NewStorage(testHashes)
require.NoError(t, err)
- assert.True(t, s.Matches(testHost))
+ assert.True(t, s.Matches(filtertest.HostAdultContent))
- n, err := s.Reset(testOtherHost)
+ const newHashes = filtertest.Host + "\n"
+
+ n, err := s.Reset(newHashes)
require.NoError(t, err)
assert.Equal(t, 1, n)
- assert.False(t, s.Matches(testHost))
+ assert.False(t, s.Matches(filtertest.HostAdultContent))
- h := sha256.Sum256([]byte(testOtherHost))
+ h := sha256.Sum256([]byte(filtertest.Host))
want := []string{hex.EncodeToString(h[:])}
p := hashprefix.Prefix{h[0], h[1]}
got := s.Hashes([]hashprefix.Prefix{p})
assert.Equal(t, want, got)
- prevHash := sha256.Sum256([]byte(testHost))
+ prevHash := sha256.Sum256([]byte(filtertest.HostAdultContent))
prev := s.Hashes([]hashprefix.Prefix{{prevHash[0], prevHash[1]}})
assert.Empty(t, prev)
// Reset again to make sure that the reuse of the map did not affect the
// results.
- n, err = s.Reset(testOtherHost)
+ n, err = s.Reset(newHashes)
require.NoError(t, err)
assert.Equal(t, 1, n)
- assert.False(t, s.Matches(testHost))
- assert.True(t, s.Matches(testOtherHost))
+ assert.False(t, s.Matches(filtertest.HostAdultContent))
+ assert.True(t, s.Matches(filtertest.Host))
}
// Sinks for benchmarks.
@@ -83,7 +86,7 @@ func BenchmarkStorage_Hashes(b *testing.B) {
var hosts []string
for i := range N {
- hosts = append(hosts, fmt.Sprintf("%d."+testHost, i))
+ hosts = append(hosts, fmt.Sprintf("%d."+filtertest.HostAdultContent, i))
}
s, err := hashprefix.NewStorage(strings.Join(hosts, "\n"))
@@ -123,7 +126,7 @@ func BenchmarkStorage_ResetHosts(b *testing.B) {
var hosts []string
for i := range N {
- hosts = append(hosts, fmt.Sprintf("%d."+testHost, i))
+ hosts = append(hosts, fmt.Sprintf("%d."+filtertest.HostAdultContent, i))
}
hostnames := strings.Join(hosts, "\n")
diff --git a/internal/filter/internal/composite/composite.go b/internal/filter/internal/composite/composite.go
index 04b3d0d..bf96949 100644
--- a/internal/filter/internal/composite/composite.go
+++ b/internal/filter/internal/composite/composite.go
@@ -7,8 +7,8 @@ import (
"fmt"
"strings"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"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"
@@ -18,9 +18,6 @@ import (
// Filter is a composite filter based on several types of safe-search and
// rule-list filters.
-//
-// An empty composite filter is a filter that always returns a nil filtering
-// result.
type Filter struct {
// custom is the custom rule-list filter of the profile, if any.
custom *rulelist.Immutable
@@ -60,21 +57,16 @@ type Config struct {
Custom *rulelist.Immutable
// RuleLists are the enabled rule-list filters of the profile or filtering
- // group.
+ // group, if any. All items must not be nil.
RuleLists []*rulelist.Refreshable
// ServiceLists are the rule-list filters of the profile's enabled blocked
- // services, if any.
+ // services, if any. All items must not be nil.
ServiceLists []*rulelist.Immutable
}
-// New returns a new composite filter. If c is nil or empty, f returns a filter
-// that always returns a nil filtering result.
+// New returns a new composite filter. c must not be nil.
func New(c *Config) (f *Filter) {
- if c == nil {
- return &Filter{}
- }
-
f = &Filter{
custom: c.Custom,
ruleLists: c.RuleLists,
@@ -104,30 +96,34 @@ func appendReqFilter[T *hashprefix.Filter | *safesearch.Filter](
}
// type check
-var _ internal.Interface = (*Filter)(nil)
+var _ filter.Interface = (*Filter)(nil)
-// FilterRequest implements the [internal.Interface] interface for *Filter. If
-// there is a safe-search result, it returns it. Otherwise, it returns the
-// action created from the filter list network rule with the highest priority.
+// FilterRequest implements the [internal.Interface] interface for *Filter. The
+// order in which the filters are applied is the following:
+//
+// 1. Custom filter.
+// 2. Rule-list filters.
+// 3. Blocked-service filters.
+// 4. Dangerous-domains filter.
+// 5. Adult-content filter.
+// 6. General safe-search filter.
+// 7. YouTube safe-search filter.
+// 8. Newly-registered domains filter.
+//
// If f is empty, it returns nil with no error.
func (f *Filter) FilterRequest(
ctx context.Context,
- req *dns.Msg,
- ri *agd.RequestInfo,
+ req *internal.Request,
) (r internal.Result, err error) {
- if f.isEmpty() {
- return nil, nil
- }
-
// 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(ri, req)
+ rlRes := f.filterReqWithRuleLists(req)
switch flRes := rlRes.(type) {
case *internal.ResultAllowed:
// Skip any additional filtering if the domain is explicitly allowed by
// user's custom rule.
- if flRes.List == agd.FilterListIDCustom {
+ if flRes.List == internal.IDCustom {
return flRes, nil
}
case
@@ -142,7 +138,7 @@ func (f *Filter) FilterRequest(
}
for _, rf := range f.reqFilters {
- r, err = rf.FilterRequest(ctx, req, ri)
+ r, err = rf.FilterRequest(ctx, req)
if err != nil {
return nil, err
} else if r != nil {
@@ -156,20 +152,16 @@ 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(ri *agd.RequestInfo, req *dns.Msg) (r internal.Result) {
- ip, host, qt := ri.RemoteIP, ri.Host, ri.QType
+func (f *Filter) filterReqWithRuleLists(req *internal.Request) (r internal.Result) {
+ ip, host, qt := req.RemoteIP, req.Host, req.QType
ufRes := &rulelist.URLFilterResult{}
if f.custom != nil {
- // Only use the device name for custom filters of profiles with devices.
- var devName string
- if _, d := ri.DeviceData(); d != nil {
- devName = string(d.Name)
- }
+ id := internal.IDCustom
- id := agd.FilterListIDCustom
- dr := f.custom.DNSResult(ip, devName, host, qt, false)
- mod := rulelist.ProcessDNSRewrites(ri.Messages, req, dr.DNSRewrites(), host, id)
+ // Only use the device name for custom filters of profiles with devices.
+ dr := f.custom.DNSResult(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
// first, because custom rules have priority over other rules.
@@ -182,7 +174,7 @@ func (f *Filter) filterReqWithRuleLists(ri *agd.RequestInfo, req *dns.Msg) (r in
for _, rl := range f.ruleLists {
id, _ := rl.ID()
dr := rl.DNSResult(ip, "", host, qt, false)
- mod := rulelist.ProcessDNSRewrites(ri.Messages, req, dr.DNSRewrites(), host, id)
+ mod := rulelist.ProcessDNSRewrites(req, dr.DNSRewrites(), id)
if mod != nil {
// DNS rewrites have higher priority, so a modified request must be
// returned immediately.
@@ -205,15 +197,10 @@ func (f *Filter) filterReqWithRuleLists(ri *agd.RequestInfo, req *dns.Msg) (r in
// results are not applied to responses.
func (f *Filter) FilterResponse(
_ context.Context,
- resp *dns.Msg,
- ri *agd.RequestInfo,
+ resp *internal.Response,
) (r internal.Result, err error) {
- if f.isEmpty() {
- return nil, nil
- }
-
- for _, ans := range resp.Answer {
- r = f.filterAnswer(ri, ans)
+ for _, ans := range resp.DNS.Answer {
+ r = f.filterAnswer(resp, ans)
if r != nil {
break
}
@@ -224,9 +211,9 @@ 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(ri *agd.RequestInfo, ans dns.RR) (r internal.Result) {
+func (f *Filter) filterAnswer(resp *internal.Response, ans dns.RR) (r internal.Result) {
if rr, ok := ans.(*dns.HTTPS); ok {
- return f.filterHTTPSAnswer(ri, rr)
+ return f.filterHTTPSAnswer(resp, rr)
}
host, rrType, ok := parseRespAnswer(ans)
@@ -234,32 +221,27 @@ func (f *Filter) filterAnswer(ri *agd.RequestInfo, ans dns.RR) (r internal.Resul
return nil
}
- return f.filterRespWithRuleLists(ri, host, rrType)
+ return f.filterRespWithRuleLists(resp, host, rrType)
}
// filterRespWithRuleLists filters one answer's information through all
// rule-list filters of the composite filter.
func (f *Filter) filterRespWithRuleLists(
- ri *agd.RequestInfo,
+ resp *internal.Response,
host string,
rrType dnsmsg.RRType,
) (r internal.Result) {
ufRes := &rulelist.URLFilterResult{}
for _, rl := range f.ruleLists {
- ufRes.Add(rl.DNSResult(ri.RemoteIP, "", host, rrType, true))
+ ufRes.Add(rl.DNSResult(resp.RemoteIP, "", host, rrType, true))
}
if f.custom != nil {
- var devName string
- if _, d := ri.DeviceData(); d != nil {
- devName = string(d.Name)
- }
-
- ufRes.Add(f.custom.DNSResult(ri.RemoteIP, devName, host, rrType, true))
+ ufRes.Add(f.custom.DNSResult(resp.RemoteIP, resp.ClientName, host, rrType, true))
}
for _, rl := range f.svcLists {
- ufRes.Add(rl.DNSResult(ri.RemoteIP, "", host, rrType, true))
+ ufRes.Add(rl.DNSResult(resp.RemoteIP, "", host, rrType, true))
}
return ufRes.ToInternal(f, rrType)
@@ -267,11 +249,11 @@ func (f *Filter) filterRespWithRuleLists(
// filterHTTPSAnswer filters HTTPS answers information through all rule list
// filters of the composite filter.
-func (f *Filter) filterHTTPSAnswer(ri *agd.RequestInfo, rr *dns.HTTPS) (r internal.Result) {
+func (f *Filter) filterHTTPSAnswer(resp *internal.Response, rr *dns.HTTPS) (r internal.Result) {
for _, kv := range rr.Value {
switch kv.Key() {
case dns.SVCB_IPV4HINT, dns.SVCB_IPV6HINT:
- r = f.filterSVCBHint(kv.String(), ri)
+ r = f.filterSVCBHint(kv.String(), resp)
if r != nil {
return r
}
@@ -285,12 +267,9 @@ func (f *Filter) filterHTTPSAnswer(ri *agd.RequestInfo, rr *dns.HTTPS) (r intern
// filterSVCBHint filters SVCB hint information through all rule list filters of
// the composite filter.
-func (f *Filter) filterSVCBHint(
- hint string,
- ri *agd.RequestInfo,
-) (r internal.Result) {
+func (f *Filter) filterSVCBHint(hint string, resp *internal.Response) (r internal.Result) {
for _, s := range strings.Split(hint, ",") {
- r = f.filterRespWithRuleLists(ri, s, dns.TypeHTTPS)
+ r = f.filterRespWithRuleLists(resp, s, dns.TypeHTTPS)
if r != nil {
return r
}
@@ -315,24 +294,13 @@ func parseRespAnswer(ans dns.RR) (hostname string, rrType dnsmsg.RRType, ok bool
}
}
-// isEmpty returns true if this composite filter is an empty filter.
-func (f *Filter) isEmpty() (ok bool) {
- return f == nil ||
- (f.custom == nil &&
- len(f.ruleLists) == 0 &&
- len(f.svcLists) == 0 &&
- len(f.reqFilters) == 0)
-}
-
// 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 agd.FilterListID, svcID agd.BlockedServiceID) {
+func (f *Filter) Map(id int) (fltID internal.ID, svcID internal.BlockedServiceID) {
for _, rl := range f.ruleLists {
if rl.URLFilterID() == id {
return rl.ID()
diff --git a/internal/filter/internal/composite/composite_internal_test.go b/internal/filter/internal/composite/composite_internal_test.go
index f8be5c8..715f58f 100644
--- a/internal/filter/internal/composite/composite_internal_test.go
+++ b/internal/filter/internal/composite/composite_internal_test.go
@@ -3,9 +3,6 @@ package composite
import (
"testing"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
- "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/internal/filtertest"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist"
@@ -20,7 +17,7 @@ var (
func BenchmarkFilter_FilterReqWithRuleLists(b *testing.B) {
blockingRL, err := rulelist.NewFromString(
- filtertest.BlockRule+"\n",
+ filtertest.RuleBlockStr+"\n",
"test",
"",
rulelist.ResultCacheEmpty{},
@@ -31,31 +28,13 @@ func BenchmarkFilter_FilterReqWithRuleLists(b *testing.B) {
RuleLists: []*rulelist.Refreshable{blockingRL},
})
- msgs, err := dnsmsg.NewConstructor(&dnsmsg.ConstructorConfig{
- Cloner: dnsmsg.NewCloner(dnsmsg.EmptyClonerStat{}),
- BlockingMode: &dnsmsg.BlockingModeNullIP{},
- StructuredErrors: &dnsmsg.StructuredDNSErrorsConfig{
- Enabled: false,
- },
- FilteredResponseTTL: filtertest.Staleness,
- EDEEnabled: false,
- })
- require.NoError(b, err)
-
- req := dnsservertest.NewReq(filtertest.ReqFQDN, dns.TypeA, dns.ClassINET)
- ri := &agd.RequestInfo{
- Messages: msgs,
- RemoteIP: filtertest.RemoteIP,
- Host: filtertest.ReqHost,
- QType: dns.TypeA,
- QClass: dns.ClassINET,
- }
+ req := filtertest.NewRequest(b, "", filtertest.HostBlocked, filtertest.IPv4Client, dns.TypeA)
b.ReportAllocs()
b.ResetTimer()
for range b.N {
- resultSink = f.filterReqWithRuleLists(ri, req)
+ resultSink = f.filterReqWithRuleLists(req)
}
// Most recent results:
diff --git a/internal/filter/internal/composite/composite_test.go b/internal/filter/internal/composite/composite_test.go
index 273cc93..129c113 100644
--- a/internal/filter/internal/composite/composite_test.go
+++ b/internal/filter/internal/composite/composite_test.go
@@ -7,13 +7,13 @@ import (
"testing"
"time"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"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/internal/composite"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/filtertest"
+ "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/golibs/logutil/slogutil"
@@ -23,15 +23,9 @@ import (
"github.com/stretchr/testify/require"
)
-// Common filter list IDs for tests.
-const (
- testFltListID1 agd.FilterListID = "fl1"
- testFltListID2 agd.FilterListID = "fl2"
-)
-
// 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 agd.FilterListID) (rl *rulelist.Refreshable) {
+func newFromStr(tb testing.TB, text string, id internal.ID) (rl *rulelist.Refreshable) {
tb.Helper()
rl, err := rulelist.NewFromString(text, id, "", rulelist.ResultCacheEmpty{})
@@ -42,7 +36,7 @@ func newFromStr(tb testing.TB, text string, id agd.FilterListID) (rl *rulelist.R
// 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 agd.FilterListID) (rl *rulelist.Immutable) {
+func newImmutable(tb testing.TB, text string, id internal.ID) (rl *rulelist.Immutable) {
tb.Helper()
rl, err := rulelist.NewImmutable(text, id, "", rulelist.ResultCacheEmpty{})
@@ -52,83 +46,48 @@ func newImmutable(tb testing.TB, text string, id agd.FilterListID) (rl *rulelist
}
// newReqData returns data for calling FilterRequest. The context uses
-// [filtertest.Timeout] and [tb.Cleanup] is used for its cancelation. Both req
-// and ri use [filtertest.ReqFQDN], [dns.TypeA], and [dns.ClassINET] for the
-// request data.
-func newReqData(tb testing.TB) (ctx context.Context, req *dns.Msg, ri *agd.RequestInfo) {
+// [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) {
tb.Helper()
ctx = testutil.ContextWithTimeout(tb, filtertest.Timeout)
- req = dnsservertest.NewReq(filtertest.ReqFQDN, dns.TypeA, dns.ClassINET)
- ri = &agd.RequestInfo{
+ req = &internal.Request{
+ DNS: dnsservertest.NewReq(filtertest.FQDNBlocked, dns.TypeA, dns.ClassINET),
Messages: agdtest.NewConstructor(tb),
- RemoteIP: filtertest.RemoteIP,
- Host: filtertest.ReqHost,
+ RemoteIP: filtertest.IPv4Client,
+ Host: filtertest.HostBlocked,
QType: dns.TypeA,
QClass: dns.ClassINET,
}
- return ctx, req, ri
-}
-
-func TestFilter_nil(t *testing.T) {
- testCases := []struct {
- flt *composite.Filter
- name string
- }{{
- flt: nil,
- name: "nil",
- }, {
- flt: composite.New(nil),
- name: "config_nil",
- }, {
- flt: composite.New(&composite.Config{}),
- name: "config_empty",
- }}
-
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- ctx, req, ri := newReqData(t)
- res, err := tc.flt.FilterRequest(ctx, req, ri)
- assert.NoError(t, err)
- assert.Nil(t, res)
-
- resp := dnsservertest.NewResp(dns.RcodeSuccess, req)
- res, err = tc.flt.FilterResponse(ctx, resp, ri)
- assert.NoError(t, err)
- assert.Nil(t, res)
- })
- }
+ return ctx, req
}
func TestFilter_FilterRequest_customWithClientName(t *testing.T) {
const (
devName = "MyDevice"
- blockRule = filtertest.BlockRule + "$client=" + devName
+ blockRule = filtertest.RuleBlockStr + "$client=" + devName
)
f := composite.New(&composite.Config{
- Custom: newImmutable(t, blockRule, agd.FilterListIDCustom),
+ Custom: newImmutable(t, blockRule, internal.IDCustom),
})
- ctx, req, ri := newReqData(t)
- res, err := f.FilterRequest(ctx, req, ri)
+ ctx, req := newReqData(t)
+ res, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
assert.Nil(t, res)
- ri.DeviceResult = &agd.DeviceResultOK{
- Device: &agd.Device{
- Name: devName,
- },
- Profile: &agd.Profile{},
- }
+ req.ClientName = devName
- res, err = f.FilterRequest(ctx, req, ri)
+ res, err = f.FilterRequest(ctx, req)
require.NoError(t, err)
wantRes := &internal.ResultBlocked{
- List: agd.FilterListIDCustom,
+ List: internal.IDCustom,
Rule: blockRule,
}
@@ -137,12 +96,12 @@ func TestFilter_FilterRequest_customWithClientName(t *testing.T) {
func TestFilter_FilterRequest_badfilter(t *testing.T) {
const (
- blockRule = filtertest.BlockRule
- badFilterRule = filtertest.BlockRule + "$badfilter"
+ blockRule = filtertest.RuleBlockStr
+ badFilterRule = filtertest.RuleBlockStr + "$badfilter"
)
- rl1 := newFromStr(t, blockRule, testFltListID1)
- rl2 := newFromStr(t, badFilterRule, testFltListID2)
+ rl1 := newFromStr(t, blockRule, filtertest.RuleListID1)
+ rl2 := newFromStr(t, badFilterRule, filtertest.RuleListID2)
testCases := []struct {
name string
@@ -151,7 +110,7 @@ func TestFilter_FilterRequest_badfilter(t *testing.T) {
}{{
name: "block",
wantRes: &internal.ResultBlocked{
- List: testFltListID1,
+ List: filtertest.RuleListID1,
Rule: blockRule,
},
ruleLists: []*rulelist.Refreshable{rl1},
@@ -171,8 +130,8 @@ func TestFilter_FilterRequest_badfilter(t *testing.T) {
RuleLists: tc.ruleLists,
})
- ctx, req, ri := newReqData(t)
- res, err := f.FilterRequest(ctx, req, ri)
+ ctx, req := newReqData(t)
+ res, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
assert.Equal(t, tc.wantRes, res)
@@ -181,22 +140,22 @@ func TestFilter_FilterRequest_badfilter(t *testing.T) {
}
func TestFilter_FilterRequest_customAllow(t *testing.T) {
- const allowRule = "@@" + filtertest.BlockRule
+ const allowRule = "@@" + filtertest.RuleBlockStr
- blockingRL := newFromStr(t, filtertest.BlockRule, testFltListID1)
- customRL := newImmutable(t, allowRule, agd.FilterListIDCustom)
+ blockingRL := newFromStr(t, filtertest.RuleBlockStr, filtertest.RuleListID1)
+ customRL := newImmutable(t, allowRule, internal.IDCustom)
f := composite.New(&composite.Config{
Custom: customRL,
RuleLists: []*rulelist.Refreshable{blockingRL},
})
- ctx, req, ri := newReqData(t)
- res, err := f.FilterRequest(ctx, req, ri)
+ ctx, req := newReqData(t)
+ res, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
want := &internal.ResultAllowed{
- List: agd.FilterListIDCustom,
+ List: internal.IDCustom,
Rule: allowRule,
}
assert.Equal(t, want, res)
@@ -204,29 +163,27 @@ func TestFilter_FilterRequest_customAllow(t *testing.T) {
func TestFilter_FilterRequest_dnsrewrite(t *testing.T) {
const (
- blockRule = filtertest.BlockRule
- dnsRewriteRuleRefused = filtertest.BlockRule + "$dnsrewrite=REFUSED"
- dnsRewriteRuleCname = filtertest.BlockRule + "$dnsrewrite=new-cname.example"
- dnsRewrite2Rules = filtertest.BlockRule + "$dnsrewrite=1.2.3.4\n" +
- filtertest.BlockRule + "$dnsrewrite=1.2.3.5"
- dnsRewriteRuleTXT = filtertest.BlockRule + "$dnsrewrite=NOERROR;TXT;abcdefg"
- dnsRewriteRuleSOA = filtertest.BlockRule + "$dnsrewrite=NOERROR;SOA;ns1." +
- filtertest.ReqFQDN + " hostmaster." + filtertest.ReqFQDN + " 1 3600 1800 604800 86400"
+ blockRule = filtertest.RuleBlockStr
+ dnsRewriteRuleRefused = filtertest.RuleBlockStr + "$dnsrewrite=REFUSED"
+ dnsRewriteRuleCname = filtertest.RuleBlockStr + "$dnsrewrite=new-cname.example"
+ dnsRewrite2Rules = filtertest.RuleBlockStr + "$dnsrewrite=1.2.3.4\n" +
+ filtertest.RuleBlockStr + "$dnsrewrite=1.2.3.5"
+ dnsRewriteRuleTXT = filtertest.RuleBlockStr + "$dnsrewrite=NOERROR;TXT;abcdefg"
+ dnsRewriteRuleSOA = filtertest.RuleBlockStr + "$dnsrewrite=NOERROR;SOA;ns1." +
+ filtertest.FQDNBlocked + " hostmaster." + filtertest.FQDNBlocked +
+ " 1 3600 1800 604800 86400"
dnsRewriteTypedRules = dnsRewriteRuleTXT + "\n" + dnsRewriteRuleSOA
- dnsRewriteRulePopup = filtertest.BlockRule + "$dnsrewrite=" + filtertest.PopupBlockPageHost
)
var (
- rlNonRewrite = newFromStr(t, blockRule, testFltListID1)
- rlRewriteIgnored = newFromStr(t, dnsRewriteRuleRefused, testFltListID2)
- rlCustomRefused = newImmutable(t, dnsRewriteRuleRefused, agd.FilterListIDCustom)
- rlCustomCname = newImmutable(t, dnsRewriteRuleCname, agd.FilterListIDCustom)
- rlCustom2Rules = newImmutable(t, dnsRewrite2Rules, agd.FilterListIDCustom)
- rlCustomTyped = newImmutable(t, dnsRewriteTypedRules, agd.FilterListIDCustom)
- rlPopup = newFromStr(t, dnsRewriteRulePopup, agd.FilterListIDAdGuardPopup)
+ 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)
)
- req := dnsservertest.NewReq(filtertest.ReqFQDN, dns.TypeA, dns.ClassINET)
+ req := dnsservertest.NewReq(filtertest.FQDNBlocked, dns.TypeA, dns.ClassINET)
// Create a CNAME-modified request.
modifiedReq := dnsmsg.Clone(req)
@@ -245,70 +202,76 @@ func TestFilter_FilterRequest_dnsrewrite(t *testing.T) {
name string
ruleLists []*rulelist.Refreshable
}{{
- custom: nil,
- req: req,
- wantRes: &internal.ResultBlocked{List: testFltListID1, Rule: blockRule},
+ custom: nil,
+ req: req,
+ wantRes: &internal.ResultBlocked{
+ List: filtertest.RuleListID1,
+ Rule: blockRule,
+ },
name: "block",
ruleLists: []*rulelist.Refreshable{rlNonRewrite},
}, {
- custom: nil,
- req: req,
- wantRes: &internal.ResultBlocked{List: testFltListID1, Rule: blockRule},
+ custom: nil,
+ req: req,
+ wantRes: &internal.ResultBlocked{
+ List: filtertest.RuleListID1,
+ Rule: blockRule,
+ },
name: "dnsrewrite_no_effect",
- ruleLists: []*rulelist.Refreshable{rlNonRewrite, rlRewriteIgnored},
+ ruleLists: []*rulelist.Refreshable{rlNonRewrite},
}, {
custom: rlCustomRefused,
req: req,
wantRes: &internal.ResultModifiedResponse{
Msg: dnsservertest.NewResp(dns.RcodeRefused, req),
- List: agd.FilterListIDCustom,
+ List: internal.IDCustom,
Rule: dnsRewriteRuleRefused,
},
name: "dnsrewrite_block",
- ruleLists: []*rulelist.Refreshable{rlNonRewrite, rlRewriteIgnored},
+ ruleLists: []*rulelist.Refreshable{rlNonRewrite},
}, {
custom: rlCustomCname,
req: req,
wantRes: &internal.ResultModifiedRequest{
Msg: modifiedReq,
- List: agd.FilterListIDCustom,
+ List: internal.IDCustom,
Rule: dnsRewriteRuleCname,
},
name: "dnsrewrite_cname",
- ruleLists: []*rulelist.Refreshable{rlNonRewrite, rlRewriteIgnored},
+ ruleLists: []*rulelist.Refreshable{rlNonRewrite},
}, {
custom: rlCustom2Rules,
req: req,
wantRes: &internal.ResultModifiedResponse{
Msg: dnsservertest.NewResp(dns.RcodeSuccess, req, dnsservertest.SectionAnswer{
dnsservertest.NewA(
- filtertest.ReqFQDN,
+ filtertest.FQDNBlocked,
agdtest.FilteredResponseTTLSec,
netip.MustParseAddr("1.2.3.4"),
),
dnsservertest.NewA(
- filtertest.ReqFQDN,
+ filtertest.FQDNBlocked,
agdtest.FilteredResponseTTLSec,
netip.MustParseAddr("1.2.3.5"),
),
}),
- List: agd.FilterListIDCustom,
+ List: internal.IDCustom,
Rule: "",
},
name: "dnsrewrite_answers",
- ruleLists: []*rulelist.Refreshable{rlNonRewrite, rlRewriteIgnored},
+ ruleLists: []*rulelist.Refreshable{rlNonRewrite},
}, {
custom: rlCustomTyped,
req: txtReq,
wantRes: &internal.ResultModifiedResponse{
Msg: dnsservertest.NewResp(dns.RcodeSuccess, txtReq, dnsservertest.SectionAnswer{
dnsservertest.NewTXT(
- filtertest.ReqFQDN,
+ filtertest.FQDNBlocked,
agdtest.FilteredResponseTTLSec,
"abcdefg",
),
}),
- List: agd.FilterListIDCustom,
+ List: internal.IDCustom,
Rule: "",
},
name: "dnsrewrite_txt",
@@ -318,20 +281,10 @@ func TestFilter_FilterRequest_dnsrewrite(t *testing.T) {
req: soaReq,
wantRes: &internal.ResultModifiedResponse{
Msg: dnsservertest.NewResp(dns.RcodeSuccess, soaReq),
- List: agd.FilterListIDCustom,
+ List: internal.IDCustom,
},
name: "dnsrewrite_soa",
ruleLists: []*rulelist.Refreshable{},
- }, {
- custom: nil,
- req: req,
- wantRes: &internal.ResultModifiedRequest{
- Msg: dnsservertest.NewReq(filtertest.PopupBlockPageFQDN, dns.TypeA, dns.ClassINET),
- List: agd.FilterListIDAdGuardPopup,
- Rule: dnsRewriteRulePopup,
- },
- name: "dnsrewrite_popup",
- ruleLists: []*rulelist.Refreshable{rlPopup},
}}
for _, tc := range testCases {
@@ -342,13 +295,13 @@ func TestFilter_FilterRequest_dnsrewrite(t *testing.T) {
})
ctx := context.Background()
- ri := &agd.RequestInfo{
+ res, fltErr := f.FilterRequest(ctx, &internal.Request{
+ DNS: tc.req,
Messages: agdtest.NewConstructor(t),
- Host: filtertest.ReqHost,
+ Host: filtertest.HostBlocked,
QType: tc.req.Question[0].Qtype,
- }
+ })
- res, fltErr := f.FilterRequest(ctx, tc.req, ri)
require.NoError(t, fltErr)
filtertest.AssertEqualResult(t, tc.wantRes, res)
@@ -368,18 +321,18 @@ func TestFilter_FilterRequest_hostsRules(t *testing.T) {
rules = blockRule4 + "\n" + blockRule6
)
- rl := newFromStr(t, rules, testFltListID1)
+ rl := newFromStr(t, rules, filtertest.RuleListID1)
f := composite.New(&composite.Config{
RuleLists: []*rulelist.Refreshable{rl},
})
resBlocked4 := &internal.ResultBlocked{
- List: testFltListID1,
+ List: filtertest.RuleListID1,
Rule: blockRule4,
}
resBlocked6 := &internal.ResultBlocked{
- List: testFltListID1,
+ List: filtertest.RuleListID1,
Rule: blockRule6,
}
@@ -417,12 +370,6 @@ func TestFilter_FilterRequest_hostsRules(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- ri := &agd.RequestInfo{
- Messages: agdtest.NewConstructor(t),
- Host: tc.reqHost,
- QType: tc.reqType,
- }
-
req := &dns.Msg{
Question: []dns.Question{{
Name: dns.Fqdn(tc.reqHost),
@@ -432,9 +379,15 @@ func TestFilter_FilterRequest_hostsRules(t *testing.T) {
}
ctx := context.Background()
+ fltReq := &internal.Request{
+ DNS: req,
+ Messages: agdtest.NewConstructor(t),
+ Host: tc.reqHost,
+ QType: tc.reqType,
+ }
- res, rerr := f.FilterRequest(ctx, req, ri)
- require.NoError(t, rerr)
+ res, fltErr := f.FilterRequest(ctx, fltReq)
+ require.NoError(t, fltErr)
assert.Equal(t, tc.wantRes, res)
assert.Equal(t, tc.wantRes, res)
@@ -443,18 +396,14 @@ func TestFilter_FilterRequest_hostsRules(t *testing.T) {
}
func TestFilter_FilterRequest_safeSearch(t *testing.T) {
- const safeSearchIPStr = "1.2.3.4"
-
- const rewriteRule = filtertest.BlockRule + "$dnsrewrite=NOERROR;A;" + safeSearchIPStr
-
- safeSearchIP := netip.MustParseAddr(safeSearchIPStr)
+ const rewriteRule = filtertest.RuleSafeSearchGeneralIPv4Str + "\n"
cachePath, srvURL := filtertest.PrepareRefreshable(t, nil, rewriteRule, http.StatusOK)
- const fltListID = agd.FilterListIDGeneralSafeSearch
+ const fltListID = internal.IDGeneralSafeSearch
gen, err := safesearch.New(
&safesearch.Config{
- Refreshable: &internal.RefreshableConfig{
+ Refreshable: &refreshable.Config{
Logger: slogutil.NewDiscardLogger(),
URL: srvURL,
ID: fltListID,
@@ -465,7 +414,7 @@ func TestFilter_FilterRequest_safeSearch(t *testing.T) {
},
CacheTTL: 1 * time.Minute,
},
- rulelist.NewResultCache(100, true),
+ rulelist.NewResultCache(filtertest.CacheCount, true),
)
require.NoError(t, err)
@@ -476,28 +425,33 @@ func TestFilter_FilterRequest_safeSearch(t *testing.T) {
GeneralSafeSearch: gen,
})
- ctx, req, ri := newReqData(t)
- res, err := f.FilterRequest(ctx, req, ri)
+ ctx, req := newReqData(t)
+ req.DNS.Question[0].Name = filtertest.FQDNSafeSearchGeneralIPv4
+ req.Host = filtertest.HostSafeSearchGeneralIPv4
+
+ res, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
- wantResp := dnsservertest.NewResp(dns.RcodeSuccess, req, dnsservertest.SectionAnswer{
- dnsservertest.NewA(filtertest.ReqFQDN, agdtest.FilteredResponseTTLSec, safeSearchIP),
+ wantResp := dnsservertest.NewResp(dns.RcodeSuccess, req.DNS, dnsservertest.SectionAnswer{
+ dnsservertest.NewA(
+ filtertest.FQDNSafeSearchGeneralIPv4,
+ agdtest.FilteredResponseTTLSec,
+ filtertest.IPv4SafeSearchRepl,
+ ),
})
want := &internal.ResultModifiedResponse{
Msg: wantResp,
List: fltListID,
- Rule: filtertest.ReqHost,
+ Rule: filtertest.HostSafeSearchGeneralIPv4,
}
assert.Equal(t, want, res)
}
func TestFilter_FilterRequest_services(t *testing.T) {
- const svcID = "test_service"
-
svcRL, err := rulelist.NewImmutable(
- filtertest.BlockRule,
- agd.FilterListIDBlockedService,
- svcID,
+ filtertest.RuleBlockStr,
+ internal.IDBlockedService,
+ filtertest.BlockedServiceID1,
rulelist.ResultCacheEmpty{},
)
require.NoError(t, err)
@@ -506,26 +460,27 @@ func TestFilter_FilterRequest_services(t *testing.T) {
ServiceLists: []*rulelist.Immutable{svcRL},
})
- ctx, req, ri := newReqData(t)
- res, err := f.FilterRequest(ctx, req, ri)
+ ctx, req := newReqData(t)
+ res, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
want := &internal.ResultBlocked{
- List: agd.FilterListIDBlockedService,
- Rule: svcID,
+ List: internal.IDBlockedService,
+ Rule: internal.RuleText(filtertest.BlockedServiceID1),
}
assert.Equal(t, want, res)
}
func TestFilter_FilterResponse(t *testing.T) {
- const cnameReqFQDN = "sub." + filtertest.ReqFQDN
+ const cnameReqFQDN = "sub." + filtertest.FQDNBlocked
const (
- blockedCNAME = filtertest.ReqHost
passedIPv4Str = "1.1.1.1"
blockedIPv4Str = "1.2.3.4"
blockedIPv6Str = "1234::cdef"
- blockRules = blockedCNAME + "\n" + blockedIPv4Str + "\n" + blockedIPv6Str + "\n"
+ blockRules = filtertest.HostBlocked + "\n" +
+ blockedIPv4Str + "\n" +
+ blockedIPv6Str + "\n"
)
var (
@@ -534,7 +489,7 @@ func TestFilter_FilterResponse(t *testing.T) {
blockedIPv6 = netip.MustParseAddr(blockedIPv6Str)
)
- blockingRL := newFromStr(t, blockRules, testFltListID1)
+ blockingRL := newFromStr(t, blockRules, filtertest.RuleListID1)
f := composite.New(&composite.Config{
RuleLists: []*rulelist.Refreshable{blockingRL},
})
@@ -544,48 +499,48 @@ func TestFilter_FilterResponse(t *testing.T) {
testCases := []struct {
name string
reqFQDN string
- wantRule agd.FilterRuleText
+ wantRule internal.RuleText
respAns dnsservertest.SectionAnswer
qType dnsmsg.RRType
}{{
name: "pass",
- reqFQDN: filtertest.ReqFQDN,
+ reqFQDN: filtertest.FQDN,
wantRule: "",
respAns: dnsservertest.SectionAnswer{
- dnsservertest.NewA(filtertest.ReqFQDN, ttl, passedIPv4),
+ dnsservertest.NewA(filtertest.FQDN, ttl, passedIPv4),
},
qType: dns.TypeA,
}, {
name: "cname",
reqFQDN: cnameReqFQDN,
- wantRule: filtertest.ReqHost,
+ wantRule: filtertest.HostBlocked,
respAns: dnsservertest.SectionAnswer{
- dnsservertest.NewCNAME(cnameReqFQDN, ttl, filtertest.ReqFQDN),
- dnsservertest.NewA(filtertest.ReqFQDN, ttl, netip.MustParseAddr("1.2.3.4")),
+ dnsservertest.NewCNAME(cnameReqFQDN, ttl, filtertest.FQDNBlocked),
+ dnsservertest.NewA(filtertest.FQDNBlocked, ttl, netip.MustParseAddr("1.2.3.4")),
},
qType: dns.TypeA,
}, {
name: "ipv4",
- reqFQDN: filtertest.ReqFQDN,
+ reqFQDN: filtertest.FQDNBlocked,
wantRule: blockedIPv4Str,
respAns: dnsservertest.SectionAnswer{
- dnsservertest.NewA(filtertest.ReqFQDN, ttl, blockedIPv4),
+ dnsservertest.NewA(filtertest.FQDNBlocked, ttl, blockedIPv4),
},
qType: dns.TypeA,
}, {
name: "ipv6",
- reqFQDN: filtertest.ReqFQDN,
+ reqFQDN: filtertest.FQDNBlocked,
wantRule: blockedIPv6Str,
respAns: dnsservertest.SectionAnswer{
- dnsservertest.NewAAAA(filtertest.ReqFQDN, ttl, blockedIPv6),
+ dnsservertest.NewAAAA(filtertest.FQDNBlocked, ttl, blockedIPv6),
},
qType: dns.TypeAAAA,
}, {
name: "ipv4hint",
- reqFQDN: filtertest.ReqFQDN,
+ reqFQDN: filtertest.FQDNBlocked,
wantRule: blockedIPv4Str,
respAns: dnsservertest.SectionAnswer{dnsservertest.NewHTTPS(
- filtertest.ReqFQDN,
+ filtertest.FQDNBlocked,
ttl,
[]netip.Addr{blockedIPv4},
[]netip.Addr{},
@@ -593,10 +548,10 @@ func TestFilter_FilterResponse(t *testing.T) {
qType: dns.TypeHTTPS,
}, {
name: "ipv6hint",
- reqFQDN: filtertest.ReqFQDN,
+ reqFQDN: filtertest.FQDNBlocked,
wantRule: blockedIPv6Str,
respAns: dnsservertest.SectionAnswer{dnsservertest.NewHTTPS(
- filtertest.ReqFQDN,
+ filtertest.FQDNBlocked,
ttl,
[]netip.Addr{},
[]netip.Addr{blockedIPv6},
@@ -604,10 +559,10 @@ func TestFilter_FilterResponse(t *testing.T) {
qType: dns.TypeHTTPS,
}, {
name: "ipv4_ipv6_hints",
- reqFQDN: filtertest.ReqFQDN,
+ reqFQDN: filtertest.FQDNBlocked,
wantRule: blockedIPv4Str,
respAns: dnsservertest.SectionAnswer{dnsservertest.NewHTTPS(
- filtertest.ReqFQDN,
+ filtertest.FQDNBlocked,
ttl,
[]netip.Addr{blockedIPv4},
[]netip.Addr{blockedIPv6},
@@ -615,10 +570,10 @@ func TestFilter_FilterResponse(t *testing.T) {
qType: dns.TypeHTTPS,
}, {
name: "pass_hints",
- reqFQDN: filtertest.ReqFQDN,
+ reqFQDN: filtertest.FQDNBlocked,
wantRule: "",
respAns: dnsservertest.SectionAnswer{dnsservertest.NewHTTPS(
- filtertest.ReqFQDN,
+ filtertest.FQDNBlocked,
ttl,
[]netip.Addr{passedIPv4},
[]netip.Addr{},
@@ -628,12 +583,14 @@ func TestFilter_FilterResponse(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- ctx, req, ri := newReqData(t)
- req.Question[0].Name = tc.reqFQDN
- req.Question[0].Qtype = tc.qType
+ ctx, req := newReqData(t)
+ req.DNS.Question[0].Name = tc.reqFQDN
+ req.DNS.Question[0].Qtype = tc.qType
- resp := dnsservertest.NewResp(dns.RcodeSuccess, req, tc.respAns)
- res, err := f.FilterResponse(ctx, resp, ri)
+ res, err := f.FilterResponse(ctx, &internal.Response{
+ DNS: dnsservertest.NewResp(dns.RcodeSuccess, req.DNS, tc.respAns),
+ RemoteIP: filtertest.IPv4Client,
+ })
require.NoError(t, err)
if tc.wantRule == "" {
@@ -643,7 +600,7 @@ func TestFilter_FilterResponse(t *testing.T) {
}
want := &internal.ResultBlocked{
- List: testFltListID1,
+ List: filtertest.RuleListID1,
Rule: tc.wantRule,
}
assert.Equal(t, want, res)
diff --git a/internal/filter/internal/custom/custom.go b/internal/filter/internal/custom/custom.go
index 1684c80..1703789 100644
--- a/internal/filter/internal/custom/custom.go
+++ b/internal/filter/internal/custom/custom.go
@@ -1,5 +1,5 @@
// Package custom contains the caching storage of filters made from custom
-// filtering rules of profiles.
+// filtering rules of clients.
package custom
import (
@@ -9,23 +9,29 @@ import (
"strings"
"time"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"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 profiles.
+// Filters contains custom filters made from custom filtering rules of clients.
type Filters struct {
logger *slog.Logger
- cache agdcache.Interface[agd.ProfileID, *customFilterCacheItem]
+ cache agdcache.Interface[string, *cacheItem]
errColl errcoll.Interface
}
-// customCacheID is a cache identifier for the custom profile's filter.
-const customCacheID = "filters/" + string(agd.FilterListIDCustom)
+// 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.
@@ -44,10 +50,10 @@ type Config struct {
}
// New returns a new custom filter storage. It also adds the cache with ID
-// [agd.FilterListIDCustom] to the cache manager. c must not be nil.
+// [CacheID] to the cache manager. c must not be nil.
func New(c *Config) (f *Filters) {
- cache := agdcache.NewLRU[agd.ProfileID, *customFilterCacheItem](c.CacheConf)
- c.CacheManager.Add(customCacheID, cache)
+ cache := agdcache.NewLRU[string, *cacheItem](c.CacheConf)
+ c.CacheManager.Add(CacheID, cache)
return &Filters{
logger: c.Logger,
@@ -56,10 +62,14 @@ func New(c *Config) (f *Filters) {
}
}
-// Get returns the custom rule-list filter made from the profile's custom rules
-// list, if any.
-func (f *Filters) Get(ctx context.Context, p *agd.Profile) (rl *rulelist.Immutable) {
- if len(p.CustomRules) == 0 {
+// 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
@@ -68,6 +78,7 @@ func (f *Filters) Get(ctx context.Context, p *agd.Profile) (rl *rulelist.Immutab
// 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,
@@ -75,28 +86,28 @@ func (f *Filters) Get(ctx context.Context, p *agd.Profile) (rl *rulelist.Immutab
)
}()
- rl = f.get(p)
+ rl = f.get(c)
if rl != nil {
return rl
}
- // TODO(a.garipov): Consider making a copy of strings.Join for
- // agd.FilterRuleText.
+ // TODO(a.garipov): Consider making a copy of [strings.Join] for
+ // [internal.RuleText].
textLen := 0
- for _, r := range p.CustomRules {
+ for _, r := range c.Rules {
textLen += len(r) + len("\n")
}
b := &strings.Builder{}
b.Grow(textLen)
- for _, r := range p.CustomRules {
+ for _, r := range c.Rules {
stringutil.WriteToBuilder(b, string(r), "\n")
}
rl, err := rulelist.NewImmutable(
b.String(),
- agd.FilterListIDCustom,
+ internal.IDCustom,
"",
// Don't use cache for users' custom filters, because resultcache
// doesn't take $client rules into account.
@@ -108,28 +119,33 @@ func (f *Filters) Get(ctx context.Context, p *agd.Profile) (rl *rulelist.Immutab
// 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 profile %s: %w", p.ID, err)
+ 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 profile", "profile_id", p.ID, "num_rules", rl.RulesCount())
+ f.logger.DebugContext(
+ ctx,
+ "got rules for client",
+ "client_id", c.ID,
+ "num_rules", rl.RulesCount(),
+ )
- f.set(p, rl)
+ f.set(c, rl)
return rl
}
// get returns the cached custom rule-list filter, if there is one and the
-// profile hasn't changed since the filter was cached.
-func (f *Filters) get(p *agd.Profile) (rl *rulelist.Immutable) {
- item, ok := f.cache.Get(p.ID)
+// 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(p.UpdateTime) {
+ if item.updTime.Before(c.UpdateTime) {
return nil
}
@@ -137,15 +153,9 @@ func (f *Filters) get(p *agd.Profile) (rl *rulelist.Immutable) {
}
// set caches the custom rule-list filter.
-func (f *Filters) set(p *agd.Profile, rl *rulelist.Immutable) {
- f.cache.Set(p.ID, &customFilterCacheItem{
- updTime: p.UpdateTime,
+func (f *Filters) set(c *ClientConfig, rl *rulelist.Immutable) {
+ f.cache.Set(c.ID, &cacheItem{
+ updTime: c.UpdateTime,
ruleList: rl,
})
}
-
-// customFilterCacheItem is an item of the custom filter cache.
-type customFilterCacheItem struct {
- updTime time.Time
- ruleList *rulelist.Immutable
-}
diff --git a/internal/filter/internal/custom/custom_test.go b/internal/filter/internal/custom/custom_test.go
index d67e589..59d6849 100644
--- a/internal/filter/internal/custom/custom_test.go
+++ b/internal/filter/internal/custom/custom_test.go
@@ -5,9 +5,9 @@ import (
"testing"
"time"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"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"
@@ -15,34 +15,35 @@ import (
"github.com/stretchr/testify/require"
)
-// testProfID is the profile ID for tests.
-const testProfID agd.ProfileID = "prof1234"
+// 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{
- Size: 1,
+ Count: 1,
},
CacheManager: agdcache.EmptyManager{},
})
- p := &agd.Profile{
- ID: testProfID,
+ c := &custom.ClientConfig{
+ ID: testClientConfID,
UpdateTime: time.Now(),
- CustomRules: []agd.FilterRuleText{
+ Rules: []internal.RuleText{
"||first.example",
},
+ Enabled: true,
}
ctx := context.Background()
- rl := f.Get(ctx, p)
+ rl := f.Get(ctx, c)
require.NotNil(t, rl)
// Recheck cached.
- cachedRL := f.Get(ctx, p)
+ cachedRL := f.Get(ctx, c)
require.NotNil(t, cachedRL)
assert.Same(t, rl, cachedRL)
@@ -55,19 +56,20 @@ func BenchmarkFilters_Get(b *testing.B) {
Logger: slogutil.NewDiscardLogger(),
ErrColl: agdtest.NewErrorCollector(),
CacheConf: &agdcache.LRUConfig{
- Size: 1,
+ Count: 1,
},
CacheManager: agdcache.EmptyManager{},
})
- p := &agd.Profile{
- ID: testProfID,
+ c := &custom.ClientConfig{
+ ID: testClientConfID,
UpdateTime: time.Now(),
- CustomRules: []agd.FilterRuleText{
+ Rules: []internal.RuleText{
"||first.example",
"||second.example",
"||third.example",
},
+ Enabled: true,
}
ctx := context.Background()
@@ -76,7 +78,7 @@ func BenchmarkFilters_Get(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for range b.N {
- ruleListSink = f.Get(ctx, p)
+ ruleListSink = f.Get(ctx, c)
}
})
@@ -86,17 +88,16 @@ func BenchmarkFilters_Get(b *testing.B) {
for range b.N {
// Update the time on each iteration to make sure that the cache is
// never used.
- p.UpdateTime = p.UpdateTime.Add(1 * time.Millisecond)
- ruleListSink = f.Get(ctx, p)
+ c.UpdateTime = c.UpdateTime.Add(1 * time.Millisecond)
+ ruleListSink = f.Get(ctx, c)
}
})
- // Most recent result, on a ThinkPad X13 with a Ryzen Pro 7 CPU:
- //
+ // 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 7870251 233.4 ns/op 16 B/op 1 allocs/op
- // BenchmarkFilters_Get/no_cache-16 53073 23490 ns/op 14610 B/op 93 allocs/op
+ // 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 12c7af9..12de8c0 100644
--- a/internal/filter/internal/filtertest/filtertest.go
+++ b/internal/filter/internal/filtertest/filtertest.go
@@ -3,145 +3,149 @@
package filtertest
import (
- "io"
- "net/http"
- "net/http/httptest"
+ "encoding/json"
"net/netip"
- "net/url"
- "os"
- "path/filepath"
- "testing"
"time"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
- "github.com/AdguardTeam/golibs/httphdr"
- "github.com/AdguardTeam/golibs/testutil"
+ "github.com/AdguardTeam/golibs/errors"
"github.com/c2h5oh/datasize"
- "github.com/miekg/dns"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
)
-// BlockRule is the common blocking rule for filtering tests that blocks
-// [ReqHost].
-const BlockRule = "|" + ReqHost + "^"
-
-// Common string representations of IP adddresses.
+// Common rules for tests.
const (
- SafeBrowsingReplIPv4Str = "192.0.2.1"
- PopupReplIPv4Str = "192.0.2.3"
+ RuleBlockStr = "|" + HostBlocked + "^"
+ RuleSafeSearchGeneralHostStr = "|" + HostSafeSearchGeneral + "^$dnsrewrite=NOERROR;CNAME;" +
+ HostSafeSearchGeneralRepl
+ RuleSafeSearchGeneralIPv4Str = "|" + HostSafeSearchGeneralIPv4 + "^$dnsrewrite=NOERROR;A;" +
+ IPv4SafeSearchReplStr
+ RuleSafeSearchGeneralIPv6Str = "|" + HostSafeSearchGeneralIPv6 + "^$dnsrewrite=NOERROR;AAAA;" +
+ IPv6SafeSearchReplStr
+ RuleSafeSearchYouTubeStr = "|" + HostSafeSearchYouTube + "^$dnsrewrite=NOERROR;CNAME;" +
+ HostSafeSearchYouTubeRepl
+
+ RuleBlock internal.RuleText = RuleBlockStr
+)
+
+// Common string representations of IP addresses.
+const (
+ IPv4ClientStr = "192.0.2.1"
+ IPv4AdultContentReplStr = "192.0.2.2"
+ IPv4SafeSearchReplStr = "192.0.2.3"
+ IPv6SafeSearchReplStr = "2001:db8::1"
)
// Common IP addresses for tests.
var (
- SafeBrowsingReplIPv4 = netip.MustParseAddr(SafeBrowsingReplIPv4Str)
- PopupReplIPv4 = netip.MustParseAddr(PopupReplIPv4Str)
+ IPv4Client = netip.MustParseAddr(IPv4ClientStr)
+ IPv4AdultContentRepl = netip.MustParseAddr(IPv4AdultContentReplStr)
+ IPv4SafeSearchRepl = netip.MustParseAddr(IPv4SafeSearchReplStr)
+ IPv6SafeSearchRepl = netip.MustParseAddr(IPv6SafeSearchReplStr)
)
-// RemoteIP is the common client IP for filtering tests
-var RemoteIP = netip.MustParseAddr("1.2.3.4")
-
+// Common hostnames and FQDNs for tests.
const (
- // ReqHost is the common request host for filtering tests.
- ReqHost = "www.host.example"
+ Host = "host.example"
+ HostAdultContent = "adult-content.example"
+ HostAdultContentSub = "a.b.c." + HostAdultContent
+ HostAdultContentRepl = "adult-content-repl.example"
+ HostBlocked = "blocked.example"
+ HostBlockedService1 = "service-1.example"
+ HostDangerous = "dangerous-domain.example"
+ HostDangerousRepl = "dangerous-domain-repl.example"
+ HostNewlyRegistered = "newly-registered.example"
+ HostNewlyRegisteredRepl = "newly-registered-repl.example"
+ HostSafeSearchGeneral = "search-host.example"
+ HostSafeSearchGeneralIPv4 = "search-ipv4.example"
+ HostSafeSearchGeneralIPv6 = "search-ipv6.example"
+ HostSafeSearchGeneralRepl = "safe.search.example"
+ HostSafeSearchYouTube = "video.example"
+ HostSafeSearchYouTubeRepl = "safe.video.example"
- // ReqFQDN is the FQDN version of ReqHost.
- ReqFQDN = ReqHost + "."
-
- // PopupBlockPageHost is the common popup block-page host for tests.
- PopupBlockPageHost = "ad-block.adguard.example"
-
- // PopupBlockPageFQDN is the FQDN version of PopupBlockPageHost.
- PopupBlockPageFQDN = PopupBlockPageHost + "."
+ FQDN = Host + "."
+ FQDNAdultContent = HostAdultContent + "."
+ FQDNAdultContentRepl = HostAdultContentRepl + "."
+ FQDNBlocked = HostBlocked + "."
+ FQDNDangerous = HostDangerous + "."
+ FQDNDangerousRepl = HostDangerousRepl + "."
+ FQDNNewlyRegistered = HostNewlyRegistered + "."
+ FQDNNewlyRegisteredRepl = HostNewlyRegisteredRepl + "."
+ FQDNSafeSearchGeneralRepl = HostSafeSearchGeneralRepl + "."
+ FQDNSafeSearchGeneralIPv4 = HostSafeSearchGeneralIPv4 + "."
+ FQDNSafeSearchGeneralIPv6 = HostSafeSearchGeneralIPv6 + "."
+ FQDNSafeSearchYouTube = HostSafeSearchYouTube + "."
+ FQDNSafeSearchYouTubeRepl = HostSafeSearchYouTubeRepl + "."
)
-// ServerName is the common server name for filtering tests.
-const ServerName = "testServer/1.0"
+// Common blocked-service IDs for tests.
+const (
+ BlockedServiceID1Str = "blocked_service_1"
+ BlockedServiceID2Str = "blocked_service_2"
+ BlockedServiceIDDoesNotExistStr = "blocked_service_none"
+
+ BlockedServiceID1 internal.BlockedServiceID = BlockedServiceID1Str
+ BlockedServiceID2 internal.BlockedServiceID = BlockedServiceID2Str
+ BlockedServiceIDDoesNotExist internal.BlockedServiceID = BlockedServiceIDDoesNotExistStr
+)
+
+// BlockedServiceIndex is a service-index response for tests.
+//
+// See https://github.com/AdguardTeam/HostlistsRegistry/blob/main/assets/services.json.
+const BlockedServiceIndex string = `{
+ "blocked_services": [
+ {
+ "id": "` + BlockedServiceID1Str + `",
+ "name": "Service 1",
+ "rules": [
+ "||` + HostBlockedService1 + `^"
+ ]
+ },
+ {
+ "id": "` + BlockedServiceID2Str + `",
+ "name": "Service 2",
+ "rules": [
+ "||service-2.example^"
+ ]
+ }
+ ]
+}
+`
+
+// Common rule-list IDs for tests.
+const (
+ RuleListID1Str = "rule_list_1"
+ RuleListID2Str = "rule_list_2"
+
+ RuleListID1 internal.ID = RuleListID1Str
+ RuleListID2 internal.ID = RuleListID2Str
+)
+
+// NewRuleListIndex returns a rule-list index containing a record for a filter
+// with [RuleListID1Str] and downloadURL as the download URL.
+func NewRuleListIndex(downloadURL string) (b []byte) {
+ return errors.Must(json.Marshal(map[string]any{
+ "filters": []map[string]any{{
+ "filterKey": RuleListID1Str,
+ "downloadUrl": downloadURL,
+ }},
+ }))
+}
// CacheTTL is the common long cache-TTL for filtering tests.
const CacheTTL = 1 * time.Hour
-// Staleness is the common long staleness for filtering tests.
-const Staleness = 1 * time.Hour
-
-// Timeout is the common timeout for filtering tests.
-const Timeout = 1 * time.Second
+// CacheCount is the common count of cache items for filtering tests.
+const CacheCount = 100
// FilterMaxSize is the maximum size of the downloadable rule-list for filtering
// tests.
const FilterMaxSize = 640 * datasize.KB
-// 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) {
- tb.Helper()
+// ServerName is the common server name for filtering tests.
+const ServerName = "testServer/1.0"
- wantRM, ok := want.(*internal.ResultModifiedRequest)
- if !ok {
- return assert.Equal(tb, want, got)
- }
+// Staleness is the common long staleness files used in filtering tests.
+const Staleness = 1 * time.Hour
- gotRM := testutil.RequireTypeAssert[*internal.ResultModifiedRequest](tb, got)
-
- return assert.Equal(tb, wantRM.List, gotRM.List) &&
- assert.Equal(tb, wantRM.Rule, gotRM.Rule) &&
- assertEqualRequests(tb, wantRM.Msg, gotRM.Msg)
-}
-
-// assertEqualRequests is a test helper that compares two DNS requests ignoring
-// the ID.
-//
-// TODO(a.garipov): Move to golibs?
-func assertEqualRequests(tb testing.TB, want, got *dns.Msg) (ok bool) {
- tb.Helper()
-
- if want == nil {
- return assert.Nil(tb, got)
- }
-
- // Use a shallow clone, because this should be enough to fix the ID.
- gotWithID := &dns.Msg{}
- *gotWithID = *got
- gotWithID.Id = want.Id
-
- return assert.Equal(tb, want, gotWithID)
-}
-
-// PrepareRefreshable launches an HTTP server serving the given text and code,
-// as well as creates a cache file. If reqCh not nil, a signal is sent every
-// time the server is called. The server uses [ServerName] as the value of the
-// Server header.
-func PrepareRefreshable(
- tb testing.TB,
- reqCh chan<- struct{},
- text string,
- code int,
-) (cachePath string, srvURL *url.URL) {
- tb.Helper()
-
- srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
- pt := testutil.PanicT{}
- if reqCh != nil {
- testutil.RequireSend(pt, reqCh, struct{}{}, Timeout)
- }
-
- w.Header().Set(httphdr.Server, ServerName)
-
- w.WriteHeader(code)
-
- _, writeErr := io.WriteString(w, text)
- require.NoError(pt, writeErr)
- }))
- tb.Cleanup(srv.Close)
-
- srvURL, err := agdhttp.ParseHTTPURL(srv.URL)
- require.NoError(tb, err)
-
- cacheDir := tb.TempDir()
- cacheFile, err := os.CreateTemp(cacheDir, filepath.Base(tb.Name()))
- require.NoError(tb, err)
- require.NoError(tb, cacheFile.Close())
-
- return cacheFile.Name(), srvURL
-}
+// Timeout is the common timeout for filtering tests.
+const Timeout = 1 * time.Second
diff --git a/internal/filter/internal/filtertest/hashprefix.go b/internal/filter/internal/filtertest/hashprefix.go
new file mode 100644
index 0000000..fba9ad5
--- /dev/null
+++ b/internal/filter/internal/filtertest/hashprefix.go
@@ -0,0 +1,100 @@
+package filtertest
+
+import (
+ "context"
+ "net/http"
+ "testing"
+
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
+ "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
+ "github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
+ "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"
+)
+
+// 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) {
+ tb.Helper()
+
+ var replHost string
+ switch id {
+ case internal.IDAdultBlocking:
+ replHost = HostAdultContentRepl
+ case internal.IDNewRegDomains:
+ replHost = HostNewlyRegisteredRepl
+ case internal.IDSafeBrowsing:
+ replHost = HostDangerousRepl
+ default:
+ tb.Fatalf("bad id: %q", id)
+ }
+
+ return NewHashprefixFilterWithRepl(tb, id, replHost)
+}
+
+// NewHashprefixFilterWithRepl is a helper constructor of hashprefix filters for
+// tests. The hash data is set in accordance with id.
+func NewHashprefixFilterWithRepl(
+ tb testing.TB,
+ id internal.ID,
+ replHost string,
+) (f *hashprefix.Filter) {
+ tb.Helper()
+
+ var data string
+ switch id {
+ case internal.IDAdultBlocking:
+ data = HostAdultContent + "\n"
+ case internal.IDNewRegDomains:
+ data = HostNewlyRegistered + "\n"
+ case internal.IDSafeBrowsing:
+ data = HostDangerous + "\n"
+ default:
+ tb.Fatalf("bad id: %q", id)
+ }
+
+ cachePath, srvURL := PrepareRefreshable(tb, nil, data, http.StatusOK)
+
+ strg, err := hashprefix.NewStorage("")
+ 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{},
+ ID: id,
+ CachePath: cachePath,
+ ReplacementHost: replHost,
+ Staleness: Staleness,
+ CacheTTL: CacheTTL,
+ CacheCount: CacheCount,
+ MaxSize: FilterMaxSize,
+ })
+ require.NoError(tb, err)
+
+ ctx := testutil.ContextWithTimeout(tb, Timeout)
+ require.NoError(tb, f.RefreshInitial(ctx))
+
+ return f
+}
+
+// errColl is a panicking error collector for filter tests. It should be
+// replaced with [agdtest.NewErrorCollector] when the import cycle is resolved.
+type errColl struct{}
+
+// type check
+var _ errcoll.Interface = errColl{}
+
+// Collect implements the [errcoll.Interface] for errColl.
+func (errColl) Collect(ctx context.Context, err error) { panic(err) }
diff --git a/internal/filter/internal/filtertest/refresh.go b/internal/filter/internal/filtertest/refresh.go
new file mode 100644
index 0000000..ddc9eb1
--- /dev/null
+++ b/internal/filter/internal/filtertest/refresh.go
@@ -0,0 +1,54 @@
+package filtertest
+
+import (
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
+ "github.com/AdguardTeam/golibs/httphdr"
+ "github.com/AdguardTeam/golibs/testutil"
+ "github.com/stretchr/testify/require"
+)
+
+// PrepareRefreshable launches an HTTP server serving the given text and code,
+// as well as creates a cache file. If reqCh not nil, a signal is sent every
+// time the server is called. The server uses [ServerName] as the value of the
+// Server header.
+func PrepareRefreshable(
+ tb testing.TB,
+ reqCh chan<- struct{},
+ text string,
+ code int,
+) (cachePath string, srvURL *url.URL) {
+ tb.Helper()
+
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ pt := testutil.PanicT{}
+ if reqCh != nil {
+ testutil.RequireSend(pt, reqCh, struct{}{}, Timeout)
+ }
+
+ w.Header().Set(httphdr.Server, ServerName)
+
+ w.WriteHeader(code)
+
+ _, writeErr := io.WriteString(w, text)
+ require.NoError(pt, writeErr)
+ }))
+ tb.Cleanup(srv.Close)
+
+ srvURL, err := agdhttp.ParseHTTPURL(srv.URL)
+ require.NoError(tb, err)
+
+ cacheDir := tb.TempDir()
+ cacheFile, err := os.CreateTemp(cacheDir, filepath.Base(tb.Name()))
+ require.NoError(tb, err)
+ require.NoError(tb, cacheFile.Close())
+
+ return cacheFile.Name(), srvURL
+}
diff --git a/internal/filter/internal/filtertest/result.go b/internal/filter/internal/filtertest/result.go
new file mode 100644
index 0000000..c04c294
--- /dev/null
+++ b/internal/filter/internal/filtertest/result.go
@@ -0,0 +1,100 @@
+package filtertest
+
+import (
+ "net/netip"
+ "testing"
+ "time"
+
+ "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
+ "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) {
+ tb.Helper()
+
+ wantRM, ok := want.(*internal.ResultModifiedRequest)
+ if !ok {
+ return assert.Equal(tb, want, got)
+ }
+
+ gotRM := testutil.RequireTypeAssert[*internal.ResultModifiedRequest](tb, got)
+
+ return assert.Equal(tb, wantRM.List, gotRM.List) &&
+ assert.Equal(tb, wantRM.Rule, gotRM.Rule) &&
+ assertEqualRequests(tb, wantRM.Msg, gotRM.Msg)
+}
+
+// assertEqualRequests is a test helper that compares two DNS requests ignoring
+// the ID.
+//
+// TODO(a.garipov): Move to golibs?
+func assertEqualRequests(tb testing.TB, want, got *dns.Msg) (ok bool) {
+ tb.Helper()
+
+ if want == nil {
+ return assert.Nil(tb, got)
+ }
+
+ // Use a shallow clone, because this should be enough to fix the ID.
+ gotWithID := &dns.Msg{}
+ *gotWithID = *got
+ gotWithID.Id = want.Id
+
+ return assert.Equal(tb, want, gotWithID)
+}
+
+// NewRequest returns a new filtering request with the given data.
+func NewRequest(
+ tb testing.TB,
+ cliName string,
+ host string,
+ ip netip.Addr,
+ qt dnsmsg.RRType,
+) (req *internal.Request) {
+ tb.Helper()
+
+ dnsReq := &dns.Msg{
+ Question: []dns.Question{{
+ Name: dns.Fqdn(host),
+ Qtype: qt,
+ Qclass: dns.ClassINET,
+ }},
+ }
+
+ // 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{
+ DNS: dnsReq,
+ Messages: msgs,
+ RemoteIP: ip,
+ ClientName: cliName,
+ Host: host,
+ QType: qt,
+ QClass: dns.ClassINET,
+ }
+}
+
+// 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) {
+ tb.Helper()
+
+ return NewRequest(tb, "", host, IPv4Client, dns.TypeA)
+}
diff --git a/internal/filter/internal/id.go b/internal/filter/internal/id.go
new file mode 100644
index 0000000..9f6cdee
--- /dev/null
+++ b/internal/filter/internal/id.go
@@ -0,0 +1,175 @@
+package internal
+
+import (
+ "fmt"
+ "unicode/utf8"
+
+ "github.com/AdguardTeam/golibs/errors"
+)
+
+// ID is the identifier of a filter. It is an opaque string.
+type ID string
+
+// The maximum and minimum lengths of a filter ID.
+const (
+ MaxIDLen = 128
+ MinIDLen = 1
+)
+
+// 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) {
+ defer func() { err = errors.Annotate(err, "bad filter id %q: %w", s) }()
+
+ err = validateInclusion(len(s), MaxIDLen, MinIDLen, unitByte)
+ if err != nil {
+ return IDNone, err
+ }
+
+ // Allow only the printable, non-whitespace ASCII characters. Technically
+ // we only need to exclude carriage return, line feed, and slash characters,
+ // but let's be more strict just in case.
+ if i, r := firstNonIDRune(s, true); i != -1 {
+ return 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.
+const (
+ // IDNone means that no filter were applied at all.
+ IDNone ID = ""
+
+ // IDBlockedService is the shared filter ID used when a request was blocked
+ // by the service blocker.
+ IDBlockedService ID = "blocked_service"
+
+ // IDCustom is the special shared filter ID used when a request was filtered
+ // 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"
+
+ // 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"
+
+ // 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.
+type RuleText string
+
+// MaxRuleTextRuneLen is the maximum length of a filter rule in runes.
+const MaxRuleTextRuneLen = 1024
+
+// NewRuleText converts a simple string into a RuleText and makes sure that it's
+// valid. This should be preferred to a simple type conversion.
+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)
+ if err != nil {
+ return "", err
+ }
+
+ return RuleText(s), nil
+}
+
+// BlockedServiceID is the ID of a blocked service. While these are usually
+// human-readable, clients should treat them as opaque strings.
+//
+// When a request is blocked by the service blocker, this ID is used as the
+// text of the blocking rule.
+type BlockedServiceID string
+
+// The maximum and minimum lengths of a blocked service ID.
+const (
+ MaxBlockedServiceIDLen = 64
+ MinBlockedServiceIDLen = 1
+)
+
+// NewBlockedServiceID converts a simple string into a BlockedServiceID and
+// makes sure that it's valid. This should be preferred to a simple type
+// conversion.
+func NewBlockedServiceID(s string) (id BlockedServiceID, err error) {
+ defer func() { err = errors.Annotate(err, "bad blocked service id %q: %w", s) }()
+
+ err = validateInclusion(len(s), MaxBlockedServiceIDLen, MinBlockedServiceIDLen, unitByte)
+ if err != nil {
+ return "", err
+ }
+
+ // Allow only the printable, non-whitespace ASCII characters. Technically
+ // we only need to exclude carriage return, line feed, and slash characters,
+ // but let's be more strict just in case.
+ if i, r := firstNonIDRune(s, true); i != -1 {
+ return "", fmt.Errorf("bad char %q at index %d", r, i)
+ }
+
+ return BlockedServiceID(s), nil
+}
diff --git a/internal/agd/filterlist_test.go b/internal/filter/internal/id_test.go
similarity index 73%
rename from internal/agd/filterlist_test.go
rename to internal/filter/internal/id_test.go
index ceeebe6..2db5f85 100644
--- a/internal/agd/filterlist_test.go
+++ b/internal/filter/internal/id_test.go
@@ -1,15 +1,15 @@
-package agd_test
+package internal_test
import (
"strings"
"testing"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
)
-func TestNewFilterListID(t *testing.T) {
+func TestNewID(t *testing.T) {
t.Parallel()
testCases := []struct {
@@ -23,22 +23,22 @@ func TestNewFilterListID(t *testing.T) {
}, {
name: "too_short",
in: "",
- wantErrMsg: `bad filter list id "": too short: got 0 bytes, min 1`,
+ wantErrMsg: `bad filter id "": too short: got 0 bytes, min 1`,
}, {
name: "too_long",
in: testLongStr,
- wantErrMsg: `bad filter list id "` + testLongStr + `": too long: got 200 bytes, max 128`,
+ wantErrMsg: `bad filter id "` + testLongStr + `": too long: got 200 bytes, max 128`,
}, {
name: "bad",
in: "bad/name",
- wantErrMsg: `bad filter list id "bad/name": bad rune '/' at index 3`,
+ wantErrMsg: `bad filter id "bad/name": bad rune '/' at index 3`,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
- id, err := agd.NewFilterListID(tc.in)
+ id, err := internal.NewID(tc.in)
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
if tc.wantErrMsg == "" && tc.in != "" {
assert.NotEmpty(t, id)
@@ -49,11 +49,11 @@ func TestNewFilterListID(t *testing.T) {
}
}
-func TestNewFilterRuleText(t *testing.T) {
+func TestNewRuleText(t *testing.T) {
t.Parallel()
- tooLong := strings.Repeat("a", agd.MaxFilterRuleTextRuneLen+1)
- tooLongUnicode := strings.Repeat("ы", agd.MaxFilterRuleTextRuneLen+1)
+ tooLong := strings.Repeat("a", internal.MaxRuleTextRuneLen+1)
+ tooLongUnicode := strings.Repeat("ы", internal.MaxRuleTextRuneLen+1)
testCases := []struct {
name string
@@ -86,7 +86,7 @@ func TestNewFilterRuleText(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
- txt, err := agd.NewFilterRuleText(tc.in)
+ txt, err := internal.NewRuleText(tc.in)
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
if tc.wantErrMsg == "" && tc.in != "" {
assert.NotEmpty(t, txt)
diff --git a/internal/filter/internal/internal.go b/internal/filter/internal/internal.go
index 89d8f5a..5f168c0 100644
--- a/internal/filter/internal/internal.go
+++ b/internal/filter/internal/internal.go
@@ -1,26 +1,83 @@
// 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/agd"
+ "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/miekg/dns"
)
// Interface is the DNS request and response filter interface.
type Interface interface {
- // FilterRequest filters the DNS request for the provided client. All
- // parameters must be non-nil. req must have exactly one question. If a is
- // nil, the request doesn't match any of the rules.
- FilterRequest(ctx context.Context, req *dns.Msg, ri *agd.RequestInfo) (r Result, err error)
+ // 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 the DNS response for the provided client. All
- // parameters must be non-nil. If a is nil, the response doesn't match any
- // of the rules.
- FilterResponse(ctx context.Context, resp *dns.Msg, ri *agd.RequestInfo) (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
@@ -31,6 +88,22 @@ const DefaultResolveTimeout = 1 * time.Second
// RequestFilter can filter a request based on the request info.
type RequestFilter interface {
- FilterRequest(ctx context.Context, req *dns.Msg, ri *agd.RequestInfo) (r Result, err error)
- ID() (id agd.FilterListID)
+ 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/internal_test.go b/internal/filter/internal/internal_test.go
new file mode 100644
index 0000000..a79895c
--- /dev/null
+++ b/internal/filter/internal/internal_test.go
@@ -0,0 +1,10 @@
+package internal_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/internal/metrics.go b/internal/filter/internal/metrics.go
new file mode 100644
index 0000000..130ceb5
--- /dev/null
+++ b/internal/filter/internal/metrics.go
@@ -0,0 +1,29 @@
+package internal
+
+import (
+ "context"
+ "time"
+)
+
+// 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
+ // nil, updTime and ruleCount are ignored.
+ SetFilterStatus(
+ ctx context.Context,
+ id string,
+ updTime time.Time,
+ ruleCount int,
+ err error,
+ )
+}
+
+// EmptyMetrics is the implementation of the [Metrics] interface that does
+// nothing.
+type EmptyMetrics struct{}
+
+// type check
+var _ Metrics = EmptyMetrics{}
+
+// SetFilterStatus implements the [Metrics] interface for EmptyMetrics.
+func (EmptyMetrics) SetFilterStatus(_ context.Context, _ string, _ time.Time, _ int, _ error) {}
diff --git a/internal/filter/internal/refreshable.go b/internal/filter/internal/refreshable/refreshable.go
similarity index 73%
rename from internal/filter/internal/refreshable.go
rename to internal/filter/internal/refreshable/refreshable.go
index 18864c4..34a619f 100644
--- a/internal/filter/internal/refreshable.go
+++ b/internal/filter/internal/refreshable/refreshable.go
@@ -1,4 +1,5 @@
-package internal
+// Package refreshable defines the refreshable part of filters and indexes.
+package refreshable
import (
"context"
@@ -13,8 +14,8 @@ import (
"strings"
"time"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/httphdr"
"github.com/AdguardTeam/golibs/ioutil"
@@ -23,46 +24,45 @@ import (
renameio "github.com/google/renameio/v2"
)
-// Refreshable contains entities common to filters that can refresh themselves
-// from a file and a URL.
+// Refreshable contains the logic common to filters and indexes that can refresh
+// themselves from a file and a URL.
type Refreshable struct {
logger *slog.Logger
http *agdhttp.Client
url *url.URL
- id agd.FilterListID
+ id filter.ID
cachePath string
staleness time.Duration
maxSize datasize.ByteSize
}
-// RefreshableConfig is the configuration structure for a refreshable filter.
-type RefreshableConfig struct {
+// Config is the configuration structure for a refreshable.
+type Config struct {
// Logger is used to log errors during refreshes.
Logger *slog.Logger
- // URL is the URL used to refresh the filter. URL should be either a file
- // URL or an HTTP(S) URL and should not be nil.
+ // URL is the URL used to refresh the data. URL should be either a file URL
+ // or an HTTP(S) URL and should not be nil.
URL *url.URL
// ID is the filter list ID for this filter.
- ID agd.FilterListID
+ ID filter.ID
- // CachePath is the path to the file containing the cached filter rules.
+ // CachePath is the path to the file containing the cached data.
CachePath string
// Staleness is the time after which a file is considered stale.
Staleness time.Duration
- // Timeout is the timeout for the HTTP client used by this refreshable
- // filter.
+ // Timeout is the timeout for the HTTP client used by this refreshable.
Timeout time.Duration
- // MaxSize is the maximum size of the downloadable filter content.
+ // MaxSize is the maximum size of the downloadable data.
MaxSize datasize.ByteSize
}
-// NewRefreshable returns a new refreshable filter. c must not be nil.
-func NewRefreshable(c *RefreshableConfig) (f *Refreshable, err error) {
+// 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)
} else if s := c.URL.Scheme; !strings.EqualFold(s, urlutil.SchemeFile) &&
@@ -83,9 +83,9 @@ func NewRefreshable(c *RefreshableConfig) (f *Refreshable, err error) {
}, nil
}
-// Refresh reloads the filter data. If acceptStale is true, refresh doesn't try
-// to load the filter data from its URL when there is already a file in the
-// cache directory, regardless of its staleness.
+// Refresh reloads the data. If acceptStale is true, refresh doesn't try to
+// load the data from its URL when there is already a file in the cache
+// directory, regardless of its staleness.
//
// TODO(a.garipov): Consider making refresh return a reader instead of a string.
func (f *Refreshable) Refresh(ctx context.Context, acceptStale bool) (text string, err error) {
@@ -101,7 +101,7 @@ func (f *Refreshable) Refresh(ctx context.Context, acceptStale bool) (text strin
}
// refreshFromFileOnly refreshes from the file in the URL. It must only be
-// called when the URL of this refreshable filter is a file URI.
+// called when the URL of this refreshable is a file URI.
func (f *Refreshable) refreshFromFileOnly(ctx context.Context) (text string, err error) {
filePath := f.url.Path
f.logger.InfoContext(ctx, "using data from file", "path", filePath)
@@ -114,11 +114,11 @@ func (f *Refreshable) refreshFromFileOnly(ctx context.Context) (text string, err
return text, nil
}
-// useCachedOrRefreshFromURL reloads the filter data from the cache file or the http
-// URL. If acceptStale is true, refresh doesn't try to load the filter data
-// from its URL when there is already a file in the cache directory, regardless
-// of its staleness. It must only be called when the URL of this refreshable
-// filter has an HTTP(S) URL.
+// useCachedOrRefreshFromURL reloads the data from the cache file or the http
+// URL. If acceptStale is true, refresh doesn't try to load the data from its
+// URL when there is already a file in the cache directory, regardless of its
+// staleness. It must only be called when the URL of this refreshable has an
+// HTTP(S) URL.
func (f *Refreshable) useCachedOrRefreshFromURL(
ctx context.Context,
acceptStale bool,
@@ -145,23 +145,23 @@ func (f *Refreshable) useCachedOrRefreshFromURL(
return text, nil
}
-// refreshFromFile loads filter data from filePath if the file's mtime shows
-// that it's still fresh relative to updTime. If acceptStale is true, and the
-// file exists, the data is read from there regardless of its staleness. If err
-// is nil and text is empty, a refresh from a URL is required.
+// refreshFromFile loads data from filePath if the file's mtime shows that it's
+// still fresh relative to updTime. If acceptStale is true, and the file
+// exists, the data is read from there regardless of its staleness. If err is
+// nil and text is empty, a refresh from a URL is required.
func (f *Refreshable) refreshFromFile(
acceptStale bool,
filePath string,
updTime time.Time,
) (text string, err error) {
// #nosec G304 -- Assume that filePath is always either cacheDir + a valid,
- // no-slash filter list ID or a path from the index env.
+ // no-slash ID or a path from the index env.
file, err := os.Open(filePath)
if errors.Is(err, os.ErrNotExist) {
// File does not exist. Refresh from the URL.
return "", nil
} else if err != nil {
- return "", fmt.Errorf("opening filter file: %w", err)
+ return "", fmt.Errorf("opening refreshable file: %w", err)
}
defer func() { err = errors.WithDeferred(err, file.Close()) }()
@@ -169,7 +169,7 @@ func (f *Refreshable) refreshFromFile(
var fi fs.FileInfo
fi, err = file.Stat()
if err != nil {
- return "", fmt.Errorf("reading filter file stat: %w", err)
+ return "", fmt.Errorf("reading refreshable file stat: %w", err)
}
if mtime := fi.ModTime(); !mtime.Add(f.staleness).After(updTime) {
@@ -180,15 +180,14 @@ func (f *Refreshable) refreshFromFile(
b := &strings.Builder{}
_, err = io.Copy(b, file)
if err != nil {
- return "", fmt.Errorf("reading filter file: %w", err)
+ return "", fmt.Errorf("reading refreshable file: %w", err)
}
return b.String(), nil
}
-// refreshFromURL loads the filter data from u, puts it into the file specified
-// by cachePath, returns its content, and also sets its atime and mtime to
-// updTime.
+// refreshFromURL loads the data from u, puts it into the file specified by
+// cachePath, returns its content, and also sets its atime and mtime to updTime.
func (f *Refreshable) refreshFromURL(
ctx context.Context,
updTime time.Time,
@@ -197,7 +196,7 @@ func (f *Refreshable) refreshFromURL(
tmpDir := renameio.TempDir(filepath.Dir(f.cachePath))
tmpFile, err := renameio.TempFile(tmpDir, f.cachePath)
if err != nil {
- return "", fmt.Errorf("creating temporary filter file: %w", err)
+ return "", fmt.Errorf("creating temporary refreshable file: %w", err)
}
defer func() { err = f.withDeferredTmpCleanup(err, tmpFile, updTime) }()
diff --git a/internal/filter/internal/refreshable_test.go b/internal/filter/internal/refreshable/refreshable_test.go
similarity index 92%
rename from internal/filter/internal/refreshable_test.go
rename to internal/filter/internal/refreshable/refreshable_test.go
index 34e4b5e..0717912 100644
--- a/internal/filter/internal/refreshable_test.go
+++ b/internal/filter/internal/refreshable/refreshable_test.go
@@ -1,4 +1,4 @@
-package internal_test
+package refreshable_test
import (
"net/http"
@@ -9,8 +9,8 @@ import (
"testing"
"time"
- "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/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/AdguardTeam/golibs/testutil"
@@ -18,7 +18,7 @@ import (
"github.com/stretchr/testify/require"
)
-// refrID is the ID of a [agd.FilterList] used for testing.
+// refrID is the ID of an [filter.ID] used for testing.
const refrID = "test_id"
// Default texts for tests.
@@ -109,7 +109,7 @@ func TestRefreshable_Refresh(t *testing.T) {
realCachePath, srvURL := filtertest.PrepareRefreshable(t, reqCh, tc.srvText, tc.srvCode)
cachePath := prepareCachePath(t, realCachePath, tc.useCacheFile)
- c := &internal.RefreshableConfig{
+ c := &refreshable.Config{
Logger: slogutil.NewDiscardLogger(),
URL: srvURL,
ID: refrID,
@@ -119,7 +119,7 @@ func TestRefreshable_Refresh(t *testing.T) {
MaxSize: filtertest.FilterMaxSize,
}
- f, err := internal.NewRefreshable(c)
+ f, err := refreshable.New(c)
require.NoError(t, err)
ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
@@ -160,9 +160,14 @@ func TestRefreshable_Refresh_properStaleness(t *testing.T) {
const responseDur = time.Second / 5
reqCh := make(chan struct{})
- cachePath, addr := filtertest.PrepareRefreshable(t, reqCh, filtertest.BlockRule, http.StatusOK)
+ cachePath, addr := filtertest.PrepareRefreshable(
+ t,
+ reqCh,
+ filtertest.RuleBlockStr,
+ http.StatusOK,
+ )
- c := &internal.RefreshableConfig{
+ c := &refreshable.Config{
Logger: slogutil.NewDiscardLogger(),
URL: addr,
ID: refrID,
@@ -172,7 +177,7 @@ func TestRefreshable_Refresh_properStaleness(t *testing.T) {
MaxSize: filtertest.FilterMaxSize,
}
- f, err := internal.NewRefreshable(c)
+ f, err := refreshable.New(c)
require.NoError(t, err)
var now time.Time
@@ -218,7 +223,7 @@ func TestRefreshable_Refresh_fileURL(t *testing.T) {
require.NoError(t, fltFile.Close())
- c := &internal.RefreshableConfig{
+ c := &refreshable.Config{
Logger: slogutil.NewDiscardLogger(),
URL: &url.URL{
Scheme: urlutil.SchemeFile,
@@ -231,7 +236,7 @@ func TestRefreshable_Refresh_fileURL(t *testing.T) {
MaxSize: filtertest.FilterMaxSize,
}
- f, err := internal.NewRefreshable(c)
+ f, err := refreshable.New(c)
require.NoError(t, err)
ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
diff --git a/internal/filter/internal/result.go b/internal/filter/internal/result.go
index ecfef17..d926a2c 100644
--- a/internal/filter/internal/result.go
+++ b/internal/filter/internal/result.go
@@ -1,13 +1,10 @@
package internal
import (
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/miekg/dns"
)
-// Filtering Results
-
// Result is a sum type of all possible filtering actions. See the following
// types as implementations:
//
@@ -17,7 +14,7 @@ import (
// - [*ResultModifiedRequest]
type Result interface {
// MatchedRule returns data about the matched rule and its rule list.
- MatchedRule() (id agd.FilterListID, text agd.FilterRuleText)
+ MatchedRule() (id ID, text RuleText)
// isResult is a marker method.
isResult()
@@ -26,15 +23,15 @@ type Result interface {
// ResultAllowed means that this request or response was allowed by an allowlist
// rule within the given filter list.
type ResultAllowed struct {
- List agd.FilterListID
- Rule agd.FilterRuleText
+ List ID
+ Rule RuleText
}
// type check
var _ Result = (*ResultAllowed)(nil)
// MatchedRule implements the [Result] interface for *ResultAllowed.
-func (a *ResultAllowed) MatchedRule() (id agd.FilterListID, text agd.FilterRuleText) {
+func (a *ResultAllowed) MatchedRule() (id ID, text RuleText) {
return a.List, a.Rule
}
@@ -44,15 +41,15 @@ func (*ResultAllowed) isResult() {}
// ResultBlocked means that this request or response was blocked by a blocklist
// rule within the given filter list.
type ResultBlocked struct {
- List agd.FilterListID
- Rule agd.FilterRuleText
+ List ID
+ Rule RuleText
}
// type check
var _ Result = (*ResultBlocked)(nil)
// MatchedRule implements the [Result] interface for *ResultBlocked.
-func (b *ResultBlocked) MatchedRule() (id agd.FilterListID, text agd.FilterRuleText) {
+func (b *ResultBlocked) MatchedRule() (id ID, text RuleText) {
return b.List, b.Rule
}
@@ -66,17 +63,17 @@ type ResultModifiedResponse struct {
Msg *dns.Msg
// List is the ID of the filter list.
- List agd.FilterListID
+ List ID
// Rule is the filtering rule that triggered the rewrite.
- Rule agd.FilterRuleText
+ Rule RuleText
}
// type check
var _ Result = (*ResultModifiedResponse)(nil)
// MatchedRule implements the [Result] interface for *ResultModifiedResponse.
-func (m *ResultModifiedResponse) MatchedRule() (id agd.FilterListID, text agd.FilterRuleText) {
+func (m *ResultModifiedResponse) MatchedRule() (id ID, text RuleText) {
return m.List, m.Rule
}
@@ -120,17 +117,17 @@ type ResultModifiedRequest struct {
Msg *dns.Msg
// List is the ID of the filter list.
- List agd.FilterListID
+ List ID
// Rule is the filtering rule that triggered the rewrite.
- Rule agd.FilterRuleText
+ Rule RuleText
}
// type check
var _ Result = (*ResultModifiedRequest)(nil)
// MatchedRule implements the [Result] interface for *ResultModifiedRequest.
-func (m *ResultModifiedRequest) MatchedRule() (id agd.FilterListID, text agd.FilterRuleText) {
+func (m *ResultModifiedRequest) MatchedRule() (id ID, text RuleText) {
return m.List, m.Rule
}
diff --git a/internal/filter/internal/rulelist/dnsrewrite.go b/internal/filter/internal/rulelist/dnsrewrite.go
index e0a8267..933cbf7 100644
--- a/internal/filter/internal/rulelist/dnsrewrite.go
+++ b/internal/filter/internal/rulelist/dnsrewrite.go
@@ -5,7 +5,6 @@ import (
"net/netip"
"strings"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
"github.com/AdguardTeam/golibs/errors"
@@ -16,13 +15,11 @@ 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(
- messages *dnsmsg.Constructor,
- req *dns.Msg,
+ req *internal.Request,
dnsr []*rules.NetworkRule,
- host string,
- id agd.FilterListID,
+ id internal.ID,
) (res internal.Result) {
- if len(dnsr) == 0 || !id.SupportsDNSRewrite() {
+ if len(dnsr) == 0 {
return nil
}
@@ -30,16 +27,16 @@ func ProcessDNSRewrites(
if resCanonName := dnsRewriteResult.CanonName; resCanonName != "" {
// Rewrite the question name to a matched CNAME.
- if strings.EqualFold(resCanonName, host) {
+ if strings.EqualFold(resCanonName, req.Host) {
// A rewrite of a host to itself.
return nil
}
- req = dnsmsg.Clone(req)
- req.Question[0].Name = dns.Fqdn(resCanonName)
+ modReq := dnsmsg.Clone(req.DNS)
+ modReq.Question[0].Name = dns.Fqdn(resCanonName)
return &internal.ResultModifiedRequest{
- Msg: req,
+ Msg: modReq,
List: id,
Rule: dnsRewriteResult.ResRuleText,
}
@@ -49,7 +46,7 @@ func ProcessDNSRewrites(
// #nosec G115 -- The value of dnsRewriteResult.RCode comes from the
// urlfilter package, where it either parsed by [dns.StringToRcode] or
// defined statically.
- resp := messages.NewBlockedRespRCode(req, dnsmsg.RCode(dnsRewriteResult.RCode))
+ resp := req.Messages.NewBlockedRespRCode(req.DNS, dnsmsg.RCode(dnsRewriteResult.RCode))
return &internal.ResultModifiedResponse{
Msg: resp,
@@ -58,7 +55,7 @@ func ProcessDNSRewrites(
}
}
- resp, err := filterDNSRewrite(messages, req, dnsRewriteResult)
+ resp, err := filterDNSRewrite(req, dnsRewriteResult)
if err != nil {
return nil
}
@@ -73,7 +70,7 @@ func ProcessDNSRewrites(
type dnsRewriteResult struct {
Response dnsRewriteResultResponse
CanonName string
- ResRuleText agd.FilterRuleText
+ ResRuleText internal.RuleText
Rules []*rules.NetworkRule
RCode rules.RCode
}
@@ -94,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: agd.FilterRuleText(rule.RuleText),
+ ResRuleText: internal.RuleText(rule.RuleText),
CanonName: dr.NewCNAME,
}
}
@@ -108,7 +105,7 @@ func processDNSRewriteRules(dnsr []*rules.NetworkRule) (res *dnsRewriteResult) {
// RcodeRefused and other such codes have higher priority. Return
// immediately.
return &dnsRewriteResult{
- ResRuleText: agd.FilterRuleText(rule.RuleText),
+ ResRuleText: internal.RuleText(rule.RuleText),
RCode: dr.RCode,
}
}
@@ -120,23 +117,21 @@ 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(
- messages *dnsmsg.Constructor,
- req *dns.Msg,
- dnsrr *dnsRewriteResult,
-) (resp *dns.Msg, err error) {
+func filterDNSRewrite(req *internal.Request, dnsrr *dnsRewriteResult) (resp *dns.Msg, err error) {
if dnsrr.Response == nil {
return nil, errors.Error("no dns rewrite rule responses")
}
- // TODO(e.burkov): Use another constructor method for this.
- resp = messages.NewBlockedRespRCode(req, dns.RcodeSuccess)
+ // TODO(e.burkov): Use a constructor method that adds a SOA for caching if
+ // necessary.
+ dnsReq := req.DNS
+ resp = req.Messages.NewBlockedRespRCode(dnsReq, dns.RcodeSuccess)
- rr := req.Question[0].Qtype
+ rr := dnsReq.Question[0].Qtype
values := dnsrr.Response[rr]
for i, v := range values {
var ans dns.RR
- ans, err = filterDNSRewriteResponse(messages, req, rr, v)
+ ans, err = filterDNSRewriteResponse(req, rr, v)
if err != nil {
return nil, fmt.Errorf("dns rewrite response for %d[%d]: %w", rr, i, err)
} else if ans == nil {
@@ -154,8 +149,7 @@ func filterDNSRewrite(
// filterDNSRewriteResponse handles a single DNS rewrite response entry.
// It returns the properly constructed answer resource record.
func filterDNSRewriteResponse(
- messages *dnsmsg.Constructor,
- req *dns.Msg,
+ req *internal.Request,
rr rules.RRType,
v rules.RRValue,
) (ans dns.RR, err error) {
@@ -165,41 +159,35 @@ func filterDNSRewriteResponse(
switch rr {
case dns.TypeA, dns.TypeAAAA:
- return newAnsFromIP(messages, v, rr, req)
+ return newAnsFromIP(req, v, rr)
case dns.TypePTR, dns.TypeTXT:
- return newAnsFromString(messages, v, rr, req)
+ return newAnsFromString(req, v, rr)
case dns.TypeMX:
- return newAnswerMX(messages, v, rr, req)
+ return newAnswerMX(req, v, rr)
case dns.TypeHTTPS, dns.TypeSVCB:
- return newAnsFromSVCB(messages, v, rr, req)
+ return newAnsFromSVCB(req, v, rr)
case dns.TypeSRV:
- return newAnswerSRV(messages, v, rr, req)
+ return newAnswerSRV(req, v, rr)
default:
return nil, nil
}
}
// newAnswerSRV returns a new resource record created from DNSSRV rules value.
-func newAnswerSRV(
- messages *dnsmsg.Constructor,
- v rules.RRValue,
- rr rules.RRType,
- req *dns.Msg,
-) (ans dns.RR, err error) {
+func newAnswerSRV(req *internal.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)
}
- return messages.NewAnswerSRV(req, srv), nil
+ return req.Messages.NewAnswerSRV(req.DNS, srv), nil
}
// newAnsFromSVCB returns a new resource record created from DNSSVCB rules value.
func newAnsFromSVCB(
- messages *dnsmsg.Constructor,
+ req *internal.Request,
v rules.RRValue,
rr rules.RRType,
- req *dns.Msg,
) (ans dns.RR, err error) {
svcb, ok := v.(*rules.DNSSVCB)
if !ok {
@@ -207,18 +195,17 @@ func newAnsFromSVCB(
}
if rr == dns.TypeHTTPS {
- return messages.NewAnswerHTTPS(req, svcb), nil
+ return req.Messages.NewAnswerHTTPS(req.DNS, svcb), nil
}
- return messages.NewAnswerSVCB(req, svcb), nil
+ return req.Messages.NewAnswerSVCB(req.DNS, svcb), nil
}
// newAnsFromString returns a new resource record created from string value.
func newAnsFromString(
- messages *dnsmsg.Constructor,
+ req *internal.Request,
v rules.RRValue,
rr rules.RRType,
- req *dns.Msg,
) (ans dns.RR, err error) {
str, ok := v.(string)
if !ok {
@@ -226,45 +213,35 @@ func newAnsFromString(
}
if rr == dns.TypeTXT {
- return messages.NewAnswerTXT(req, []string{str})
+ return req.Messages.NewAnswerTXT(req.DNS, []string{str})
}
- return messages.NewAnswerPTR(req, str), nil
+ return req.Messages.NewAnswerPTR(req.DNS, str), nil
}
// newAnsFromIP returns a new resource record with an IP address. ip must be an
// IPv4 or IPv6 address.
-func newAnsFromIP(
- messages *dnsmsg.Constructor,
- v rules.RRValue,
- rr rules.RRType,
- req *dns.Msg,
-) (ans dns.RR, err error) {
+func newAnsFromIP(req *internal.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)
}
- target := req.Question[0].Name
+ target := req.DNS.Question[0].Name
if rr == dns.TypeA {
- return messages.NewAnswerA(target, ip)
+ return req.Messages.NewAnswerA(target, ip)
}
- return messages.NewAnswerAAAA(target, ip)
+ return req.Messages.NewAnswerAAAA(target, ip)
}
// newAnswerMX returns a new resource record created from DNSMX rules value.
-func newAnswerMX(
- messages *dnsmsg.Constructor,
- v rules.RRValue,
- rr rules.RRType,
- req *dns.Msg,
-) (ans dns.RR, err error) {
+func newAnswerMX(req *internal.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)
}
- return messages.NewAnswerMX(req, mx), nil
+ return req.Messages.NewAnswerMX(req.DNS, mx), nil
}
diff --git a/internal/filter/internal/rulelist/immutable.go b/internal/filter/internal/rulelist/immutable.go
index 09e21e2..38644bf 100644
--- a/internal/filter/internal/rulelist/immutable.go
+++ b/internal/filter/internal/rulelist/immutable.go
@@ -1,21 +1,21 @@
package rulelist
import (
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
)
-// 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.
+// 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.
//
-// TODO(a.garipov): Consider not using rule-list engines for service and custom
+// TODO(a.garipov): Consider not using rule-list engines for service and custom
// filters at all. It could be faster to simply go through all enabled rules
-// sequentially instead. Alternatively, rework the urlfilter.DNSEngine and make
-// it use the sequential scan if the number of rules is less than some constant
-// value.
+// sequentially instead. Alternatively, rework the [urlfilter.DNSEngine] and
+// make it use the sequential scan if the number of rules is less than some
+// constant value.
//
// See AGDNS-342.
type Immutable struct {
- // TODO(a.garipov): Find ways to embed it in a way that shows the methods,
+ // 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
}
@@ -24,8 +24,8 @@ type Immutable struct {
// the provided rule text and ID.
func NewImmutable(
text string,
- id agd.FilterListID,
- svcID agd.BlockedServiceID,
+ id internal.ID,
+ svcID internal.BlockedServiceID,
cache ResultCache,
) (f *Immutable, err error) {
f = &Immutable{}
diff --git a/internal/filter/internal/rulelist/refreshable.go b/internal/filter/internal/rulelist/refreshable.go
index cbd8b37..9f48500 100644
--- a/internal/filter/internal/rulelist/refreshable.go
+++ b/internal/filter/internal/rulelist/refreshable.go
@@ -8,9 +8,9 @@ import (
"strings"
"sync"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/refreshable"
"github.com/AdguardTeam/golibs/netutil/urlutil"
"github.com/AdguardTeam/urlfilter"
"github.com/AdguardTeam/urlfilter/filterlist"
@@ -27,17 +27,20 @@ type Refreshable struct {
logger *slog.Logger
- // mu protects filter.engine.
+ // mu protects [filter.engine].
+ //
+ // Do not add it to [filter], because the latter is used in [Immutable],
+ // where serialization of access is not required.
mu *sync.RWMutex
// refr contains data for refreshing the filter.
- refr *internal.Refreshable
+ refr *refreshable.Refreshable
}
// NewRefreshable returns a new refreshable DNS request and response filter
// based on the provided rule list. c must be non-nil. c.URL should be an
// HTTP(S) URL. The initial refresh should be called explicitly if necessary.
-func NewRefreshable(c *internal.RefreshableConfig, cache ResultCache) (f *Refreshable, err error) {
+func NewRefreshable(c *refreshable.Config, cache ResultCache) (f *Refreshable, err error) {
f = &Refreshable{
logger: c.Logger,
mu: &sync.RWMutex{},
@@ -47,7 +50,7 @@ func NewRefreshable(c *internal.RefreshableConfig, cache ResultCache) (f *Refres
return nil, fmt.Errorf("unsupported url %q", c.URL)
}
- f.refr, err = internal.NewRefreshable(&internal.RefreshableConfig{
+ f.refr, err = refreshable.New(&refreshable.Config{
Logger: c.Logger,
URL: c.URL,
ID: c.ID,
@@ -75,8 +78,8 @@ func NewRefreshable(c *internal.RefreshableConfig, cache ResultCache) (f *Refres
// TODO(a.garipov): Only used in tests. Consider removing later.
func NewFromString(
text string,
- id agd.FilterListID,
- svcID agd.BlockedServiceID,
+ id internal.ID,
+ svcID internal.BlockedServiceID,
cache ResultCache,
) (f *Refreshable, err error) {
filter, err := newFilter(text, id, svcID, cache)
diff --git a/internal/filter/internal/rulelist/refreshable_test.go b/internal/filter/internal/rulelist/refreshable_test.go
index f2c1f5d..d871dfa 100644
--- a/internal/filter/internal/rulelist/refreshable_test.go
+++ b/internal/filter/internal/rulelist/refreshable_test.go
@@ -5,9 +5,9 @@ import (
"net/netip"
"testing"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"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/rulelist"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
@@ -23,7 +23,7 @@ const testReqHost = "blocked.example"
var testRemoteIP = netip.MustParseAddr("1.2.3.4")
// testFltListID is the common filter list IDs for tests.
-const testFltListID agd.FilterListID = "fl1"
+const testFltListID internal.ID = "fl1"
// testBlockRule is the common blocking rule for tests.
const testBlockRule = "||" + testReqHost + "\n"
@@ -41,7 +41,7 @@ func TestRefreshable_RulesCount(t *testing.T) {
}
func TestRefreshable_DNSResult_cache(t *testing.T) {
- cache := rulelist.NewResultCache(100, true)
+ cache := rulelist.NewResultCache(filtertest.CacheCount, true)
rl, err := rulelist.NewFromString(testBlockRule, testFltListID, "", cache)
require.NoError(t, err)
@@ -71,7 +71,7 @@ func TestRefreshable_DNSResult_cache(t *testing.T) {
}
func TestRefreshable_ID(t *testing.T) {
- const svcID = agd.BlockedServiceID("test_service")
+ const svcID = internal.BlockedServiceID("test_service")
rl, err := rulelist.NewFromString(
testBlockRule,
testFltListID,
@@ -88,7 +88,7 @@ func TestRefreshable_ID(t *testing.T) {
func TestRefreshable_Refresh(t *testing.T) {
cachePath, srvURL := filtertest.PrepareRefreshable(t, nil, testBlockRule, http.StatusOK)
rl, err := rulelist.NewRefreshable(
- &internal.RefreshableConfig{
+ &refreshable.Config{
Logger: slogutil.NewDiscardLogger(),
URL: srvURL,
ID: testFltListID,
@@ -96,7 +96,7 @@ func TestRefreshable_Refresh(t *testing.T) {
Staleness: filtertest.Staleness,
MaxSize: filtertest.FilterMaxSize,
},
- rulelist.NewResultCache(100, true),
+ rulelist.NewResultCache(filtertest.CacheCount, true),
)
require.NoError(t, err)
diff --git a/internal/filter/internal/rulelist/result.go b/internal/filter/internal/rulelist/result.go
index 79b68ea..b1ccf1b 100644
--- a/internal/filter/internal/rulelist/result.go
+++ b/internal/filter/internal/rulelist/result.go
@@ -1,7 +1,6 @@
package rulelist
import (
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
"github.com/AdguardTeam/urlfilter"
@@ -39,7 +38,7 @@ func (r *URLFilterResult) ToInternal(m IDMapper, rrType dnsmsg.RRType) (res inte
// IDMapper maps an internal urlfilter ID to AdGuard DNS IDs.
type IDMapper interface {
- Map(ufID int) (id agd.FilterListID, svcID agd.BlockedServiceID)
+ Map(ufID int) (id internal.ID, svcID internal.BlockedServiceID)
}
// hostsRulesToResult converts /etc/hosts-style rules into a filtering result.
@@ -74,11 +73,11 @@ func (r *URLFilterResult) hostsRulesToResult(m IDMapper, rrType dnsmsg.RRType) (
func ruleDataToResult(m IDMapper, ufID int, ruleText string, isAllowlist bool) (r internal.Result) {
fltID, svcID := m.Map(ufID)
- var rule agd.FilterRuleText
- if fltID == agd.FilterListIDBlockedService {
- rule = agd.FilterRuleText(svcID)
+ var rule internal.RuleText
+ if fltID == internal.IDBlockedService {
+ rule = internal.RuleText(svcID)
} else {
- rule = agd.FilterRuleText(ruleText)
+ rule = internal.RuleText(ruleText)
}
if isAllowlist {
diff --git a/internal/filter/internal/rulelist/rulelist.go b/internal/filter/internal/rulelist/rulelist.go
index e52af4f..0849f5d 100644
--- a/internal/filter/internal/rulelist/rulelist.go
+++ b/internal/filter/internal/rulelist/rulelist.go
@@ -7,7 +7,6 @@ import (
"math/rand"
"net/netip"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
@@ -38,15 +37,15 @@ type (
ResultCacheEmpty = agdcache.Empty[internal.CacheKey, *CacheItem]
)
-// NewResultCache returns a new initialized cache with the given size. If
-// useCache is false, it returns a cache implementation that does nothing.
-func NewResultCache(size int, useCache bool) (cache ResultCache) {
+// 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{
- Size: size,
+ Count: count,
})
}
@@ -55,10 +54,10 @@ func NewResultCache(size int, useCache bool) (cache ResultCache) {
func NewManagedResultCache(
m agdcache.Manager,
id string,
- size int,
+ count int,
useCache bool,
) (cache ResultCache) {
- cache = NewResultCache(size, useCache)
+ cache = NewResultCache(count, useCache)
m.Add(id, cache)
return cache
@@ -100,8 +99,8 @@ func itemFromCache(
type filter struct {
// engine is the DNS filtering engine.
//
- // NOTE: We do not save the [filterlist.RuleList] used to create the engine
- // to close it, because we exclusively use [filterlist.StringRuleList],
+ // NOTE: Do not save the [filterlist.RuleList] used to create the engine to
+ // close it, because filter exclusively uses [filterlist.StringRuleList],
// which doesn't require closing.
engine *urlfilter.DNSEngine
@@ -111,10 +110,10 @@ type filter struct {
cache ResultCache
// id is the filter list ID, if any.
- id agd.FilterListID
+ id internal.ID
// svcID is the additional identifier for blocked service lists. If id is
- svcID agd.BlockedServiceID
+ svcID internal.BlockedServiceID
// urlFilterID is the synthetic integer identifier for the urlfilter engine.
//
@@ -127,8 +126,8 @@ type filter struct {
// provided rule text and ID.
func newFilter(
text string,
- id agd.FilterListID,
- svcID agd.BlockedServiceID,
+ id internal.ID,
+ svcID internal.BlockedServiceID,
cache agdcache.Interface[internal.CacheKey, *CacheItem],
) (f *filter, err error) {
f = &filter{
@@ -203,7 +202,7 @@ 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 agd.FilterListID, svcID agd.BlockedServiceID) {
+func (f *filter) ID() (id internal.ID, svcID internal.BlockedServiceID) {
return f.id, f.svcID
}
diff --git a/internal/filter/internal/safesearch/safesearch.go b/internal/filter/internal/safesearch/safesearch.go
index 92ff7df..ade529a 100644
--- a/internal/filter/internal/safesearch/safesearch.go
+++ b/internal/filter/internal/safesearch/safesearch.go
@@ -7,8 +7,8 @@ import (
"fmt"
"time"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/refreshable"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist"
"github.com/miekg/dns"
)
@@ -23,7 +23,7 @@ type Filter struct {
type Config struct {
// Refreshable is the configuration of the refreshable filter-list within
// the safe-search filter.
- Refreshable *internal.RefreshableConfig
+ Refreshable *refreshable.Config
// CacheTTL is the time to live of the result cache-items.
//
@@ -51,10 +51,9 @@ var _ internal.RequestFilter = (*Filter)(nil)
// It modifies the response if host matches f.
func (f *Filter) FilterRequest(
ctx context.Context,
- req *dns.Msg,
- ri *agd.RequestInfo,
+ req *internal.Request,
) (r internal.Result, err error) {
- qt := ri.QType
+ qt := req.QType
switch qt {
case dns.TypeA, dns.TypeAAAA, dns.TypeHTTPS:
// Go on.
@@ -62,11 +61,11 @@ func (f *Filter) FilterRequest(
return nil, nil
}
- host := ri.Host
- dr := f.flt.DNSResult(ri.RemoteIP, "", host, qt, false)
+ host := req.Host
+ dr := f.flt.DNSResult(req.RemoteIP, "", host, qt, false)
id, _ := f.flt.ID()
- r = rulelist.ProcessDNSRewrites(ri.Messages, req, dr.DNSRewrites(), host, id)
+ r = rulelist.ProcessDNSRewrites(req, dr.DNSRewrites(), id)
replaceRule(r, host)
@@ -76,7 +75,7 @@ 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 := agd.FilterRuleText(host)
+ rule := internal.RuleText(host)
switch r := r.(type) {
case nil:
// Do nothing.
@@ -90,7 +89,7 @@ func replaceRule(r internal.Result, host string) {
}
// ID implements the [internal.RequestFilter] interface for *Filter.
-func (f *Filter) ID() (id agd.FilterListID) {
+func (f *Filter) ID() (id internal.ID) {
id, _ = f.flt.ID()
return id
diff --git a/internal/filter/internal/safesearch/safesearch_test.go b/internal/filter/internal/safesearch/safesearch_test.go
index 333975a..eabafb2 100644
--- a/internal/filter/internal/safesearch/safesearch_test.go
+++ b/internal/filter/internal/safesearch/safesearch_test.go
@@ -7,12 +7,12 @@ import (
"testing"
"time"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"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/internal/filtertest"
+ "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/golibs/logutil/slogutil"
@@ -48,9 +48,9 @@ func TestFilter(t *testing.T) {
f, newErr := safesearch.New(
&safesearch.Config{
- Refreshable: &internal.RefreshableConfig{
+ Refreshable: &refreshable.Config{
Logger: slogutil.NewDiscardLogger(),
- ID: agd.FilterListIDGeneralSafeSearch,
+ ID: internal.IDGeneralSafeSearch,
URL: srvURL,
CachePath: cachePath,
Staleness: filtertest.Staleness,
@@ -59,7 +59,7 @@ func TestFilter(t *testing.T) {
},
CacheTTL: 1 * time.Minute,
},
- rulelist.NewResultCache(100, true),
+ rulelist.NewResultCache(filtertest.CacheCount, true),
)
require.NoError(t, newErr)
@@ -70,14 +70,14 @@ func TestFilter(t *testing.T) {
require.True(t, t.Run("no_match", func(t *testing.T) {
ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
- req, ri := newReq(t, testOther, dns.TypeA)
- res, err := f.FilterRequest(ctx, req, ri)
+ req := newReq(t, testOther, dns.TypeA)
+ res, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
assert.Nil(t, res)
require.True(t, t.Run("cached", func(t *testing.T) {
- res, err = f.FilterRequest(ctx, req, ri)
+ res, err = f.FilterRequest(ctx, req)
require.NoError(t, err)
// TODO(a.garipov): Find a way to make caches more inspectable.
@@ -87,8 +87,8 @@ func TestFilter(t *testing.T) {
require.True(t, t.Run("txt", func(t *testing.T) {
ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
- req, ri := newReq(t, testEngineWithIP, dns.TypeTXT)
- res, err := f.FilterRequest(ctx, req, ri)
+ req := newReq(t, testEngineWithIP, dns.TypeTXT)
+ res, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
assert.Nil(t, res)
@@ -96,23 +96,23 @@ func TestFilter(t *testing.T) {
require.True(t, t.Run("ip", func(t *testing.T) {
ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
- req, ri := newReq(t, testEngineWithIP, dns.TypeA)
- res, err := f.FilterRequest(ctx, req, ri)
+ req := newReq(t, testEngineWithIP, dns.TypeA)
+ res, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
rm := testutil.RequireTypeAssert[*internal.ResultModifiedResponse](t, res)
require.Len(t, rm.Msg.Answer, 1)
- assert.Equal(t, rm.Rule, agd.FilterRuleText(testEngineWithIP))
+ assert.Equal(t, rm.Rule, internal.RuleText(testEngineWithIP))
a := testutil.RequireTypeAssert[*dns.A](t, rm.Msg.Answer[0])
assert.Equal(t, net.IP(testIPOfEngineWithIP.AsSlice()), a.A)
t.Run("cached", func(t *testing.T) {
- newReq, newRI := newReq(t, testEngineWithIP, dns.TypeA)
+ newReq := newReq(t, testEngineWithIP, dns.TypeA)
var cachedRes internal.Result
- cachedRes, err = f.FilterRequest(ctx, newReq, newRI)
+ cachedRes, err = f.FilterRequest(ctx, newReq)
require.NoError(t, err)
// Do not assert that the results are the same, since a modified
@@ -121,7 +121,7 @@ func TestFilter(t *testing.T) {
// fields set properly.
cachedMR := testutil.RequireTypeAssert[*internal.ResultModifiedResponse](t, cachedRes)
assert.NotSame(t, cachedMR, rm)
- assert.Equal(t, cachedMR.Msg.Id, newReq.Id)
+ assert.Equal(t, cachedMR.Msg.Id, newReq.DNS.Id)
assert.Equal(t, cachedMR.List, rm.List)
assert.Equal(t, cachedMR.Rule, rm.Rule)
})
@@ -129,8 +129,8 @@ func TestFilter(t *testing.T) {
require.True(t, t.Run("domain", func(t *testing.T) {
ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
- req, ri := newReq(t, testEngineWithDomain, dns.TypeA)
- res, err := f.FilterRequest(ctx, req, ri)
+ req := newReq(t, testEngineWithDomain, dns.TypeA)
+ res, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
rm := testutil.RequireTypeAssert[*internal.ResultModifiedRequest](t, res)
@@ -138,7 +138,7 @@ func TestFilter(t *testing.T) {
require.Len(t, rm.Msg.Question, 1)
assert.False(t, rm.Msg.Response)
- assert.Equal(t, rm.Rule, agd.FilterRuleText(testEngineWithDomain))
+ assert.Equal(t, rm.Rule, internal.RuleText(testEngineWithDomain))
q := rm.Msg.Question[0]
assert.Equal(t, dns.TypeA, q.Qtype)
@@ -147,8 +147,8 @@ func TestFilter(t *testing.T) {
require.True(t, t.Run("https", func(t *testing.T) {
ctx := testutil.ContextWithTimeout(t, filtertest.Timeout)
- req, ri := newReq(t, testEngineWithDomain, dns.TypeHTTPS)
- res, err := f.FilterRequest(ctx, req, ri)
+ req := newReq(t, testEngineWithDomain, dns.TypeHTTPS)
+ res, err := f.FilterRequest(ctx, req)
require.NoError(t, err)
rm := testutil.RequireTypeAssert[*internal.ResultModifiedRequest](t, res)
@@ -156,7 +156,7 @@ func TestFilter(t *testing.T) {
require.Len(t, rm.Msg.Question, 1)
assert.False(t, rm.Msg.Response)
- assert.Equal(t, rm.Rule, agd.FilterRuleText(testEngineWithDomain))
+ assert.Equal(t, rm.Rule, internal.RuleText(testEngineWithDomain))
q := rm.Msg.Question[0]
assert.Equal(t, dns.TypeHTTPS, q.Qtype)
@@ -164,18 +164,16 @@ func TestFilter(t *testing.T) {
}))
}
-// newReq is a test helper that returns the DNS request and its accompanying
-// request info with the given data.
-func newReq(tb testing.TB, host string, qt dnsmsg.RRType) (req *dns.Msg, ri *agd.RequestInfo) {
+// 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) {
tb.Helper()
- req = dnsservertest.NewReq(host, qt, dns.ClassINET)
- ri = &agd.RequestInfo{
+ return &internal.Request{
+ DNS: dnsservertest.NewReq(host, qt, dns.ClassINET),
Messages: agdtest.NewConstructor(tb),
Host: host,
QType: qt,
QClass: dns.ClassINET,
}
-
- return req, ri
}
diff --git a/internal/filter/internal/serviceblock/index.go b/internal/filter/internal/serviceblock/index.go
index 2c11e9a..c6b8a69 100644
--- a/internal/filter/internal/serviceblock/index.go
+++ b/internal/filter/internal/serviceblock/index.go
@@ -7,9 +7,9 @@ import (
"path"
"strings"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"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/golibs/errors"
)
@@ -26,7 +26,7 @@ func (r *indexResp) toInternal(
logger *slog.Logger,
errColl errcoll.Interface,
cacheManager agdcache.Manager,
- cacheSize int,
+ cacheCount int,
useCache bool,
) (services serviceRuleLists, err error) {
l := len(r.BlockedServices)
@@ -38,11 +38,11 @@ func (r *indexResp) toInternal(
errs := make([]error, len(r.BlockedServices))
for i, svc := range r.BlockedServices {
var (
- svcID agd.BlockedServiceID
+ svcID internal.BlockedServiceID
rl *rulelist.Immutable
)
- svcID, rl, err = svc.toInternal(ctx, logger, errColl, cacheManager, cacheSize, useCache)
+ svcID, rl, err = svc.toInternal(ctx, logger, errColl, cacheManager, cacheCount, useCache)
if err != nil {
errs[i] = fmt.Errorf("service at index %d: %w", i, err)
@@ -71,17 +71,17 @@ 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 "[agd.FilterListIDBlockedService]/[svc.ID]" to
+// also adds the cache with ID "[internal.IDBlockedService]/[svc.ID]" to
// the cache manager.
func (svc *indexRespService) toInternal(
ctx context.Context,
logger *slog.Logger,
errColl errcoll.Interface,
cacheManager agdcache.Manager,
- cacheSize int,
+ cacheCount int,
useCache bool,
-) (svcID agd.BlockedServiceID, rl *rulelist.Immutable, err error) {
- svcID, err = agd.NewBlockedServiceID(svc.ID)
+) (svcID internal.BlockedServiceID, rl *rulelist.Immutable, err error) {
+ svcID, err = internal.NewBlockedServiceID(svc.ID)
if err != nil {
return "", nil, fmt.Errorf("validating id: %w", err)
}
@@ -91,16 +91,11 @@ func (svc *indexRespService) toInternal(
logger.WarnContext(ctx, "service has no rules", "svc_id", svcID)
}
- fltIDStr := path.Join(cachePrefix, string(agd.FilterListIDBlockedService), string(svcID))
- cache := rulelist.NewManagedResultCache(
- cacheManager,
- fltIDStr,
- cacheSize,
- useCache,
- )
+ fltIDStr := path.Join(cachePrefix, string(internal.IDBlockedService), string(svcID))
+ cache := rulelist.NewManagedResultCache(cacheManager, fltIDStr, cacheCount, useCache)
rl, err = rulelist.NewImmutable(
strings.Join(svc.Rules, "\n"),
- agd.FilterListIDBlockedService,
+ internal.IDBlockedService,
svcID,
cache,
)
diff --git a/internal/filter/internal/serviceblock/serviceblock.go b/internal/filter/internal/serviceblock/serviceblock.go
index 563eb8c..d2d5001 100644
--- a/internal/filter/internal/serviceblock/serviceblock.go
+++ b/internal/filter/internal/serviceblock/serviceblock.go
@@ -10,50 +10,62 @@ import (
"log/slog"
"strings"
"sync"
+ "time"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"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/refreshable"
"github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist"
- "github.com/AdguardTeam/AdGuardDNS/internal/metrics"
)
// Filter is a service-blocking filter that uses rule lists that it gets from an
// index.
type Filter struct {
logger *slog.Logger
-
- // refr is the helper entity containing the refreshable part of the index
- // refresh and caching logic.
- refr *internal.Refreshable
+ refr *refreshable.Refreshable
// mu protects services.
- mu *sync.RWMutex
-
- // services is an ID to filter mapping.
+ mu *sync.RWMutex
services serviceRuleLists
- // errColl used to collect non-critical and rare errors.
errColl errcoll.Interface
+ metrics internal.Metrics
}
// serviceRuleLists is convenient alias for an ID to filter mapping.
-type serviceRuleLists = map[agd.BlockedServiceID]*rulelist.Immutable
+type serviceRuleLists = map[internal.BlockedServiceID]*rulelist.Immutable
-// New returns a fully initialized service blocker.
-func New(c *internal.RefreshableConfig, errColl errcoll.Interface) (f *Filter, err error) {
- refr, err := internal.NewRefreshable(c)
+// Config is the configuration for the service-blocking filter.
+type Config struct {
+ // Refreshable is the configuration of the refreshable index of the
+ // service-blocking filter. It must not be nil and must be valid.
+ Refreshable *refreshable.Config
+
+ // ErrColl used to collect non-critical and rare errors. It must not be
+ // nil.
+ ErrColl errcoll.Interface
+
+ // Metrics are the metrics for the service-blocking filter. It must not be
+ // nil.
+ Metrics internal.Metrics
+}
+
+// New returns a fully initialized service blocker. c must not be nil and must
+// be valid.
+func New(c *Config) (f *Filter, err error) {
+ refr, err := refreshable.New(c.Refreshable)
if err != nil {
return nil, fmt.Errorf("creating refreshable for service index: %w", err)
}
return &Filter{
- logger: c.Logger,
+ logger: c.Refreshable.Logger,
refr: refr,
mu: &sync.RWMutex{},
services: serviceRuleLists{},
- errColl: errColl,
+ errColl: c.ErrColl,
+ metrics: c.Metrics,
}, nil
}
@@ -61,7 +73,7 @@ func New(c *internal.RefreshableConfig, errColl errcoll.Interface) (f *Filter, e
// The order of the elements in rls is undefined.
func (f *Filter) RuleLists(
ctx context.Context,
- ids []agd.BlockedServiceID,
+ ids []internal.BlockedServiceID,
) (rls []*rulelist.Immutable) {
if len(ids) == 0 {
return nil
@@ -86,15 +98,14 @@ func (f *Filter) RuleLists(
func (f *Filter) Refresh(
ctx context.Context,
cacheManager agdcache.Manager,
- cacheSize int,
+ cacheCount int,
useCache bool,
acceptStale bool,
) (err error) {
- fltIDStr := string(agd.FilterListIDBlockedService)
+ var count int
defer func() {
- if err != nil {
- metrics.FilterUpdatedStatus.WithLabelValues(fltIDStr).Set(0)
- }
+ // TODO(a.garipov): Consider using [agdtime.Clock].
+ f.metrics.SetFilterStatus(ctx, string(internal.IDBlockedService), time.Now(), count, err)
}()
resp, err := f.loadIndex(ctx, acceptStale)
@@ -103,21 +114,16 @@ func (f *Filter) Refresh(
return err
}
- services, err := resp.toInternal(ctx, f.logger, f.errColl, cacheManager, cacheSize, useCache)
+ services, err := resp.toInternal(ctx, f.logger, f.errColl, cacheManager, cacheCount, useCache)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
- count := 0
for _, s := range services {
count += s.RulesCount()
}
- metrics.FilterRulesTotal.WithLabelValues(fltIDStr).Set(float64(count))
- metrics.FilterUpdatedTime.WithLabelValues(fltIDStr).SetToCurrentTime()
- metrics.FilterUpdatedStatus.WithLabelValues(fltIDStr).Set(1)
-
f.mu.Lock()
defer f.mu.Unlock()
diff --git a/internal/filter/internal/serviceblock/serviceblock_test.go b/internal/filter/internal/serviceblock/serviceblock_test.go
index fe2c1c0..3ed5f46 100644
--- a/internal/filter/internal/serviceblock/serviceblock_test.go
+++ b/internal/filter/internal/serviceblock/serviceblock_test.go
@@ -5,11 +5,12 @@ import (
"net/http"
"testing"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"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"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
@@ -17,48 +18,28 @@ import (
"github.com/stretchr/testify/require"
)
-// Common blocked service IDs for tests.
-const (
- testSvcID1 agd.BlockedServiceID = "svc_1"
- testSvcID2 agd.BlockedServiceID = "svc_2"
- testSvcIDNotPresent agd.BlockedServiceID = "svc_not_present"
-)
-
-// testData is a sample of a service index response.
-//
-// See https://github.com/AdguardTeam/HostlistsRegistry/blob/main/assets/services.json.
-const testData string = `{
- "blocked_services": [
- {
- "id": "` + string(testSvcID1) + `",
- "name": "Service 1",
- "rules": [
- "||service-1.example^"
- ]
- },
- {
- "id": "` + string(testSvcID2) + `",
- "name": "Service 2",
- "rules": [
- "||service-2.example^"
- ]
- }
- ]
-}`
-
func TestFilter(t *testing.T) {
reqCh := make(chan struct{}, 1)
- cachePath, srvURL := filtertest.PrepareRefreshable(t, reqCh, testData, http.StatusOK)
+ cachePath, srvURL := filtertest.PrepareRefreshable(
+ t,
+ reqCh,
+ filtertest.BlockedServiceIndex,
+ http.StatusOK,
+ )
- f, err := serviceblock.New(&internal.RefreshableConfig{
- Logger: slogutil.NewDiscardLogger(),
- URL: srvURL,
- ID: agd.FilterListIDBlockedService,
- CachePath: cachePath,
- Staleness: filtertest.Staleness,
- Timeout: filtertest.Timeout,
- MaxSize: filtertest.FilterMaxSize,
- }, agdtest.NewErrorCollector())
+ f, err := serviceblock.New(&serviceblock.Config{
+ Refreshable: &refreshable.Config{
+ Logger: slogutil.NewDiscardLogger(),
+ URL: srvURL,
+ ID: internal.IDBlockedService,
+ CachePath: cachePath,
+ Staleness: filtertest.Staleness,
+ Timeout: filtertest.Timeout,
+ MaxSize: filtertest.FilterMaxSize,
+ },
+ ErrColl: agdtest.NewErrorCollector(),
+ Metrics: filter.EmptyMetrics{},
+ })
require.NoError(t, err)
@@ -68,23 +49,23 @@ func TestFilter(t *testing.T) {
testutil.RequireReceive(t, reqCh, filtertest.Timeout)
- rls := f.RuleLists(ctx, []agd.BlockedServiceID{
- testSvcID1,
- testSvcID2,
- testSvcIDNotPresent,
+ rls := f.RuleLists(ctx, []internal.BlockedServiceID{
+ filtertest.BlockedServiceID1,
+ filtertest.BlockedServiceID2,
+ filtertest.BlockedServiceIDDoesNotExist,
})
require.Len(t, rls, 2)
- wantSvcIDs := []agd.BlockedServiceID{
- testSvcID1,
- testSvcID2,
+ wantSvcIDs := []internal.BlockedServiceID{
+ filtertest.BlockedServiceID1,
+ filtertest.BlockedServiceID2,
}
- gotFltIDs := make([]agd.FilterListID, 2)
- gotSvcIDs := make([]agd.BlockedServiceID, 2)
+ gotFltIDs := make([]internal.ID, 2)
+ gotSvcIDs := make([]internal.BlockedServiceID, 2)
gotFltIDs[0], gotSvcIDs[0] = rls[0].ID()
gotFltIDs[1], gotSvcIDs[1] = rls[1].ID()
- assert.Equal(t, agd.FilterListIDBlockedService, gotFltIDs[0])
- assert.Equal(t, agd.FilterListIDBlockedService, gotFltIDs[1])
+ assert.Equal(t, internal.IDBlockedService, gotFltIDs[0])
+ assert.Equal(t, internal.IDBlockedService, gotFltIDs[1])
assert.ElementsMatch(t, wantSvcIDs, gotSvcIDs)
}
diff --git a/internal/filter/schedule.go b/internal/filter/schedule.go
new file mode 100644
index 0000000..7054b81
--- /dev/null
+++ b/internal/filter/schedule.go
@@ -0,0 +1,94 @@
+package filter
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
+ "github.com/AdguardTeam/golibs/errors"
+)
+
+// DayInterval is an interval within a single day. The interval is exclusive at
+// the end. An empty DayInterval is zero-length.
+type DayInterval struct {
+ // Start is the inclusive start of the interval in minutes. It must be
+ // within the range from 00:00:00 (0) to 23:59:59
+ // ([MaxDayIntervalStartMinutes]).
+ Start uint16
+
+ // End is the exclusive end of the interval in minutes. It must be within
+ // the range from 00:00:00 (0) to 00:00:00 of the next day
+ // ([MaxDayIntervalEndMinutes]).
+ End uint16
+}
+
+const (
+ // MaxDayIntervalStartMinutes is the maximum value for [DayInterval.Start].
+ MaxDayIntervalStartMinutes = 24*60 - 1
+
+ // MaxDayIntervalEndMinutes is the maximum value for [DayInterval.End].
+ MaxDayIntervalEndMinutes = 24 * 60
+)
+
+// Validate returns the day range validation errors, if any. A nil DayInterval
+// is considered valid.
+func (r *DayInterval) Validate() (err error) {
+ switch {
+ case r == nil, *r == DayInterval{}:
+ return nil
+ case r.End < r.Start:
+ return fmt.Errorf(
+ "end: %w: %d is less than start %d",
+ errors.ErrOutOfRange,
+ r.End,
+ r.Start,
+ )
+ case r.Start > MaxDayIntervalStartMinutes:
+ return fmt.Errorf(
+ "start: %w: %d is greater than %d",
+ errors.ErrOutOfRange,
+ r.Start,
+ MaxDayIntervalStartMinutes,
+ )
+ case r.End > MaxDayIntervalEndMinutes:
+ return fmt.Errorf(
+ "end: %w: %d is greater than %d",
+ errors.ErrOutOfRange,
+ r.End,
+ MaxDayIntervalEndMinutes,
+ )
+ default:
+ return nil
+ }
+}
+
+// WeeklySchedule is a schedule for one week. The index is the same as
+// [time.Weekday] values. That is, 0 is Sunday, 1 is Monday, etc. A nil
+// DayInterval means that there is no schedule for this day.
+type WeeklySchedule [7]*DayInterval
+
+// ConfigSchedule is the schedule of a client's parental protection. All
+// fields must not be nil.
+type ConfigSchedule struct {
+ // Week is the parental protection schedule for every day of the week. It
+ // must not be nil.
+ Week *WeeklySchedule
+
+ // TimeZone is the profile's time zone. It must not be nil.
+ TimeZone *agdtime.Location
+}
+
+// Contains returns true if t is within the allowed schedule.
+func (s *ConfigSchedule) Contains(t time.Time) (ok bool) {
+ t = t.In(&s.TimeZone.Location)
+ r := s.Week[int(t.Weekday())]
+ if r == nil || *r == (DayInterval{}) {
+ return false
+ }
+
+ day := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, &s.TimeZone.Location)
+ start := day.Add(time.Duration(r.Start) * time.Minute)
+ end := day.Add(time.Duration(r.End) * time.Minute)
+
+ return !t.Before(start) && t.Before(end)
+}
diff --git a/internal/agd/profile_test.go b/internal/filter/schedule_test.go
similarity index 59%
rename from internal/agd/profile_test.go
rename to internal/filter/schedule_test.go
index 5c61018..2114a99 100644
--- a/internal/agd/profile_test.go
+++ b/internal/filter/schedule_test.go
@@ -1,63 +1,78 @@
-package agd_test
+package filter_test
import (
"testing"
"time"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/golibs/timeutil"
"github.com/stretchr/testify/assert"
)
-func TestDayRange_Validate(t *testing.T) {
+func TestDayInterval_Validate(t *testing.T) {
testCases := []struct {
+ ivl *filter.DayInterval
name string
wantErrMsg string
- rng agd.DayRange
}{{
+ ivl: &filter.DayInterval{
+ Start: 11 * 60,
+ End: 13*60 - 1,
+ },
name: "ok",
wantErrMsg: "",
- rng: agd.DayRange{Start: 11 * 60, End: 13*60 - 1},
}, {
+ ivl: &filter.DayInterval{
+ Start: 0,
+ End: 0,
+ },
name: "ok_zeroes",
wantErrMsg: "",
- rng: agd.DayRange{Start: 0, End: 0},
}, {
+ ivl: nil,
+ name: "ok_nil",
+ wantErrMsg: "",
+ }, {
+ ivl: &filter.DayInterval{
+ Start: filter.MaxDayIntervalStartMinutes,
+ End: filter.MaxDayIntervalEndMinutes,
+ },
name: "ok_max",
wantErrMsg: "",
- rng: agd.DayRange{
- Start: agd.MaxDayRangeMinutes,
- End: agd.MaxDayRangeMinutes,
+ }, {
+ ivl: &filter.DayInterval{
+ Start: 1,
+ End: 0,
},
- }, {
- name: "ok_zero_length",
- wantErrMsg: "",
- rng: agd.ZeroLengthDayRange(),
- }, {
name: "err_before",
- wantErrMsg: "bad day range: end 0 less than start 1",
- rng: agd.DayRange{Start: 1, End: 0},
+ wantErrMsg: "end: out of range: 0 is less than start 1",
}, {
+ ivl: &filter.DayInterval{
+ Start: 10_000,
+ End: 10_000,
+ },
name: "err_bad_start",
- wantErrMsg: "bad day range: start 10000 greater than 1439",
- rng: agd.DayRange{Start: 10_000, End: 10_000},
+ wantErrMsg: "start: out of range: 10000 is greater than 1439",
}, {
+ ivl: &filter.DayInterval{
+ Start: 0,
+ End: 10_000,
+ },
name: "err_bad_end",
- wantErrMsg: "bad day range: end 10000 greater than 1439",
- rng: agd.DayRange{Start: 0, End: 10_000},
+ wantErrMsg: "end: out of range: 10000 is greater than 1440",
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- err := tc.rng.Validate()
+ err := tc.ivl.Validate()
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
})
}
}
-func TestParentalProtectionSchedule_Contains(t *testing.T) {
+func TestFilterConfigSchedule_Contains(t *testing.T) {
baseTime := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
otherTime := baseTime.Add(1 * timeutil.Day)
@@ -66,41 +81,31 @@ func TestParentalProtectionSchedule_Contains(t *testing.T) {
otherTZ := time.FixedZone("Etc/GMT-3", 3*60*60)
// baseSchedule, 12:00:00 to 13:59:59.
- baseSchedule := &agd.ParentalProtectionSchedule{
- Week: &agd.WeeklySchedule{
- time.Sunday: agd.ZeroLengthDayRange(),
- time.Monday: agd.ZeroLengthDayRange(),
- time.Tuesday: agd.ZeroLengthDayRange(),
- time.Wednesday: agd.ZeroLengthDayRange(),
- time.Thursday: agd.ZeroLengthDayRange(),
-
+ baseSchedule := &filter.ConfigSchedule{
+ Week: &filter.WeeklySchedule{
// baseTime is on Friday.
- time.Friday: agd.DayRange{12 * 60, 14*60 - 1},
-
- time.Saturday: agd.ZeroLengthDayRange(),
+ time.Friday: &filter.DayInterval{
+ Start: 12 * 60,
+ End: 14 * 60,
+ },
},
TimeZone: agdtime.UTC(),
}
// allDaySchedule, 00:00:00 to 23:59:59.
- allDaySchedule := &agd.ParentalProtectionSchedule{
- Week: &agd.WeeklySchedule{
- time.Sunday: agd.ZeroLengthDayRange(),
- time.Monday: agd.ZeroLengthDayRange(),
- time.Tuesday: agd.ZeroLengthDayRange(),
- time.Wednesday: agd.ZeroLengthDayRange(),
- time.Thursday: agd.ZeroLengthDayRange(),
-
+ allDaySchedule := &filter.ConfigSchedule{
+ Week: &filter.WeeklySchedule{
// baseTime is on Friday.
- time.Friday: agd.DayRange{0, 24*60 - 1},
-
- time.Saturday: agd.ZeroLengthDayRange(),
+ time.Friday: &filter.DayInterval{
+ Start: 0,
+ End: filter.MaxDayIntervalEndMinutes,
+ },
},
TimeZone: agdtime.UTC(),
}
testCases := []struct {
- schedule *agd.ParentalProtectionSchedule
+ schedule *filter.ConfigSchedule
assert assert.BoolAssertionFunc
t time.Time
name string
@@ -114,6 +119,21 @@ func TestParentalProtectionSchedule_Contains(t *testing.T) {
assert: assert.True,
t: baseTime.Add(13 * time.Hour),
name: "same_day_inside",
+ }, {
+ schedule: baseSchedule,
+ assert: assert.True,
+ t: baseTime.Add(12 * time.Hour),
+ name: "same_day_start",
+ }, {
+ schedule: baseSchedule,
+ assert: assert.False,
+ t: baseTime.Add(14 * time.Hour),
+ name: "same_day_end",
+ }, {
+ schedule: baseSchedule,
+ assert: assert.True,
+ t: baseTime.Add(14 * time.Hour).Add(-1),
+ name: "same_day_almost_end",
}, {
schedule: baseSchedule,
assert: assert.False,
diff --git a/internal/filter/storage.go b/internal/filter/storage.go
index 597bf2a..5a92ffb 100644
--- a/internal/filter/storage.go
+++ b/internal/filter/storage.go
@@ -2,336 +2,8 @@ package filter
import (
"context"
- "encoding/json"
- "fmt"
- "log/slog"
- "net/url"
- "path"
- "path/filepath"
- "slices"
- "strings"
- "sync"
- "time"
-
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
- "github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
- "github.com/AdguardTeam/AdGuardDNS/internal/errcoll"
- "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/custom"
- "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/AdGuardDNS/internal/metrics"
- "github.com/AdguardTeam/golibs/logutil/slogutil"
- "github.com/c2h5oh/datasize"
)
-// Storage is a storage for filters.
-type Storage interface {
- // FilterFromContext returns a filter combining rules and types of filtering
- // for all entities from the context. ri must not be nil.
- FilterFromContext(ctx context.Context, ri *agd.RequestInfo) (f Interface)
-
- // HasListID returns true if id is within the rule lists that are currently
- // in the storage.
- HasListID(id agd.FilterListID) (ok bool)
-}
-
-// DefaultStorage is the default storage for filters, including the filters
-// based on rule lists, custom filters of profiles, safe browsing, and safe
-// search ones. It should be initially refreshed with
-// [DefaultStorage.RefreshInitial].
-type DefaultStorage struct {
- // baseLogger is used to create loggers for other filters in the storage.
- baseLogger *slog.Logger
-
- // logger is the logger of the storage itself.
- logger *slog.Logger
-
- // refr is the helper entity containing the refreshable part of the index
- // refresh and caching logic.
- refr *internal.Refreshable
-
- // mu protects ruleLists.
- mu *sync.RWMutex
-
- // ruleLists are the filter list ID to a rule list filter map.
- ruleLists filteringRuleLists
-
- // services is the service blocking filter.
- services *serviceblock.Filter
-
- // safeBrowsing is the general safe browsing filter.
- safeBrowsing *hashprefix.Filter
-
- // adultBlocking is the adult content blocking safe browsing filter.
- adultBlocking *hashprefix.Filter
-
- // newRegDomains is the newly registered domains filter.
- newRegDomains *hashprefix.Filter
-
- // genSafeSearch is the general safe search filter.
- genSafeSearch *safesearch.Filter
-
- // ytSafeSearch is the YouTube safe search filter.
- ytSafeSearch *safesearch.Filter
-
- // now returns the current time.
- now func() (t time.Time)
-
- // errColl used to collect non-critical and rare errors, for example caching
- // errors.
- errColl errcoll.Interface
-
- // cacheManager is the global cache manager. cacheManager must not be nil.
- cacheManager agdcache.Manager
-
- // customFilters is the storage of custom filters for profiles.
- customFilters *custom.Filters
-
- // cacheDir is the path to the directory where the cached filter files are
- // put. The directory must exist.
- cacheDir string
-
- // refreshIvl is the refresh interval for this storage. It defines how
- // often the filter rule lists are updated from the index.
- refreshIvl time.Duration
-
- // ruleListRefreshTimeout is the timeout for the filter update operation of
- // each rule-list.
- ruleListRefreshTimeout time.Duration
-
- // RuleListCacheSize defines the size of the LRU cache of rule-list
- // filtering results.
- ruleListCacheSize int
-
- // maxRuleListSize is the maximum size in bytes of the downloadable
- // rule-list content.
- maxRuleListSize datasize.ByteSize
-
- // useRuleListCache, if true, enables rule list cache.
- useRuleListCache bool
-}
-
-// filteringRuleLists is convenient alias for an ID to filter mapping.
-type filteringRuleLists = map[agd.FilterListID]*rulelist.Refreshable
-
-// Filenames for filter indexes.
-//
-// TODO(ameshkov): Consider making configurable.
-const (
- ruleListIndexFilename = "filters.json"
- serviceIndexFilename = "services.json"
-)
-
-// DefaultStorageConfig contains configuration for a filter storage based on
-// rule lists.
-type DefaultStorageConfig struct {
- // BaseLogger is used to create loggers with custom prefixes for filters and
- // the storage itself.
- BaseLogger *slog.Logger
-
- // FilterIndexURL is the URL of the filtering rule index document.
- FilterIndexURL *url.URL
-
- // BlockedServiceIndexURL is the URL of the blocked service index document.
- // If nil, no blocked service filtering is performed.
- BlockedServiceIndexURL *url.URL
-
- // GeneralSafeSearchRulesURL is the URL to refresh general safe search rules
- // list. If nil, no general safe search filtering is performed.
- GeneralSafeSearchRulesURL *url.URL
-
- // YoutubeSafeSearchRulesURL is the URL to refresh YouTube safe search rules
- // list. If nil, no youtube safe search filtering is performed.
- YoutubeSafeSearchRulesURL *url.URL
-
- // SafeBrowsing is the configuration for the default safe browsing filter.
- // If nil, no safe-browsing filtering is performed.
- SafeBrowsing *hashprefix.Filter
-
- // AdultBlocking is the configuration for the adult content blocking safe
- // browsing filter. If nil, no adult-blocking filtering is performed.
- AdultBlocking *hashprefix.Filter
-
- // NewRegDomains is the configuration for the newly-registered domains
- // safe-browsing filter. If nil, no blocking of newly-registered domains is
- // performed.
- NewRegDomains *hashprefix.Filter
-
- // Now is a function that returns current time.
- Now func() (now time.Time)
-
- // ErrColl is used to collect non-critical and rare errors as well as
- // refresh errors.
- ErrColl errcoll.Interface
-
- // CacheManager is the global cache manager. CacheManager must not be nil.
- CacheManager agdcache.Manager
-
- // CacheDir is the path to the directory where the cached filter files are
- // put. The directory must exist.
- CacheDir string
-
- // CustomFilterCacheSize is the number of cached custom filters for
- // profiles.
- CustomFilterCacheSize int
-
- // SafeSearchCacheSize is the size of the LRU cache of results of the safe
- // search filters: the general one and the YouTube one.
- SafeSearchCacheSize int
-
- // SafeSearchCacheTTL is the time-to-live value used for the cache of
- // results of the safe search filters: the general one and the YouTube one.
- SafeSearchCacheTTL time.Duration
-
- // RuleListCacheSize defines the size of the LRU cache of rule-list
- // filtering results.
- RuleListCacheSize int
-
- // RefreshIvl is the refresh interval for this storage. It defines how
- // often the filter rule lists are updated from the index.
- //
- // TODO(a.garipov): This value is used both for refreshes and for filter
- // staleness, which can cause issues. Consider splitting the two.
- RefreshIvl time.Duration
-
- // IndexRefreshTimeout is the timeout for the filter rule-list index update
- // operation.
- IndexRefreshTimeout time.Duration
-
- // RuleListRefreshTimeout is the timeout for the filter update operation of
- // each rule-list.
- RuleListRefreshTimeout time.Duration
-
- // UseRuleListCache, if true, enables rule list cache.
- UseRuleListCache bool
-
- // MaxRuleListSize is the maximum size in bytes of the downloadable
- // rule-list content.
- MaxRuleListSize datasize.ByteSize
-}
-
-// svcIdxRefreshTimeout is the default timeout to use when fetching the
-// blocked-service index.
-const svcIdxRefreshTimeout = 3 * time.Minute
-
-// Constants that define cache identifiers for the cache manager.
-const (
- // cachePrefixSafeSearch is used as a cache category.
- cachePrefixSafeSearch = "filters/safe_search"
-
- // cachePrefixRuleList is used a cache category.
- cachePrefixRuleList = "filters/rulelist"
-)
-
-// NewDefaultStorage returns a new filter storage. It also adds the caches with
-// IDs [agd.FilterListIDGeneralSafeSearch] and
-// [agd.FilterListIDYoutubeSafeSearch] to the cache manager. c must not be nil.
-func NewDefaultStorage(c *DefaultStorageConfig) (s *DefaultStorage) {
- genSafeSearch, err := newSafeSearchFilter(
- c,
- c.GeneralSafeSearchRulesURL,
- agd.FilterListIDGeneralSafeSearch,
- )
- if err != nil {
- // Shouldn't happen, since the safe-search URL must be checked in cmd.
- //
- // TODO(a.garipov): Consider returning.
- panic(fmt.Errorf("creating refreshable for general safe search: %w", err))
- }
-
- ytSafeSearch, err := newSafeSearchFilter(
- c,
- c.YoutubeSafeSearchRulesURL,
- agd.FilterListIDYoutubeSafeSearch,
- )
- if err != nil {
- // Shouldn't happen, since the safe-search URL must be checked in cmd.
- //
- // TODO(a.garipov): Consider returning.
- panic(fmt.Errorf("creating refreshable for youtube safe search: %w", err))
- }
-
- ruleListIdxID := "rule_list_index"
- ruleListIdxRefr, err := internal.NewRefreshable(&internal.RefreshableConfig{
- Logger: c.BaseLogger.With(slogutil.KeyPrefix, path.Join("filters", ruleListIdxID)),
- URL: c.FilterIndexURL,
- // TODO(a.garipov): Consider adding special IDs for indexes.
- ID: agd.FilterListID(ruleListIdxID),
- CachePath: filepath.Join(c.CacheDir, ruleListIndexFilename),
- Staleness: c.RefreshIvl,
- Timeout: c.IndexRefreshTimeout,
- // TODO(a.garipov): Consider using a different limit here.
- MaxSize: c.MaxRuleListSize,
- })
- if err != nil {
- // Shouldn't happen, since the index URL must be checked in cmd.
- //
- // TODO(a.garipov): Consider returning.
- panic(fmt.Errorf("creating refreshable for rule-list index: %w", err))
- }
-
- var svcBlockFilter *serviceblock.Filter
- if c.BlockedServiceIndexURL != nil {
- // TODO(a.garipov): Consider adding special IDs for indexes.
- id := "blocked_service_index"
- svcBlockFilter, err = serviceblock.New(&internal.RefreshableConfig{
- Logger: c.BaseLogger.With(slogutil.KeyPrefix, path.Join("filters", id)),
- URL: c.BlockedServiceIndexURL,
- ID: agd.FilterListID(id),
- CachePath: filepath.Join(c.CacheDir, serviceIndexFilename),
- Staleness: c.RefreshIvl,
- // TODO(ameshkov): Consider making configurable.
- Timeout: svcIdxRefreshTimeout,
- // TODO(a.garipov): Consider using a different limit here.
- MaxSize: c.MaxRuleListSize,
- }, c.ErrColl)
- if err != nil {
- // Shouldn't happen, since the index URL must be checked in cmd.
- //
- // TODO(a.garipov): Consider returning.
- panic(fmt.Errorf("creating refreshable for service index: %w", err))
- }
- }
-
- return &DefaultStorage{
- baseLogger: c.BaseLogger,
- logger: c.BaseLogger.With(slogutil.KeyPrefix, StoragePrefix),
- refr: ruleListIdxRefr,
- mu: &sync.RWMutex{},
- services: svcBlockFilter,
- safeBrowsing: c.SafeBrowsing,
- adultBlocking: c.AdultBlocking,
- newRegDomains: c.NewRegDomains,
- genSafeSearch: genSafeSearch,
- ytSafeSearch: ytSafeSearch,
- now: c.Now,
- errColl: c.ErrColl,
- cacheManager: c.CacheManager,
- customFilters: custom.New(&custom.Config{
- Logger: c.BaseLogger.With(slogutil.KeyPrefix, path.Join(
- "filters",
- string(agd.FilterListIDCustom),
- )),
- ErrColl: c.ErrColl,
- CacheConf: &agdcache.LRUConfig{
- Size: c.CustomFilterCacheSize,
- },
- CacheManager: c.CacheManager,
- }),
- cacheDir: c.CacheDir,
- refreshIvl: c.RefreshIvl,
- ruleListRefreshTimeout: c.RuleListRefreshTimeout,
- ruleListCacheSize: c.RuleListCacheSize,
- maxRuleListSize: c.MaxRuleListSize,
- useRuleListCache: c.UseRuleListCache,
- }
-}
-
// StoragePrefix is a common prefix for logging and refreshes of the filter
// storage.
//
@@ -339,449 +11,13 @@ func NewDefaultStorage(c *DefaultStorageConfig) (s *DefaultStorage) {
// other package.
const StoragePrefix = "filters/storage"
-// newSafeSearchFilter returns an initialized safe search filter. If
-// safeSearchURL is nil, then the filter is nil. Otherwise, c must not be nil,
-// and both fltID and cacheID should not be empty.
-func newSafeSearchFilter(
- c *DefaultStorageConfig,
- safeSearchURL *url.URL,
- fltID agd.FilterListID,
-) (f *safesearch.Filter, err error) {
- if safeSearchURL == nil {
- return nil, nil
- }
+// Storage is the interface for filter storages that can build a filter based
+// on a configuration.
+type Storage interface {
+ // ForConfig returns a filter created from the configuration. If c is nil,
+ // f is [filter.Empty].
+ ForConfig(ctx context.Context, c Config) (f Interface)
- fltIDStr := string(fltID)
- cacheID := path.Join(cachePrefixSafeSearch, fltIDStr)
- cache := rulelist.NewManagedResultCache(c.CacheManager, cacheID, c.SafeSearchCacheSize, true)
-
- return safesearch.New(
- &safesearch.Config{
- Refreshable: &internal.RefreshableConfig{
- Logger: c.BaseLogger.With(slogutil.KeyPrefix, cacheID),
- URL: safeSearchURL,
- ID: fltID,
- CachePath: filepath.Join(c.CacheDir, fltIDStr),
- Staleness: c.RefreshIvl,
- Timeout: c.RuleListRefreshTimeout,
- MaxSize: c.MaxRuleListSize,
- },
- CacheTTL: c.SafeSearchCacheTTL,
- },
- cache,
- )
-}
-
-// type check
-var _ Storage = (*DefaultStorage)(nil)
-
-// FilterFromContext implements the Storage interface for *DefaultStorage.
-func (s *DefaultStorage) FilterFromContext(ctx context.Context, ri *agd.RequestInfo) (f Interface) {
- if p, d := ri.DeviceData(); p != nil {
- return s.filterForProfile(ctx, p, d)
- }
-
- c := &composite.Config{}
-
- g := ri.FilteringGroup
- if g.RuleListsEnabled {
- c.RuleLists = s.filters(g.RuleListIDs)
- }
-
- c.SafeBrowsing, c.AdultBlocking, c.NewRegisteredDomains = s.safeBrowsingForGroup(g)
- c.GeneralSafeSearch, c.YouTubeSafeSearch = s.safeSearchForGroup(g)
-
- return composite.New(c)
-}
-
-// filterForProfile returns a composite filter for profile. All arguments must
-// not be nil.
-func (s *DefaultStorage) filterForProfile(
- ctx context.Context,
- prof *agd.Profile,
- dev *agd.Device,
-) (f Interface) {
- if !prof.FilteringEnabled {
- // According to the current requirements, this means that the profile
- // should receive no filtering at all.
- return composite.New(nil)
- }
-
- if !dev.FilteringEnabled {
- // According to the current requirements, this means that the device
- // should receive no filtering at all.
- return composite.New(nil)
- }
-
- // Assume that if we have a profile then we also have a device.
-
- c := &composite.Config{}
- c.RuleLists = s.filters(prof.RuleListIDs)
- c.Custom = s.customFilters.Get(ctx, prof)
-
- pp := prof.Parental
- parentalEnabled := pp != nil && pp.Enabled && s.pcBySchedule(pp.Schedule)
-
- c.ServiceLists = s.serviceFilters(ctx, prof, parentalEnabled)
-
- c.SafeBrowsing, c.AdultBlocking, c.NewRegisteredDomains = s.safeBrowsingForProfile(
- prof,
- parentalEnabled,
- )
- c.GeneralSafeSearch, c.YouTubeSafeSearch = s.safeSearchForProfile(prof, parentalEnabled)
-
- return composite.New(c)
-}
-
-// serviceFilters returns the blocked service rule lists for the profile.
-func (s *DefaultStorage) serviceFilters(
- ctx context.Context,
- p *agd.Profile,
- parentalEnabled bool,
-) (rls []*rulelist.Immutable) {
- if !parentalEnabled || len(p.Parental.BlockedServices) == 0 || s.services == nil {
- return nil
- }
-
- return s.services.RuleLists(ctx, p.Parental.BlockedServices)
-}
-
-// pcBySchedule returns true if the profile's schedule allows parental control
-// filtering at the moment.
-func (s *DefaultStorage) pcBySchedule(sch *agd.ParentalProtectionSchedule) (ok bool) {
- if sch == nil {
- // No schedule, so always filter.
- return true
- }
-
- return !sch.Contains(s.now())
-}
-
-// safeBrowsingForProfile returns safe browsing filters based on the information
-// in the profile. p and p.Parental must not be nil.
-func (s *DefaultStorage) safeBrowsingForProfile(
- p *agd.Profile,
- parentalEnabled bool,
-) (safeBrowsing, adultBlocking, newRegDomains *hashprefix.Filter) {
- if p.SafeBrowsing != nil && p.SafeBrowsing.Enabled {
- if p.SafeBrowsing.BlockDangerousDomains {
- safeBrowsing = s.safeBrowsing
- }
-
- if p.SafeBrowsing.BlockNewlyRegisteredDomains {
- newRegDomains = s.newRegDomains
- }
- }
-
- if parentalEnabled && p.Parental.BlockAdult {
- adultBlocking = s.adultBlocking
- }
-
- return safeBrowsing, adultBlocking, newRegDomains
-}
-
-// safeSearchForProfile returns safe search filters based on the information in
-// the profile. p and p.Parental must not be nil.
-func (s *DefaultStorage) safeSearchForProfile(
- p *agd.Profile,
- parentalEnabled bool,
-) (gen, yt *safesearch.Filter) {
- if !parentalEnabled {
- return nil, nil
- }
-
- if p.Parental.GeneralSafeSearch {
- gen = s.genSafeSearch
- }
-
- if p.Parental.YoutubeSafeSearch {
- yt = s.ytSafeSearch
- }
-
- return gen, yt
-}
-
-// safeBrowsingForGroup returns safe browsing filters based on the information
-// in the filtering group. g must not be nil.
-func (s *DefaultStorage) safeBrowsingForGroup(
- g *agd.FilteringGroup,
-) (safeBrowsing, adultBlocking, newRegDomains *hashprefix.Filter) {
- if g.SafeBrowsingEnabled {
- if g.BlockDangerousDomains {
- safeBrowsing = s.safeBrowsing
- }
-
- if g.BlockNewlyRegisteredDomains {
- newRegDomains = s.newRegDomains
- }
- }
-
- if g.ParentalEnabled && g.BlockAdult {
- adultBlocking = s.adultBlocking
- }
-
- return safeBrowsing, adultBlocking, newRegDomains
-}
-
-// safeSearchForGroup returns safe search filters based on the information in
-// the filtering group. g must not be nil.
-func (s *DefaultStorage) safeSearchForGroup(g *agd.FilteringGroup) (gen, yt *safesearch.Filter) {
- if !g.ParentalEnabled {
- return nil, nil
- }
-
- if g.GeneralSafeSearch {
- gen = s.genSafeSearch
- }
-
- if g.YoutubeSafeSearch {
- yt = s.ytSafeSearch
- }
-
- return gen, yt
-}
-
-// filters returns all rule list filters with the given filtering rule list IDs.
-func (s *DefaultStorage) filters(ids []agd.FilterListID) (rls []*rulelist.Refreshable) {
- if len(ids) == 0 {
- return nil
- }
-
- s.mu.RLock()
- defer s.mu.RUnlock()
-
- for _, id := range ids {
- rl := s.ruleLists[id]
- if rl != nil {
- rls = append(rls, rl)
- }
- }
-
- return rls
-}
-
-// HasListID implements the Storage interface for *DefaultStorage.
-func (s *DefaultStorage) HasListID(id agd.FilterListID) (ok bool) {
- s.mu.RLock()
- defer s.mu.RUnlock()
-
- _, ok = s.ruleLists[id]
-
- return ok
-}
-
-// type check
-var _ agdservice.Refresher = (*DefaultStorage)(nil)
-
-// Refresh implements the [agdservice.Refresher] interface for *DefaultStorage.
-func (s *DefaultStorage) Refresh(ctx context.Context) (err error) {
- s.logger.InfoContext(ctx, "refresh started")
- defer s.logger.InfoContext(ctx, "refresh finished")
-
- err = s.refresh(ctx, false)
- if err != nil {
- errcoll.Collect(ctx, s.errColl, s.logger, "refresh", enrichFromContext(ctx, err))
- }
-
- return err
-}
-
-// RefreshInitial loads the content of the storage, using cached files if any,
-// regardless of their staleness.
-func (s *DefaultStorage) RefreshInitial(ctx context.Context) (err error) {
- s.logger.InfoContext(ctx, "initial refresh started")
- defer s.logger.InfoContext(ctx, "initial refresh finished")
-
- err = s.refresh(ctx, true)
- if err != nil {
- return fmt.Errorf("refreshing filter storage initially: %w", err)
- }
-
- return nil
-}
-
-// enrichFromContext adds information from ctx to origErr if it can assume that
-// origErr is caused by ctx being canceled.
-func enrichFromContext(ctx context.Context, origErr error) (err error) {
- if ctx.Err() == nil {
- return origErr
- }
-
- // Assume that a deadline is always present here in non-test code.
- dl, _ := ctx.Deadline()
-
- // Strip monotonic-clock values.
- dl = dl.Truncate(0)
-
- return fmt.Errorf("storage refresh with deadline at %s: %w", dl, origErr)
-}
-
-// refresh refreshes the index from the index URL and updates all rule list
-// filters, as well as the service filters. If acceptStale is true, the cache
-// files are used regardless of their staleness.
-func (s *DefaultStorage) refresh(ctx context.Context, acceptStale bool) (err error) {
- resp, err := s.loadIndex(ctx, acceptStale)
- if err != nil {
- // Don't wrap the error, because it's informative enough as is.
- return err
- }
-
- s.logger.InfoContext(ctx, "loaded index", "num_filters", len(resp.Filters))
-
- fls := resp.toInternal(ctx, s.logger, s.errColl)
-
- s.logger.InfoContext(ctx, "validated lists", "num_lists", len(fls))
-
- ruleLists := make(filteringRuleLists, len(resp.Filters))
- for _, fl := range fls {
- s.addRuleList(ctx, ruleLists, fl, acceptStale)
-
- if ctxErr := ctx.Err(); ctxErr != nil {
- // If the context has already been canceled, no need to continue, as
- // the other refreshes won't be able to finish either way.
- s.logger.ErrorContext(ctx, "after refreshing lists", slogutil.KeyError, ctxErr)
-
- return fmt.Errorf("after refreshing rule lists: %w", ctxErr)
- }
- }
-
- s.logger.InfoContext(ctx, "compiled lists", "num_lists", len(ruleLists))
-
- if s.services != nil {
- err = s.services.Refresh(
- ctx,
- s.cacheManager,
- s.ruleListCacheSize,
- s.useRuleListCache,
- acceptStale,
- )
- }
- if err != nil {
- return fmt.Errorf("refreshing blocked services: %w", err)
- }
-
- if s.genSafeSearch != nil {
- err = s.genSafeSearch.Refresh(ctx, acceptStale)
- }
- if err != nil {
- return fmt.Errorf("refreshing general safe search: %w", err)
- }
-
- if s.ytSafeSearch != nil {
- err = s.ytSafeSearch.Refresh(ctx, acceptStale)
- }
- if err != nil {
- return fmt.Errorf("refreshing youtube safe search: %w", err)
- }
-
- s.setRuleLists(ruleLists)
-
- return nil
-}
-
-// addRuleList adds the data from fl to ruleLists and handles all validations
-// and errors. It also adds the cache with [filterIndexFilterData.id] to the
-// cache manager.
-func (s *DefaultStorage) addRuleList(
- ctx context.Context,
- ruleLists filteringRuleLists,
- fl *indexData,
- acceptStale bool,
-) {
- if _, ok := ruleLists[fl.id]; ok {
- err := fmt.Errorf("duplicated rule-list id %q", fl.id)
- errcoll.Collect(ctx, s.errColl, s.logger, "adding rule list", err)
-
- return
- }
-
- fltIDStr := string(fl.id)
- cacheID := path.Join(cachePrefixRuleList, fltIDStr)
- cache := rulelist.NewManagedResultCache(
- s.cacheManager,
- cacheID,
- s.ruleListCacheSize,
- s.useRuleListCache,
- )
-
- rl, err := rulelist.NewRefreshable(
- &internal.RefreshableConfig{
- Logger: s.baseLogger.With(slogutil.KeyPrefix, cacheID),
- URL: fl.url,
- ID: fl.id,
- CachePath: filepath.Join(s.cacheDir, fltIDStr),
- Staleness: s.refreshIvl,
- Timeout: s.ruleListRefreshTimeout,
- MaxSize: s.maxRuleListSize,
- },
- cache,
- )
- if err != nil {
- s.reportRuleListError(ctx, ruleLists, fl, fmt.Errorf("creating rulelist: %w", err))
-
- return
- }
-
- err = rl.Refresh(ctx, acceptStale)
- if err != nil {
- s.reportRuleListError(ctx, ruleLists, fl, fmt.Errorf("refreshing rulelist: %w", err))
-
- return
- }
-
- ruleLists[fl.id] = rl
-
- metrics.FilterUpdatedStatus.WithLabelValues(fltIDStr).Set(1)
- metrics.FilterUpdatedTime.WithLabelValues(fltIDStr).SetToCurrentTime()
- metrics.FilterRulesTotal.WithLabelValues(fltIDStr).Set(float64(rl.RulesCount()))
-}
-
-// reportRuleListError reports the error encountered when refreshing a rule-list
-// filter and makes sure that the previous version of the filter is used, if
-// there is one.
-func (s *DefaultStorage) reportRuleListError(
- ctx context.Context,
- ruleLists filteringRuleLists,
- fl *indexData,
- err error,
-) {
- errcoll.Collect(ctx, s.errColl, s.logger, "rule-list error", err)
- metrics.FilterUpdatedStatus.WithLabelValues(string(fl.id)).Set(0)
-
- // If we can't get the new filter, and there is an old version of the same
- // rule list, use it.
- rls := s.filters([]agd.FilterListID{fl.id})
- if len(rls) > 0 {
- ruleLists[fl.id] = rls[0]
- }
-}
-
-// loadIndex fetches, decodes, and returns the filter list index data of the
-// storage. resp.Filters are sorted.
-func (s *DefaultStorage) loadIndex(
- ctx context.Context,
- acceptStale bool,
-) (resp *indexResp, err error) {
- text, err := s.refr.Refresh(ctx, acceptStale)
- if err != nil {
- return nil, fmt.Errorf("loading index: %w", err)
- }
-
- resp = &indexResp{}
- err = json.NewDecoder(strings.NewReader(text)).Decode(resp)
- if err != nil {
- return nil, fmt.Errorf("decoding: %w", err)
- }
-
- slices.SortStableFunc(resp.Filters, (*indexRespFilter).compare)
-
- return resp, nil
-}
-
-// setRuleLists replaces the storage's rule lists.
-func (s *DefaultStorage) setRuleLists(ruleLists filteringRuleLists) {
- s.mu.Lock()
- defer s.mu.Unlock()
-
- s.ruleLists = ruleLists
+ // HasListID returns true if id is known to the storage.
+ HasListID(id ID) (ok bool)
}
diff --git a/internal/filter/storage_test.go b/internal/filter/storage_test.go
deleted file mode 100644
index 9423530..0000000
--- a/internal/filter/storage_test.go
+++ /dev/null
@@ -1,902 +0,0 @@
-package filter_test
-
-import (
- "context"
- "net/http"
- "testing"
- "time"
-
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
- "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/hashprefix"
- "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/filtertest"
- "github.com/AdguardTeam/golibs/logutil/slogutil"
- "github.com/AdguardTeam/golibs/netutil"
- "github.com/AdguardTeam/golibs/testutil"
- "github.com/miekg/dns"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-// testTimeout is the common timeout for tests and contexts.
-const testTimeout = 10 * time.Second
-
-// TODO(a.garipov): Refactor the common stages, such as storage initialization,
-// into a single method.
-
-func TestStorage_FilterFromContext(t *testing.T) {
- c := prepareConf(t)
- c.ErrColl = agdtest.NewErrorCollector()
-
- s := filter.NewDefaultStorage(c)
-
- ctx := testutil.ContextWithTimeout(t, testTimeout)
- require.NoError(t, s.RefreshInitial(ctx))
-
- p := &agd.Profile{
- ID: "prof1234",
- RuleListIDs: []agd.FilterListID{
- testFilterID,
- },
- CustomRules: []agd.FilterRuleText{
- customRule,
- },
- FilteringEnabled: true,
- }
-
- g := &agd.FilteringGroup{
- ID: "default",
- RuleListIDs: []agd.FilterListID{testFilterID},
- RuleListsEnabled: true,
- }
-
- t.Run("filter_list", func(t *testing.T) {
- req := &dns.Msg{
- Question: []dns.Question{{
- Name: blockedFQDN,
- Qtype: dns.TypeA,
- Qclass: dns.ClassINET,
- }},
- }
-
- ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
- ri := newReqInfo(t, g, p, blockedHost, clientIP, dns.TypeA)
- ctx = agd.ContextWithRequestInfo(ctx, ri)
-
- f := s.FilterFromContext(ctx, ri)
- require.NotNil(t, f)
-
- r, err := f.FilterRequest(ctx, req, ri)
- require.NoError(t, err)
-
- rb := testutil.RequireTypeAssert[*filter.ResultBlocked](t, r)
-
- assert.Contains(t, rb.Rule, blockedHost)
- assert.Equal(t, rb.List, testFilterID)
- })
-
- t.Run("custom", func(t *testing.T) {
- req := &dns.Msg{
- Question: []dns.Question{{
- Name: customFQDN,
- Qtype: dns.TypeA,
- Qclass: dns.ClassINET,
- }},
- }
-
- ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
- ri := newReqInfo(t, g, p, customHost, clientIP, dns.TypeA)
- ctx = agd.ContextWithRequestInfo(ctx, ri)
-
- f := s.FilterFromContext(ctx, ri)
- require.NotNil(t, f)
-
- r, err := f.FilterRequest(ctx, req, ri)
- require.NoError(t, err)
-
- rb := testutil.RequireTypeAssert[*filter.ResultBlocked](t, r)
-
- assert.Contains(t, rb.Rule, customHost)
- assert.Equal(t, rb.List, agd.FilterListIDCustom)
- })
-
- t.Run("unknown_profile", func(t *testing.T) {
- req := &dns.Msg{
- Question: []dns.Question{{
- Name: customFQDN,
- Qtype: dns.TypeA,
- Qclass: dns.ClassINET,
- }},
- }
-
- ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
- ri := newReqInfo(t, g, &agd.Profile{}, customHost, clientIP, dns.TypeA)
- ctx = agd.ContextWithRequestInfo(ctx, ri)
-
- f := s.FilterFromContext(ctx, ri)
- require.NotNil(t, f)
-
- r, err := f.FilterRequest(ctx, req, ri)
- require.NoError(t, err)
-
- assert.Nil(t, r)
- })
-}
-
-func TestStorage_FilterFromContext_customAllow(t *testing.T) {
- errColl := agdtest.NewErrorCollector()
- cachePath, srvURL := filtertest.PrepareRefreshable(t, nil, safeBrowsingHost+"\n", http.StatusOK)
- hashes, err := hashprefix.NewStorage(safeBrowsingHost)
- require.NoError(t, err)
-
- c := prepareConf(t)
-
- safeBrowsing, err := hashprefix.NewFilter(&hashprefix.FilterConfig{
- Logger: slogutil.NewDiscardLogger(),
- Cloner: agdtest.NewCloner(),
- CacheManager: agdcache.EmptyManager{},
- Hashes: hashes,
- URL: srvURL,
- ErrColl: errColl,
- ID: agd.FilterListIDSafeBrowsing,
- CachePath: cachePath,
- ReplacementHost: safeBrowsingReplHost,
- Staleness: filtertest.Staleness,
- CacheTTL: filtertest.CacheTTL,
- CacheSize: 100,
- MaxSize: filtertest.FilterMaxSize,
- })
- require.NoError(t, err)
-
- ctx := testutil.ContextWithTimeout(t, testTimeout)
- require.NoError(t, safeBrowsing.RefreshInitial(ctx))
-
- c.ErrColl = errColl
- c.SafeBrowsing = safeBrowsing
-
- s := filter.NewDefaultStorage(c)
- require.NoError(t, s.RefreshInitial(ctx))
-
- const safeBrowsingAllowRule = "@@||" + safeBrowsingHost + "^"
- p := &agd.Profile{
- Parental: &agd.ParentalProtectionSettings{
- Enabled: true,
- },
- ID: "prof1234",
- FilteringEnabled: true,
- SafeBrowsing: &agd.SafeBrowsingSettings{
- Enabled: true,
- BlockDangerousDomains: true,
- BlockNewlyRegisteredDomains: false,
- },
- CustomRules: []agd.FilterRuleText{
- safeBrowsingAllowRule,
- },
- }
-
- g := &agd.FilteringGroup{
- ID: "default",
- RuleListIDs: []agd.FilterListID{},
- }
-
- req := &dns.Msg{
- Question: []dns.Question{{
- Name: safeBrowsingSubFQDN,
- Qtype: dns.TypeA,
- Qclass: dns.ClassINET,
- }},
- }
-
- ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
- ri := newReqInfo(t, g, p, safeBrowsingSubHost, clientIP, dns.TypeA)
- ctx = agd.ContextWithRequestInfo(ctx, ri)
-
- f := s.FilterFromContext(ctx, ri)
- require.NotNil(t, f)
-
- r, err := f.FilterRequest(ctx, req, ri)
- require.NoError(t, err)
-
- ra := testutil.RequireTypeAssert[*filter.ResultAllowed](t, r)
-
- assert.Equal(t, ra.Rule, agd.FilterRuleText(safeBrowsingAllowRule))
- assert.Equal(t, ra.List, agd.FilterListIDCustom)
-}
-
-func TestStorage_FilterFromContext_schedule(t *testing.T) {
- // The current time is 12:00:00, while the schedule allows disabling the
- // parental protection from 11:00:00 until 12:59:59.
- nowTime := time.Date(2021, 1, 1, 12, 0, 0, 0, time.UTC)
-
- errColl := agdtest.NewErrorCollector()
- cachePath, srvURL := filtertest.PrepareRefreshable(t, nil, safeBrowsingHost+"\n", http.StatusOK)
- hashes, err := hashprefix.NewStorage(safeBrowsingHost)
- require.NoError(t, err)
-
- c := prepareConf(t)
-
- // Use AdultBlocking, because SafeBrowsing is NOT affected by the schedule.
- adultBlocking, err := hashprefix.NewFilter(&hashprefix.FilterConfig{
- Logger: slogutil.NewDiscardLogger(),
- Cloner: agdtest.NewCloner(),
- CacheManager: agdcache.EmptyManager{},
- Hashes: hashes,
- URL: srvURL,
- ErrColl: errColl,
- ID: agd.FilterListIDAdultBlocking,
- CachePath: cachePath,
- ReplacementHost: filtertest.SafeBrowsingReplIPv4Str,
- Staleness: filtertest.Staleness,
- CacheTTL: filtertest.CacheTTL,
- CacheSize: 100,
- MaxSize: filtertest.FilterMaxSize,
- })
- require.NoError(t, err)
-
- ctx := testutil.ContextWithTimeout(t, testTimeout)
- require.NoError(t, adultBlocking.RefreshInitial(ctx))
-
- c.Now = func() (t time.Time) {
- return nowTime
- }
- c.ErrColl = errColl
- c.AdultBlocking = adultBlocking
-
- s := filter.NewDefaultStorage(c)
- require.NoError(t, s.RefreshInitial(ctx))
-
- // Set up our profile with the schedule that disables filtering at the
- // current moment.
- sch := &agd.ParentalProtectionSchedule{
- TimeZone: agdtime.UTC(),
- Week: &agd.WeeklySchedule{
- time.Sunday: agd.ZeroLengthDayRange(),
- time.Monday: agd.ZeroLengthDayRange(),
- time.Tuesday: agd.ZeroLengthDayRange(),
- time.Wednesday: agd.ZeroLengthDayRange(),
- time.Thursday: agd.ZeroLengthDayRange(),
-
- // nowTime is on Friday.
- time.Friday: agd.DayRange{
- Start: 11 * 60,
- End: 12 * 60,
- },
-
- time.Saturday: agd.ZeroLengthDayRange(),
- },
- }
-
- p := &agd.Profile{
- Parental: &agd.ParentalProtectionSettings{
- Schedule: sch,
- Enabled: true,
- BlockAdult: true,
- },
- ID: "prof1234",
- FilteringEnabled: true,
- }
-
- g := &agd.FilteringGroup{
- ID: "default",
- RuleListIDs: []agd.FilterListID{},
- }
-
- req := &dns.Msg{
- Question: []dns.Question{{
- Name: safeBrowsingSubFQDN,
- Qtype: dns.TypeA,
- Qclass: dns.ClassINET,
- }},
- }
-
- ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
- ri := newReqInfo(t, g, p, safeBrowsingSubHost, clientIP, dns.TypeA)
- ctx = agd.ContextWithRequestInfo(ctx, ri)
-
- // The adult blocking filter should not be triggered, since we're within the
- // schedule.
- f := s.FilterFromContext(ctx, ri)
- require.NotNil(t, f)
-
- r, err := f.FilterRequest(ctx, req, ri)
- require.NoError(t, err)
-
- assert.Nil(t, r)
-
- // Change the schedule and try again.
- sch.Week[int(time.Friday)].End = 11 * 60
-
- f = s.FilterFromContext(ctx, ri)
- require.NotNil(t, f)
-
- r, err = f.FilterRequest(ctx, req, ri)
- require.NoError(t, err)
-
- rm := testutil.RequireTypeAssert[*filter.ResultModifiedResponse](t, r)
-
- assert.Equal(t, rm.Rule, agd.FilterRuleText(safeBrowsingHost))
- assert.Equal(t, rm.List, agd.FilterListIDAdultBlocking)
-
- ans := testutil.RequireTypeAssert[*dns.A](t, rm.Msg.Answer[0])
-
- ansIP, err := netutil.IPToAddr(ans.A, netutil.AddrFamilyIPv4)
- assert.NoError(t, err)
- assert.Equal(t, filtertest.SafeBrowsingReplIPv4, ansIP)
-}
-
-func TestStorage_FilterFromContext_ruleList_request(t *testing.T) {
- c := prepareConf(t)
- c.ErrColl = agdtest.NewErrorCollector()
-
- s := filter.NewDefaultStorage(c)
-
- ctx := testutil.ContextWithTimeout(t, testTimeout)
- require.NoError(t, s.RefreshInitial(ctx))
-
- g := &agd.FilteringGroup{
- ID: "default",
- RuleListIDs: []agd.FilterListID{testFilterID},
- RuleListsEnabled: true,
- }
-
- t.Run("blocked", func(t *testing.T) {
- req := &dns.Msg{
- Question: []dns.Question{{
- Name: blockedFQDN,
- Qtype: dns.TypeA,
- Qclass: dns.ClassINET,
- }},
- }
-
- ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
- ri := newReqInfo(t, g, nil, blockedHost, clientIP, dns.TypeA)
- ctx = agd.ContextWithRequestInfo(ctx, ri)
-
- f := s.FilterFromContext(ctx, ri)
- require.NotNil(t, f)
-
- r, err := f.FilterRequest(ctx, req, ri)
- require.NoError(t, err)
-
- rb := testutil.RequireTypeAssert[*filter.ResultBlocked](t, r)
-
- assert.Contains(t, rb.Rule, blockedHost)
- assert.Equal(t, rb.List, testFilterID)
- })
-
- t.Run("allowed", func(t *testing.T) {
- req := &dns.Msg{
- Question: []dns.Question{{
- Name: allowedFQDN,
- Qtype: dns.TypeA,
- Qclass: dns.ClassINET,
- }},
- }
-
- ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
- ri := newReqInfo(t, g, nil, allowedHost, clientIP, dns.TypeA)
- ctx = agd.ContextWithRequestInfo(ctx, ri)
-
- f := s.FilterFromContext(ctx, ri)
- require.NotNil(t, f)
-
- r, err := f.FilterRequest(ctx, req, ri)
- require.NoError(t, err)
-
- ra := testutil.RequireTypeAssert[*filter.ResultAllowed](t, r)
-
- assert.Contains(t, ra.Rule, allowedHost)
- assert.Equal(t, ra.List, testFilterID)
- })
-
- t.Run("blocked_client", func(t *testing.T) {
- req := &dns.Msg{
- Question: []dns.Question{{
- Name: blockedClientFQDN,
- Qtype: dns.TypeA,
- Qclass: dns.ClassINET,
- }},
- }
-
- ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
- ri := newReqInfo(t, g, nil, blockedClientHost, clientIP, dns.TypeA)
- ctx = agd.ContextWithRequestInfo(ctx, ri)
-
- f := s.FilterFromContext(ctx, ri)
- require.NotNil(t, f)
-
- r, err := f.FilterRequest(ctx, req, ri)
- require.NoError(t, err)
-
- rb := testutil.RequireTypeAssert[*filter.ResultBlocked](t, r)
-
- assert.Contains(t, rb.Rule, blockedClientHost)
- assert.Equal(t, rb.List, testFilterID)
- })
-
- t.Run("allowed_client", func(t *testing.T) {
- req := &dns.Msg{
- Question: []dns.Question{{
- Name: allowedClientFQDN,
- Qtype: dns.TypeA,
- Qclass: dns.ClassINET,
- }},
- }
-
- ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
- ri := newReqInfo(t, g, nil, allowedClientHost, clientIP, dns.TypeA)
- ctx = agd.ContextWithRequestInfo(ctx, ri)
-
- f := s.FilterFromContext(ctx, ri)
- require.NotNil(t, f)
-
- r, err := f.FilterRequest(ctx, req, ri)
- require.NoError(t, err)
-
- ra := testutil.RequireTypeAssert[*filter.ResultAllowed](t, r)
-
- assert.Contains(t, ra.Rule, allowedClientHost)
- assert.Equal(t, ra.List, testFilterID)
- })
-
- t.Run("none", func(t *testing.T) {
- req := &dns.Msg{
- Question: []dns.Question{{
- Name: otherNetFQDN,
- Qtype: dns.TypeA,
- Qclass: dns.ClassINET,
- }},
- }
-
- ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
- ri := newReqInfo(t, g, nil, otherNetHost, clientIP, dns.TypeA)
- ctx = agd.ContextWithRequestInfo(ctx, ri)
-
- f := s.FilterFromContext(ctx, ri)
- require.NotNil(t, f)
-
- r, err := f.FilterRequest(ctx, req, ri)
- require.NoError(t, err)
-
- assert.Nil(t, r)
- })
-}
-
-func TestStorage_FilterFromContext_customDevice(t *testing.T) {
- c := prepareConf(t)
- c.ErrColl = agdtest.NewErrorCollector()
-
- s := filter.NewDefaultStorage(c)
-
- ctx := testutil.ContextWithTimeout(t, testTimeout)
- require.NoError(t, s.RefreshInitial(ctx))
-
- g := &agd.FilteringGroup{}
- p := &agd.Profile{
- CustomRules: []agd.FilterRuleText{
- `||blocked-device.example.com^$client="My Device"`,
- `@@||allowed-device.example.com^$client="My Device"`,
- },
- FilteringEnabled: true,
- RuleListsEnabled: true,
- }
-
- t.Run("blocked_device", func(t *testing.T) {
- req := &dns.Msg{
- Question: []dns.Question{{
- Name: blockedDeviceFQDN,
- Qtype: dns.TypeA,
- Qclass: dns.ClassINET,
- }},
- }
-
- ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
- ri := newReqInfo(t, g, p, blockedDeviceHost, deviceIP, dns.TypeA)
- ctx = agd.ContextWithRequestInfo(ctx, ri)
-
- f := s.FilterFromContext(ctx, ri)
- require.NotNil(t, f)
-
- r, err := f.FilterRequest(ctx, req, ri)
- require.NoError(t, err)
-
- rb := testutil.RequireTypeAssert[*filter.ResultBlocked](t, r)
-
- assert.Contains(t, rb.Rule, blockedDeviceHost)
- assert.Equal(t, rb.List, agd.FilterListIDCustom)
- })
-
- t.Run("allowed_device", func(t *testing.T) {
- req := &dns.Msg{
- Question: []dns.Question{{
- Name: allowedDeviceFQDN,
- Qtype: dns.TypeA,
- Qclass: dns.ClassINET,
- }},
- }
-
- ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
- ri := newReqInfo(t, g, p, allowedDeviceHost, deviceIP, dns.TypeA)
- ctx = agd.ContextWithRequestInfo(ctx, ri)
-
- f := s.FilterFromContext(ctx, ri)
- require.NotNil(t, f)
-
- r, err := f.FilterRequest(ctx, req, ri)
- require.NoError(t, err)
-
- ra := testutil.RequireTypeAssert[*filter.ResultAllowed](t, r)
-
- assert.Contains(t, ra.Rule, allowedDeviceHost)
- assert.Equal(t, ra.List, agd.FilterListIDCustom)
- })
-}
-
-func TestStorage_FilterFromContext_ruleList_response(t *testing.T) {
- c := prepareConf(t)
- c.ErrColl = agdtest.NewErrorCollector()
-
- s := filter.NewDefaultStorage(c)
-
- ctx := testutil.ContextWithTimeout(t, testTimeout)
- require.NoError(t, s.RefreshInitial(ctx))
-
- g := &agd.FilteringGroup{
- ID: "default",
- RuleListIDs: []agd.FilterListID{testFilterID},
- RuleListsEnabled: true,
- }
-
- ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
- ri := newReqInfo(t, g, nil, otherNetHost, clientIP, dns.TypeA)
- ctx = agd.ContextWithRequestInfo(ctx, ri)
-
- f := s.FilterFromContext(ctx, ri)
- require.NotNil(t, f)
-
- question := []dns.Question{{
- Name: otherNetFQDN,
- Qtype: dns.TypeA,
- Qclass: dns.ClassINET,
- }}
-
- t.Run("blocked_a", func(t *testing.T) {
- resp := &dns.Msg{
- Question: question,
- Answer: []dns.RR{&dns.A{
- A: blockedIP4,
- }},
- }
-
- r, err := f.FilterResponse(ctx, resp, ri)
- require.NoError(t, err)
-
- rb := testutil.RequireTypeAssert[*filter.ResultBlocked](t, r)
-
- assert.Contains(t, rb.Rule, blockedIP4.String())
- assert.Equal(t, rb.List, testFilterID)
- })
-
- t.Run("allowed_a", func(t *testing.T) {
- resp := &dns.Msg{
- Question: question,
- Answer: []dns.RR{&dns.A{
- A: allowedIP4,
- }},
- }
-
- r, err := f.FilterResponse(ctx, resp, ri)
- require.NoError(t, err)
-
- ra := testutil.RequireTypeAssert[*filter.ResultAllowed](t, r)
-
- assert.Contains(t, ra.Rule, allowedIP4.String())
- assert.Equal(t, ra.List, testFilterID)
- })
-
- t.Run("blocked_cname", func(t *testing.T) {
- resp := &dns.Msg{
- Question: question,
- Answer: []dns.RR{&dns.CNAME{
- Target: blockedFQDN,
- }},
- }
-
- r, err := f.FilterResponse(ctx, resp, ri)
- require.NoError(t, err)
-
- rb := testutil.RequireTypeAssert[*filter.ResultBlocked](t, r)
-
- assert.Contains(t, rb.Rule, blockedHost)
- assert.Equal(t, rb.List, testFilterID)
- })
-
- t.Run("allowed_cname", func(t *testing.T) {
- resp := &dns.Msg{
- Question: question,
- Answer: []dns.RR{&dns.CNAME{
- Target: allowedFQDN,
- }},
- }
-
- r, err := f.FilterResponse(ctx, resp, ri)
- require.NoError(t, err)
-
- ra := testutil.RequireTypeAssert[*filter.ResultAllowed](t, r)
-
- assert.Contains(t, ra.Rule, allowedHost)
- assert.Equal(t, ra.List, testFilterID)
- })
-
- t.Run("blocked_client", func(t *testing.T) {
- resp := &dns.Msg{
- Question: question,
- Answer: []dns.RR{&dns.CNAME{
- Target: blockedClientFQDN,
- }},
- }
-
- r, err := f.FilterResponse(ctx, resp, ri)
- require.NoError(t, err)
-
- rb := testutil.RequireTypeAssert[*filter.ResultBlocked](t, r)
-
- assert.Contains(t, rb.Rule, blockedClientHost)
- assert.Equal(t, rb.List, testFilterID)
- })
-
- t.Run("allowed_client", func(t *testing.T) {
- req := &dns.Msg{
- Question: question,
- Answer: []dns.RR{&dns.CNAME{
- Target: allowedClientFQDN,
- }},
- }
-
- r, err := f.FilterResponse(ctx, req, ri)
- require.NoError(t, err)
-
- ra := testutil.RequireTypeAssert[*filter.ResultAllowed](t, r)
-
- assert.Contains(t, ra.Rule, allowedClientHost)
- assert.Equal(t, ra.List, testFilterID)
- })
-
- t.Run("exception_cname", func(t *testing.T) {
- req := &dns.Msg{
- Question: question,
- Answer: []dns.RR{&dns.CNAME{
- Target: "cname.exception.",
- }},
- }
-
- r, err := f.FilterResponse(ctx, req, ri)
- require.NoError(t, err)
-
- assert.Nil(t, r)
- })
-
- t.Run("exception_cname_blocked", func(t *testing.T) {
- req := &dns.Msg{
- Question: question,
- Answer: []dns.RR{&dns.CNAME{
- Target: "cname.blocked.",
- }},
- }
-
- r, err := f.FilterResponse(ctx, req, ri)
- require.NoError(t, err)
-
- rb := testutil.RequireTypeAssert[*filter.ResultBlocked](t, r)
-
- assert.Contains(t, rb.Rule, "cname.blocked")
- assert.Equal(t, rb.List, testFilterID)
- })
-
- t.Run("none", func(t *testing.T) {
- req := &dns.Msg{
- Question: question,
- Answer: []dns.RR{&dns.CNAME{
- Target: otherOrgFQDN,
- }},
- }
-
- r, err := f.FilterRequest(ctx, req, ri)
- require.NoError(t, err)
-
- assert.Nil(t, r)
- })
-}
-
-func TestStorage_FilterFromContext_safeBrowsing(t *testing.T) {
- cachePath, srvURL := filtertest.PrepareRefreshable(t, nil, safeBrowsingHost+"\n", http.StatusOK)
- hashes, err := hashprefix.NewStorage("")
- require.NoError(t, err)
-
- errColl := agdtest.NewErrorCollector()
-
- ctx := testutil.ContextWithTimeout(t, testTimeout)
- safeBrowsing, err := hashprefix.NewFilter(&hashprefix.FilterConfig{
- Logger: slogutil.NewDiscardLogger(),
- Cloner: agdtest.NewCloner(),
- CacheManager: agdcache.EmptyManager{},
- Hashes: hashes,
- URL: srvURL,
- ErrColl: errColl,
- ID: agd.FilterListIDSafeBrowsing,
- CachePath: cachePath,
- ReplacementHost: safeBrowsingReplHost,
- Staleness: filtertest.Staleness,
- CacheTTL: filtertest.CacheTTL,
- CacheSize: 100,
- MaxSize: filtertest.FilterMaxSize,
- })
- require.NoError(t, err)
- require.NoError(t, safeBrowsing.RefreshInitial(ctx))
-
- c := prepareConf(t)
- c.ErrColl = errColl
- c.SafeBrowsing = safeBrowsing
-
- s := filter.NewDefaultStorage(c)
- require.NoError(t, s.RefreshInitial(ctx))
-
- g := &agd.FilteringGroup{
- ID: "default",
- RuleListIDs: []agd.FilterListID{},
- ParentalEnabled: true,
- SafeBrowsingEnabled: true,
- BlockDangerousDomains: true,
- BlockNewlyRegisteredDomains: true,
- }
-
- // Test
-
- req := &dns.Msg{
- Question: []dns.Question{{
- Name: safeBrowsingSubFQDN,
- Qtype: dns.TypeA,
- Qclass: dns.ClassINET,
- }},
- }
-
- ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
- ri := newReqInfo(t, g, nil, safeBrowsingSubHost, clientIP, dns.TypeA)
- ctx = agd.ContextWithRequestInfo(ctx, ri)
-
- f := s.FilterFromContext(ctx, ri)
- require.NotNil(t, f)
-
- r, err := f.FilterRequest(ctx, req, ri)
- require.NoError(t, err)
-
- rm := testutil.RequireTypeAssert[*filter.ResultModifiedRequest](t, r)
-
- assert.Equal(t, rm.Msg.Question[0].Name, safeBrowsingReplFQDN)
- assert.Equal(t, rm.Rule, agd.FilterRuleText(safeBrowsingHost))
- assert.Equal(t, rm.List, agd.FilterListIDSafeBrowsing)
-}
-
-func TestStorage_FilterFromContext_safeSearch(t *testing.T) {
- c := prepareConf(t)
-
- s := filter.NewDefaultStorage(c)
-
- ctx := testutil.ContextWithTimeout(t, testTimeout)
- require.NoError(t, s.RefreshInitial(ctx))
-
- g := &agd.FilteringGroup{
- ID: "default",
- ParentalEnabled: true,
- GeneralSafeSearch: true,
- }
-
- ttl := uint32(agdtest.FilteredResponseTTLSec)
-
- testCases := []struct {
- name string
- host string
- want []dns.RR
- rrtype uint16
- wantReq bool
- }{{
- want: []dns.RR{
- dnsservertest.NewA(safeSearchIPv4Host, ttl, safeSearchIPRespIP4),
- },
- name: "ip4",
- host: safeSearchIPv4Host,
- rrtype: dns.TypeA,
- wantReq: false,
- }, {
- want: []dns.RR{
- dnsservertest.NewAAAA(safeSearchIPv6Host, ttl, safeSearchIPRespIP6),
- },
- name: "ip6",
- host: safeSearchIPv6Host,
- rrtype: dns.TypeAAAA,
- wantReq: false,
- }, {
- want: nil,
- name: "host_ip4",
- host: safeSearchHost,
- rrtype: dns.TypeA,
- wantReq: true,
- }, {
- want: nil,
- name: "host_ip6",
- host: safeSearchHost,
- rrtype: dns.TypeAAAA,
- wantReq: true,
- }}
-
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- ctx = testutil.ContextWithTimeout(t, filtertest.Timeout)
- ri := newReqInfo(t, g, nil, tc.host, clientIP, tc.rrtype)
- ctx = agd.ContextWithRequestInfo(ctx, ri)
-
- f := s.FilterFromContext(ctx, ri)
- require.NotNil(t, f)
-
- req := dnsservertest.CreateMessage(tc.host, tc.rrtype)
-
- r, err := f.FilterRequest(ctx, req, ri)
- require.NoError(t, err)
- require.NotNil(t, r)
-
- id, rule := r.MatchedRule()
- assert.Contains(t, rule, tc.host)
- assert.Equal(t, id, agd.FilterListIDGeneralSafeSearch)
-
- var msg *dns.Msg
- if tc.wantReq {
- rm := testutil.RequireTypeAssert[*filter.ResultModifiedRequest](t, r)
-
- msg = rm.Msg
- } else {
- rm := testutil.RequireTypeAssert[*filter.ResultModifiedResponse](t, r)
-
- msg = rm.Msg
- }
-
- require.NotNil(t, msg)
-
- assert.Equal(t, tc.wantReq, !msg.Response)
- assert.Equal(t, tc.want, msg.Answer)
- })
- }
-}
-
-// Typed sinks for benchmarks.
-var (
- errSink error
-)
-
-func BenchmarkStorage_DefaultStorage_Initialize(b *testing.B) {
- c := prepareConf(b)
- c.ErrColl = agdtest.NewErrorCollector()
-
- s := filter.NewDefaultStorage(c)
- ctx := context.Background()
-
- b.ReportAllocs()
- b.ResetTimer()
-
- for range b.N {
- errSink = s.RefreshInitial(ctx)
- }
-
- assert.NoError(b, errSink)
-
- // Recent result on MBP 15:
- //
- // goos: darwin
- // goarch: amd64
- // pkg: github.com/AdguardTeam/AdGuardDNS/internal/filter
- // cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
- // BenchmarkStorage_DefaultStorage_Initialize-12 5301 221029 ns/op 260449 B/op 806 allocs/op
-}
diff --git a/internal/filter/testdata/filter b/internal/filter/testdata/filter
deleted file mode 100644
index f02e8d8..0000000
--- a/internal/filter/testdata/filter
+++ /dev/null
@@ -1,15 +0,0 @@
-# General Blocking
-||blocked.example.com^
-@@||allowed.example.com^
-#
-# Per-Client Blocking
-||blocked-client.example.com^$client=1.2.3.4
-@@||allowed-client.example.com^$client=1.2.3.4
-#
-# IP-level blocking.
-6.6.6.13
-@@7.7.7.42
-#
-# RRType blocking.
-||cname.exception^$dnstype=~CNAME
-||cname.blocked^$dnstype=~A
diff --git a/internal/filter/testdata/general_safe_search b/internal/filter/testdata/general_safe_search
deleted file mode 100644
index cbfa1ff..0000000
--- a/internal/filter/testdata/general_safe_search
+++ /dev/null
@@ -1,6 +0,0 @@
-# CNAME rewrite.
-||duckduckgo.com^$dnsrewrite=NOERROR;CNAME;safe.duckduckgo.com
-# IPv4 rewrite.
-||www.yandex.by^$dnsrewrite=NOERROR;A;213.180.193.56
-# IPv6 rewrite.
-||www.google.com^$dnsrewrite=NOERROR;AAAA;2001:4860:4802:32::78
diff --git a/internal/geoip/asntops.go b/internal/geoip/asntops.go
index 596ec23..4e0b562 100644
--- a/internal/geoip/asntops.go
+++ b/internal/geoip/asntops.go
@@ -6,17 +6,17 @@ import "github.com/AdguardTeam/golibs/container"
// DefaultTopASNs contains all specially handled ASNs.
var DefaultTopASNs = container.NewMapSet[ASN](
- 3,
137,
174,
209,
224,
- 376,
+ 513,
559,
577,
+ 680,
701,
- 714,
719,
+ 760,
766,
786,
803,
@@ -24,7 +24,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
852,
855,
906,
- 932,
967,
984,
1103,
@@ -45,7 +44,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
1835,
1836,
1853,
- 1886,
1897,
1930,
1955,
@@ -55,9 +53,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
2110,
2116,
2119,
- 2199,
2200,
- 2470,
2497,
2514,
2516,
@@ -72,8 +68,9 @@ var DefaultTopASNs = container.NewMapSet[ASN](
2611,
2614,
2740,
+ 2764,
+ 2843,
2847,
- 2850,
2852,
2856,
2860,
@@ -101,28 +98,26 @@ var DefaultTopASNs = container.NewMapSet[ASN](
3342,
3352,
3356,
- 3363,
3399,
3462,
3549,
3559,
3605,
3695,
- 3737,
3741,
3758,
3786,
3816,
3855,
4007,
+ 4049,
4134,
4181,
4230,
- 4515,
4538,
4609,
4638,
- 4651,
+ 4648,
4657,
4685,
4713,
@@ -150,20 +145,21 @@ var DefaultTopASNs = container.NewMapSet[ASN](
5089,
5377,
5378,
+ 5379,
5384,
- 5385,
5390,
5391,
- 5394,
5408,
5410,
5413,
5416,
5432,
5466,
+ 5470,
5479,
5483,
5518,
+ 5532,
5578,
5603,
5607,
@@ -185,9 +181,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
6327,
6400,
6407,
- 6412,
- 6429,
- 6453,
6535,
6568,
6639,
@@ -200,9 +193,9 @@ var DefaultTopASNs = container.NewMapSet[ASN](
6713,
6730,
6739,
+ 6747,
6752,
6758,
- 6769,
6772,
6799,
6802,
@@ -210,13 +203,13 @@ var DefaultTopASNs = container.NewMapSet[ASN](
6810,
6821,
6830,
+ 6843,
6848,
6849,
6855,
6866,
6871,
6876,
- 6877,
6939,
7018,
7029,
@@ -230,7 +223,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
7470,
7482,
7497,
- 7511,
7522,
7524,
7545,
@@ -241,7 +233,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
7727,
7738,
7794,
- 7862,
7922,
7979,
7992,
@@ -249,14 +240,16 @@ var DefaultTopASNs = container.NewMapSet[ASN](
8048,
8053,
8075,
- 8094,
8151,
8167,
8193,
8200,
8220,
+ 8248,
8251,
8257,
+ 8273,
+ 8285,
8290,
8301,
8339,
@@ -267,7 +260,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
8374,
8376,
8386,
- 8399,
8400,
8402,
8412,
@@ -285,7 +277,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
8544,
8551,
8560,
- 8562,
8585,
8612,
8632,
@@ -300,7 +291,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
8764,
8767,
8771,
- 8778,
+ 8772,
8781,
8814,
8818,
@@ -325,7 +316,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
9051,
9063,
9070,
- 9085,
9105,
9119,
9121,
@@ -337,6 +327,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
9155,
9158,
9198,
+ 9199,
9208,
9231,
9245,
@@ -345,6 +336,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
9260,
9269,
9299,
+ 9303,
9304,
9316,
9318,
@@ -356,7 +348,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
9365,
9381,
9416,
- 9431,
9443,
9471,
9484,
@@ -364,19 +355,17 @@ var DefaultTopASNs = container.NewMapSet[ASN](
9506,
9534,
9541,
- 9587,
9595,
9605,
9617,
9644,
9674,
- 9676,
9694,
9751,
- 9770,
9790,
9808,
9824,
+ 9825,
9829,
9845,
9873,
@@ -387,16 +376,16 @@ var DefaultTopASNs = container.NewMapSet[ASN](
9930,
9931,
9934,
+ 9946,
9976,
- 9981,
- 9988,
+ 9997,
10010,
10013,
10030,
10036,
- 10054,
10066,
10075,
+ 10076,
10094,
10099,
10101,
@@ -404,15 +393,17 @@ var DefaultTopASNs = container.NewMapSet[ASN](
10131,
10139,
10143,
+ 10214,
10219,
- 10222,
10226,
10269,
10292,
10299,
10396,
+ 10412,
10429,
10474,
+ 10617,
10620,
10796,
10834,
@@ -423,7 +414,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
11232,
11259,
11260,
- 11269,
11290,
11315,
11351,
@@ -433,19 +423,22 @@ var DefaultTopASNs = container.NewMapSet[ASN](
11492,
11556,
11562,
+ 11580,
11594,
11664,
+ 11721,
11776,
11814,
+ 11815,
11816,
11830,
11845,
11888,
+ 11992,
12046,
12066,
12083,
12091,
- 12150,
12252,
12271,
12297,
@@ -464,19 +457,18 @@ var DefaultTopASNs = container.NewMapSet[ASN](
12400,
12406,
12414,
- 12426,
12430,
12436,
12444,
12455,
12479,
12491,
- 12496,
+ 12508,
+ 12539,
12552,
- 12556,
- 12570,
12576,
12578,
+ 12597,
12605,
12668,
12684,
@@ -487,17 +479,17 @@ var DefaultTopASNs = container.NewMapSet[ASN](
12741,
12754,
12764,
+ 12796,
12810,
12829,
- 12835,
12849,
12874,
12876,
+ 12905,
12912,
12929,
12946,
12958,
- 12963,
12969,
12975,
12978,
@@ -518,22 +510,24 @@ var DefaultTopASNs = container.NewMapSet[ASN](
13188,
13189,
13194,
+ 13208,
+ 13213,
13280,
13285,
13306,
13335,
13489,
- 13682,
13771,
13999,
14061,
14080,
14117,
+ 14234,
14259,
14434,
14522,
- 14537,
14593,
+ 14618,
14638,
14709,
14754,
@@ -554,14 +548,12 @@ var DefaultTopASNs = container.NewMapSet[ASN](
15397,
15399,
15404,
- 15405,
- 15419,
+ 15425,
15433,
15435,
15440,
15457,
15474,
- 15480,
15502,
15511,
15516,
@@ -570,12 +562,15 @@ var DefaultTopASNs = container.NewMapSet[ASN](
15557,
15600,
15614,
+ 15623,
15659,
15704,
15706,
15723,
15735,
15751,
+ 15765,
+ 15766,
15774,
15796,
15802,
@@ -584,11 +579,13 @@ var DefaultTopASNs = container.NewMapSet[ASN](
15836,
15895,
15897,
+ 15899,
15924,
15943,
15958,
15962,
15964,
+ 15975,
15994,
16010,
16019,
@@ -597,7 +594,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
16082,
16086,
16116,
- 16117,
+ 16125,
16135,
16178,
16190,
@@ -605,8 +602,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
16223,
16229,
16232,
- 16234,
- 16245,
16246,
16276,
16322,
@@ -615,12 +610,10 @@ var DefaultTopASNs = container.NewMapSet[ASN](
16345,
16347,
16367,
- 16413,
16437,
16509,
16591,
16592,
- 16625,
16629,
16637,
16705,
@@ -634,7 +627,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
17411,
17421,
17451,
- 17456,
17458,
17465,
17470,
@@ -655,11 +647,12 @@ var DefaultTopASNs = container.NewMapSet[ASN](
17665,
17676,
17698,
- 17709,
+ 17705,
+ 17716,
17809,
17816,
17828,
- 17839,
+ 17849,
17853,
17858,
17882,
@@ -681,12 +674,12 @@ var DefaultTopASNs = container.NewMapSet[ASN](
18199,
18200,
18209,
+ 18278,
+ 18371,
18390,
- 18399,
18403,
18412,
18419,
- 18429,
18734,
18747,
18809,
@@ -701,7 +694,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
19422,
19429,
19624,
- 19626,
19711,
19863,
19901,
@@ -719,34 +711,31 @@ var DefaultTopASNs = container.NewMapSet[ASN](
20473,
20485,
20545,
- 20626,
20634,
20661,
- 20719,
- 20723,
20771,
20776,
20804,
20845,
20860,
20875,
- 20880,
+ 20879,
20910,
20911,
- 20928,
- 20940,
+ 20960,
20963,
20978,
- 21001,
21003,
21021,
21040,
21050,
21056,
+ 21100,
21107,
- 21127,
+ 21171,
21183,
21211,
+ 21221,
21230,
21232,
21246,
@@ -758,8 +747,8 @@ var DefaultTopASNs = container.NewMapSet[ASN](
21351,
21412,
21430,
- 21449,
21450,
+ 21466,
21491,
21497,
21502,
@@ -780,7 +769,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
22423,
22581,
22652,
- 22690,
22724,
22735,
22773,
@@ -792,9 +780,10 @@ var DefaultTopASNs = container.NewMapSet[ASN](
23114,
23201,
23243,
+ 23383,
+ 23470,
23487,
23520,
- 23563,
23655,
23657,
23673,
@@ -802,9 +791,9 @@ var DefaultTopASNs = container.NewMapSet[ASN](
23688,
23693,
23700,
- 23724,
- 23750,
23752,
+ 23764,
+ 23783,
23860,
23889,
23900,
@@ -814,23 +803,26 @@ var DefaultTopASNs = container.NewMapSet[ASN](
23956,
23969,
24016,
+ 24020,
24033,
24086,
- 24088,
24157,
24158,
24163,
24164,
24165,
+ 24183,
24186,
24203,
24309,
+ 24324,
24337,
24378,
24389,
24400,
24432,
24439,
+ 24441,
24444,
24445,
24492,
@@ -842,6 +834,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
24589,
24608,
24631,
+ 24645,
24651,
24691,
24722,
@@ -856,9 +849,10 @@ var DefaultTopASNs = container.NewMapSet[ASN](
24889,
24921,
24940,
+ 24953,
24955,
+ 24971,
25019,
- 25031,
25106,
25117,
25133,
@@ -868,14 +862,12 @@ var DefaultTopASNs = container.NewMapSet[ASN](
25159,
25184,
25190,
- 25206,
+ 25198,
25229,
25248,
25250,
25255,
- 25273,
25274,
- 25310,
25369,
25374,
25375,
@@ -886,14 +878,14 @@ var DefaultTopASNs = container.NewMapSet[ASN](
25441,
25447,
25454,
+ 25467,
25471,
25472,
+ 25490,
25491,
25509,
25512,
25513,
- 25521,
- 25528,
25543,
25607,
25620,
@@ -902,7 +894,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
26130,
26210,
26383,
- 26523,
26599,
26611,
26615,
@@ -914,6 +905,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
27665,
27668,
27672,
+ 27680,
27694,
27695,
27696,
@@ -936,8 +928,8 @@ var DefaultTopASNs = container.NewMapSet[ASN](
27789,
27796,
27800,
+ 27806,
27813,
- 27828,
27831,
27833,
27837,
@@ -959,6 +951,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
27932,
27947,
27951,
+ 27953,
27983,
27984,
27988,
@@ -968,20 +961,21 @@ var DefaultTopASNs = container.NewMapSet[ASN](
28015,
28024,
28032,
- 28033,
28036,
28048,
+ 28049,
+ 28061,
28067,
28075,
- 28080,
28094,
28103,
28104,
28118,
28126,
28146,
+ 28182,
28186,
- 28198,
+ 28191,
28201,
28210,
28220,
@@ -990,15 +984,18 @@ var DefaultTopASNs = container.NewMapSet[ASN](
28343,
28398,
28403,
+ 28435,
28458,
28469,
28481,
28509,
28512,
28530,
+ 28531,
28532,
28534,
28536,
+ 28537,
28541,
28545,
28548,
@@ -1008,7 +1005,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
28598,
28649,
28668,
- 28682,
+ 28680,
28683,
28685,
28717,
@@ -1018,9 +1015,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
28787,
28812,
28840,
- 28849,
- 28851,
- 28854,
28878,
28884,
28885,
@@ -1030,18 +1024,16 @@ var DefaultTopASNs = container.NewMapSet[ASN](
28972,
29027,
29030,
- 29031,
- 29039,
29049,
29061,
29070,
29084,
29091,
29119,
- 29124,
- 29148,
29170,
+ 29182,
29208,
+ 29222,
29238,
29244,
29247,
@@ -1049,28 +1041,33 @@ var DefaultTopASNs = container.NewMapSet[ASN](
29286,
29310,
29314,
+ 29340,
+ 29348,
29355,
29357,
29405,
29447,
29465,
+ 29467,
29485,
29492,
29518,
29544,
+ 29545,
29555,
29571,
29580,
29582,
29584,
+ 29600,
29614,
29687,
- 29694,
+ 29689,
29695,
29975,
30036,
30058,
- 30272,
+ 30344,
30526,
30600,
30619,
@@ -1080,6 +1077,8 @@ var DefaultTopASNs = container.NewMapSet[ASN](
30844,
30848,
30873,
+ 30896,
+ 30929,
30982,
30983,
30985,
@@ -1109,11 +1108,11 @@ var DefaultTopASNs = container.NewMapSet[ASN](
31242,
31245,
31246,
- 31250,
31252,
- 31257,
+ 31263,
31272,
31287,
+ 31390,
31404,
31423,
31435,
@@ -1122,27 +1121,22 @@ var DefaultTopASNs = container.NewMapSet[ASN](
31543,
31549,
31615,
- 31642,
31655,
- 31679,
31689,
31721,
- 31725,
31726,
+ 31856,
31898,
31960,
32020,
32098,
- 32189,
32398,
- 32787,
32860,
33363,
33392,
33567,
33576,
33582,
- 33588,
33763,
33765,
33771,
@@ -1150,22 +1144,22 @@ var DefaultTopASNs = container.NewMapSet[ASN](
33781,
33788,
33796,
+ 33852,
33874,
+ 33883,
33885,
33915,
33922,
- 33924,
33983,
34001,
+ 34032,
34058,
+ 34080,
34087,
- 34120,
34170,
- 34187,
34224,
34244,
34245,
- 34263,
34295,
34296,
34318,
@@ -1173,19 +1167,15 @@ var DefaultTopASNs = container.NewMapSet[ASN](
34447,
34458,
34471,
+ 34488,
34515,
- 34524,
- 34525,
34533,
- 34547,
34557,
- 34569,
34577,
34594,
- 34606,
- 34636,
34661,
34666,
+ 34683,
34700,
34702,
34718,
@@ -1195,32 +1185,31 @@ var DefaultTopASNs = container.NewMapSet[ASN](
34779,
34797,
34803,
+ 34820,
34857,
34876,
34977,
34984,
- 35007,
+ 34989,
35046,
35047,
35091,
35104,
+ 35107,
35132,
35141,
35179,
35191,
35197,
35223,
- 35224,
35228,
35244,
- 35277,
35297,
35311,
+ 35320,
35328,
35346,
35362,
- 35370,
- 35394,
35432,
35444,
35457,
@@ -1230,23 +1219,20 @@ var DefaultTopASNs = container.NewMapSet[ASN](
35567,
35568,
35612,
- 35613,
35699,
- 35706,
35725,
35729,
35753,
35790,
35805,
35807,
- 35816,
35819,
35900,
- 35911,
36290,
- 36408,
+ 36384,
36511,
36549,
+ 36599,
36864,
36865,
36866,
@@ -1269,15 +1255,17 @@ var DefaultTopASNs = container.NewMapSet[ASN](
36924,
36925,
36926,
+ 36930,
36935,
36937,
36939,
36947,
36955,
36958,
+ 36959,
36962,
36963,
- 36968,
+ 36965,
36972,
36974,
36977,
@@ -1290,7 +1278,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
37002,
37006,
37009,
- 37012,
37014,
37020,
37027,
@@ -1306,6 +1293,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
37063,
37069,
37073,
+ 37074,
37075,
37076,
37081,
@@ -1320,7 +1308,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
37119,
37123,
37124,
- 37126,
37129,
37133,
37136,
@@ -1349,6 +1336,8 @@ var DefaultTopASNs = container.NewMapSet[ASN](
37228,
37229,
37233,
+ 37239,
+ 37254,
37257,
37273,
37282,
@@ -1359,6 +1348,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
37305,
37309,
37313,
+ 37315,
37323,
37329,
37332,
@@ -1390,14 +1380,16 @@ var DefaultTopASNs = container.NewMapSet[ASN](
37457,
37460,
37461,
+ 37462,
37463,
37473,
37475,
+ 37480,
37487,
+ 37489,
37492,
37503,
37508,
- 37515,
37517,
37518,
37524,
@@ -1408,6 +1400,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
37541,
37542,
37545,
+ 37548,
37550,
37552,
37559,
@@ -1425,9 +1418,9 @@ var DefaultTopASNs = container.NewMapSet[ASN](
37614,
37616,
37622,
- 37638,
37642,
37645,
+ 37647,
37649,
37654,
37665,
@@ -1436,19 +1429,16 @@ var DefaultTopASNs = container.NewMapSet[ASN](
37680,
37682,
37693,
+ 37697,
37705,
+ 37713,
37721,
- 37722,
- 37723,
- 37731,
37963,
38009,
+ 38019,
38067,
38077,
- 38082,
- 38107,
38136,
- 38176,
38195,
38198,
38201,
@@ -1458,8 +1448,8 @@ var DefaultTopASNs = container.NewMapSet[ASN](
38247,
38264,
38266,
- 38286,
38322,
+ 38333,
38442,
38466,
38511,
@@ -1469,22 +1459,19 @@ var DefaultTopASNs = container.NewMapSet[ASN](
38600,
38623,
38742,
- 38794,
38800,
38805,
38819,
38841,
- 38851,
38875,
38901,
- 38932,
38999,
39007,
39010,
39032,
39067,
- 39074,
39184,
+ 39199,
39216,
39232,
39273,
@@ -1495,39 +1482,39 @@ var DefaultTopASNs = container.NewMapSet[ASN](
39397,
39402,
39440,
- 39501,
39544,
- 39571,
- 39574,
39603,
39608,
39611,
+ 39615,
39642,
+ 39647,
39686,
- 39699,
+ 39791,
39823,
39824,
+ 39826,
39891,
39906,
- 39927,
+ 39912,
+ 40021,
40029,
40786,
40788,
40945,
- 40994,
41007,
+ 41019,
41068,
41096,
41124,
41164,
41202,
41230,
- 41280,
+ 41275,
+ 41313,
41329,
41330,
- 41354,
41378,
- 41436,
41454,
41496,
41549,
@@ -1535,9 +1522,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
41563,
41564,
41676,
- 41697,
41704,
- 41712,
41714,
41733,
41738,
@@ -1545,13 +1530,9 @@ var DefaultTopASNs = container.NewMapSet[ASN](
41750,
41798,
41833,
- 41872,
- 41881,
41897,
41956,
- 41966,
41997,
- 41998,
42003,
42013,
42082,
@@ -1560,24 +1541,28 @@ var DefaultTopASNs = container.NewMapSet[ASN](
42162,
42183,
42232,
+ 42235,
42298,
42306,
42313,
42334,
42337,
42437,
+ 42455,
42473,
+ 42525,
42532,
42541,
42560,
42571,
42580,
42581,
- 42586,
42610,
42652,
+ 42689,
42708,
42713,
+ 42714,
42772,
42779,
42828,
@@ -1585,6 +1570,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
42841,
42863,
42864,
+ 42905,
42908,
42912,
42925,
@@ -1592,100 +1578,100 @@ var DefaultTopASNs = container.NewMapSet[ASN](
42991,
43019,
43060,
+ 43118,
43139,
43197,
43205,
43242,
43248,
43256,
+ 43284,
43350,
43375,
- 43406,
43451,
43452,
43513,
43529,
43533,
43568,
+ 43581,
43612,
43627,
+ 43633,
+ 43656,
43700,
+ 43708,
+ 43709,
43733,
43754,
43766,
43769,
+ 43791,
43824,
+ 43873,
+ 43892,
+ 43922,
43925,
43939,
43940,
- 44021,
+ 43957,
44027,
44034,
44086,
44087,
- 44090,
- 44124,
+ 44118,
44134,
44143,
44213,
44217,
44234,
44244,
- 44272,
- 44309,
+ 44285,
44313,
44327,
44377,
- 44384,
44391,
44395,
44477,
44483,
44489,
44515,
+ 44549,
44558,
44566,
- 44631,
+ 44575,
44702,
44708,
44735,
- 44814,
44869,
+ 44925,
45090,
45102,
45143,
45177,
45178,
- 45193,
- 45230,
45245,
+ 45267,
45271,
45305,
45345,
45355,
45356,
45361,
- 45410,
- 45430,
45458,
45461,
45498,
45543,
- 45558,
45588,
45609,
- 45629,
45637,
45650,
45669,
- 45671,
45700,
45754,
45758,
45763,
45766,
- 45773,
- 45780,
45879,
45891,
45899,
@@ -1696,48 +1682,50 @@ var DefaultTopASNs = container.NewMapSet[ASN](
45960,
46198,
46408,
+ 46573,
46650,
46868,
- 47114,
- 47132,
+ 46941,
47139,
- 47159,
47169,
- 47171,
47204,
47217,
47232,
- 47234,
47237,
47253,
47331,
47377,
47394,
+ 47474,
47485,
+ 47493,
47524,
- 47556,
+ 47583,
47588,
47589,
47790,
- 47798,
+ 47794,
47881,
47883,
47887,
47898,
+ 47943,
47956,
+ 47959,
47962,
48014,
48092,
- 48101,
48133,
48147,
48161,
48190,
48206,
+ 48239,
48252,
48260,
+ 48266,
48288,
- 48359,
+ 48289,
48418,
48431,
48437,
@@ -1745,13 +1733,12 @@ var DefaultTopASNs = container.NewMapSet[ASN](
48492,
48503,
48506,
- 48602,
+ 48584,
48629,
48675,
- 48695,
- 48715,
48728,
- 48789,
+ 48747,
+ 48781,
48830,
48832,
48847,
@@ -1760,53 +1747,58 @@ var DefaultTopASNs = container.NewMapSet[ASN](
48944,
48966,
49020,
+ 49040,
+ 49044,
49056,
49100,
49101,
+ 49110,
49115,
+ 49117,
49129,
49223,
49242,
49273,
- 49285,
- 49290,
+ 49282,
+ 49362,
+ 49453,
+ 49460,
49561,
49628,
49724,
- 49770,
+ 49760,
+ 49798,
49800,
49808,
- 49824,
- 49826,
49889,
49902,
49911,
49914,
49981,
- 49985,
50010,
- 50053,
50181,
50195,
50223,
- 50242,
+ 50231,
50251,
50261,
50266,
50274,
50294,
50304,
+ 50318,
50334,
50349,
- 50411,
+ 50436,
50463,
50467,
50482,
50500,
+ 50506,
+ 50558,
50581,
50613,
50616,
- 50624,
50635,
50648,
50670,
@@ -1817,10 +1809,8 @@ var DefaultTopASNs = container.NewMapSet[ASN](
50810,
50821,
50825,
- 50925,
- 50953,
+ 50917,
50959,
- 50962,
50973,
50979,
51018,
@@ -1828,21 +1818,24 @@ var DefaultTopASNs = container.NewMapSet[ASN](
51026,
51104,
51110,
+ 51132,
+ 51142,
51167,
51175,
51184,
51207,
51265,
51336,
+ 51342,
51346,
51375,
51395,
- 51399,
51407,
51430,
- 51469,
+ 51495,
51504,
51582,
+ 51604,
51645,
51653,
51684,
@@ -1851,11 +1844,12 @@ var DefaultTopASNs = container.NewMapSet[ASN](
51809,
51825,
51847,
+ 51873,
51878,
51896,
- 51964,
- 52080,
- 52116,
+ 51947,
+ 52075,
+ 52173,
52228,
52233,
52238,
@@ -1874,7 +1868,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
52373,
52398,
52405,
- 52409,
52412,
52433,
52436,
@@ -1888,20 +1881,23 @@ var DefaultTopASNs = container.NewMapSet[ASN](
53006,
53667,
53764,
+ 53813,
+ 53926,
54198,
54614,
54801,
- 54994,
- 55033,
- 55329,
+ 55081,
+ 55286,
55330,
- 55387,
55391,
55392,
+ 55415,
55424,
55427,
55430,
55501,
+ 55523,
+ 55561,
55577,
55685,
55699,
@@ -1912,11 +1908,11 @@ var DefaultTopASNs = container.NewMapSet[ASN](
55836,
55850,
55853,
- 55872,
+ 55900,
55915,
- 55933,
55943,
55944,
+ 55990,
56017,
56030,
56040,
@@ -1938,49 +1934,50 @@ var DefaultTopASNs = container.NewMapSet[ASN](
56300,
56309,
56329,
- 56349,
+ 56354,
56369,
- 56400,
56410,
+ 56465,
+ 56468,
56478,
- 56491,
+ 56494,
+ 56497,
56568,
- 56572,
56653,
56655,
- 56656,
56665,
56696,
- 56704,
- 56803,
+ 56709,
+ 56882,
56902,
56933,
56971,
+ 56995,
57000,
57013,
57016,
57043,
57070,
- 57112,
57134,
- 57184,
+ 57169,
57218,
57223,
+ 57248,
57269,
57279,
57293,
57344,
+ 57367,
57370,
57374,
57388,
57389,
57443,
- 57491,
57513,
57564,
57566,
57630,
- 57728,
+ 57634,
57743,
57760,
57761,
@@ -1989,13 +1986,15 @@ var DefaultTopASNs = container.NewMapSet[ASN](
57794,
57852,
57869,
- 57873,
57888,
58061,
58065,
+ 58087,
+ 58098,
+ 58118,
58130,
58224,
- 58299,
+ 58309,
58322,
58328,
58453,
@@ -2005,9 +2004,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
58504,
58507,
58524,
- 58539,
- 58541,
- 58542,
+ 58593,
58610,
58656,
58682,
@@ -2016,34 +2013,30 @@ var DefaultTopASNs = container.NewMapSet[ASN](
58717,
58731,
58821,
- 58893,
58895,
58945,
58952,
58955,
59108,
59125,
+ 59126,
59257,
- 59317,
- 59318,
59355,
59362,
- 59441,
+ 59381,
+ 59396,
59497,
- 59577,
59588,
59625,
59668,
- 59702,
59729,
59847,
59861,
59890,
59989,
- 60016,
60068,
+ 60171,
60258,
- 60277,
60294,
60296,
60304,
@@ -2051,68 +2044,68 @@ var DefaultTopASNs = container.NewMapSet[ASN](
60367,
60372,
60377,
- 60398,
60415,
60471,
60587,
- 60636,
+ 60602,
+ 60631,
60656,
60690,
60725,
+ 60754,
60757,
60781,
+ 60791,
60806,
- 60849,
- 60895,
60999,
+ 61006,
+ 61010,
61071,
61079,
61112,
61135,
61138,
61143,
- 61174,
61189,
61211,
61272,
61275,
61287,
61307,
+ 61317,
+ 61367,
+ 61424,
61449,
61461,
61466,
61468,
61478,
+ 61490,
61512,
62005,
62013,
- 62059,
62161,
62179,
- 62183,
62211,
62212,
+ 62214,
62240,
62250,
+ 62281,
62282,
- 62336,
- 62337,
62419,
62563,
- 62627,
62651,
63023,
63199,
63526,
- 63852,
63859,
63888,
63949,
- 63962,
63969,
63991,
+ 64037,
64043,
- 64050,
64073,
64098,
64105,
@@ -2125,7 +2118,8 @@ var DefaultTopASNs = container.NewMapSet[ASN](
131207,
131267,
131284,
- 131325,
+ 131285,
+ 131316,
131429,
131445,
131464,
@@ -2137,39 +2131,34 @@ var DefaultTopASNs = container.NewMapSet[ASN](
131627,
131706,
131769,
- 132021,
- 132045,
132061,
- 132080,
+ 132077,
132148,
132165,
- 132167,
132173,
132199,
132203,
132204,
- 132222,
- 132255,
132298,
- 132347,
132429,
132447,
+ 132449,
132462,
132468,
132471,
+ 132525,
132618,
132686,
- 132831,
- 132857,
+ 132815,
133012,
133076,
133199,
133200,
+ 133210,
133334,
133384,
- 133385,
- 133401,
133433,
+ 133480,
133481,
133524,
133533,
@@ -2179,41 +2168,33 @@ var DefaultTopASNs = container.NewMapSet[ASN](
133623,
133661,
133752,
- 133774,
133875,
133894,
133897,
- 133957,
133982,
134090,
134113,
134134,
+ 134139,
134204,
134356,
- 134420,
- 134467,
+ 134359,
134489,
- 134525,
134562,
134651,
134674,
134697,
134707,
- 134714,
134715,
134732,
- 134739,
- 134762,
- 134768,
134783,
134810,
134840,
134995,
135043,
- 135069,
+ 135097,
135126,
135298,
- 135333,
135341,
135371,
135375,
@@ -2222,25 +2203,21 @@ var DefaultTopASNs = container.NewMapSet[ASN](
135405,
135407,
135409,
- 135427,
135477,
135478,
135589,
135600,
135607,
135887,
- 135905,
- 136030,
+ 136000,
136039,
136093,
136119,
136141,
136167,
- 136168,
- 136195,
136210,
- 136224,
136238,
+ 136239,
136255,
136379,
136380,
@@ -2249,108 +2226,105 @@ var DefaultTopASNs = container.NewMapSet[ASN](
136442,
136454,
136474,
- 136477,
136479,
- 136480,
136515,
136525,
136557,
136617,
- 136780,
136787,
136873,
+ 136907,
136919,
- 136958,
136969,
+ 136972,
136986,
136994,
+ 137040,
137047,
137080,
- 137187,
- 137259,
+ 137226,
137409,
137412,
137424,
+ 137443,
137449,
137453,
137503,
- 137511,
137526,
- 137549,
137561,
- 137693,
- 137697,
+ 137853,
137872,
- 137891,
137895,
- 137905,
137959,
137967,
- 137989,
138089,
- 138109,
- 138164,
+ 138134,
138167,
- 138168,
138179,
138197,
- 138322,
138345,
138346,
138368,
138384,
138423,
- 138500,
138529,
138558,
138590,
+ 138606,
138640,
138655,
138754,
138886,
- 138902,
138915,
- 138958,
+ 138926,
138964,
138965,
- 139003,
+ 138997,
139009,
139029,
139043,
+ 139058,
139224,
139238,
- 139285,
- 139381,
- 139609,
- 139659,
+ 139325,
+ 139628,
+ 139692,
+ 139704,
+ 139741,
139759,
139820,
139831,
139841,
+ 139849,
+ 139879,
139898,
139922,
139952,
- 139967,
139981,
139994,
140045,
140061,
140072,
+ 140091,
140220,
140292,
- 140401,
+ 140345,
+ 140485,
140499,
140504,
+ 140594,
140659,
- 140707,
+ 140726,
+ 140867,
140900,
- 140989,
+ 141015,
141024,
141031,
141039,
+ 141047,
+ 141127,
141140,
141145,
- 141177,
141216,
141342,
141421,
@@ -2363,47 +2337,47 @@ var DefaultTopASNs = container.NewMapSet[ASN](
141995,
142032,
142065,
+ 142139,
+ 142271,
142295,
- 142352,
- 142539,
+ 142386,
142647,
+ 146961,
+ 147023,
147049,
147302,
- 147314,
149034,
149173,
149359,
149360,
149419,
149456,
- 149521,
- 149660,
+ 149487,
149707,
149771,
- 150153,
+ 150222,
150331,
150371,
- 150381,
+ 150407,
150452,
150683,
150692,
150748,
150750,
150774,
- 150812,
- 151066,
151080,
- 151341,
151396,
- 151482,
+ 151491,
+ 151636,
+ 151965,
151983,
+ 152178,
152317,
+ 152329,
152337,
- 152466,
152605,
152677,
196640,
- 196735,
196838,
196874,
196925,
@@ -2411,21 +2385,22 @@ var DefaultTopASNs = container.NewMapSet[ASN](
197207,
197225,
197248,
+ 197296,
197301,
197350,
197398,
197423,
+ 197470,
197540,
197556,
- 197623,
- 197648,
197706,
+ 197716,
197830,
197862,
197882,
197897,
- 198004,
198023,
+ 198068,
198256,
198265,
198279,
@@ -2433,80 +2408,80 @@ var DefaultTopASNs = container.NewMapSet[ASN](
198440,
198441,
198471,
+ 198499,
198504,
- 198537,
198589,
198605,
+ 198632,
198668,
+ 198820,
198890,
+ 198930,
198961,
198966,
199081,
199140,
199155,
+ 199173,
199276,
+ 199326,
199469,
199490,
199493,
199524,
+ 199620,
199636,
- 199698,
199707,
199731,
+ 199736,
199739,
199785,
- 199811,
- 199995,
200019,
+ 200076,
200134,
200154,
200200,
200313,
200446,
200590,
- 200612,
- 200628,
+ 200665,
200724,
200736,
- 200740,
200742,
200845,
200865,
- 200899,
- 200923,
- 200964,
+ 201004,
201019,
201073,
201089,
- 201096,
201107,
- 201120,
- 201150,
201167,
201241,
201249,
201411,
+ 201476,
201505,
201540,
201577,
- 201589,
201596,
+ 201601,
201603,
+ 201746,
201749,
201767,
201776,
201838,
201884,
- 201890,
+ 201973,
201986,
201997,
202023,
202087,
202098,
- 202103,
202204,
202254,
202293,
+ 202316,
202422,
202433,
202441,
@@ -2515,31 +2490,33 @@ var DefaultTopASNs = container.NewMapSet[ASN](
202561,
202613,
202616,
- 202618,
202632,
- 202635,
202651,
+ 202652,
202662,
202710,
- 202722,
+ 202759,
202870,
+ 202876,
202921,
202940,
+ 202960,
202987,
203020,
203136,
+ 203143,
203206,
203214,
203217,
203257,
203424,
- 203446,
203451,
+ 203561,
203622,
203675,
203680,
203715,
- 203827,
+ 203811,
203877,
203912,
203916,
@@ -2553,107 +2530,95 @@ var DefaultTopASNs = container.NewMapSet[ASN](
204020,
204106,
204108,
- 204149,
204151,
- 204165,
204170,
+ 204267,
204274,
204279,
- 204281,
204317,
- 204403,
- 204429,
+ 204342,
204457,
+ 204467,
204566,
204592,
204595,
- 204650,
- 204716,
- 204894,
+ 204649,
+ 204793,
+ 204816,
204918,
204957,
204986,
+ 205015,
205110,
205168,
205244,
205254,
- 205275,
205278,
- 205293,
+ 205367,
205368,
- 205516,
+ 205371,
205547,
205638,
- 205645,
205647,
205714,
205800,
- 205832,
+ 205814,
205889,
- 205984,
+ 206013,
206026,
206065,
206067,
- 206092,
206119,
206206,
206238,
206260,
206262,
206283,
- 206351,
+ 206358,
206375,
206406,
+ 206429,
206446,
- 206471,
- 206485,
+ 206478,
206519,
206557,
+ 206575,
206610,
206666,
206774,
206783,
- 206804,
+ 206784,
206892,
- 206912,
- 206928,
+ 206920,
206977,
- 207044,
207097,
207137,
- 207154,
207192,
- 207246,
207251,
- 207348,
207369,
207375,
- 207474,
+ 207408,
207502,
- 207541,
207569,
207589,
- 207617,
- 207713,
207782,
- 207790,
207810,
207876,
207990,
207991,
- 208149,
208286,
208320,
208324,
208339,
+ 208343,
+ 208448,
208570,
208592,
208671,
208730,
208734,
- 208864,
+ 208859,
208905,
- 208909,
208972,
208997,
209012,
@@ -2665,113 +2630,115 @@ var DefaultTopASNs = container.NewMapSet[ASN](
209262,
209273,
209277,
+ 209302,
209360,
209424,
+ 209429,
209442,
209491,
209531,
+ 209778,
209835,
209839,
209854,
209948,
- 210001,
210003,
210016,
210021,
210022,
+ 210061,
+ 210070,
210125,
210147,
- 210150,
210218,
210273,
210278,
210315,
210402,
210509,
+ 210595,
210616,
210625,
210644,
- 210693,
210740,
+ 210747,
210808,
- 210964,
+ 210865,
211028,
211057,
- 211147,
- 211196,
- 211210,
211211,
211235,
- 211250,
211309,
211356,
211450,
+ 211458,
211468,
+ 211555,
211559,
- 211774,
+ 211759,
211908,
- 211913,
211995,
- 212046,
212183,
212238,
+ 212271,
212330,
212444,
212449,
212531,
212572,
- 212607,
212616,
212637,
212645,
212655,
212661,
+ 212766,
+ 212851,
+ 212865,
212898,
- 212910,
- 212986,
212999,
213155,
+ 213207,
213295,
213320,
213398,
213402,
+ 213957,
214739,
- 214869,
- 214990,
+ 214790,
+ 214996,
215052,
215284,
- 215287,
215304,
215346,
215355,
- 215416,
+ 215421,
215423,
215501,
215540,
- 215733,
+ 215597,
215746,
+ 215886,
215910,
- 215921,
+ 215968,
+ 216046,
216071,
- 216086,
216139,
+ 216165,
216183,
- 216312,
- 216325,
+ 216200,
216374,
216463,
262145,
262146,
262159,
- 262179,
262181,
262186,
+ 262188,
262191,
262197,
- 262199,
262202,
262210,
- 262220,
+ 262215,
262223,
262234,
262239,
@@ -2782,7 +2749,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
262287,
262354,
262378,
- 262459,
262468,
262481,
262494,
@@ -2791,8 +2757,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
262753,
262773,
262916,
- 262932,
- 263026,
263073,
263175,
263210,
@@ -2805,14 +2769,15 @@ var DefaultTopASNs = container.NewMapSet[ASN](
263248,
263292,
263327,
+ 263684,
263686,
263689,
263694,
263698,
- 263699,
263703,
263717,
263725,
+ 263732,
263749,
263750,
263751,
@@ -2832,7 +2797,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
264605,
264609,
264628,
- 264631,
264635,
264637,
264640,
@@ -2841,14 +2805,14 @@ var DefaultTopASNs = container.NewMapSet[ASN](
264646,
264663,
264685,
+ 264686,
264694,
- 264696,
264731,
- 264732,
264733,
264738,
264744,
264750,
+ 264754,
264756,
264758,
264770,
@@ -2860,63 +2824,55 @@ var DefaultTopASNs = container.NewMapSet[ASN](
264821,
264825,
264837,
- 264838,
264847,
+ 264984,
265509,
265540,
265561,
265594,
265606,
+ 265608,
265629,
265631,
265632,
265636,
- 265662,
- 265663,
265675,
265684,
+ 265686,
+ 265688,
265691,
- 265705,
- 265706,
265711,
265721,
+ 265724,
265727,
- 265728,
- 265735,
- 265749,
- 265757,
+ 265762,
265767,
265780,
265798,
265799,
- 265816,
265818,
265822,
- 265826,
- 265840,
+ 265825,
265855,
265867,
+ 265880,
266445,
266668,
266673,
266694,
- 266734,
+ 266725,
266742,
- 266755,
266757,
- 266762,
266792,
266802,
- 266809,
- 266812,
266814,
266815,
266831,
+ 266832,
266841,
266853,
266858,
266860,
- 266880,
266893,
266894,
266904,
@@ -2929,36 +2885,32 @@ var DefaultTopASNs = container.NewMapSet[ASN](
267749,
267761,
267765,
- 267795,
- 267796,
+ 267767,
267797,
267803,
267809,
- 267815,
267828,
- 267837,
267845,
267846,
- 267869,
267882,
267883,
- 267896,
+ 267900,
267904,
+ 267905,
+ 267916,
+ 267920,
268323,
268976,
269036,
- 269194,
269729,
269730,
269733,
269734,
- 269738,
269749,
269750,
269769,
- 269782,
269783,
- 269794,
+ 269788,
269797,
269804,
269806,
@@ -2967,7 +2919,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
269822,
269831,
269832,
- 269838,
269840,
269843,
269846,
@@ -2976,7 +2927,6 @@ var DefaultTopASNs = container.NewMapSet[ASN](
269894,
269901,
269908,
- 269918,
269919,
269921,
269927,
@@ -2984,20 +2934,19 @@ var DefaultTopASNs = container.NewMapSet[ASN](
269934,
269940,
269946,
+ 269950,
269953,
- 269955,
- 269960,
269964,
269965,
269973,
269976,
+ 269981,
269983,
269984,
- 269986,
269989,
270007,
- 270023,
270026,
+ 270029,
270035,
270036,
270049,
@@ -3005,134 +2954,136 @@ var DefaultTopASNs = container.NewMapSet[ASN](
270058,
270068,
270071,
+ 270075,
+ 270081,
270096,
- 270108,
- 270158,
+ 270098,
270161,
270186,
271773,
271781,
271791,
271795,
- 271804,
+ 271806,
271808,
- 271812,
271814,
- 271819,
271835,
271837,
- 271868,
271874,
- 271880,
- 271898,
271899,
271907,
271909,
271911,
+ 271916,
271929,
271930,
- 271932,
271933,
271935,
- 271936,
271942,
+ 271943,
271951,
+ 271956,
271965,
271971,
- 271978,
- 271996,
- 272011,
272018,
272026,
272027,
- 272037,
- 272057,
- 272059,
+ 272062,
+ 272066,
272083,
+ 272094,
272099,
272102,
+ 272104,
272106,
- 272109,
272110,
272112,
272120,
272122,
+ 272132,
272134,
+ 272138,
272809,
+ 272814,
272818,
+ 272827,
272836,
+ 272838,
272851,
+ 272869,
272882,
272886,
272906,
- 272914,
272916,
+ 272921,
272953,
272955,
- 272978,
- 272980,
+ 272962,
272991,
273019,
+ 273024,
273054,
+ 273063,
273067,
273093,
+ 273098,
273113,
- 273123,
273133,
- 273155,
- 273171,
+ 273139,
273172,
- 273189,
+ 273173,
+ 273180,
+ 273203,
+ 273824,
+ 273872,
327687,
327693,
327697,
- 327700,
327707,
- 327708,
327712,
327716,
327724,
+ 327725,
327728,
- 327733,
327738,
327750,
+ 327754,
327756,
- 327760,
327765,
327769,
327771,
327776,
327782,
327786,
- 327794,
327795,
+ 327798,
327799,
327802,
- 327804,
327819,
327820,
327828,
+ 327829,
327830,
327862,
327863,
327864,
327871,
- 327879,
327885,
327900,
327901,
327903,
- 327907,
+ 327921,
327931,
+ 327932,
327934,
- 327941,
+ 327947,
327972,
327975,
+ 327987,
+ 327990,
327991,
- 327992,
- 327996,
+ 328001,
328061,
- 328068,
328073,
328075,
328079,
@@ -3141,10 +3092,8 @@ var DefaultTopASNs = container.NewMapSet[ASN](
328136,
328140,
328146,
- 328154,
328169,
328181,
- 328182,
328191,
328196,
328198,
@@ -3152,22 +3101,25 @@ var DefaultTopASNs = container.NewMapSet[ASN](
328207,
328223,
328228,
+ 328230,
+ 328249,
328250,
328253,
- 328269,
+ 328258,
+ 328282,
328284,
328286,
328297,
- 328317,
+ 328304,
328319,
+ 328320,
328331,
328341,
- 328344,
328358,
328411,
+ 328432,
328436,
328442,
- 328443,
328469,
328471,
328473,
@@ -3176,6 +3128,7 @@ var DefaultTopASNs = container.NewMapSet[ASN](
328480,
328488,
328490,
+ 328491,
328494,
328509,
328510,
@@ -3198,53 +3151,55 @@ var DefaultTopASNs = container.NewMapSet[ASN](
328619,
328628,
328638,
+ 328652,
328659,
328676,
328697,
328702,
328708,
+ 328716,
328717,
328727,
- 328733,
- 328734,
- 328743,
+ 328753,
328755,
+ 328770,
+ 328777,
328817,
328844,
- 328849,
328856,
+ 328857,
328858,
328880,
328895,
328899,
328923,
328943,
+ 328950,
328954,
328959,
328961,
328965,
- 328975,
328977,
- 328979,
328987,
328988,
328993,
328997,
329006,
329014,
- 329020,
- 329021,
329027,
329029,
329044,
- 329074,
329078,
329082,
+ 329095,
329101,
+ 329103,
+ 329110,
+ 329126,
329129,
- 329135,
+ 329140,
+ 329155,
329167,
- 329169,
329170,
329174,
329179,
@@ -3254,41 +3209,48 @@ var DefaultTopASNs = container.NewMapSet[ASN](
329211,
329219,
329220,
- 329227,
+ 329223,
329253,
- 329254,
329261,
329274,
329286,
+ 329288,
329301,
+ 329322,
+ 329373,
329387,
329390,
- 329413,
+ 329411,
329415,
329422,
+ 329425,
+ 329426,
329437,
329472,
- 393275,
+ 329504,
393629,
393894,
394311,
394381,
394684,
+ 395092,
395561,
- 395570,
+ 395916,
395965,
396304,
396338,
396357,
396420,
396982,
+ 397735,
397961,
398228,
398721,
398901,
+ 399077,
399498,
399724,
- 400619,
+ 400099,
)
// DefaultCountryTopASNs is a mapping of a country to their top ASNs.
@@ -3297,7 +3259,7 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryAE: 5384,
CountryAF: 55330,
CountryAG: 11594,
- CountryAI: 396304,
+ CountryAI: 2740,
CountryAL: 50973,
CountryAM: 43733,
CountryAO: 37119,
@@ -3321,11 +3283,11 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryBM: 32020,
CountryBN: 10094,
CountryBO: 6568,
- CountryBQ: 27694,
+ CountryBQ: 28104,
CountryBR: 26599,
CountryBS: 15146,
CountryBT: 18024,
- CountryBW: 14988,
+ CountryBW: 37014,
CountryBY: 25106,
CountryBZ: 10269,
CountryCA: 812,
@@ -3336,9 +3298,9 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryCI: 29571,
CountryCK: 10131,
CountryCL: 27651,
- CountryCM: 36912,
+ CountryCM: 30992,
CountryCN: 4134,
- CountryCO: 27831,
+ CountryCO: 10620,
CountryCR: 52263,
CountryCU: 27725,
CountryCV: 37517,
@@ -3347,19 +3309,19 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryCZ: 5610,
CountryDE: 3320,
CountryDJ: 30990,
- CountryDK: 13335,
+ CountryDK: 212238,
CountryDM: 40945,
CountryDO: 6400,
CountryDZ: 36947,
CountryEC: 27947,
CountryEE: 3249,
CountryEG: 8452,
- CountryEH: 6713,
CountryER: 24757,
CountryES: 3352,
CountryET: 24757,
CountryFI: 51765,
CountryFJ: 38442,
+ CountryFK: 198605,
CountryFM: 139759,
CountryFO: 15389,
CountryFR: 3215,
@@ -3367,14 +3329,14 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryGB: 2856,
CountryGD: 46650,
CountryGE: 16010,
- CountryGF: 16028,
+ CountryGF: 3215,
CountryGG: 8680,
CountryGH: 30986,
- CountryGI: 202087,
+ CountryGI: 8301,
CountryGL: 8818,
CountryGM: 37552,
CountryGN: 37461,
- CountryGP: 16028,
+ CountryGP: 3215,
CountryGQ: 37173,
CountryGR: 6799,
CountryGT: 14754,
@@ -3384,39 +3346,40 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryHK: 4760,
CountryHN: 14754,
CountryHR: 5391,
- CountryHT: 27653,
+ CountryHT: 52260,
CountryHU: 5483,
CountryID: 7713,
CountryIE: 15502,
- CountryIL: 12400,
+ CountryIL: 1680,
CountryIM: 13122,
CountryIN: 55836,
CountryIO: 17458,
CountryIQ: 203214,
CountryIR: 197207,
- CountryIS: 56704,
+ CountryIS: 44735,
CountryIT: 1267,
- CountryJE: 8680,
+ CountryJE: 8681,
CountryJM: 30689,
CountryJO: 48832,
CountryJP: 2516,
CountryKE: 33771,
CountryKG: 47237,
CountryKH: 38623,
- CountryKI: 135409,
- CountryKM: 36939,
+ CountryKI: 134783,
+ CountryKM: 328061,
CountryKN: 11139,
+ CountryKP: 199707,
CountryKR: 4766,
CountryKW: 29357,
CountryKY: 6639,
CountryKZ: 206026,
CountryLA: 9873,
- CountryLB: 38999,
+ CountryLB: 57513,
CountryLC: 15344,
CountryLI: 136787,
CountryLK: 18001,
- CountryLR: 37410,
- CountryLS: 33567,
+ CountryLR: 37094,
+ CountryLS: 37057,
CountryLT: 8764,
CountryLU: 6661,
CountryLV: 24921,
@@ -3430,30 +3393,29 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryMH: 24439,
CountryMK: 6821,
CountryML: 30985,
- CountryMM: 136255,
+ CountryMM: 134840,
CountryMN: 17882,
CountryMO: 4609,
CountryMP: 7131,
- CountryMQ: 20776,
+ CountryMQ: 16028,
CountryMR: 29544,
- CountryMS: 11139,
+ CountryMS: 396304,
CountryMT: 12709,
CountryMU: 23889,
CountryMV: 7642,
- CountryMW: 37294,
+ CountryMW: 37440,
CountryMX: 8151,
CountryMY: 4788,
CountryMZ: 37342,
CountryNA: 36996,
- CountryNC: 17480,
- CountryNE: 37233,
+ CountryNC: 18200,
+ CountryNE: 37531,
CountryNG: 29465,
CountryNI: 14754,
CountryNL: 1136,
CountryNO: 9009,
CountryNP: 17501,
CountryNR: 140504,
- CountryNU: 198605,
CountryNZ: 9790,
CountryOM: 28885,
CountryPA: 11556,
@@ -3464,13 +3426,13 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryPK: 45669,
CountryPL: 5617,
CountryPM: 3695,
- CountryPR: 21928,
+ CountryPR: 14638,
CountryPS: 12975,
- CountryPT: 12353,
+ CountryPT: 3243,
CountryPW: 17893,
CountryPY: 23201,
CountryQA: 8781,
- CountryRE: 199140,
+ CountryRE: 3215,
CountryRO: 8708,
CountryRS: 8400,
CountryRU: 8359,
@@ -3479,12 +3441,12 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountrySB: 45891,
CountrySC: 36958,
CountrySD: 15706,
- CountrySE: 13335,
+ CountrySE: 60068,
CountrySG: 4773,
- CountrySH: 37645,
- CountrySI: 3212,
+ CountrySH: 17400,
+ CountrySI: 21283,
CountrySK: 6855,
- CountrySL: 37164,
+ CountrySL: 36988,
CountrySM: 15433,
CountrySN: 8346,
CountrySO: 37371,
@@ -3500,14 +3462,15 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryTG: 36924,
CountryTH: 131445,
CountryTJ: 43197,
+ CountryTK: 55523,
CountryTL: 58731,
CountryTM: 20661,
- CountryTN: 37705,
+ CountryTN: 37693,
CountryTO: 38201,
CountryTR: 47331,
CountryTT: 27800,
CountryTW: 3462,
- CountryTZ: 37035,
+ CountryTZ: 36908,
CountryUA: 15895,
CountryUG: 37075,
CountryUS: 7922,
@@ -3516,10 +3479,10 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryVA: 8978,
CountryVC: 46408,
CountryVE: 8048,
- CountryVG: 396357,
+ CountryVG: 11139,
CountryVI: 14434,
CountryVN: 7552,
- CountryVU: 132429,
+ CountryVU: 9249,
CountryWF: 45879,
CountryWS: 38800,
CountryXK: 21246,
@@ -3527,5 +3490,5 @@ var DefaultCountryTopASNs = map[Country]ASN{
CountryYT: 3215,
CountryZA: 37457,
CountryZM: 37287,
- CountryZW: 37204,
+ CountryZW: 56696,
}
diff --git a/internal/geoip/file.go b/internal/geoip/file.go
index 3c5c310..b9d15d6 100644
--- a/internal/geoip/file.go
+++ b/internal/geoip/file.go
@@ -49,12 +49,12 @@ type FileConfig struct {
// databases containing subdivisions and cities info are also supported.
CountryPath string
- // HostCacheSize is how many lookups are cached by hostname. Zero means no
+ // HostCacheCount is how many lookups are cached by hostname. Zero means no
// host caching is performed.
- HostCacheSize int
+ HostCacheCount int
- // IPCacheSize is how many lookups are cached by IP address.
- IPCacheSize int
+ // IPCacheCount is how many lookups are cached by IP address.
+ IPCacheCount int
}
// File is a file implementation of [geoip.Interface]. It should be initially
@@ -128,16 +128,16 @@ func newLocationKey(asn ASN, ctry Country, subdiv string) (l locationKey) {
// manager.
func NewFile(c *FileConfig) (f *File) {
var hostCache agdcache.Interface[string, *Location]
- if c.HostCacheSize == 0 {
+ if c.HostCacheCount == 0 {
hostCache = agdcache.Empty[string, *Location]{}
} else {
hostCache = agdcache.NewLRU[string, *Location](&agdcache.LRUConfig{
- Size: c.HostCacheSize,
+ Count: c.HostCacheCount,
})
}
ipCache := agdcache.NewLRU[any, *Location](&agdcache.LRUConfig{
- Size: c.IPCacheSize,
+ Count: c.IPCacheCount,
})
c.CacheManager.Add(cacheIDHost, hostCache)
diff --git a/internal/geoip/file_test.go b/internal/geoip/file_test.go
index e4aa425..7fd3c5b 100644
--- a/internal/geoip/file_test.go
+++ b/internal/geoip/file_test.go
@@ -39,8 +39,8 @@ func TestFile_Data_cityDB(t *testing.T) {
CacheManager: agdcache.EmptyManager{},
ASNPath: asnPath,
CountryPath: cityPath,
- HostCacheSize: 0,
- IPCacheSize: 1,
+ HostCacheCount: 0,
+ IPCacheCount: 1,
AllTopASNs: allTopASNs,
CountryTopASNs: countryTopASNs,
}
@@ -66,8 +66,8 @@ func TestFile_Data_countryDB(t *testing.T) {
CacheManager: agdcache.EmptyManager{},
ASNPath: asnPath,
CountryPath: countryPath,
- HostCacheSize: 0,
- IPCacheSize: 1,
+ HostCacheCount: 0,
+ IPCacheCount: 1,
AllTopASNs: allTopASNs,
CountryTopASNs: countryTopASNs,
}
@@ -93,8 +93,8 @@ func TestFile_Data_hostCache(t *testing.T) {
CacheManager: agdcache.EmptyManager{},
ASNPath: asnPath,
CountryPath: cityPath,
- HostCacheSize: 1,
- IPCacheSize: 1,
+ HostCacheCount: 1,
+ IPCacheCount: 1,
AllTopASNs: allTopASNs,
CountryTopASNs: countryTopASNs,
}
@@ -123,8 +123,8 @@ func TestFile_SubnetByLocation(t *testing.T) {
CacheManager: agdcache.EmptyManager{},
ASNPath: asnPath,
CountryPath: cityPath,
- HostCacheSize: 0,
- IPCacheSize: 1,
+ HostCacheCount: 0,
+ IPCacheCount: 1,
AllTopASNs: allTopASNs,
CountryTopASNs: countryTopASNs,
}
@@ -202,8 +202,8 @@ func BenchmarkFile_Data(b *testing.B) {
CacheManager: agdcache.EmptyManager{},
ASNPath: asnPath,
CountryPath: cityPath,
- HostCacheSize: 0,
- IPCacheSize: 1,
+ HostCacheCount: 0,
+ IPCacheCount: 1,
AllTopASNs: geoip.DefaultTopASNs,
CountryTopASNs: geoip.DefaultCountryTopASNs,
}
@@ -254,8 +254,8 @@ func BenchmarkFile_Refresh(b *testing.B) {
CacheManager: agdcache.EmptyManager{},
ASNPath: asnPath,
CountryPath: cityPath,
- HostCacheSize: 0,
- IPCacheSize: 1,
+ HostCacheCount: 0,
+ IPCacheCount: 1,
AllTopASNs: geoip.DefaultTopASNs,
CountryTopASNs: geoip.DefaultCountryTopASNs,
}
diff --git a/internal/metrics/backend.go b/internal/metrics/backend.go
index ba48919..669da83 100644
--- a/internal/metrics/backend.go
+++ b/internal/metrics/backend.go
@@ -3,19 +3,18 @@ package metrics
import (
"context"
"fmt"
- "time"
- "github.com/AdguardTeam/golibs/container"
- "github.com/AdguardTeam/golibs/errors"
"github.com/prometheus/client_golang/prometheus"
)
// GRPCError is a type alias for string that contains gGRPC error type.
//
-// See [backendpb.IncrementGRPCErrorCount.
+// See [backendpb.GRPCMetrics.IncrementErrorCount].
type GRPCError = string
// gRPC errors of [GRPCError] type.
+//
+// NOTE: Keep in sync with [backendpb.GRPCError].
const (
GRPCErrAuthentication GRPCError = "auth"
GRPCErrBadRequest GRPCError = "bad_req"
@@ -25,38 +24,21 @@ const (
GRPCErrTimeout GRPCError = "timeout"
)
-// BackendPB is the Prometheus-based implementation of the [backendpb.Metrics]
-// interface.
-type BackendPB struct {
- // devicesInvalidTotal is a gauge with the number of invalid user devices
- // loaded from the backend.
- devicesInvalidTotal prometheus.Counter
-
- // grpcAvgProfileRecvDuration is a histogram with the average duration of a
- // receive of a single profile during a backend call.
- grpcAvgProfileRecvDuration prometheus.Histogram
-
- // grpcAvgProfileDecDuration is a histogram with the average duration of
- // decoding a single profile during a backend call.
- grpcAvgProfileDecDuration prometheus.Histogram
-
- grpcErrorsTotalAuthentication prometheus.Counter
- grpcErrorsTotalBadRequest prometheus.Counter
- grpcErrorsTotalDeviceQuota prometheus.Counter
- grpcErrorsTotalOther prometheus.Counter
- grpcErrorsTotalRateLimit prometheus.Counter
- grpcErrorsTotalTimeout prometheus.Counter
+// BackendGRPC is the Prometheus-based implementation of the
+// [backendpb.GRPCMetrics] interface.
+type BackendGRPC struct {
+ errorsTotalAuthentication prometheus.Counter
+ errorsTotalBadRequest prometheus.Counter
+ errorsTotalDeviceQuota prometheus.Counter
+ errorsTotalOther prometheus.Counter
+ errorsTotalRateLimit prometheus.Counter
+ errorsTotalTimeout prometheus.Counter
}
-// NewBackendPB registers the protobuf errors metrics in reg and returns a
-// properly initialized [BackendPB].
-func NewBackendPB(namespace string, reg prometheus.Registerer) (m *BackendPB, err error) {
- const (
- devicesInvalidTotal = "devices_invalid_total"
- grpcAvgProfileRecvDuration = "grpc_avg_profile_recv_duration_seconds"
- grpcAvgProfileDecDuration = "grpc_avg_profile_dec_duration_seconds"
- grpcErrorsTotal = "grpc_errors_total"
- )
+// NewBackendGRPC registers the protobuf errors metrics in reg and returns a
+// properly initialized [BackendGRPC].
+func NewBackendGRPC(namespace string, reg prometheus.Registerer) (m *BackendGRPC, err error) {
+ const grpcErrorsTotal = "grpc_errors_total"
// grpcErrorsTotalCounterVec is a vector of counters of gRPC errors by type.
grpcErrorsTotalCounterVec := prometheus.NewCounterVec(prometheus.CounterOpts{
@@ -66,97 +48,43 @@ func NewBackendPB(namespace string, reg prometheus.Registerer) (m *BackendPB, er
Help: "The total number of errors by type.",
}, []string{"type"})
- m = &BackendPB{
- devicesInvalidTotal: prometheus.NewCounter(prometheus.CounterOpts{
- Name: devicesInvalidTotal,
- Subsystem: subsystemBackend,
- Namespace: namespace,
- Help: "The total number of invalid user devices loaded from the backend.",
- }),
- grpcAvgProfileRecvDuration: prometheus.NewHistogram(prometheus.HistogramOpts{
- Name: grpcAvgProfileRecvDuration,
- Subsystem: subsystemBackend,
- Namespace: namespace,
- Help: "The average duration of a receive of a profile during a call to the backend, " +
- "in seconds.",
- Buckets: []float64{0.000_001, 0.000_010, 0.000_100, 0.001},
- }),
- grpcAvgProfileDecDuration: prometheus.NewHistogram(prometheus.HistogramOpts{
- Name: grpcAvgProfileDecDuration,
- Subsystem: subsystemBackend,
- Namespace: namespace,
- Help: "The average duration of decoding one profile during a call to the backend, " +
- "in seconds.",
- Buckets: []float64{0.000_001, 0.000_01, 0.000_1, 0.001},
- }),
- grpcErrorsTotalAuthentication: grpcErrorsTotalCounterVec.WithLabelValues(GRPCErrAuthentication),
- grpcErrorsTotalBadRequest: grpcErrorsTotalCounterVec.WithLabelValues(GRPCErrBadRequest),
- grpcErrorsTotalDeviceQuota: grpcErrorsTotalCounterVec.WithLabelValues(GRPCErrDeviceQuota),
- grpcErrorsTotalOther: grpcErrorsTotalCounterVec.WithLabelValues(GRPCErrOther),
- grpcErrorsTotalRateLimit: grpcErrorsTotalCounterVec.WithLabelValues(GRPCErrRateLimit),
- grpcErrorsTotalTimeout: grpcErrorsTotalCounterVec.WithLabelValues(GRPCErrTimeout),
+ m = &BackendGRPC{
+ errorsTotalAuthentication: grpcErrorsTotalCounterVec.WithLabelValues(GRPCErrAuthentication),
+ errorsTotalBadRequest: grpcErrorsTotalCounterVec.WithLabelValues(GRPCErrBadRequest),
+ errorsTotalDeviceQuota: grpcErrorsTotalCounterVec.WithLabelValues(GRPCErrDeviceQuota),
+ errorsTotalOther: grpcErrorsTotalCounterVec.WithLabelValues(GRPCErrOther),
+ errorsTotalRateLimit: grpcErrorsTotalCounterVec.WithLabelValues(GRPCErrRateLimit),
+ errorsTotalTimeout: grpcErrorsTotalCounterVec.WithLabelValues(GRPCErrTimeout),
}
- var errs []error
- collectors := container.KeyValues[string, prometheus.Collector]{{
- Key: devicesInvalidTotal,
- Value: m.devicesInvalidTotal,
- }, {
- Key: grpcAvgProfileRecvDuration,
- Value: m.grpcAvgProfileRecvDuration,
- }, {
- Key: grpcAvgProfileDecDuration,
- Value: m.grpcAvgProfileDecDuration,
- }, {
- Key: grpcErrorsTotal,
- Value: grpcErrorsTotalCounterVec,
- }}
-
- 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
+ err = reg.Register(grpcErrorsTotalCounterVec)
+ if err != nil {
+ return nil, fmt.Errorf("registering metrics %q: %w", grpcErrorsTotal, err)
}
return m, nil
}
-// IncrementGRPCErrorCount implements the [Metrics] interface for BackendPB.
-func (m *BackendPB) IncrementGRPCErrorCount(_ context.Context, errType GRPCError) {
+// IncrementErrorCount implements the [backendpb.GRPCMetrics] interface for
+// BackendGRPC.
+func (m *BackendGRPC) IncrementErrorCount(_ context.Context, errType GRPCError) {
var ctr prometheus.Counter
switch errType {
case GRPCErrAuthentication:
- ctr = m.grpcErrorsTotalAuthentication
+ ctr = m.errorsTotalAuthentication
case GRPCErrBadRequest:
- ctr = m.grpcErrorsTotalBadRequest
+ ctr = m.errorsTotalBadRequest
case GRPCErrDeviceQuota:
- ctr = m.grpcErrorsTotalDeviceQuota
+ ctr = m.errorsTotalDeviceQuota
case GRPCErrOther:
- ctr = m.grpcErrorsTotalOther
+ ctr = m.errorsTotalOther
case GRPCErrRateLimit:
- ctr = m.grpcErrorsTotalRateLimit
+ ctr = m.errorsTotalRateLimit
case GRPCErrTimeout:
- ctr = m.grpcErrorsTotalTimeout
+ ctr = m.errorsTotalTimeout
default:
- panic(fmt.Errorf("BackendPB.IncrementGRPCErrorCount: bad type %q", errType))
+ panic(fmt.Errorf("BackendGRPC.IncrementErrorCount: bad type %q", errType))
}
ctr.Inc()
}
-
-// IncrementInvalidDevicesCount implements the [Metrics] interface for
-// BackendPB.
-func (m *BackendPB) IncrementInvalidDevicesCount(_ context.Context) {
- m.devicesInvalidTotal.Inc()
-}
-
-// UpdateStats implements the [Metrics] interface for BackendPB.
-func (m *BackendPB) UpdateStats(_ context.Context, avgRecv, avgDec time.Duration) {
- m.grpcAvgProfileRecvDuration.Observe(avgRecv.Seconds())
- m.grpcAvgProfileDecDuration.Observe(avgDec.Seconds())
-}
diff --git a/internal/metrics/dnssvc.go b/internal/metrics/dnssvc.go
index cf79082..3dadeec 100644
--- a/internal/metrics/dnssvc.go
+++ b/internal/metrics/dnssvc.go
@@ -25,6 +25,13 @@ var (
"kind": "bad_resolver_arpa",
})
+ // DNSSvcChromePrefetchRequestsTotal is a counter with total number of
+ // requests for the domain name that Chrome uses to check if it should use
+ // its prefetch proxy.
+ DNSSvcChromePrefetchRequestsTotal = specialRequestsTotal.With(prometheus.Labels{
+ "kind": "chrome_prefetch",
+ })
+
// DNSSvcFirefoxRequestsTotal is a counter with total number of requests for
// the domain name that Firefox uses to check if it should use its own
// DNS-over-HTTPS settings.
diff --git a/internal/metrics/filter.go b/internal/metrics/filter.go
index 47f9c52..2945932 100644
--- a/internal/metrics/filter.go
+++ b/internal/metrics/filter.go
@@ -1,38 +1,17 @@
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 (
- // FilterRulesTotal is a gauge with the number of rules loaded by each
- // filter.
- FilterRulesTotal = promauto.NewGaugeVec(prometheus.GaugeOpts{
- Name: "rules_total",
- Subsystem: subsystemFilter,
- Namespace: namespace,
- Help: "The number of rules loaded by filters.",
- }, []string{"filter"})
-
- // FilterUpdatedTime is a gauge with the last time when the filter was last
- // time updated.
- FilterUpdatedTime = promauto.NewGaugeVec(prometheus.GaugeOpts{
- Name: "updated_time",
- Subsystem: subsystemFilter,
- Namespace: namespace,
- Help: "Time when the filter was last time updated.",
- }, []string{"filter"})
-
- // FilterUpdatedStatus is a gauge with status of the last filter update.
- // "0" means error, "1" means success.
- FilterUpdatedStatus = promauto.NewGaugeVec(prometheus.GaugeOpts{
- Name: "update_status",
- Subsystem: subsystemFilter,
- Namespace: namespace,
- Help: "Status of the filter update. 1 means success.",
- }, []string{"filter"})
-
// 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".
@@ -133,3 +112,96 @@ var (
"filter": "newly_registered_domains",
})
)
+
+// Filter is the Prometheus-based implementation of the [Filter]
+// interface.
+type Filter struct {
+ // rulesTotal is the gauge vector with the number of rules loaded by each
+ // filter.
+ rulesTotal *prometheus.GaugeVec
+
+ // updateStatus is the gauge vector with status of the last filter update.
+ // "0" means error, "1" means success.
+ updateStatus *prometheus.GaugeVec
+
+ // updateTime is the gauge vector with the last time when the filter was
+ // last updated.
+ updatedTime *prometheus.GaugeVec
+}
+
+// NewFilter registers the filtering metrics in reg and returns a properly
+// initialized *Filter.
+func NewFilter(namespace string, reg prometheus.Registerer) (m *Filter, err error) {
+ const (
+ rulesTotal = "rules_total"
+ updateStatus = "update_status"
+ updatedTime = "updated_time"
+ )
+
+ m = &Filter{
+ rulesTotal: prometheus.NewGaugeVec(prometheus.GaugeOpts{
+ Name: rulesTotal,
+ Subsystem: subsystemFilter,
+ Namespace: namespace,
+ Help: "The number of rules loaded by filters.",
+ }, []string{"filter"}),
+
+ updateStatus: prometheus.NewGaugeVec(prometheus.GaugeOpts{
+ Name: updateStatus,
+ Subsystem: subsystemFilter,
+ Namespace: namespace,
+ Help: "Status of the filter update. 1 means success.",
+ }, []string{"filter"}),
+
+ updatedTime: prometheus.NewGaugeVec(prometheus.GaugeOpts{
+ Name: updatedTime,
+ Subsystem: subsystemFilter,
+ Namespace: namespace,
+ Help: "Time when the filter was last time updated.",
+ }, []string{"filter"}),
+ }
+
+ var errs []error
+ collectors := container.KeyValues[string, prometheus.Collector]{{
+ Key: rulesTotal,
+ Value: m.rulesTotal,
+ }, {
+ Key: updateStatus,
+ Value: m.updateStatus,
+ }, {
+ Key: updatedTime,
+ Value: m.updatedTime,
+ }}
+
+ 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
+}
+
+// SetFilterStatus implements the [filter.Metrics] interface for *Filter.
+func (m *Filter) SetFilterStatus(
+ ctx context.Context,
+ id string,
+ updTime time.Time,
+ ruleCount int,
+ err error,
+) {
+ if err != nil {
+ m.updateStatus.WithLabelValues(id).Set(0)
+
+ return
+ }
+
+ m.rulesTotal.WithLabelValues(id).Set(float64(ruleCount))
+ m.updateStatus.WithLabelValues(id).Set(1)
+ m.updatedTime.WithLabelValues(id).Set(float64(updTime.UnixNano()) / float64(time.Second))
+}
diff --git a/internal/metrics/mainmw.go b/internal/metrics/mainmw.go
index 0f7dbfd..3b25f95 100644
--- a/internal/metrics/mainmw.go
+++ b/internal/metrics/mainmw.go
@@ -47,7 +47,8 @@ type DefaultMainMiddleware struct {
requestPerASNTotal *prometheus.CounterVec
// requestPerCountryTotal is a counter with the total number of queries
- // processed labeled by country and continent.
+ // processed labeled by country, continent, and whether any filter has been
+ // applied.
requestPerCountryTotal *prometheus.CounterVec
// requestPerFilterTotal is a counter with the total number of queries
@@ -117,8 +118,9 @@ func NewDefaultMainMiddleware(
Name: requestPerCountryTotal,
Namespace: namespace,
Subsystem: subsystemDNSSvc,
- Help: "The number of processed DNS requests labeled by country and continent.",
- }, []string{"continent", "country"}),
+ Help: "The number of processed DNS requests labeled by country and continent. " +
+ "filters_applied=0 means that no filter has been applied",
+ }, []string{"continent", "country", "filters_applied"}),
requestPerFilterTotal: prometheus.NewCounterVec(prometheus.CounterOpts{
Name: requestPerFilterTotal,
@@ -181,16 +183,19 @@ func NewDefaultMainMiddleware(
// OnRequest implements the [Metrics] interface for *DefaultMainMiddleware.
func (m *DefaultMainMiddleware) OnRequest(_ context.Context, rm *MainMiddlewareRequestMetrics) {
- asnStr := strconv.FormatUint(uint64(rm.ASN), 10)
-
m.filteringDuration.Observe(rm.FilteringDuration.Seconds())
+
+ asnStr := strconv.FormatUint(uint64(rm.ASN), 10)
m.requestPerASNTotal.WithLabelValues(rm.Country, asnStr).Inc()
- m.requestPerCountryTotal.WithLabelValues(rm.Continent, rm.Country).Inc()
+
+ // FilterListID is only empty if no filter has been applied.
+ filtersApplied := BoolString(rm.FilterListID != "")
+ m.requestPerCountryTotal.WithLabelValues(rm.Continent, rm.Country, filtersApplied).Inc()
+
m.requestPerFilterTotal.WithLabelValues(rm.FilterListID, BoolString(rm.IsAnonymous)).Inc()
// 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)
}
diff --git a/internal/metrics/metrics_test.go b/internal/metrics/metrics_test.go
index 0a50d9c..73c2259 100644
--- a/internal/metrics/metrics_test.go
+++ b/internal/metrics/metrics_test.go
@@ -6,6 +6,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/consul"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/dnssvc"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/metrics"
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb"
"github.com/AdguardTeam/AdGuardDNS/internal/remotekv/rediskv"
@@ -17,7 +18,9 @@ import (
// TODO(s.chzhen): Move into the package itself when all metrics are
// refactored.
var (
- _ backendpb.Metrics = (*metrics.BackendPB)(nil)
+ _ backendpb.GRPCMetrics = (*metrics.BackendGRPC)(nil)
+ _ backendpb.ProfileDBMetrics = (*metrics.BackendProfileDB)(nil)
+ _ backendpb.RemoteKVMetrics = (*metrics.BackendRemoteKV)(nil)
_ billstat.Metrics = (*metrics.Billstat)(nil)
_ consul.Metrics = (*metrics.Allowlist)(nil)
_ dnsmsg.ClonerStat = metrics.ClonerStat{}
@@ -25,6 +28,7 @@ var (
_ dnssvc.MainMiddlewareMetrics = metrics.MainMiddleware(nil)
_ dnssvc.RatelimitMiddlewareMetrics = (*metrics.DefaultRatelimitMiddleware)(nil)
_ dnssvc.RatelimitMiddlewareMetrics = metrics.RatelimitMiddleware(nil)
+ _ filter.Metrics = (*metrics.Filter)(nil)
_ profiledb.Metrics = (*metrics.ProfileDB)(nil)
_ rediskv.Metrics = (*metrics.RedisKV)(nil)
_ tlsconfig.Metrics = (*metrics.TLSConfig)(nil)
diff --git a/internal/metrics/profiledb.go b/internal/metrics/profiledb.go
index f886f56..d899213 100644
--- a/internal/metrics/profiledb.go
+++ b/internal/metrics/profiledb.go
@@ -107,7 +107,8 @@ func NewProfileDB(namespace string, reg prometheus.Registerer) (m *ProfileDB, er
Name: devicesNewCount,
Subsystem: subsystemBackend,
Namespace: namespace,
- Help: "The number of user devices that were changed or added since the previous sync.",
+ Help: "The number of user devices that were changed or added since " +
+ "the previous sync.",
}),
profilesCount: prometheus.NewGauge(prometheus.GaugeOpts{
Name: profilesCount,
@@ -119,7 +120,8 @@ func NewProfileDB(namespace string, reg prometheus.Registerer) (m *ProfileDB, er
Name: profilesNewCount,
Subsystem: subsystemBackend,
Namespace: namespace,
- Help: "The number of user profiles that were changed or added since the previous sync.",
+ Help: "The number of user profiles that were changed or added since " +
+ "the previous sync.",
}),
profilesDeletedTotal: prometheus.NewCounter(prometheus.CounterOpts{
Name: profilesDeletedTotal,
@@ -251,3 +253,95 @@ func (m *ProfileDB) IncrementSyncTimeouts(_ context.Context, isFullSync bool) {
func (m *ProfileDB) IncrementDeleted(_ context.Context) {
m.profilesDeletedTotal.Inc()
}
+
+// BackendProfileDB is the Prometheus-based implementation of the
+// [backendpb.ProfileDBMetrics] interface.
+type BackendProfileDB struct {
+ // devicesInvalidTotal is a gauge with the number of invalid user devices
+ // loaded from the backend.
+ devicesInvalidTotal prometheus.Counter
+
+ // grpcAvgProfileRecvDuration is a histogram with the average duration of a
+ // receive of a single profile during a backend call.
+ grpcAvgProfileRecvDuration prometheus.Histogram
+
+ // grpcAvgProfileDecDuration is a histogram with the average duration of
+ // decoding a single profile during a backend call.
+ grpcAvgProfileDecDuration prometheus.Histogram
+}
+
+// NewBackendProfileDB registers the protobuf errors metrics in reg and returns
+// a properly initialized [BackendProfileDB].
+func NewBackendProfileDB(
+ namespace string,
+ reg prometheus.Registerer,
+) (m *BackendProfileDB, err error) {
+ const (
+ devicesInvalidTotal = "devices_invalid_total"
+ grpcAvgProfileRecvDuration = "grpc_avg_profile_recv_duration_seconds"
+ grpcAvgProfileDecDuration = "grpc_avg_profile_dec_duration_seconds"
+ )
+
+ m = &BackendProfileDB{
+ devicesInvalidTotal: prometheus.NewCounter(prometheus.CounterOpts{
+ Name: devicesInvalidTotal,
+ Subsystem: subsystemBackend,
+ Namespace: namespace,
+ Help: "The total number of invalid user devices loaded from the backend.",
+ }),
+ grpcAvgProfileRecvDuration: prometheus.NewHistogram(prometheus.HistogramOpts{
+ Name: grpcAvgProfileRecvDuration,
+ Subsystem: subsystemBackend,
+ Namespace: namespace,
+ Help: "The average duration of a receive of a profile during a call to the backend, " +
+ "in seconds.",
+ Buckets: []float64{0.000_001, 0.000_010, 0.000_100, 0.001},
+ }),
+ grpcAvgProfileDecDuration: prometheus.NewHistogram(prometheus.HistogramOpts{
+ Name: grpcAvgProfileDecDuration,
+ Subsystem: subsystemBackend,
+ Namespace: namespace,
+ Help: "The average duration of decoding one profile during a call to the backend, " +
+ "in seconds.",
+ Buckets: []float64{0.000_001, 0.000_01, 0.000_1, 0.001},
+ }),
+ }
+
+ var errs []error
+ collectors := container.KeyValues[string, prometheus.Collector]{{
+ Key: devicesInvalidTotal,
+ Value: m.devicesInvalidTotal,
+ }, {
+ Key: grpcAvgProfileRecvDuration,
+ Value: m.grpcAvgProfileRecvDuration,
+ }, {
+ Key: grpcAvgProfileDecDuration,
+ Value: m.grpcAvgProfileDecDuration,
+ }}
+
+ 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
+}
+
+// IncrementInvalidDevicesCount implements the [backendpb.ProfileDBMetrics]
+// interface for BackendProfileDB.
+func (m *BackendProfileDB) IncrementInvalidDevicesCount(_ context.Context) {
+ m.devicesInvalidTotal.Inc()
+}
+
+// UpdateStats implements the [backendpb.ProfileDBMetrics] interface for
+// BackendProfileDB.
+func (m *BackendProfileDB) UpdateStats(_ context.Context, avgRecv, avgDec time.Duration) {
+ m.grpcAvgProfileRecvDuration.Observe(avgRecv.Seconds())
+ m.grpcAvgProfileDecDuration.Observe(avgDec.Seconds())
+}
diff --git a/internal/metrics/rediskv.go b/internal/metrics/rediskv.go
deleted file mode 100644
index df4c9a3..0000000
--- a/internal/metrics/rediskv.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package metrics
-
-import (
- "context"
- "fmt"
-
- "github.com/AdguardTeam/golibs/container"
- "github.com/AdguardTeam/golibs/errors"
- "github.com/prometheus/client_golang/prometheus"
-)
-
-// RedisKV is the Prometheus-based implementation of the [rediskv.Metrics]
-// interface.
-type RedisKV struct {
- // activeConnections is a gauge with the total number of active connections
- // in Redis pool. The count includes idle connections and connections in
- // use.
- activeConnections prometheus.Gauge
-
- // errors is a counter of errors occurred with Redis KV.
- errors prometheus.Counter
-}
-
-// NewRedisKV registers the Redis KV metrics in reg and returns a properly
-// initialized [RedisKV].
-func NewRedisKV(namespace string, reg prometheus.Registerer) (m *RedisKV, err error) {
- const (
- redisActiveConnections = "redis_active_connections"
- redisErrors = "redis_errors_total"
- )
-
- m = &RedisKV{
- activeConnections: prometheus.NewGauge(prometheus.GaugeOpts{
- Name: redisActiveConnections,
- Subsystem: subsystemDNSCheck,
- Namespace: namespace,
- Help: "Total number of active connections in redis pool",
- }),
- errors: prometheus.NewCounter(prometheus.CounterOpts{
- Name: redisErrors,
- Subsystem: subsystemDNSCheck,
- Namespace: namespace,
- Help: "Total number of errors encountered with redis pool",
- }),
- }
-
- var errs []error
- collectors := container.KeyValues[string, prometheus.Collector]{{
- Key: redisActiveConnections,
- Value: m.activeConnections,
- }, {
- Key: redisErrors,
- Value: m.errors,
- }}
-
- 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
-}
-
-// UpdateMetrics implements the [rediskv.Metrics] interface for *RedisKV.
-func (m *RedisKV) UpdateMetrics(_ context.Context, val uint, isSuccess bool) {
- m.activeConnections.Set(float64(val))
-
- if !isSuccess {
- m.errors.Inc()
- }
-}
diff --git a/internal/metrics/remotekv.go b/internal/metrics/remotekv.go
new file mode 100644
index 0000000..e7a09d3
--- /dev/null
+++ b/internal/metrics/remotekv.go
@@ -0,0 +1,189 @@
+package metrics
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/AdguardTeam/golibs/container"
+ "github.com/AdguardTeam/golibs/errors"
+ "github.com/prometheus/client_golang/prometheus"
+)
+
+// RedisKV is the Prometheus-based implementation of the [rediskv.Metrics]
+// interface.
+type RedisKV struct {
+ // activeConnections is a gauge with the total number of active connections
+ // in Redis pool. The count includes idle connections and connections in
+ // use.
+ activeConnections prometheus.Gauge
+
+ // errors is a counter of errors occurred with Redis KV.
+ errors prometheus.Counter
+}
+
+// NewRedisKV registers the Redis KV metrics in reg and returns a properly
+// initialized [RedisKV].
+func NewRedisKV(namespace string, reg prometheus.Registerer) (m *RedisKV, err error) {
+ const (
+ redisActiveConnections = "redis_active_connections"
+ redisErrors = "redis_errors_total"
+ )
+
+ m = &RedisKV{
+ activeConnections: prometheus.NewGauge(prometheus.GaugeOpts{
+ Name: redisActiveConnections,
+ Subsystem: subsystemDNSCheck,
+ Namespace: namespace,
+ Help: "Total number of active connections in redis pool",
+ }),
+ errors: prometheus.NewCounter(prometheus.CounterOpts{
+ Name: redisErrors,
+ Subsystem: subsystemDNSCheck,
+ Namespace: namespace,
+ Help: "Total number of errors encountered with redis pool",
+ }),
+ }
+
+ var errs []error
+ collectors := container.KeyValues[string, prometheus.Collector]{{
+ Key: redisActiveConnections,
+ Value: m.activeConnections,
+ }, {
+ Key: redisErrors,
+ Value: m.errors,
+ }}
+
+ 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
+}
+
+// UpdateMetrics implements the [rediskv.Metrics] interface for *RedisKV.
+func (m *RedisKV) UpdateMetrics(_ context.Context, val uint, isSuccess bool) {
+ m.activeConnections.Set(float64(val))
+
+ if !isSuccess {
+ m.errors.Inc()
+ }
+}
+
+// RemoteKVOp is the type alias for string that contains remote key-value
+// storage operation name.
+//
+// See [backendpb.RemoteKVMetrics.ObserveOperation].
+type RemoteKVOp = string
+
+// Remote key-value storage operation names for [RemoteKVOp].
+//
+// NOTE: Keep in sync with [backendpb.RemoteKVOp].
+const (
+ RemoteKVOpGet RemoteKVOp = "get"
+ RemoteKVOpSet RemoteKVOp = "set"
+)
+
+// BackendRemoteKV is the Prometheus-based implementation of the
+// [backendpb.Metrics] interface.
+type BackendRemoteKV struct {
+ // getDuration is a histogram with the duration of a receive of a single
+ // value during a call to backend remote key-value storage.
+ getDuration prometheus.Observer
+
+ // setDuration is a histogram with the duration of a sending of a single
+ // value during a call to backend remote key-value storage.
+ setDuration prometheus.Observer
+
+ // hits is a counter of the total number of lookups to the remote key-value
+ // storage that succeeded.
+ hits prometheus.Counter
+
+ // misses is a counter of the total number of lookups to the remote
+ // key-value storage that resulted in a miss.
+ misses prometheus.Counter
+}
+
+// NewBackendRemoteKV registers the backend remote key-value storage metrics in
+// reg and returns a properly initialized [BackendRemoteKV].
+func NewBackendRemoteKV(
+ namespace string,
+ reg prometheus.Registerer,
+) (m *BackendRemoteKV, err error) {
+ const (
+ backendOpDuration = "grpc_remotekv_op_duration_seconds"
+ backendLookups = "grpc_remotekv_lookups_total"
+ )
+
+ opDuration := prometheus.NewHistogramVec(prometheus.HistogramOpts{
+ Name: backendOpDuration,
+ Subsystem: subsystemBackend,
+ Namespace: namespace,
+ Help: "Duration of a single remote key-value storage operation. " +
+ "Label op is the corresponding operation name.",
+ Buckets: []float64{0.000_001, 0.000_010, 0.000_100, 0.001, 0.010, 0.100},
+ }, []string{"op"})
+
+ lookups := prometheus.NewCounterVec(prometheus.CounterOpts{
+ Name: backendLookups,
+ Subsystem: subsystemBackend,
+ Namespace: namespace,
+ Help: "Total number of lookups to the remote key-value storage. " +
+ "Lable hit is the lookup result, either 1 for hit or 0 for miss.",
+ }, []string{"hit"})
+
+ m = &BackendRemoteKV{
+ getDuration: opDuration.WithLabelValues(RemoteKVOpGet),
+ setDuration: opDuration.WithLabelValues(RemoteKVOpSet),
+ hits: lookups.WithLabelValues("1"),
+ misses: lookups.WithLabelValues("0"),
+ }
+
+ var errs []error
+ collectors := container.KeyValues[string, prometheus.Collector]{{
+ Key: backendOpDuration,
+ Value: opDuration,
+ }, {
+ Key: backendLookups,
+ 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
+}
+
+// ObserveOperation implements the [backendpb.RemoteKVMetrics] interface for
+// *BackendRemoteKV.
+func (m *BackendRemoteKV) ObserveOperation(_ context.Context, op string, dur time.Duration) {
+ switch op {
+ case RemoteKVOpGet:
+ m.getDuration.Observe(dur.Seconds())
+ case RemoteKVOpSet:
+ m.setDuration.Observe(dur.Seconds())
+ default:
+ panic(fmt.Errorf("operation: %w: %q", errors.ErrBadEnumValue, op))
+ }
+}
+
+// IncrementLookups implements the [backendpb.RemoteKVMetrics] interface for
+// *BackendRemoteKV.
+func (m *BackendRemoteKV) IncrementLookups(_ context.Context, hit bool) {
+ IncrementCond(hit, m.hits, m.misses)
+}
diff --git a/internal/metrics/tls.go b/internal/metrics/tls.go
index e38d2b8..5aed2e3 100644
--- a/internal/metrics/tls.go
+++ b/internal/metrics/tls.go
@@ -42,7 +42,7 @@ type TLSConfig struct {
handshakeTotal *prometheus.CounterVec
}
-// NewTLSConfig registers the TLS related metrics in reg and returns a properly
+// NewTLSConfig registers the TLS-related metrics in reg and returns a properly
// initialized [TLSConfig].
func NewTLSConfig(namespace string, reg prometheus.Registerer) (m *TLSConfig, err error) {
const (
@@ -172,7 +172,7 @@ func (m *TLSConfig) AfterHandshake(
proto string,
srvName string,
devDomains []string,
- srvCerts []tls.Certificate,
+ srvCerts []*tls.Certificate,
) (f func(tls.ConnectionState) error) {
return func(state tls.ConnectionState) error {
sLabel := serverNameToLabel(state.ServerName, srvName, devDomains, srvCerts)
@@ -245,7 +245,7 @@ func serverNameToLabel(
sni string,
srvName string,
devDomains []string,
- srvCerts []tls.Certificate,
+ srvCerts []*tls.Certificate,
) (label string) {
if sni == "" {
// SNI is empty, so the request is probably made on the IP address.
@@ -260,7 +260,7 @@ func serverNameToLabel(
}
// matchServerNames matches sni with known servers.
-func matchServerNames(sni string, devDomains []string, srvCerts []tls.Certificate) (match string) {
+func matchServerNames(sni string, devDomains []string, srvCerts []*tls.Certificate) (match string) {
if matchedDomain := matchDeviceDomains(sni, devDomains); matchedDomain != "" {
return matchedDomain
}
@@ -287,7 +287,7 @@ func matchDeviceDomains(sni string, domains []string) (matchedDomain string) {
}
// matchSrvCerts matches sni to DNSNames in srvCerts.
-func matchSrvCerts(sni string, srvCerts []tls.Certificate) (match string) {
+func matchSrvCerts(sni string, srvCerts []*tls.Certificate) (match string) {
for _, cert := range srvCerts {
leaf := cert.Leaf
if leaf == nil {
diff --git a/internal/metrics/tls_test.go b/internal/metrics/tls_test.go
index 6f4c042..ef1aacb 100644
--- a/internal/metrics/tls_test.go
+++ b/internal/metrics/tls_test.go
@@ -85,18 +85,18 @@ func TestTLSConfig_AfterHandshake(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- x509Cert := x509.Certificate{}
+ x509Cert := &x509.Certificate{}
if tc.DNSNames != nil {
x509Cert.DNSNames = append(x509Cert.DNSNames, tc.DNSNames...)
}
- cert := tls.Certificate{Leaf: &x509Cert}
+ cert := &tls.Certificate{Leaf: x509Cert}
listener := m.AfterHandshake(
"",
serverName,
tc.devDomains,
- []tls.Certificate{cert},
+ []*tls.Certificate{cert},
)
err = listener(tls.ConnectionState{ServerName: tc.connectionServerName})
diff --git a/internal/profiledb/internal/filecachepb/filecache.pb.go b/internal/profiledb/internal/filecachepb/filecache.pb.go
index 10a5db1..e926633 100644
--- a/internal/profiledb/internal/filecachepb/filecache.pb.go
+++ b/internal/profiledb/internal/filecachepb/filecache.pb.go
@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
-// protoc-gen-go v1.35.1
+// protoc-gen-go v1.35.2
// protoc v5.28.3
// source: filecache.proto
@@ -96,9 +96,8 @@ type Profile struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- Access *AccessSettings `protobuf:"bytes,21,opt,name=access,proto3" json:"access,omitempty"`
- Parental *ParentalProtectionSettings `protobuf:"bytes,1,opt,name=parental,proto3" json:"parental,omitempty"`
- SafeBrowsing *SafeBrowsingSettings `protobuf:"bytes,19,opt,name=safe_browsing,json=safeBrowsing,proto3" json:"safe_browsing,omitempty"`
+ 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:
//
// *Profile_BlockingModeCustomIp
@@ -106,23 +105,18 @@ type Profile struct {
// *Profile_BlockingModeNullIp
// *Profile_BlockingModeRefused
BlockingMode isProfile_BlockingMode `protobuf_oneof:"blocking_mode"`
- ProfileId string `protobuf:"bytes,6,opt,name=profile_id,json=profileId,proto3" json:"profile_id,omitempty"`
- UpdateTime *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty"`
- DeviceIds []string `protobuf:"bytes,8,rep,name=device_ids,json=deviceIds,proto3" json:"device_ids,omitempty"`
- RuleListIds []string `protobuf:"bytes,9,rep,name=rule_list_ids,json=ruleListIds,proto3" json:"rule_list_ids,omitempty"`
- CustomRules []string `protobuf:"bytes,10,rep,name=custom_rules,json=customRules,proto3" json:"custom_rules,omitempty"`
- FilteredResponseTtl *durationpb.Duration `protobuf:"bytes,11,opt,name=filtered_response_ttl,json=filteredResponseTtl,proto3" json:"filtered_response_ttl,omitempty"`
- FilteringEnabled bool `protobuf:"varint,12,opt,name=filtering_enabled,json=filteringEnabled,proto3" json:"filtering_enabled,omitempty"`
- // Deprecated: Marked as deprecated in filecache.proto.
- SafeBrowsingEnabled bool `protobuf:"varint,13,opt,name=safe_browsing_enabled,json=safeBrowsingEnabled,proto3" json:"safe_browsing_enabled,omitempty"`
- RuleListsEnabled bool `protobuf:"varint,14,opt,name=rule_lists_enabled,json=ruleListsEnabled,proto3" json:"rule_lists_enabled,omitempty"`
- QueryLogEnabled bool `protobuf:"varint,15,opt,name=query_log_enabled,json=queryLogEnabled,proto3" json:"query_log_enabled,omitempty"`
- Deleted bool `protobuf:"varint,16,opt,name=deleted,proto3" json:"deleted,omitempty"`
- BlockPrivateRelay bool `protobuf:"varint,17,opt,name=block_private_relay,json=blockPrivateRelay,proto3" json:"block_private_relay,omitempty"`
- BlockFirefoxCanary bool `protobuf:"varint,18,opt,name=block_firefox_canary,json=blockFirefoxCanary,proto3" json:"block_firefox_canary,omitempty"`
- IpLogEnabled bool `protobuf:"varint,20,opt,name=ip_log_enabled,json=ipLogEnabled,proto3" json:"ip_log_enabled,omitempty"`
- AutoDevicesEnabled bool `protobuf:"varint,22,opt,name=auto_devices_enabled,json=autoDevicesEnabled,proto3" json:"auto_devices_enabled,omitempty"`
- RateLimit *RateLimitSettings `protobuf:"bytes,23,opt,name=rate_limit,json=rateLimit,proto3" json:"rate_limit,omitempty"`
+ Ratelimiter *Ratelimiter `protobuf:"bytes,7,opt,name=ratelimiter,proto3" json:"ratelimiter,omitempty"`
+ ProfileId string `protobuf:"bytes,8,opt,name=profile_id,json=profileId,proto3" json:"profile_id,omitempty"`
+ DeviceIds []string `protobuf:"bytes,9,rep,name=device_ids,json=deviceIds,proto3" json:"device_ids,omitempty"`
+ FilteredResponseTtl *durationpb.Duration `protobuf:"bytes,10,opt,name=filtered_response_ttl,json=filteredResponseTtl,proto3" json:"filtered_response_ttl,omitempty"`
+ AutoDevicesEnabled bool `protobuf:"varint,11,opt,name=auto_devices_enabled,json=autoDevicesEnabled,proto3" json:"auto_devices_enabled,omitempty"`
+ BlockChromePrefetch bool `protobuf:"varint,12,opt,name=block_chrome_prefetch,json=blockChromePrefetch,proto3" json:"block_chrome_prefetch,omitempty"`
+ BlockFirefoxCanary bool `protobuf:"varint,13,opt,name=block_firefox_canary,json=blockFirefoxCanary,proto3" json:"block_firefox_canary,omitempty"`
+ BlockPrivateRelay bool `protobuf:"varint,14,opt,name=block_private_relay,json=blockPrivateRelay,proto3" json:"block_private_relay,omitempty"`
+ Deleted bool `protobuf:"varint,15,opt,name=deleted,proto3" json:"deleted,omitempty"`
+ 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"`
}
func (x *Profile) Reset() {
@@ -155,27 +149,20 @@ func (*Profile) Descriptor() ([]byte, []int) {
return file_filecache_proto_rawDescGZIP(), []int{1}
}
-func (x *Profile) GetAccess() *AccessSettings {
+func (x *Profile) GetFilterConfig() *FilterConfig {
+ if x != nil {
+ return x.FilterConfig
+ }
+ return nil
+}
+
+func (x *Profile) GetAccess() *Access {
if x != nil {
return x.Access
}
return nil
}
-func (x *Profile) GetParental() *ParentalProtectionSettings {
- if x != nil {
- return x.Parental
- }
- return nil
-}
-
-func (x *Profile) GetSafeBrowsing() *SafeBrowsingSettings {
- if x != nil {
- return x.SafeBrowsing
- }
- return nil
-}
-
func (m *Profile) GetBlockingMode() isProfile_BlockingMode {
if m != nil {
return m.BlockingMode
@@ -211,6 +198,13 @@ func (x *Profile) GetBlockingModeRefused() *BlockingModeREFUSED {
return nil
}
+func (x *Profile) GetRatelimiter() *Ratelimiter {
+ if x != nil {
+ return x.Ratelimiter
+ }
+ return nil
+}
+
func (x *Profile) GetProfileId() string {
if x != nil {
return x.ProfileId
@@ -218,13 +212,6 @@ func (x *Profile) GetProfileId() string {
return ""
}
-func (x *Profile) GetUpdateTime() *timestamppb.Timestamp {
- if x != nil {
- return x.UpdateTime
- }
- return nil
-}
-
func (x *Profile) GetDeviceIds() []string {
if x != nil {
return x.DeviceIds
@@ -232,20 +219,6 @@ func (x *Profile) GetDeviceIds() []string {
return nil
}
-func (x *Profile) GetRuleListIds() []string {
- if x != nil {
- return x.RuleListIds
- }
- return nil
-}
-
-func (x *Profile) GetCustomRules() []string {
- if x != nil {
- return x.CustomRules
- }
- return nil
-}
-
func (x *Profile) GetFilteredResponseTtl() *durationpb.Duration {
if x != nil {
return x.FilteredResponseTtl
@@ -253,45 +226,16 @@ func (x *Profile) GetFilteredResponseTtl() *durationpb.Duration {
return nil
}
-func (x *Profile) GetFilteringEnabled() bool {
+func (x *Profile) GetAutoDevicesEnabled() bool {
if x != nil {
- return x.FilteringEnabled
+ return x.AutoDevicesEnabled
}
return false
}
-// Deprecated: Marked as deprecated in filecache.proto.
-func (x *Profile) GetSafeBrowsingEnabled() bool {
+func (x *Profile) GetBlockChromePrefetch() bool {
if x != nil {
- return x.SafeBrowsingEnabled
- }
- return false
-}
-
-func (x *Profile) GetRuleListsEnabled() bool {
- if x != nil {
- return x.RuleListsEnabled
- }
- return false
-}
-
-func (x *Profile) GetQueryLogEnabled() bool {
- if x != nil {
- return x.QueryLogEnabled
- }
- return false
-}
-
-func (x *Profile) GetDeleted() bool {
- if x != nil {
- return x.Deleted
- }
- return false
-}
-
-func (x *Profile) GetBlockPrivateRelay() bool {
- if x != nil {
- return x.BlockPrivateRelay
+ return x.BlockChromePrefetch
}
return false
}
@@ -303,6 +247,27 @@ func (x *Profile) GetBlockFirefoxCanary() bool {
return false
}
+func (x *Profile) GetBlockPrivateRelay() bool {
+ if x != nil {
+ return x.BlockPrivateRelay
+ }
+ return false
+}
+
+func (x *Profile) GetDeleted() bool {
+ if x != nil {
+ return x.Deleted
+ }
+ return false
+}
+
+func (x *Profile) GetFilteringEnabled() bool {
+ if x != nil {
+ return x.FilteringEnabled
+ }
+ return false
+}
+
func (x *Profile) GetIpLogEnabled() bool {
if x != nil {
return x.IpLogEnabled
@@ -310,38 +275,31 @@ func (x *Profile) GetIpLogEnabled() bool {
return false
}
-func (x *Profile) GetAutoDevicesEnabled() bool {
+func (x *Profile) GetQueryLogEnabled() bool {
if x != nil {
- return x.AutoDevicesEnabled
+ return x.QueryLogEnabled
}
return false
}
-func (x *Profile) GetRateLimit() *RateLimitSettings {
- if x != nil {
- return x.RateLimit
- }
- return nil
-}
-
type isProfile_BlockingMode interface {
isProfile_BlockingMode()
}
type Profile_BlockingModeCustomIp struct {
- BlockingModeCustomIp *BlockingModeCustomIP `protobuf:"bytes,2,opt,name=blocking_mode_custom_ip,json=blockingModeCustomIp,proto3,oneof"`
+ BlockingModeCustomIp *BlockingModeCustomIP `protobuf:"bytes,3,opt,name=blocking_mode_custom_ip,json=blockingModeCustomIp,proto3,oneof"`
}
type Profile_BlockingModeNxdomain struct {
- BlockingModeNxdomain *BlockingModeNXDOMAIN `protobuf:"bytes,3,opt,name=blocking_mode_nxdomain,json=blockingModeNxdomain,proto3,oneof"`
+ BlockingModeNxdomain *BlockingModeNXDOMAIN `protobuf:"bytes,4,opt,name=blocking_mode_nxdomain,json=blockingModeNxdomain,proto3,oneof"`
}
type Profile_BlockingModeNullIp struct {
- BlockingModeNullIp *BlockingModeNullIP `protobuf:"bytes,4,opt,name=blocking_mode_null_ip,json=blockingModeNullIp,proto3,oneof"`
+ BlockingModeNullIp *BlockingModeNullIP `protobuf:"bytes,5,opt,name=blocking_mode_null_ip,json=blockingModeNullIp,proto3,oneof"`
}
type Profile_BlockingModeRefused struct {
- BlockingModeRefused *BlockingModeREFUSED `protobuf:"bytes,5,opt,name=blocking_mode_refused,json=blockingModeRefused,proto3,oneof"`
+ BlockingModeRefused *BlockingModeREFUSED `protobuf:"bytes,6,opt,name=blocking_mode_refused,json=blockingModeRefused,proto3,oneof"`
}
func (*Profile_BlockingModeCustomIp) isProfile_BlockingMode() {}
@@ -352,33 +310,31 @@ func (*Profile_BlockingModeNullIp) isProfile_BlockingMode() {}
func (*Profile_BlockingModeRefused) isProfile_BlockingMode() {}
-type ParentalProtectionSettings struct {
+type FilterConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- Schedule *ParentalProtectionSchedule `protobuf:"bytes,1,opt,name=schedule,proto3" json:"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"`
- BlockAdult bool `protobuf:"varint,4,opt,name=block_adult,json=blockAdult,proto3" json:"block_adult,omitempty"`
- GeneralSafeSearch bool `protobuf:"varint,5,opt,name=general_safe_search,json=generalSafeSearch,proto3" json:"general_safe_search,omitempty"`
- YoutubeSafeSearch bool `protobuf:"varint,6,opt,name=youtube_safe_search,json=youtubeSafeSearch,proto3" json:"youtube_safe_search,omitempty"`
+ 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"`
}
-func (x *ParentalProtectionSettings) Reset() {
- *x = ParentalProtectionSettings{}
+func (x *FilterConfig) Reset() {
+ *x = FilterConfig{}
mi := &file_filecache_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
-func (x *ParentalProtectionSettings) String() string {
+func (x *FilterConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
-func (*ParentalProtectionSettings) ProtoMessage() {}
+func (*FilterConfig) ProtoMessage() {}
-func (x *ParentalProtectionSettings) ProtoReflect() protoreflect.Message {
+func (x *FilterConfig) ProtoReflect() protoreflect.Message {
mi := &file_filecache_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@@ -390,216 +346,40 @@ func (x *ParentalProtectionSettings) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
-// Deprecated: Use ParentalProtectionSettings.ProtoReflect.Descriptor instead.
-func (*ParentalProtectionSettings) Descriptor() ([]byte, []int) {
+// Deprecated: Use FilterConfig.ProtoReflect.Descriptor instead.
+func (*FilterConfig) Descriptor() ([]byte, []int) {
return file_filecache_proto_rawDescGZIP(), []int{2}
}
-func (x *ParentalProtectionSettings) GetSchedule() *ParentalProtectionSchedule {
+func (x *FilterConfig) GetCustom() *FilterConfig_Custom {
if x != nil {
- return x.Schedule
+ return x.Custom
}
return nil
}
-func (x *ParentalProtectionSettings) GetBlockedServices() []string {
+func (x *FilterConfig) GetParental() *FilterConfig_Parental {
if x != nil {
- return x.BlockedServices
+ return x.Parental
}
return nil
}
-func (x *ParentalProtectionSettings) GetEnabled() bool {
+func (x *FilterConfig) GetRuleList() *FilterConfig_RuleList {
if x != nil {
- return x.Enabled
- }
- return false
-}
-
-func (x *ParentalProtectionSettings) GetBlockAdult() bool {
- if x != nil {
- return x.BlockAdult
- }
- return false
-}
-
-func (x *ParentalProtectionSettings) GetGeneralSafeSearch() bool {
- if x != nil {
- return x.GeneralSafeSearch
- }
- return false
-}
-
-func (x *ParentalProtectionSettings) GetYoutubeSafeSearch() bool {
- if x != nil {
- return x.YoutubeSafeSearch
- }
- return false
-}
-
-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"`
- BlockNewlyRegisteredDomains bool `protobuf:"varint,3,opt,name=block_newly_registered_domains,json=blockNewlyRegisteredDomains,proto3" json:"block_newly_registered_domains,omitempty"`
-}
-
-func (x *SafeBrowsingSettings) Reset() {
- *x = SafeBrowsingSettings{}
- mi := &file_filecache_proto_msgTypes[3]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
-}
-
-func (x *SafeBrowsingSettings) String() string {
- return protoimpl.X.MessageStringOf(x)
-}
-
-func (*SafeBrowsingSettings) ProtoMessage() {}
-
-func (x *SafeBrowsingSettings) ProtoReflect() protoreflect.Message {
- mi := &file_filecache_proto_msgTypes[3]
- if x != nil {
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- if ms.LoadMessageInfo() == nil {
- ms.StoreMessageInfo(mi)
- }
- return ms
- }
- return mi.MessageOf(x)
-}
-
-// Deprecated: Use SafeBrowsingSettings.ProtoReflect.Descriptor instead.
-func (*SafeBrowsingSettings) Descriptor() ([]byte, []int) {
- return file_filecache_proto_rawDescGZIP(), []int{3}
-}
-
-func (x *SafeBrowsingSettings) GetEnabled() bool {
- if x != nil {
- return x.Enabled
- }
- return false
-}
-
-func (x *SafeBrowsingSettings) GetBlockDangerousDomains() bool {
- if x != nil {
- return x.BlockDangerousDomains
- }
- return false
-}
-
-func (x *SafeBrowsingSettings) GetBlockNewlyRegisteredDomains() bool {
- if x != nil {
- return x.BlockNewlyRegisteredDomains
- }
- return false
-}
-
-type ParentalProtectionSchedule struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- TimeZone string `protobuf:"bytes,1,opt,name=time_zone,json=timeZone,proto3" json:"time_zone,omitempty"`
- Mon *DayRange `protobuf:"bytes,2,opt,name=mon,proto3" json:"mon,omitempty"`
- Tue *DayRange `protobuf:"bytes,3,opt,name=tue,proto3" json:"tue,omitempty"`
- Wed *DayRange `protobuf:"bytes,4,opt,name=wed,proto3" json:"wed,omitempty"`
- Thu *DayRange `protobuf:"bytes,5,opt,name=thu,proto3" json:"thu,omitempty"`
- Fri *DayRange `protobuf:"bytes,6,opt,name=fri,proto3" json:"fri,omitempty"`
- Sat *DayRange `protobuf:"bytes,7,opt,name=sat,proto3" json:"sat,omitempty"`
- Sun *DayRange `protobuf:"bytes,8,opt,name=sun,proto3" json:"sun,omitempty"`
-}
-
-func (x *ParentalProtectionSchedule) Reset() {
- *x = ParentalProtectionSchedule{}
- mi := &file_filecache_proto_msgTypes[4]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
-}
-
-func (x *ParentalProtectionSchedule) String() string {
- return protoimpl.X.MessageStringOf(x)
-}
-
-func (*ParentalProtectionSchedule) ProtoMessage() {}
-
-func (x *ParentalProtectionSchedule) ProtoReflect() protoreflect.Message {
- mi := &file_filecache_proto_msgTypes[4]
- if x != nil {
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- if ms.LoadMessageInfo() == nil {
- ms.StoreMessageInfo(mi)
- }
- return ms
- }
- return mi.MessageOf(x)
-}
-
-// Deprecated: Use ParentalProtectionSchedule.ProtoReflect.Descriptor instead.
-func (*ParentalProtectionSchedule) Descriptor() ([]byte, []int) {
- return file_filecache_proto_rawDescGZIP(), []int{4}
-}
-
-func (x *ParentalProtectionSchedule) GetTimeZone() string {
- if x != nil {
- return x.TimeZone
- }
- return ""
-}
-
-func (x *ParentalProtectionSchedule) GetMon() *DayRange {
- if x != nil {
- return x.Mon
+ return x.RuleList
}
return nil
}
-func (x *ParentalProtectionSchedule) GetTue() *DayRange {
+func (x *FilterConfig) GetSafeBrowsing() *FilterConfig_SafeBrowsing {
if x != nil {
- return x.Tue
+ return x.SafeBrowsing
}
return nil
}
-func (x *ParentalProtectionSchedule) GetWed() *DayRange {
- if x != nil {
- return x.Wed
- }
- return nil
-}
-
-func (x *ParentalProtectionSchedule) GetThu() *DayRange {
- if x != nil {
- return x.Thu
- }
- return nil
-}
-
-func (x *ParentalProtectionSchedule) GetFri() *DayRange {
- if x != nil {
- return x.Fri
- }
- return nil
-}
-
-func (x *ParentalProtectionSchedule) GetSat() *DayRange {
- if x != nil {
- return x.Sat
- }
- return nil
-}
-
-func (x *ParentalProtectionSchedule) GetSun() *DayRange {
- if x != nil {
- return x.Sun
- }
- return nil
-}
-
-type DayRange struct {
+type DayInterval struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
@@ -608,21 +388,21 @@ type DayRange struct {
End uint32 `protobuf:"varint,2,opt,name=end,proto3" json:"end,omitempty"`
}
-func (x *DayRange) Reset() {
- *x = DayRange{}
- mi := &file_filecache_proto_msgTypes[5]
+func (x *DayInterval) Reset() {
+ *x = DayInterval{}
+ mi := &file_filecache_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
-func (x *DayRange) String() string {
+func (x *DayInterval) String() string {
return protoimpl.X.MessageStringOf(x)
}
-func (*DayRange) ProtoMessage() {}
+func (*DayInterval) ProtoMessage() {}
-func (x *DayRange) ProtoReflect() protoreflect.Message {
- mi := &file_filecache_proto_msgTypes[5]
+func (x *DayInterval) ProtoReflect() protoreflect.Message {
+ mi := &file_filecache_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -633,19 +413,19 @@ func (x *DayRange) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
-// Deprecated: Use DayRange.ProtoReflect.Descriptor instead.
-func (*DayRange) Descriptor() ([]byte, []int) {
- return file_filecache_proto_rawDescGZIP(), []int{5}
+// Deprecated: Use DayInterval.ProtoReflect.Descriptor instead.
+func (*DayInterval) Descriptor() ([]byte, []int) {
+ return file_filecache_proto_rawDescGZIP(), []int{3}
}
-func (x *DayRange) GetStart() uint32 {
+func (x *DayInterval) GetStart() uint32 {
if x != nil {
return x.Start
}
return 0
}
-func (x *DayRange) GetEnd() uint32 {
+func (x *DayInterval) GetEnd() uint32 {
if x != nil {
return x.End
}
@@ -663,7 +443,7 @@ type BlockingModeCustomIP struct {
func (x *BlockingModeCustomIP) Reset() {
*x = BlockingModeCustomIP{}
- mi := &file_filecache_proto_msgTypes[6]
+ mi := &file_filecache_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -675,7 +455,7 @@ func (x *BlockingModeCustomIP) String() string {
func (*BlockingModeCustomIP) ProtoMessage() {}
func (x *BlockingModeCustomIP) ProtoReflect() protoreflect.Message {
- mi := &file_filecache_proto_msgTypes[6]
+ mi := &file_filecache_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -688,7 +468,7 @@ func (x *BlockingModeCustomIP) ProtoReflect() protoreflect.Message {
// Deprecated: Use BlockingModeCustomIP.ProtoReflect.Descriptor instead.
func (*BlockingModeCustomIP) Descriptor() ([]byte, []int) {
- return file_filecache_proto_rawDescGZIP(), []int{6}
+ return file_filecache_proto_rawDescGZIP(), []int{4}
}
func (x *BlockingModeCustomIP) GetIpv4() [][]byte {
@@ -713,7 +493,7 @@ type BlockingModeNXDOMAIN struct {
func (x *BlockingModeNXDOMAIN) Reset() {
*x = BlockingModeNXDOMAIN{}
- mi := &file_filecache_proto_msgTypes[7]
+ mi := &file_filecache_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -725,7 +505,7 @@ func (x *BlockingModeNXDOMAIN) String() string {
func (*BlockingModeNXDOMAIN) ProtoMessage() {}
func (x *BlockingModeNXDOMAIN) ProtoReflect() protoreflect.Message {
- mi := &file_filecache_proto_msgTypes[7]
+ mi := &file_filecache_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -738,7 +518,7 @@ func (x *BlockingModeNXDOMAIN) ProtoReflect() protoreflect.Message {
// Deprecated: Use BlockingModeNXDOMAIN.ProtoReflect.Descriptor instead.
func (*BlockingModeNXDOMAIN) Descriptor() ([]byte, []int) {
- return file_filecache_proto_rawDescGZIP(), []int{7}
+ return file_filecache_proto_rawDescGZIP(), []int{5}
}
type BlockingModeNullIP struct {
@@ -749,7 +529,7 @@ type BlockingModeNullIP struct {
func (x *BlockingModeNullIP) Reset() {
*x = BlockingModeNullIP{}
- mi := &file_filecache_proto_msgTypes[8]
+ mi := &file_filecache_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -761,7 +541,7 @@ func (x *BlockingModeNullIP) String() string {
func (*BlockingModeNullIP) ProtoMessage() {}
func (x *BlockingModeNullIP) ProtoReflect() protoreflect.Message {
- mi := &file_filecache_proto_msgTypes[8]
+ mi := &file_filecache_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -774,7 +554,7 @@ func (x *BlockingModeNullIP) ProtoReflect() protoreflect.Message {
// Deprecated: Use BlockingModeNullIP.ProtoReflect.Descriptor instead.
func (*BlockingModeNullIP) Descriptor() ([]byte, []int) {
- return file_filecache_proto_rawDescGZIP(), []int{8}
+ return file_filecache_proto_rawDescGZIP(), []int{6}
}
type BlockingModeREFUSED struct {
@@ -785,7 +565,7 @@ type BlockingModeREFUSED struct {
func (x *BlockingModeREFUSED) Reset() {
*x = BlockingModeREFUSED{}
- mi := &file_filecache_proto_msgTypes[9]
+ mi := &file_filecache_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -797,7 +577,7 @@ func (x *BlockingModeREFUSED) String() string {
func (*BlockingModeREFUSED) ProtoMessage() {}
func (x *BlockingModeREFUSED) ProtoReflect() protoreflect.Message {
- mi := &file_filecache_proto_msgTypes[9]
+ mi := &file_filecache_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -810,7 +590,7 @@ func (x *BlockingModeREFUSED) ProtoReflect() protoreflect.Message {
// Deprecated: Use BlockingModeREFUSED.ProtoReflect.Descriptor instead.
func (*BlockingModeREFUSED) Descriptor() ([]byte, []int) {
- return file_filecache_proto_rawDescGZIP(), []int{9}
+ return file_filecache_proto_rawDescGZIP(), []int{7}
}
type Device struct {
@@ -829,7 +609,7 @@ type Device struct {
func (x *Device) Reset() {
*x = Device{}
- mi := &file_filecache_proto_msgTypes[10]
+ mi := &file_filecache_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -841,7 +621,7 @@ func (x *Device) String() string {
func (*Device) ProtoMessage() {}
func (x *Device) ProtoReflect() protoreflect.Message {
- mi := &file_filecache_proto_msgTypes[10]
+ mi := &file_filecache_proto_msgTypes[8]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -854,7 +634,7 @@ func (x *Device) ProtoReflect() protoreflect.Message {
// Deprecated: Use Device.ProtoReflect.Descriptor instead.
func (*Device) Descriptor() ([]byte, []int) {
- return file_filecache_proto_rawDescGZIP(), []int{10}
+ return file_filecache_proto_rawDescGZIP(), []int{8}
}
func (x *Device) GetAuthentication() *AuthenticationSettings {
@@ -906,33 +686,33 @@ func (x *Device) GetFilteringEnabled() bool {
return false
}
-type AccessSettings struct {
+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"`
- 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"`
+ BlocklistDomainRules []string `protobuf:"bytes,3,rep,name=blocklist_domain_rules,json=blocklistDomainRules,proto3" json:"blocklist_domain_rules,omitempty"`
}
-func (x *AccessSettings) Reset() {
- *x = AccessSettings{}
- mi := &file_filecache_proto_msgTypes[11]
+func (x *Access) Reset() {
+ *x = Access{}
+ mi := &file_filecache_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
-func (x *AccessSettings) String() string {
+func (x *Access) String() string {
return protoimpl.X.MessageStringOf(x)
}
-func (*AccessSettings) ProtoMessage() {}
+func (*Access) ProtoMessage() {}
-func (x *AccessSettings) ProtoReflect() protoreflect.Message {
- mi := &file_filecache_proto_msgTypes[11]
+func (x *Access) ProtoReflect() protoreflect.Message {
+ mi := &file_filecache_proto_msgTypes[9]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -943,40 +723,40 @@ func (x *AccessSettings) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
-// Deprecated: Use AccessSettings.ProtoReflect.Descriptor instead.
-func (*AccessSettings) Descriptor() ([]byte, []int) {
- return file_filecache_proto_rawDescGZIP(), []int{11}
+// Deprecated: Use Access.ProtoReflect.Descriptor instead.
+func (*Access) Descriptor() ([]byte, []int) {
+ return file_filecache_proto_rawDescGZIP(), []int{9}
}
-func (x *AccessSettings) GetAllowlistCidr() []*CidrRange {
- if x != nil {
- return x.AllowlistCidr
- }
- return nil
-}
-
-func (x *AccessSettings) GetBlocklistCidr() []*CidrRange {
- if x != nil {
- return x.BlocklistCidr
- }
- return nil
-}
-
-func (x *AccessSettings) GetAllowlistAsn() []uint32 {
+func (x *Access) GetAllowlistAsn() []uint32 {
if x != nil {
return x.AllowlistAsn
}
return nil
}
-func (x *AccessSettings) GetBlocklistAsn() []uint32 {
+func (x *Access) GetAllowlistCidr() []*CidrRange {
+ if x != nil {
+ return x.AllowlistCidr
+ }
+ return nil
+}
+
+func (x *Access) GetBlocklistAsn() []uint32 {
if x != nil {
return x.BlocklistAsn
}
return nil
}
-func (x *AccessSettings) GetBlocklistDomainRules() []string {
+func (x *Access) GetBlocklistCidr() []*CidrRange {
+ if x != nil {
+ return x.BlocklistCidr
+ }
+ return nil
+}
+
+func (x *Access) GetBlocklistDomainRules() []string {
if x != nil {
return x.BlocklistDomainRules
}
@@ -994,7 +774,7 @@ type CidrRange struct {
func (x *CidrRange) Reset() {
*x = CidrRange{}
- mi := &file_filecache_proto_msgTypes[12]
+ mi := &file_filecache_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1006,7 +786,7 @@ func (x *CidrRange) String() string {
func (*CidrRange) ProtoMessage() {}
func (x *CidrRange) ProtoReflect() protoreflect.Message {
- mi := &file_filecache_proto_msgTypes[12]
+ mi := &file_filecache_proto_msgTypes[10]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1019,7 +799,7 @@ func (x *CidrRange) ProtoReflect() protoreflect.Message {
// Deprecated: Use CidrRange.ProtoReflect.Descriptor instead.
func (*CidrRange) Descriptor() ([]byte, []int) {
- return file_filecache_proto_rawDescGZIP(), []int{12}
+ return file_filecache_proto_rawDescGZIP(), []int{10}
}
func (x *CidrRange) GetAddress() []byte {
@@ -1050,7 +830,7 @@ type AuthenticationSettings struct {
func (x *AuthenticationSettings) Reset() {
*x = AuthenticationSettings{}
- mi := &file_filecache_proto_msgTypes[13]
+ mi := &file_filecache_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1062,7 +842,7 @@ func (x *AuthenticationSettings) String() string {
func (*AuthenticationSettings) ProtoMessage() {}
func (x *AuthenticationSettings) ProtoReflect() protoreflect.Message {
- mi := &file_filecache_proto_msgTypes[13]
+ mi := &file_filecache_proto_msgTypes[11]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1075,7 +855,7 @@ func (x *AuthenticationSettings) ProtoReflect() protoreflect.Message {
// Deprecated: Use AuthenticationSettings.ProtoReflect.Descriptor instead.
func (*AuthenticationSettings) Descriptor() ([]byte, []int) {
- return file_filecache_proto_rawDescGZIP(), []int{13}
+ return file_filecache_proto_rawDescGZIP(), []int{11}
}
func (x *AuthenticationSettings) GetDohAuthOnly() bool {
@@ -1109,30 +889,163 @@ type AuthenticationSettings_PasswordHashBcrypt struct {
func (*AuthenticationSettings_PasswordHashBcrypt) isAuthenticationSettings_DohPasswordHash() {}
-type RateLimitSettings struct {
+type Ratelimiter struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
+ 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"`
- ClientCidr []*CidrRange `protobuf:"bytes,3,rep,name=client_cidr,json=clientCidr,proto3" json:"client_cidr,omitempty"`
+ Enabled bool `protobuf:"varint,3,opt,name=enabled,proto3" json:"enabled,omitempty"`
}
-func (x *RateLimitSettings) Reset() {
- *x = RateLimitSettings{}
+func (x *Ratelimiter) Reset() {
+ *x = Ratelimiter{}
+ mi := &file_filecache_proto_msgTypes[12]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Ratelimiter) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Ratelimiter) ProtoMessage() {}
+
+func (x *Ratelimiter) ProtoReflect() protoreflect.Message {
+ mi := &file_filecache_proto_msgTypes[12]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Ratelimiter.ProtoReflect.Descriptor instead.
+func (*Ratelimiter) Descriptor() ([]byte, []int) {
+ return file_filecache_proto_rawDescGZIP(), []int{12}
+}
+
+func (x *Ratelimiter) GetClientCidr() []*CidrRange {
+ if x != nil {
+ return x.ClientCidr
+ }
+ return nil
+}
+
+func (x *Ratelimiter) GetRps() uint32 {
+ if x != nil {
+ return x.Rps
+ }
+ return 0
+}
+
+func (x *Ratelimiter) GetEnabled() bool {
+ if x != nil {
+ return x.Enabled
+ }
+ return false
+}
+
+type FilterConfig_Custom struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ 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"`
+}
+
+func (x *FilterConfig_Custom) Reset() {
+ *x = FilterConfig_Custom{}
+ mi := &file_filecache_proto_msgTypes[13]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *FilterConfig_Custom) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FilterConfig_Custom) ProtoMessage() {}
+
+func (x *FilterConfig_Custom) ProtoReflect() protoreflect.Message {
+ mi := &file_filecache_proto_msgTypes[13]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use FilterConfig_Custom.ProtoReflect.Descriptor instead.
+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
+ }
+ return nil
+}
+
+func (x *FilterConfig_Custom) GetEnabled() bool {
+ if x != nil {
+ return x.Enabled
+ }
+ return false
+}
+
+type FilterConfig_Parental struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ 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"`
+}
+
+func (x *FilterConfig_Parental) Reset() {
+ *x = FilterConfig_Parental{}
mi := &file_filecache_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
-func (x *RateLimitSettings) String() string {
+func (x *FilterConfig_Parental) String() string {
return protoimpl.X.MessageStringOf(x)
}
-func (*RateLimitSettings) ProtoMessage() {}
+func (*FilterConfig_Parental) ProtoMessage() {}
-func (x *RateLimitSettings) ProtoReflect() protoreflect.Message {
+func (x *FilterConfig_Parental) ProtoReflect() protoreflect.Message {
mi := &file_filecache_proto_msgTypes[14]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@@ -1144,32 +1057,313 @@ func (x *RateLimitSettings) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
-// Deprecated: Use RateLimitSettings.ProtoReflect.Descriptor instead.
-func (*RateLimitSettings) Descriptor() ([]byte, []int) {
- return file_filecache_proto_rawDescGZIP(), []int{14}
+// Deprecated: Use FilterConfig_Parental.ProtoReflect.Descriptor instead.
+func (*FilterConfig_Parental) Descriptor() ([]byte, []int) {
+ return file_filecache_proto_rawDescGZIP(), []int{2, 1}
}
-func (x *RateLimitSettings) GetEnabled() bool {
+func (x *FilterConfig_Parental) GetPauseSchedule() *FilterConfig_Schedule {
+ if x != nil {
+ return x.PauseSchedule
+ }
+ return nil
+}
+
+func (x *FilterConfig_Parental) GetBlockedServices() []string {
+ if x != nil {
+ return x.BlockedServices
+ }
+ return nil
+}
+
+func (x *FilterConfig_Parental) GetEnabled() bool {
if x != nil {
return x.Enabled
}
return false
}
-func (x *RateLimitSettings) GetRps() uint32 {
+func (x *FilterConfig_Parental) GetAdultBlockingEnabled() bool {
if x != nil {
- return x.Rps
+ return x.AdultBlockingEnabled
}
- return 0
+ return false
}
-func (x *RateLimitSettings) GetClientCidr() []*CidrRange {
+func (x *FilterConfig_Parental) GetSafeSearchGeneralEnabled() bool {
if x != nil {
- return x.ClientCidr
+ return x.SafeSearchGeneralEnabled
+ }
+ return false
+}
+
+func (x *FilterConfig_Parental) GetSafeSearchYoutubeEnabled() bool {
+ if x != nil {
+ return x.SafeSearchYoutubeEnabled
+ }
+ return false
+}
+
+type FilterConfig_Schedule struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ 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"`
+}
+
+func (x *FilterConfig_Schedule) Reset() {
+ *x = FilterConfig_Schedule{}
+ mi := &file_filecache_proto_msgTypes[15]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *FilterConfig_Schedule) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FilterConfig_Schedule) ProtoMessage() {}
+
+func (x *FilterConfig_Schedule) ProtoReflect() protoreflect.Message {
+ mi := &file_filecache_proto_msgTypes[15]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use FilterConfig_Schedule.ProtoReflect.Descriptor instead.
+func (*FilterConfig_Schedule) Descriptor() ([]byte, []int) {
+ return file_filecache_proto_rawDescGZIP(), []int{2, 2}
+}
+
+func (x *FilterConfig_Schedule) GetWeek() *FilterConfig_WeeklySchedule {
+ if x != nil {
+ return x.Week
}
return nil
}
+func (x *FilterConfig_Schedule) GetTimeZone() string {
+ if x != nil {
+ return x.TimeZone
+ }
+ return ""
+}
+
+type FilterConfig_WeeklySchedule struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ 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"`
+}
+
+func (x *FilterConfig_WeeklySchedule) Reset() {
+ *x = FilterConfig_WeeklySchedule{}
+ mi := &file_filecache_proto_msgTypes[16]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *FilterConfig_WeeklySchedule) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FilterConfig_WeeklySchedule) ProtoMessage() {}
+
+func (x *FilterConfig_WeeklySchedule) ProtoReflect() protoreflect.Message {
+ mi := &file_filecache_proto_msgTypes[16]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use FilterConfig_WeeklySchedule.ProtoReflect.Descriptor instead.
+func (*FilterConfig_WeeklySchedule) Descriptor() ([]byte, []int) {
+ return file_filecache_proto_rawDescGZIP(), []int{2, 3}
+}
+
+func (x *FilterConfig_WeeklySchedule) GetMon() *DayInterval {
+ if x != nil {
+ return x.Mon
+ }
+ return nil
+}
+
+func (x *FilterConfig_WeeklySchedule) GetTue() *DayInterval {
+ if x != nil {
+ return x.Tue
+ }
+ return nil
+}
+
+func (x *FilterConfig_WeeklySchedule) GetWed() *DayInterval {
+ if x != nil {
+ return x.Wed
+ }
+ return nil
+}
+
+func (x *FilterConfig_WeeklySchedule) GetThu() *DayInterval {
+ if x != nil {
+ return x.Thu
+ }
+ return nil
+}
+
+func (x *FilterConfig_WeeklySchedule) GetFri() *DayInterval {
+ if x != nil {
+ return x.Fri
+ }
+ return nil
+}
+
+func (x *FilterConfig_WeeklySchedule) GetSat() *DayInterval {
+ if x != nil {
+ return x.Sat
+ }
+ return nil
+}
+
+func (x *FilterConfig_WeeklySchedule) GetSun() *DayInterval {
+ if x != nil {
+ return x.Sun
+ }
+ return nil
+}
+
+type FilterConfig_RuleList struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ 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"`
+}
+
+func (x *FilterConfig_RuleList) Reset() {
+ *x = FilterConfig_RuleList{}
+ mi := &file_filecache_proto_msgTypes[17]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *FilterConfig_RuleList) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FilterConfig_RuleList) ProtoMessage() {}
+
+func (x *FilterConfig_RuleList) ProtoReflect() protoreflect.Message {
+ mi := &file_filecache_proto_msgTypes[17]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use FilterConfig_RuleList.ProtoReflect.Descriptor instead.
+func (*FilterConfig_RuleList) Descriptor() ([]byte, []int) {
+ return file_filecache_proto_rawDescGZIP(), []int{2, 4}
+}
+
+func (x *FilterConfig_RuleList) GetIds() []string {
+ if x != nil {
+ return x.Ids
+ }
+ return nil
+}
+
+func (x *FilterConfig_RuleList) GetEnabled() bool {
+ if x != nil {
+ return x.Enabled
+ }
+ return false
+}
+
+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"`
+}
+
+func (x *FilterConfig_SafeBrowsing) Reset() {
+ *x = FilterConfig_SafeBrowsing{}
+ mi := &file_filecache_proto_msgTypes[18]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *FilterConfig_SafeBrowsing) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FilterConfig_SafeBrowsing) ProtoMessage() {}
+
+func (x *FilterConfig_SafeBrowsing) ProtoReflect() protoreflect.Message {
+ mi := &file_filecache_proto_msgTypes[18]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use FilterConfig_SafeBrowsing.ProtoReflect.Descriptor instead.
+func (*FilterConfig_SafeBrowsing) Descriptor() ([]byte, []int) {
+ return file_filecache_proto_rawDescGZIP(), []int{2, 5}
+}
+
+func (x *FilterConfig_SafeBrowsing) GetEnabled() bool {
+ if x != nil {
+ return x.Enabled
+ }
+ return false
+}
+
+func (x *FilterConfig_SafeBrowsing) GetDangerousDomainsEnabled() bool {
+ if x != nil {
+ return x.DangerousDomainsEnabled
+ }
+ return false
+}
+
+func (x *FilterConfig_SafeBrowsing) GetNewlyRegisteredDomainsEnabled() bool {
+ if x != nil {
+ return x.NewlyRegisteredDomainsEnabled
+ }
+ return false
+}
+
var File_filecache_proto protoreflect.FileDescriptor
var file_filecache_proto_rawDesc = []byte{
@@ -1190,140 +1384,160 @@ var file_filecache_proto_rawDesc = []byte{
0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64,
0x62, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,
0x73, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01,
- 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x94, 0x0a, 0x0a, 0x07,
- 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x31, 0x0a, 0x06, 0x61, 0x63, 0x63, 0x65, 0x73,
- 0x73, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c,
- 0x65, 0x64, 0x62, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
- 0x67, 0x73, 0x52, 0x06, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x41, 0x0a, 0x08, 0x70, 0x61,
- 0x72, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70,
- 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x61,
- 0x6c, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69,
- 0x6e, 0x67, 0x73, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x12, 0x44, 0x0a,
- 0x0d, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x62, 0x72, 0x6f, 0x77, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x13,
- 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62,
- 0x2e, 0x53, 0x61, 0x66, 0x65, 0x42, 0x72, 0x6f, 0x77, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74,
- 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x0c, 0x73, 0x61, 0x66, 0x65, 0x42, 0x72, 0x6f, 0x77, 0x73,
- 0x69, 0x6e, 0x67, 0x12, 0x58, 0x0a, 0x17, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f,
- 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x69, 0x70, 0x18, 0x02,
- 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62,
- 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73,
- 0x74, 0x6f, 0x6d, 0x49, 0x50, 0x48, 0x00, 0x52, 0x14, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e,
- 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x49, 0x70, 0x12, 0x57, 0x0a,
- 0x16, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x6e,
- 0x78, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e,
- 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69,
- 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x58, 0x44, 0x4f, 0x4d, 0x41, 0x49, 0x4e, 0x48, 0x00,
- 0x52, 0x14, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x78,
- 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x52, 0x0a, 0x15, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69,
- 0x6e, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x75, 0x6c, 0x6c, 0x5f, 0x69, 0x70, 0x18,
- 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64,
- 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x75,
- 0x6c, 0x6c, 0x49, 0x50, 0x48, 0x00, 0x52, 0x12, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67,
- 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x75, 0x6c, 0x6c, 0x49, 0x70, 0x12, 0x54, 0x0a, 0x15, 0x62, 0x6c,
- 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x72, 0x65, 0x66, 0x75,
- 0x73, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x66,
- 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f,
- 0x64, 0x65, 0x52, 0x45, 0x46, 0x55, 0x53, 0x45, 0x44, 0x48, 0x00, 0x52, 0x13, 0x62, 0x6c, 0x6f,
- 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x66, 0x75, 0x73, 0x65, 0x64,
- 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x06,
- 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x12,
- 0x3b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x07,
- 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, 0x1d, 0x0a, 0x0a,
- 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09,
- 0x52, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x72,
- 0x75, 0x6c, 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03,
- 0x28, 0x09, 0x52, 0x0b, 0x72, 0x75, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x64, 0x73, 0x12,
- 0x21, 0x0a, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18,
- 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x75, 0x6c,
- 0x65, 0x73, 0x12, 0x4d, 0x0a, 0x15, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x72,
- 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x74, 0x74, 0x6c, 0x18, 0x0b, 0x20, 0x01, 0x28,
- 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
- 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x66, 0x69,
- 0x6c, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x74,
- 0x6c, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x65,
- 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x66, 0x69,
- 0x6c, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x36,
- 0x0a, 0x15, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x62, 0x72, 0x6f, 0x77, 0x73, 0x69, 0x6e, 0x67, 0x5f,
- 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x42, 0x02, 0x18,
- 0x01, 0x52, 0x13, 0x73, 0x61, 0x66, 0x65, 0x42, 0x72, 0x6f, 0x77, 0x73, 0x69, 0x6e, 0x67, 0x45,
- 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x6c,
- 0x69, 0x73, 0x74, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0e, 0x20, 0x01,
- 0x28, 0x08, 0x52, 0x10, 0x72, 0x75, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x61,
- 0x62, 0x6c, 0x65, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x6c, 0x6f,
- 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52,
- 0x0f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
- 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x10, 0x20, 0x01, 0x28,
- 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x62, 0x6c,
- 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61,
- 0x79, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x50, 0x72,
- 0x69, 0x76, 0x61, 0x74, 0x65, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x62, 0x6c,
+ 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x88, 0x08, 0x0a, 0x07,
+ 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x66, 0x69, 0x6c, 0x74, 0x65,
+ 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17,
+ 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65,
+ 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x43,
+ 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x0a, 0x06, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64,
+ 0x62, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x06, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73,
+ 0x12, 0x58, 0x0a, 0x17, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x6f, 0x64,
+ 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x42, 0x6c,
+ 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d,
+ 0x49, 0x50, 0x48, 0x00, 0x52, 0x14, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f,
+ 0x64, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x49, 0x70, 0x12, 0x57, 0x0a, 0x16, 0x62, 0x6c,
+ 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x78, 0x64, 0x6f,
+ 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f,
+ 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d,
+ 0x6f, 0x64, 0x65, 0x4e, 0x58, 0x44, 0x4f, 0x4d, 0x41, 0x49, 0x4e, 0x48, 0x00, 0x52, 0x14, 0x62,
+ 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x78, 0x64, 0x6f, 0x6d,
+ 0x61, 0x69, 0x6e, 0x12, 0x52, 0x0a, 0x15, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f,
+ 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x75, 0x6c, 0x6c, 0x5f, 0x69, 0x70, 0x18, 0x05, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x42,
+ 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x4e, 0x75, 0x6c, 0x6c, 0x49,
+ 0x50, 0x48, 0x00, 0x52, 0x12, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64,
+ 0x65, 0x4e, 0x75, 0x6c, 0x6c, 0x49, 0x70, 0x12, 0x54, 0x0a, 0x15, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
+ 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x72, 0x65, 0x66, 0x75, 0x73, 0x65, 0x64,
+ 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65,
+ 0x64, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x52,
+ 0x45, 0x46, 0x55, 0x53, 0x45, 0x44, 0x48, 0x00, 0x52, 0x13, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69,
+ 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x66, 0x75, 0x73, 0x65, 0x64, 0x12, 0x38, 0x0a,
+ 0x0b, 0x72, 0x61, 0x74, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x52,
+ 0x61, 0x74, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x52, 0x0b, 0x72, 0x61, 0x74, 0x65,
+ 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x66, 0x69,
+ 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f,
+ 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,
+ 0x5f, 0x69, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x64, 0x65, 0x76, 0x69,
+ 0x63, 0x65, 0x49, 0x64, 0x73, 0x12, 0x4d, 0x0a, 0x15, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x65,
+ 0x64, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x74, 0x74, 0x6c, 0x18, 0x0a,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
+ 0x13, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+ 0x65, 0x54, 0x74, 0x6c, 0x12, 0x30, 0x0a, 0x14, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x64, 0x65, 0x76,
+ 0x69, 0x63, 0x65, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01,
+ 0x28, 0x08, 0x52, 0x12, 0x61, 0x75, 0x74, 0x6f, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x45,
+ 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f,
+ 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x74, 0x63, 0x68, 0x18,
+ 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x68, 0x72, 0x6f,
+ 0x6d, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x74, 0x63, 0x68, 0x12, 0x30, 0x0a, 0x14, 0x62, 0x6c,
0x6f, 0x63, 0x6b, 0x5f, 0x66, 0x69, 0x72, 0x65, 0x66, 0x6f, 0x78, 0x5f, 0x63, 0x61, 0x6e, 0x61,
- 0x72, 0x79, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x46,
- 0x69, 0x72, 0x65, 0x66, 0x6f, 0x78, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x12, 0x24, 0x0a, 0x0e,
- 0x69, 0x70, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x14,
- 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x70, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c,
- 0x65, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63,
- 0x65, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x16, 0x20, 0x01, 0x28, 0x08,
- 0x52, 0x12, 0x61, 0x75, 0x74, 0x6f, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x61,
- 0x62, 0x6c, 0x65, 0x64, 0x12, 0x3b, 0x0a, 0x0a, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6c, 0x69, 0x6d,
- 0x69, 0x74, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69,
- 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x53, 0x65,
- 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x09, 0x72, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69,
- 0x74, 0x42, 0x0f, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x6f,
- 0x64, 0x65, 0x22, 0xa5, 0x02, 0x0a, 0x1a, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x50,
- 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
- 0x73, 0x12, 0x41, 0x0a, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20,
- 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e,
- 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69,
- 0x6f, 0x6e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x08, 0x73, 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, 0x1f, 0x0a, 0x0b, 0x62, 0x6c, 0x6f,
- 0x63, 0x6b, 0x5f, 0x61, 0x64, 0x75, 0x6c, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a,
- 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x64, 0x75, 0x6c, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x67, 0x65,
- 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63,
- 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
- 0x53, 0x61, 0x66, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x2e, 0x0a, 0x13, 0x79, 0x6f,
- 0x75, 0x74, 0x75, 0x62, 0x65, 0x5f, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63,
- 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x79, 0x6f, 0x75, 0x74, 0x75, 0x62, 0x65,
- 0x53, 0x61, 0x66, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x22, 0xad, 0x01, 0x0a, 0x14, 0x53,
- 0x61, 0x66, 0x65, 0x42, 0x72, 0x6f, 0x77, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69,
- 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x36, 0x0a,
- 0x17, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73,
- 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15,
- 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x6f, 0x75, 0x73, 0x44, 0x6f,
- 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x43, 0x0a, 0x1e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e,
+ 0x72, 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x46,
+ 0x69, 0x72, 0x65, 0x66, 0x6f, 0x78, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x12, 0x2e, 0x0a, 0x13,
+ 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65,
+ 0x6c, 0x61, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
+ 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x18, 0x0a, 0x07,
+ 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64,
+ 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72,
+ 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x10, 0x20, 0x01, 0x28,
+ 0x08, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62,
+ 0x6c, 0x65, 0x64, 0x12, 0x24, 0x0a, 0x0e, 0x69, 0x70, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x65, 0x6e,
+ 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x70, 0x4c,
+ 0x6f, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x71, 0x75, 0x65,
+ 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,
+ 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,
+ 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x06, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x12,
+ 0x3c, 0x0a, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x18, 0x02, 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, 0x50, 0x61, 0x72, 0x65, 0x6e,
+ 0x74, 0x61, 0x6c, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x12, 0x3d, 0x0a,
+ 0x09, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x03, 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, 0x52, 0x75, 0x6c, 0x65, 0x4c, 0x69,
+ 0x73, 0x74, 0x52, 0x08, 0x72, 0x75, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x49, 0x0a, 0x0d,
+ 0x73, 0x61, 0x66, 0x65, 0x5f, 0x62, 0x72, 0x6f, 0x77, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20,
+ 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,
+ 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,
+ 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, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x62,
- 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x65, 0x77, 0x6c, 0x79, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65,
- 0x72, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x22, 0xca, 0x02, 0x0a, 0x1a, 0x50,
- 0x61, 0x72, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f,
- 0x6e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x69, 0x6d,
- 0x65, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x69,
- 0x6d, 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x25, 0x0a, 0x03, 0x6d, 0x6f, 0x6e, 0x18, 0x02, 0x20,
- 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e,
- 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x6d, 0x6f, 0x6e, 0x12, 0x25, 0x0a,
- 0x03, 0x74, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f,
- 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52,
- 0x03, 0x74, 0x75, 0x65, 0x12, 0x25, 0x0a, 0x03, 0x77, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28,
- 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x44, 0x61,
- 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x77, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x03, 0x74,
- 0x68, 0x75, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69,
- 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x74,
- 0x68, 0x75, 0x12, 0x25, 0x0a, 0x03, 0x66, 0x72, 0x69, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32,
- 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x44, 0x61, 0x79, 0x52,
- 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x66, 0x72, 0x69, 0x12, 0x25, 0x0a, 0x03, 0x73, 0x61, 0x74,
- 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65,
- 0x64, 0x62, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x03, 0x73, 0x61, 0x74,
- 0x12, 0x25, 0x0a, 0x03, 0x73, 0x75, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e,
- 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x64, 0x62, 0x2e, 0x44, 0x61, 0x79, 0x52, 0x61, 0x6e,
- 0x67, 0x65, 0x52, 0x03, 0x73, 0x75, 0x6e, 0x22, 0x32, 0x0a, 0x08, 0x44, 0x61, 0x79, 0x52, 0x61,
- 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01,
+ 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,
@@ -1352,45 +1566,44 @@ var file_filecache_proto_rawDesc = []byte{
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, 0x8a, 0x02, 0x0a, 0x0e, 0x41, 0x63,
- 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 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, 0x3b, 0x0a, 0x0e, 0x62, 0x6c, 0x6f,
- 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28,
+ 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, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69,
- 0x73, 0x74, 0x43, 0x69, 0x64, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x6c,
- 0x69, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x61,
- 0x6c, 0x6c, 0x6f, 0x77, 0x6c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x62,
- 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x6e, 0x18, 0x04, 0x20, 0x03,
- 0x28, 0x0d, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x6e,
- 0x12, 0x34, 0x0a, 0x16, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x64, 0x6f,
- 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09,
- 0x52, 0x14, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69,
- 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x73, 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, 0x76, 0x0a,
- 0x11, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
- 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20,
- 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03,
- 0x72, 0x70, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x72, 0x70, 0x73, 0x12, 0x35,
- 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x03, 0x20,
+ 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, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e,
- 0x74, 0x43, 0x69, 0x64, 0x72, 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,
+ 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,
}
var (
@@ -1405,57 +1618,65 @@ func file_filecache_proto_rawDescGZIP() []byte {
return file_filecache_proto_rawDescData
}
-var file_filecache_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
+var file_filecache_proto_msgTypes = make([]protoimpl.MessageInfo, 19)
var file_filecache_proto_goTypes = []any{
- (*FileCache)(nil), // 0: profiledb.FileCache
- (*Profile)(nil), // 1: profiledb.Profile
- (*ParentalProtectionSettings)(nil), // 2: profiledb.ParentalProtectionSettings
- (*SafeBrowsingSettings)(nil), // 3: profiledb.SafeBrowsingSettings
- (*ParentalProtectionSchedule)(nil), // 4: profiledb.ParentalProtectionSchedule
- (*DayRange)(nil), // 5: profiledb.DayRange
- (*BlockingModeCustomIP)(nil), // 6: profiledb.BlockingModeCustomIP
- (*BlockingModeNXDOMAIN)(nil), // 7: profiledb.BlockingModeNXDOMAIN
- (*BlockingModeNullIP)(nil), // 8: profiledb.BlockingModeNullIP
- (*BlockingModeREFUSED)(nil), // 9: profiledb.BlockingModeREFUSED
- (*Device)(nil), // 10: profiledb.Device
- (*AccessSettings)(nil), // 11: profiledb.AccessSettings
- (*CidrRange)(nil), // 12: profiledb.CidrRange
- (*AuthenticationSettings)(nil), // 13: profiledb.AuthenticationSettings
- (*RateLimitSettings)(nil), // 14: profiledb.RateLimitSettings
- (*timestamppb.Timestamp)(nil), // 15: google.protobuf.Timestamp
- (*durationpb.Duration)(nil), // 16: google.protobuf.Duration
+ (*FileCache)(nil), // 0: profiledb.FileCache
+ (*Profile)(nil), // 1: profiledb.Profile
+ (*FilterConfig)(nil), // 2: profiledb.FilterConfig
+ (*DayInterval)(nil), // 3: profiledb.DayInterval
+ (*BlockingModeCustomIP)(nil), // 4: profiledb.BlockingModeCustomIP
+ (*BlockingModeNXDOMAIN)(nil), // 5: profiledb.BlockingModeNXDOMAIN
+ (*BlockingModeNullIP)(nil), // 6: profiledb.BlockingModeNullIP
+ (*BlockingModeREFUSED)(nil), // 7: profiledb.BlockingModeREFUSED
+ (*Device)(nil), // 8: profiledb.Device
+ (*Access)(nil), // 9: profiledb.Access
+ (*CidrRange)(nil), // 10: profiledb.CidrRange
+ (*AuthenticationSettings)(nil), // 11: profiledb.AuthenticationSettings
+ (*Ratelimiter)(nil), // 12: profiledb.Ratelimiter
+ (*FilterConfig_Custom)(nil), // 13: profiledb.FilterConfig.Custom
+ (*FilterConfig_Parental)(nil), // 14: profiledb.FilterConfig.Parental
+ (*FilterConfig_Schedule)(nil), // 15: profiledb.FilterConfig.Schedule
+ (*FilterConfig_WeeklySchedule)(nil), // 16: profiledb.FilterConfig.WeeklySchedule
+ (*FilterConfig_RuleList)(nil), // 17: profiledb.FilterConfig.RuleList
+ (*FilterConfig_SafeBrowsing)(nil), // 18: profiledb.FilterConfig.SafeBrowsing
+ (*timestamppb.Timestamp)(nil), // 19: google.protobuf.Timestamp
+ (*durationpb.Duration)(nil), // 20: google.protobuf.Duration
}
var file_filecache_proto_depIdxs = []int32{
- 15, // 0: profiledb.FileCache.sync_time:type_name -> google.protobuf.Timestamp
+ 19, // 0: profiledb.FileCache.sync_time:type_name -> google.protobuf.Timestamp
1, // 1: profiledb.FileCache.profiles:type_name -> profiledb.Profile
- 10, // 2: profiledb.FileCache.devices:type_name -> profiledb.Device
- 11, // 3: profiledb.Profile.access:type_name -> profiledb.AccessSettings
- 2, // 4: profiledb.Profile.parental:type_name -> profiledb.ParentalProtectionSettings
- 3, // 5: profiledb.Profile.safe_browsing:type_name -> profiledb.SafeBrowsingSettings
- 6, // 6: profiledb.Profile.blocking_mode_custom_ip:type_name -> profiledb.BlockingModeCustomIP
- 7, // 7: profiledb.Profile.blocking_mode_nxdomain:type_name -> profiledb.BlockingModeNXDOMAIN
- 8, // 8: profiledb.Profile.blocking_mode_null_ip:type_name -> profiledb.BlockingModeNullIP
- 9, // 9: profiledb.Profile.blocking_mode_refused:type_name -> profiledb.BlockingModeREFUSED
- 15, // 10: profiledb.Profile.update_time:type_name -> google.protobuf.Timestamp
- 16, // 11: profiledb.Profile.filtered_response_ttl:type_name -> google.protobuf.Duration
- 14, // 12: profiledb.Profile.rate_limit:type_name -> profiledb.RateLimitSettings
- 4, // 13: profiledb.ParentalProtectionSettings.schedule:type_name -> profiledb.ParentalProtectionSchedule
- 5, // 14: profiledb.ParentalProtectionSchedule.mon:type_name -> profiledb.DayRange
- 5, // 15: profiledb.ParentalProtectionSchedule.tue:type_name -> profiledb.DayRange
- 5, // 16: profiledb.ParentalProtectionSchedule.wed:type_name -> profiledb.DayRange
- 5, // 17: profiledb.ParentalProtectionSchedule.thu:type_name -> profiledb.DayRange
- 5, // 18: profiledb.ParentalProtectionSchedule.fri:type_name -> profiledb.DayRange
- 5, // 19: profiledb.ParentalProtectionSchedule.sat:type_name -> profiledb.DayRange
- 5, // 20: profiledb.ParentalProtectionSchedule.sun:type_name -> profiledb.DayRange
- 13, // 21: profiledb.Device.authentication:type_name -> profiledb.AuthenticationSettings
- 12, // 22: profiledb.AccessSettings.allowlist_cidr:type_name -> profiledb.CidrRange
- 12, // 23: profiledb.AccessSettings.blocklist_cidr:type_name -> profiledb.CidrRange
- 12, // 24: profiledb.RateLimitSettings.client_cidr:type_name -> profiledb.CidrRange
- 25, // [25:25] is the sub-list for method output_type
- 25, // [25:25] is the sub-list for method input_type
- 25, // [25:25] is the sub-list for extension type_name
- 25, // [25:25] is the sub-list for extension extendee
- 0, // [0:25] is the sub-list for field type_name
+ 8, // 2: profiledb.FileCache.devices:type_name -> profiledb.Device
+ 2, // 3: profiledb.Profile.filter_config:type_name -> profiledb.FilterConfig
+ 9, // 4: profiledb.Profile.access:type_name -> profiledb.Access
+ 4, // 5: profiledb.Profile.blocking_mode_custom_ip:type_name -> profiledb.BlockingModeCustomIP
+ 5, // 6: profiledb.Profile.blocking_mode_nxdomain:type_name -> profiledb.BlockingModeNXDOMAIN
+ 6, // 7: profiledb.Profile.blocking_mode_null_ip:type_name -> profiledb.BlockingModeNullIP
+ 7, // 8: profiledb.Profile.blocking_mode_refused:type_name -> profiledb.BlockingModeREFUSED
+ 12, // 9: profiledb.Profile.ratelimiter:type_name -> profiledb.Ratelimiter
+ 20, // 10: profiledb.Profile.filtered_response_ttl:type_name -> google.protobuf.Duration
+ 13, // 11: profiledb.FilterConfig.custom:type_name -> profiledb.FilterConfig.Custom
+ 14, // 12: profiledb.FilterConfig.parental:type_name -> profiledb.FilterConfig.Parental
+ 17, // 13: profiledb.FilterConfig.rule_list:type_name -> profiledb.FilterConfig.RuleList
+ 18, // 14: profiledb.FilterConfig.safe_browsing:type_name -> profiledb.FilterConfig.SafeBrowsing
+ 11, // 15: profiledb.Device.authentication:type_name -> profiledb.AuthenticationSettings
+ 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
}
func init() { file_filecache_proto_init() }
@@ -1469,7 +1690,7 @@ func file_filecache_proto_init() {
(*Profile_BlockingModeNullIp)(nil),
(*Profile_BlockingModeRefused)(nil),
}
- file_filecache_proto_msgTypes[13].OneofWrappers = []any{
+ file_filecache_proto_msgTypes[11].OneofWrappers = []any{
(*AuthenticationSettings_PasswordHashBcrypt)(nil),
}
type x struct{}
@@ -1478,7 +1699,7 @@ func file_filecache_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_filecache_proto_rawDesc,
NumEnums: 0,
- NumMessages: 15,
+ NumMessages: 19,
NumExtensions: 0,
NumServices: 0,
},
diff --git a/internal/profiledb/internal/filecachepb/filecache.proto b/internal/profiledb/internal/filecachepb/filecache.proto
index 9303d70..24586f1 100644
--- a/internal/profiledb/internal/filecachepb/filecache.proto
+++ b/internal/profiledb/internal/filecachepb/filecache.proto
@@ -15,60 +15,82 @@ message FileCache {
}
message Profile {
- AccessSettings access = 21;
- ParentalProtectionSettings parental = 1;
- SafeBrowsingSettings safe_browsing = 19;
+ FilterConfig filter_config = 1;
+
+ Access access = 2;
oneof blocking_mode {
- BlockingModeCustomIP blocking_mode_custom_ip = 2;
- BlockingModeNXDOMAIN blocking_mode_nxdomain = 3;
- BlockingModeNullIP blocking_mode_null_ip = 4;
- BlockingModeREFUSED blocking_mode_refused = 5;
+ BlockingModeCustomIP blocking_mode_custom_ip = 3;
+ BlockingModeNXDOMAIN blocking_mode_nxdomain = 4;
+ BlockingModeNullIP blocking_mode_null_ip = 5;
+ BlockingModeREFUSED blocking_mode_refused = 6;
}
- string profile_id = 6;
- google.protobuf.Timestamp update_time = 7;
- repeated string device_ids = 8;
- repeated string rule_list_ids = 9;
- repeated string custom_rules = 10;
- google.protobuf.Duration filtered_response_ttl = 11;
- bool filtering_enabled = 12;
- bool safe_browsing_enabled = 13 [deprecated = true];
- bool rule_lists_enabled = 14;
- bool query_log_enabled = 15;
- bool deleted = 16;
- bool block_private_relay = 17;
- bool block_firefox_canary = 18;
- bool ip_log_enabled = 20;
- bool auto_devices_enabled = 22;
- RateLimitSettings rate_limit = 23;
+ Ratelimiter ratelimiter = 7;
+
+ string profile_id = 8;
+ repeated string device_ids = 9;
+
+ google.protobuf.Duration filtered_response_ttl = 10;
+
+ bool auto_devices_enabled = 11;
+ bool block_chrome_prefetch = 12;
+ bool block_firefox_canary = 13;
+ bool block_private_relay = 14;
+ bool deleted = 15;
+ bool filtering_enabled = 16;
+ bool ip_log_enabled = 17;
+ bool query_log_enabled = 18;
}
-message ParentalProtectionSettings {
- ParentalProtectionSchedule schedule = 1;
- repeated string blocked_services = 2;
- bool enabled = 3;
- bool block_adult = 4;
- bool general_safe_search = 5;
- bool youtube_safe_search = 6;
+message FilterConfig {
+ message Custom {
+ string id = 1;
+ google.protobuf.Timestamp update_time = 2;
+ repeated string rules = 3;
+ bool enabled = 4;
+ }
+
+ message Parental {
+ Schedule pause_schedule = 1;
+ repeated string blocked_services = 2;
+ bool enabled = 3;
+ bool adult_blocking_enabled = 4;
+ bool safe_search_general_enabled = 5;
+ bool safe_search_youtube_enabled = 6;
+ }
+
+ message Schedule {
+ WeeklySchedule week = 1;
+ string time_zone = 2;
+ }
+
+ message WeeklySchedule {
+ DayInterval mon = 1;
+ DayInterval tue = 2;
+ DayInterval wed = 3;
+ DayInterval thu = 4;
+ DayInterval fri = 5;
+ DayInterval sat = 6;
+ DayInterval sun = 7;
+ }
+
+ message RuleList {
+ repeated string ids = 1;
+ bool enabled = 2;
+ }
+
+ message SafeBrowsing {
+ bool enabled = 1;
+ bool dangerous_domains_enabled = 2;
+ bool newly_registered_domains_enabled = 3;
+ }
+
+ Custom custom = 1;
+ Parental parental = 2;
+ RuleList rule_list = 3;
+ SafeBrowsing safe_browsing = 4;
}
-message SafeBrowsingSettings {
- bool enabled = 1;
- bool block_dangerous_domains = 2;
- bool block_newly_registered_domains = 3;
-}
-
-message ParentalProtectionSchedule {
- string time_zone = 1;
- DayRange mon = 2;
- DayRange tue = 3;
- DayRange wed = 4;
- DayRange thu = 5;
- DayRange fri = 6;
- DayRange sat = 7;
- DayRange sun = 8;
-}
-
-message DayRange {
+message DayInterval {
uint32 start = 1;
uint32 end = 2;
}
@@ -94,12 +116,12 @@ message Device {
bool filtering_enabled = 5;
}
-message AccessSettings {
+message Access {
+ repeated uint32 allowlist_asn = 4;
repeated CidrRange allowlist_cidr = 1;
+ repeated uint32 blocklist_asn = 5;
repeated CidrRange blocklist_cidr = 2;
- repeated uint32 allowlist_asn = 3;
- repeated uint32 blocklist_asn = 4;
- repeated string blocklist_domain_rules = 5;
+ repeated string blocklist_domain_rules = 3;
}
message CidrRange {
@@ -114,8 +136,8 @@ message AuthenticationSettings {
}
}
-message RateLimitSettings {
- bool enabled = 1;
+message Ratelimiter {
+ repeated CidrRange client_cidr = 1;
uint32 rps = 2;
- repeated CidrRange client_cidr = 3;
+ bool enabled = 3;
}
diff --git a/internal/profiledb/internal/filecachepb/filecachepb.go b/internal/profiledb/internal/filecachepb/filecachepb.go
index 538c418..35286e2 100644
--- a/internal/profiledb/internal/filecachepb/filecachepb.go
+++ b/internal/profiledb/internal/filecachepb/filecachepb.go
@@ -12,6 +12,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdprotobuf"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb/internal"
"github.com/c2h5oh/datasize"
@@ -70,72 +71,77 @@ func profilesToInternal(
// toInternal converts a protobuf profile structure to an internal one.
func (x *Profile) toInternal(respSzEst datasize.ByteSize) (prof *agd.Profile, err error) {
- parental, err := x.Parental.toInternal()
- if err != nil {
- return nil, fmt.Errorf("parental: %w", err)
- }
-
m, err := blockingModeToInternal(x.BlockingMode)
if err != nil {
return nil, fmt.Errorf("blocking mode: %w", err)
}
+ pbFltConf := x.FilterConfig
+ schedule, err := pbFltConf.Parental.PauseSchedule.toInternal()
+ if err != nil {
+ return nil, fmt.Errorf("pause schedule: %w", err)
+ }
+
+ 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),
+ Enabled: pbFltConf.Custom.Enabled,
+ },
+ Parental: &filter.ConfigParental{
+ PauseSchedule: schedule,
+ // Consider blocked-service IDs to have been prevalidated.
+ BlockedServices: unsafelyConvertStrSlice[string, filter.BlockedServiceID](
+ pbFltConf.Parental.BlockedServices,
+ ),
+ Enabled: pbFltConf.Parental.Enabled,
+ AdultBlockingEnabled: pbFltConf.Parental.AdultBlockingEnabled,
+ SafeSearchGeneralEnabled: pbFltConf.Parental.SafeSearchGeneralEnabled,
+ SafeSearchYouTubeEnabled: pbFltConf.Parental.SafeSearchYoutubeEnabled,
+ },
+ RuleList: &filter.ConfigRuleList{
+ // Consider rule-list IDs to have been prevalidated.
+ IDs: unsafelyConvertStrSlice[string, filter.ID](pbFltConf.RuleList.Ids),
+ Enabled: pbFltConf.RuleList.Enabled,
+ },
+ SafeBrowsing: &filter.ConfigSafeBrowsing{
+ Enabled: pbFltConf.SafeBrowsing.Enabled,
+ DangerousDomainsEnabled: pbFltConf.SafeBrowsing.DangerousDomainsEnabled,
+ NewlyRegisteredDomainsEnabled: pbFltConf.SafeBrowsing.NewlyRegisteredDomainsEnabled,
+ },
+ }
+
return &agd.Profile{
- Parental: parental,
- Ratelimiter: x.RateLimit.toInternal(respSzEst),
+ FilterConfig: fltConf,
+
+ Access: x.Access.toInternal(),
BlockingMode: m,
- ID: agd.ProfileID(x.ProfileId),
- UpdateTime: x.UpdateTime.AsTime(),
+ Ratelimiter: x.Ratelimiter.toInternal(respSzEst),
+
+ ID: agd.ProfileID(x.ProfileId),
+
// Consider device IDs to have been prevalidated.
DeviceIDs: unsafelyConvertStrSlice[string, agd.DeviceID](x.DeviceIds),
+
// Consider rule-list IDs to have been prevalidated.
- RuleListIDs: unsafelyConvertStrSlice[string, agd.FilterListID](x.RuleListIds),
- // Consider rule-list IDs to have been prevalidated.
- CustomRules: unsafelyConvertStrSlice[string, agd.FilterRuleText](
- x.CustomRules,
- ),
FilteredResponseTTL: x.FilteredResponseTtl.AsDuration(),
- FilteringEnabled: x.FilteringEnabled,
- SafeBrowsing: x.SafeBrowsing.toInternal(),
- Access: x.Access.toInternal(),
- RuleListsEnabled: x.RuleListsEnabled,
- QueryLogEnabled: x.QueryLogEnabled,
- Deleted: x.Deleted,
- BlockPrivateRelay: x.BlockPrivateRelay,
- BlockFirefoxCanary: x.BlockFirefoxCanary,
- IPLogEnabled: x.IpLogEnabled,
+
AutoDevicesEnabled: x.AutoDevicesEnabled,
- }, nil
-}
-
-// toInternal converts a protobuf parental-settings structure to an internal
-// one.
-func (x *ParentalProtectionSettings) toInternal() (s *agd.ParentalProtectionSettings, err error) {
- if x == nil {
- return nil, nil
- }
-
- schedule, err := x.Schedule.toInternal()
- if err != nil {
- return nil, fmt.Errorf("schedule: %w", err)
- }
-
- return &agd.ParentalProtectionSettings{
- Schedule: schedule,
- // Consider block service IDs to have been prevalidated.
- BlockedServices: unsafelyConvertStrSlice[string, agd.BlockedServiceID](
- x.BlockedServices,
- ),
- Enabled: x.Enabled,
- BlockAdult: x.BlockAdult,
- GeneralSafeSearch: x.GeneralSafeSearch,
- YoutubeSafeSearch: x.YoutubeSafeSearch,
+ BlockChromePrefetch: x.BlockChromePrefetch,
+ BlockFirefoxCanary: x.BlockFirefoxCanary,
+ BlockPrivateRelay: x.BlockPrivateRelay,
+ Deleted: x.Deleted,
+ FilteringEnabled: x.FilteringEnabled,
+ IPLogEnabled: x.IpLogEnabled,
+ QueryLogEnabled: x.QueryLogEnabled,
}, nil
}
// toInternal converts a protobuf protection-schedule structure to an internal
-// one.
-func (x *ParentalProtectionSchedule) toInternal() (s *agd.ParentalProtectionSchedule, err error) {
+// one. If x is nil, c is nil.
+func (x *FilterConfig_Schedule) toInternal() (c *filter.ConfigSchedule, err error) {
if x == nil {
return nil, nil
}
@@ -145,28 +151,36 @@ func (x *ParentalProtectionSchedule) toInternal() (s *agd.ParentalProtectionSche
return nil, fmt.Errorf("time zone: %w", err)
}
- return &agd.ParentalProtectionSchedule{
+ return &filter.ConfigSchedule{
// Consider the lengths to be prevalidated.
- Week: &agd.WeeklySchedule{
- // #nosec G115 -- The values put in these are always from uint16s.
- time.Monday: {Start: uint16(x.Mon.Start), End: uint16(x.Mon.End)},
- // #nosec G115 -- The values put in these are always from uint16s.
- time.Tuesday: {Start: uint16(x.Tue.Start), End: uint16(x.Tue.End)},
- // #nosec G115 -- The values put in these are always from uint16s.
- time.Wednesday: {Start: uint16(x.Wed.Start), End: uint16(x.Wed.End)},
- // #nosec G115 -- The values put in these are always from uint16s.
- time.Thursday: {Start: uint16(x.Thu.Start), End: uint16(x.Thu.End)},
- // #nosec G115 -- The values put in these are always from uint16s.
- time.Friday: {Start: uint16(x.Fri.Start), End: uint16(x.Fri.End)},
- // #nosec G115 -- The values put in these are always from uint16s.
- time.Saturday: {Start: uint16(x.Sat.Start), End: uint16(x.Sat.End)},
- // #nosec G115 -- The values put in these are always from uint16s.
- time.Sunday: {Start: uint16(x.Sun.Start), End: uint16(x.Sun.End)},
+ Week: &filter.WeeklySchedule{
+ time.Monday: x.Week.Mon.toInternal(),
+ time.Tuesday: x.Week.Tue.toInternal(),
+ time.Wednesday: x.Week.Wed.toInternal(),
+ time.Thursday: x.Week.Thu.toInternal(),
+ time.Friday: x.Week.Fri.toInternal(),
+ time.Saturday: x.Week.Sat.toInternal(),
+ time.Sunday: x.Week.Sun.toInternal(),
},
TimeZone: loc,
}, nil
}
+// toInternal converts a protobuf day interval to an internal one. If x is nil,
+// i is nil.
+func (x *DayInterval) toInternal() (i *filter.DayInterval) {
+ if x == nil {
+ return nil
+ }
+
+ return &filter.DayInterval{
+ // #nosec G115 -- The values put in these are always from uint16s.
+ Start: uint16(x.Start),
+ // #nosec G115 -- The values put in these are always from uint16s.
+ End: uint16(x.End),
+ }
+}
+
// blockingModeToInternal converts a protobuf blocking-mode sum-type to an
// internal one.
func blockingModeToInternal(pbm isProfile_BlockingMode) (m dnsmsg.BlockingMode, err error) {
@@ -289,7 +303,7 @@ func dohPasswordToInternal(
// toInternal converts a protobuf rate-limiting settings structure to an
// internal one.
-func (x *RateLimitSettings) toInternal(respSzEst datasize.ByteSize) (r agd.Ratelimiter) {
+func (x *Ratelimiter) toInternal(respSzEst datasize.ByteSize) (r agd.Ratelimiter) {
if x == nil || !x.Enabled {
return agd.GlobalRatelimiter{}
}
@@ -301,23 +315,9 @@ func (x *RateLimitSettings) toInternal(respSzEst datasize.ByteSize) (r agd.Ratel
}, respSzEst)
}
-// toInternal converts a protobuf safe browsing settings structure to an
-// internal one.
-func (x *SafeBrowsingSettings) toInternal() (s *agd.SafeBrowsingSettings) {
- if x == nil {
- return nil
- }
-
- return &agd.SafeBrowsingSettings{
- Enabled: x.Enabled,
- BlockDangerousDomains: x.BlockDangerousDomains,
- BlockNewlyRegisteredDomains: x.BlockNewlyRegisteredDomains,
- }
-}
-
// toInternal converts protobuf access settings to an internal structure. If x
// is nil, toInternal returns [access.EmptyProfile].
-func (x *AccessSettings) toInternal() (a access.Profile) {
+func (x *Access) toInternal() (a access.Profile) {
if x == nil {
return access.EmptyProfile{}
}
@@ -362,37 +362,94 @@ func profilesToProtobuf(profiles []*agd.Profile) (pbProfiles []*Profile) {
pbProfiles = make([]*Profile, 0, len(profiles))
for _, p := range profiles {
pbProfiles = append(pbProfiles, &Profile{
- Parental: parentalToProtobuf(p.Parental),
- BlockingMode: blockingModeToProtobuf(p.BlockingMode),
- Access: accessToProtobuf(p.Access.Config()),
- ProfileId: string(p.ID),
- UpdateTime: timestamppb.New(p.UpdateTime),
- DeviceIds: unsafelyConvertStrSlice[agd.DeviceID, string](p.DeviceIDs),
- RuleListIds: unsafelyConvertStrSlice[agd.FilterListID, string](
- p.RuleListIDs,
- ),
- CustomRules: unsafelyConvertStrSlice[agd.FilterRuleText, string](
- p.CustomRules,
- ),
+ FilterConfig: filterConfigToProtobuf(p.FilterConfig),
+ Access: accessToProtobuf(p.Access.Config()),
+ BlockingMode: blockingModeToProtobuf(p.BlockingMode),
+ Ratelimiter: ratelimiterToProtobuf(p.Ratelimiter.Config()),
+ ProfileId: string(p.ID),
+ DeviceIds: unsafelyConvertStrSlice[agd.DeviceID, string](p.DeviceIDs),
FilteredResponseTtl: durationpb.New(p.FilteredResponseTTL),
- FilteringEnabled: p.FilteringEnabled,
- SafeBrowsing: safeBrowsingToProtobuf(p.SafeBrowsing),
- RuleListsEnabled: p.RuleListsEnabled,
- QueryLogEnabled: p.QueryLogEnabled,
- Deleted: p.Deleted,
- BlockPrivateRelay: p.BlockPrivateRelay,
- BlockFirefoxCanary: p.BlockFirefoxCanary,
- IpLogEnabled: p.IPLogEnabled,
AutoDevicesEnabled: p.AutoDevicesEnabled,
- RateLimit: rateLimitToProtobuf(p.Ratelimiter.Config()),
+ BlockChromePrefetch: p.BlockChromePrefetch,
+ BlockFirefoxCanary: p.BlockFirefoxCanary,
+ BlockPrivateRelay: p.BlockPrivateRelay,
+ Deleted: p.Deleted,
+ FilteringEnabled: p.FilteringEnabled,
+ IpLogEnabled: p.IPLogEnabled,
+ QueryLogEnabled: p.QueryLogEnabled,
})
}
return pbProfiles
}
+// filterConfigToProtobuf converts the filtering configration to protobuf.
+func filterConfigToProtobuf(c *filter.ConfigClient) (fc *FilterConfig) {
+ 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,
+ },
+ Parental: &FilterConfig_Parental{
+ PauseSchedule: scheduleToProtobuf(c.Parental.PauseSchedule),
+ BlockedServices: unsafelyConvertStrSlice[filter.BlockedServiceID, string](
+ c.Parental.BlockedServices,
+ ),
+ Enabled: c.Parental.Enabled,
+ AdultBlockingEnabled: c.Parental.AdultBlockingEnabled,
+ SafeSearchGeneralEnabled: c.Parental.SafeSearchGeneralEnabled,
+ SafeSearchYoutubeEnabled: c.Parental.SafeSearchYouTubeEnabled,
+ },
+ RuleList: &FilterConfig_RuleList{
+ Ids: unsafelyConvertStrSlice[filter.ID, string](c.RuleList.IDs),
+ Enabled: c.RuleList.Enabled,
+ },
+ SafeBrowsing: &FilterConfig_SafeBrowsing{
+ Enabled: c.SafeBrowsing.Enabled,
+ DangerousDomainsEnabled: c.SafeBrowsing.DangerousDomainsEnabled,
+ NewlyRegisteredDomainsEnabled: c.SafeBrowsing.NewlyRegisteredDomainsEnabled,
+ },
+ }
+}
+
+// scheduleToProtobuf converts schedule configuration to protobuf. If c is nil,
+// conf is nil.
+func scheduleToProtobuf(c *filter.ConfigSchedule) (conf *FilterConfig_Schedule) {
+ if c == nil {
+ return nil
+ }
+
+ return &FilterConfig_Schedule{
+ Week: &FilterConfig_WeeklySchedule{
+ Mon: dayIntervalToProtobuf(c.Week[time.Monday]),
+ Tue: dayIntervalToProtobuf(c.Week[time.Tuesday]),
+ Wed: dayIntervalToProtobuf(c.Week[time.Wednesday]),
+ Thu: dayIntervalToProtobuf(c.Week[time.Thursday]),
+ Fri: dayIntervalToProtobuf(c.Week[time.Friday]),
+ Sat: dayIntervalToProtobuf(c.Week[time.Saturday]),
+ Sun: dayIntervalToProtobuf(c.Week[time.Sunday]),
+ },
+ TimeZone: c.TimeZone.String(),
+ }
+}
+
+// dayIntervalToProtobuf converts a daily schedule interval to protobuf. If i
+// is nil, ivl is nil.
+func dayIntervalToProtobuf(i *filter.DayInterval) (ivl *DayInterval) {
+ if i == nil {
+ return nil
+ }
+
+ return &DayInterval{
+ Start: uint32(i.Start),
+ End: uint32(i.End),
+ }
+}
+
// accessToProtobuf converts access settings to protobuf structure.
-func accessToProtobuf(c *access.ProfileConfig) (ac *AccessSettings) {
+func accessToProtobuf(c *access.ProfileConfig) (ac *Access) {
if c == nil {
return nil
}
@@ -407,11 +464,11 @@ func accessToProtobuf(c *access.ProfileConfig) (ac *AccessSettings) {
blockedASNs = append(blockedASNs, uint32(asn))
}
- return &AccessSettings{
- AllowlistCidr: prefixesToProtobuf(c.AllowedNets),
- BlocklistCidr: prefixesToProtobuf(c.BlockedNets),
+ return &Access{
AllowlistAsn: allowedASNs,
+ AllowlistCidr: prefixesToProtobuf(c.AllowedNets),
BlocklistAsn: blockedASNs,
+ BlocklistCidr: prefixesToProtobuf(c.BlockedNets),
BlocklistDomainRules: c.BlocklistDomainRules,
}
}
@@ -431,63 +488,6 @@ func prefixesToProtobuf(nets []netip.Prefix) (cidrs []*CidrRange) {
return cidrs
}
-// parentalToProtobuf converts parental settings to protobuf structure.
-func parentalToProtobuf(s *agd.ParentalProtectionSettings) (pbSetts *ParentalProtectionSettings) {
- if s == nil {
- return nil
- }
-
- return &ParentalProtectionSettings{
- Schedule: scheduleToProtobuf(s.Schedule),
- BlockedServices: unsafelyConvertStrSlice[agd.BlockedServiceID, string](
- s.BlockedServices,
- ),
- Enabled: s.Enabled,
- BlockAdult: s.BlockAdult,
- GeneralSafeSearch: s.GeneralSafeSearch,
- YoutubeSafeSearch: s.YoutubeSafeSearch,
- }
-}
-
-// parentalToProtobuf converts parental-settings schedule to protobuf structure.
-func scheduleToProtobuf(s *agd.ParentalProtectionSchedule) (pbSched *ParentalProtectionSchedule) {
- if s == nil {
- return nil
- }
-
- return &ParentalProtectionSchedule{
- TimeZone: s.TimeZone.String(),
- Mon: &DayRange{
- Start: uint32(s.Week[time.Monday].Start),
- End: uint32(s.Week[time.Monday].End),
- },
- Tue: &DayRange{
- Start: uint32(s.Week[time.Tuesday].Start),
- End: uint32(s.Week[time.Tuesday].End),
- },
- Wed: &DayRange{
- Start: uint32(s.Week[time.Wednesday].Start),
- End: uint32(s.Week[time.Wednesday].End),
- },
- Thu: &DayRange{
- Start: uint32(s.Week[time.Thursday].Start),
- End: uint32(s.Week[time.Thursday].End),
- },
- Fri: &DayRange{
- Start: uint32(s.Week[time.Friday].Start),
- End: uint32(s.Week[time.Friday].End),
- },
- Sat: &DayRange{
- Start: uint32(s.Week[time.Saturday].Start),
- End: uint32(s.Week[time.Saturday].End),
- },
- Sun: &DayRange{
- Start: uint32(s.Week[time.Sunday].Start),
- End: uint32(s.Week[time.Sunday].End),
- },
- }
-}
-
// blockingModeToProtobuf converts a blocking-mode sum-type to a protobuf one.
func blockingModeToProtobuf(m dnsmsg.BlockingMode) (pbBlockingMode isProfile_BlockingMode) {
switch m := m.(type) {
@@ -515,6 +515,21 @@ func blockingModeToProtobuf(m dnsmsg.BlockingMode) (pbBlockingMode isProfile_Blo
}
}
+// ipsToByteSlices is a wrapper around netip.Addr.MarshalBinary that ignores the
+// always-nil errors.
+func ipsToByteSlices(ips []netip.Addr) (data [][]byte) {
+ if ips == nil {
+ return nil
+ }
+
+ data = make([][]byte, 0, len(ips))
+ for _, ip := range ips {
+ data = append(data, ipToBytes(ip))
+ }
+
+ return data
+}
+
// ipToBytes is a wrapper around netip.Addr.MarshalBinary that ignores the
// always-nil error.
func ipToBytes(ip netip.Addr) (b []byte) {
@@ -523,6 +538,19 @@ func ipToBytes(ip netip.Addr) (b []byte) {
return b
}
+// ratelimiterToProtobuf converts the rate-limit settings to protobuf.
+func ratelimiterToProtobuf(c *agd.RatelimitConfig) (r *Ratelimiter) {
+ if c == nil {
+ return nil
+ }
+
+ return &Ratelimiter{
+ ClientCidr: prefixesToProtobuf(c.ClientSubnets),
+ Rps: c.RPS,
+ Enabled: c.Enabled,
+ }
+}
+
// devicesToProtobuf converts a slice of devices to protobuf structures.
func devicesToProtobuf(devices []*agd.Device) (pbDevices []*Device) {
pbDevices = make([]*Device, 0, len(devices))
@@ -570,44 +598,3 @@ func dohPasswordToProtobuf(
panic(fmt.Errorf("bad password hash %T(%[1]v)", p))
}
}
-
-// ipsToByteSlices is a wrapper around netip.Addr.MarshalBinary that ignores the
-// always-nil errors.
-func ipsToByteSlices(ips []netip.Addr) (data [][]byte) {
- if ips == nil {
- return nil
- }
-
- data = make([][]byte, 0, len(ips))
- for _, ip := range ips {
- data = append(data, ipToBytes(ip))
- }
-
- return data
-}
-
-// safeBrowsingToProtobuf converts safe browsing settings to protobuf structure.
-func safeBrowsingToProtobuf(s *agd.SafeBrowsingSettings) (sbSetts *SafeBrowsingSettings) {
- if s == nil {
- return nil
- }
-
- return &SafeBrowsingSettings{
- Enabled: s.Enabled,
- BlockDangerousDomains: s.BlockDangerousDomains,
- BlockNewlyRegisteredDomains: s.BlockNewlyRegisteredDomains,
- }
-}
-
-// rateLimitToProtobuf converts rate limit settings to protobuf structure.
-func rateLimitToProtobuf(c *agd.RatelimitConfig) (ac *RateLimitSettings) {
- if c == nil {
- return nil
- }
-
- return &RateLimitSettings{
- Enabled: c.Enabled,
- Rps: c.RPS,
- ClientCidr: prefixesToProtobuf(c.ClientSubnets),
- }
-}
diff --git a/internal/profiledb/internal/internal.go b/internal/profiledb/internal/internal.go
index f3eeaf1..cd4e7f1 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 = 13
+const FileCacheVersion = 15
// 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 cdf4e27..b694cfc 100644
--- a/internal/profiledb/internal/profiledbtest/profiledbtest.go
+++ b/internal/profiledb/internal/profiledbtest/profiledbtest.go
@@ -11,6 +11,7 @@ import (
"github.com/AdguardTeam/AdGuardDNS/internal/agdpasswd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtime"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
"github.com/c2h5oh/datasize"
"github.com/stretchr/testify/require"
@@ -34,7 +35,9 @@ const (
// RespSzEst is a response-size estimate for tests.
const RespSzEst datasize.ByteSize = 1 * datasize.KB
-// NewProfile returns the common profile and device for tests.
+// NewProfile returns the common profile and device for tests. The profile has
+// ID [ProfileID] and the device, [DeviceID]. The response size estimate for
+// the rate limiter is [RespSzEst].
func NewProfile(tb testing.TB) (p *agd.Profile, d *agd.Device) {
tb.Helper()
@@ -56,42 +59,42 @@ func NewProfile(tb testing.TB) (p *agd.Profile, d *agd.Device) {
FilteringEnabled: true,
}
- return &agd.Profile{
- Parental: &agd.ParentalProtectionSettings{
- Schedule: &agd.ParentalProtectionSchedule{
- Week: &agd.WeeklySchedule{
- {Start: 0, End: 700},
- {Start: 0, End: 700},
- {Start: 0, End: 700},
- {Start: 0, End: 700},
- {Start: 0, End: 700},
- {Start: 0, End: 700},
- {Start: 0, End: 700},
- },
- TimeZone: loc,
+ const schedEnd = 701
+
+ parental := &filter.ConfigParental{
+ PauseSchedule: &filter.ConfigSchedule{
+ Week: &filter.WeeklySchedule{
+ time.Monday: {Start: 0, End: schedEnd},
+ time.Tuesday: {Start: 0, End: schedEnd},
+ time.Wednesday: {Start: 0, End: schedEnd},
+ time.Thursday: {Start: 0, End: schedEnd},
+ time.Friday: {Start: 0, End: schedEnd},
+ time.Saturday: nil,
+ time.Sunday: nil,
},
- Enabled: true,
+ TimeZone: loc,
},
- BlockingMode: &dnsmsg.BlockingModeNullIP{},
- ID: ProfileID,
- DeviceIDs: []agd.DeviceID{dev.ID},
- RuleListIDs: []agd.FilterListID{
- "adguard_dns_filter",
- },
- CustomRules: []agd.FilterRuleText{
- "|blocked-by-custom.example",
- },
- FilteredResponseTTL: 10 * time.Second,
- FilteringEnabled: true,
- Ratelimiter: agd.NewDefaultRatelimiter(&agd.RatelimitConfig{
- ClientSubnets: []netip.Prefix{netip.MustParsePrefix("5.5.5.0/24")},
- RPS: 100,
- Enabled: true,
- }, RespSzEst),
- SafeBrowsing: &agd.SafeBrowsingSettings{
- Enabled: true,
- BlockDangerousDomains: true,
- BlockNewlyRegisteredDomains: false,
+ Enabled: true,
+ }
+
+ 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,
+ },
+ Parental: parental,
+ RuleList: &filter.ConfigRuleList{
+ IDs: []filter.ID{filter.IDAdGuardDNS},
+ Enabled: true,
+ },
+ SafeBrowsing: &filter.ConfigSafeBrowsing{
+ Enabled: true,
+ DangerousDomainsEnabled: true,
+ NewlyRegisteredDomainsEnabled: false,
+ },
},
Access: access.NewDefaultProfile(&access.ProfileConfig{
AllowedNets: []netip.Prefix{netip.MustParsePrefix("1.1.1.0/24")},
@@ -100,11 +103,22 @@ func NewProfile(tb testing.TB) (p *agd.Profile, d *agd.Device) {
BlockedASN: []geoip.ASN{2},
BlocklistDomainRules: []string{"block.test"},
}),
- RuleListsEnabled: true,
- QueryLogEnabled: true,
- BlockPrivateRelay: true,
- BlockFirefoxCanary: true,
- IPLogEnabled: true,
- AutoDevicesEnabled: true,
+ BlockingMode: &dnsmsg.BlockingModeNullIP{},
+ Ratelimiter: agd.NewDefaultRatelimiter(&agd.RatelimitConfig{
+ ClientSubnets: []netip.Prefix{netip.MustParsePrefix("5.5.5.0/24")},
+ RPS: 100,
+ Enabled: true,
+ }, RespSzEst),
+ ID: ProfileID,
+ DeviceIDs: []agd.DeviceID{dev.ID},
+ FilteredResponseTTL: 10 * time.Second,
+ AutoDevicesEnabled: true,
+ BlockChromePrefetch: true,
+ BlockFirefoxCanary: true,
+ BlockPrivateRelay: true,
+ Deleted: false,
+ FilteringEnabled: true,
+ IPLogEnabled: true,
+ QueryLogEnabled: true,
}, dev
}
diff --git a/internal/profiledb/profiledb.go b/internal/profiledb/profiledb.go
index 215ec25..db57f4d 100644
--- a/internal/profiledb/profiledb.go
+++ b/internal/profiledb/profiledb.go
@@ -239,7 +239,7 @@ 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", "pb")
+ logger := c.Logger.With("cache_type", "pb")
cacheStorage = filecachepb.New(logger, c.CacheFilePath, c.ResponseSizeEstimate)
} else {
return nil, fmt.Errorf("file %q is not protobuf", c.CacheFilePath)
@@ -427,7 +427,7 @@ func (db *Default) needsFullSync(ctx context.Context) (sinceFull time.Duration,
func (db *Default) loadFileCache(ctx context.Context) (err error) {
start := time.Now()
- l := db.logger.With("cache", "load")
+ l := db.logger.With("cache_op", "load")
l.InfoContext(ctx, "initial loading")
c, err := db.cache.Load(ctx)
diff --git a/internal/querylog/entry.go b/internal/querylog/entry.go
index 1b337cb..348b26f 100644
--- a/internal/querylog/entry.go
+++ b/internal/querylog/entry.go
@@ -87,7 +87,7 @@ const (
// resultData returns the resultCode, filter list ID, and filtering rule from
// the request and response filtering results.
-func resultData(req, resp filter.Result) (c resultCode, id agd.FilterListID, r agd.FilterRuleText) {
+func resultData(req, resp filter.Result) (c resultCode, id filter.ID, r filter.RuleText) {
if req == nil {
c = toResultCode(resp, true)
if resp != nil {
@@ -178,14 +178,14 @@ type jsonlEntry struct {
// If no rules matched, this field is omitted.
//
// The short name "l" stands for "list of filter rules".
- FilterListID agd.FilterListID `json:"l,omitempty"`
+ FilterListID filter.ID `json:"l,omitempty"`
// FilterRule is the first rule that matched the request or the ID of the
- // blocked service, if FilterListID is agd.FilterListIDBlockedService. If
- // no rules matched, this field is omitted.
+ // blocked service, if FilterListID is [filter.IDBlockedService]. If no
+ // rules matched, this field is omitted.
//
// The short name "m" stands for "match".
- FilterRule agd.FilterRuleText `json:"m,omitempty"`
+ FilterRule filter.RuleText `json:"m,omitempty"`
// Timestamp is the Unix time of receiving the request in milliseconds.
//
diff --git a/internal/remotekv/cachekv_test.go b/internal/remotekv/cachekv_test.go
index fd39c82..a494ea3 100644
--- a/internal/remotekv/cachekv_test.go
+++ b/internal/remotekv/cachekv_test.go
@@ -21,7 +21,7 @@ func TestNewCache(t *testing.T) {
cache := remotekv.NewCache(&remotekv.CacheConfig{
Cache: agdcache.NewLRU[string, []byte](&agdcache.LRUConfig{
- Size: 1,
+ Count: 1,
}),
})
diff --git a/internal/rulestat/http.go b/internal/rulestat/http.go
index 0baf3e3..d23e340 100644
--- a/internal/rulestat/http.go
+++ b/internal/rulestat/http.go
@@ -11,10 +11,10 @@ import (
"sync"
"time"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"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"
@@ -25,7 +25,7 @@ import (
// is a temporary restriction.
//
// TODO(ameshkov): Consider making the backend accept the current IDs.
-const statFilterListLegacyID agd.FilterListID = "15"
+const statFilterListLegacyID filter.ID = "15"
// HTTP is the filtering rule statistics collector that uploads the statistics
// to the given URL when it's refreshed.
@@ -44,7 +44,7 @@ type HTTP struct {
}
// statsSet is an alias for the stats set type.
-type statsSet = map[agd.FilterListID]map[agd.FilterRuleText]uint64
+type statsSet = map[filter.ID]map[filter.RuleText]uint64
// HTTPConfig is the configuration structure for the filtering rule statistics
// collector that uploads the statistics to a URL. All fields must not be nil.
@@ -79,8 +79,8 @@ func NewHTTP(c *HTTPConfig) (s *HTTP) {
var _ Interface = (*HTTP)(nil)
// Collect implements the Interface interface for *HTTP.
-func (s *HTTP) Collect(_ context.Context, id agd.FilterListID, text agd.FilterRuleText) {
- if id != agd.FilterListIDAdGuardDNS {
+func (s *HTTP) Collect(_ context.Context, id filter.ID, text filter.RuleText) {
+ if id != filter.IDAdGuardDNS {
return
}
@@ -99,7 +99,7 @@ func (s *HTTP) Collect(_ context.Context, id agd.FilterListID, text agd.FilterRu
return
}
- s.stats[id] = map[agd.FilterRuleText]uint64{
+ s.stats[id] = map[filter.RuleText]uint64{
text: 1,
}
}
diff --git a/internal/rulestat/http_test.go b/internal/rulestat/http_test.go
index 5a80bd4..c251ffb 100644
--- a/internal/rulestat/http_test.go
+++ b/internal/rulestat/http_test.go
@@ -9,9 +9,9 @@ import (
"net/url"
"testing"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
"github.com/AdguardTeam/AdGuardDNS/internal/agdhttp"
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
"github.com/AdguardTeam/AdGuardDNS/internal/rulestat"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"github.com/AdguardTeam/golibs/testutil"
@@ -50,19 +50,19 @@ func TestHTTP_Collect(t *testing.T) {
testCases := []struct {
name string
want string
- rules []agd.FilterRuleText
+ rules []filter.RuleText
}{{
name: "single",
want: `{"filters":{"15":{"||example.org^":1}}}`,
- rules: []agd.FilterRuleText{"||example.org^"},
+ rules: []filter.RuleText{"||example.org^"},
}, {
name: "several_alike",
want: `{"filters":{"15":{"||example.org^":3}}}`,
- rules: []agd.FilterRuleText{"||example.org^", "||example.org^", "||example.org^"},
+ rules: []filter.RuleText{"||example.org^", "||example.org^", "||example.org^"},
}, {
name: "several_different",
want: `{"filters":{"15":{"||example.org^":1, "||example.com^":1, "||пример.рф^":1}}}`,
- rules: []agd.FilterRuleText{"||example.org^", "||example.com^", "||пример.рф^"},
+ rules: []filter.RuleText{"||example.org^", "||example.com^", "||пример.рф^"},
}}
for _, tc := range testCases {
@@ -72,7 +72,7 @@ func TestHTTP_Collect(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
ctx := testutil.ContextWithTimeout(t, testTimeout)
for _, rule := range tc.rules {
- h.Collect(ctx, agd.FilterListIDAdGuardDNS, rule)
+ h.Collect(ctx, filter.IDAdGuardDNS, rule)
}
// Use the context different from the above one.
diff --git a/internal/rulestat/rulestat.go b/internal/rulestat/rulestat.go
index 62b0e4b..20634d2 100644
--- a/internal/rulestat/rulestat.go
+++ b/internal/rulestat/rulestat.go
@@ -4,7 +4,7 @@ package rulestat
import (
"context"
- "github.com/AdguardTeam/AdGuardDNS/internal/agd"
+ "github.com/AdguardTeam/AdGuardDNS/internal/filter"
)
// Interface is an ephemeral storage of the filtering rule list statistics
@@ -12,7 +12,7 @@ import (
//
// All methods must be safe for concurrent use.
type Interface interface {
- Collect(ctx context.Context, id agd.FilterListID, r agd.FilterRuleText)
+ Collect(ctx context.Context, id filter.ID, r filter.RuleText)
}
// type check
@@ -22,4 +22,4 @@ var _ Interface = Empty{}
type Empty struct{}
// Collect implements the Interface interface for Empty.
-func (Empty) Collect(_ context.Context, _ agd.FilterListID, _ agd.FilterRuleText) {}
+func (Empty) Collect(_ context.Context, _ filter.ID, _ filter.RuleText) {}
diff --git a/internal/tlsconfig/certstorage.go b/internal/tlsconfig/certstorage.go
new file mode 100644
index 0000000..2eec760
--- /dev/null
+++ b/internal/tlsconfig/certstorage.go
@@ -0,0 +1,96 @@
+package tlsconfig
+
+import (
+ "crypto/tls"
+ "slices"
+
+ "github.com/AdguardTeam/golibs/errors"
+)
+
+// certPaths contains a certificate path and a key path.
+type certPaths struct {
+ certPath string
+ keyPath string
+}
+
+// certStorage holds TLS certificates and their associated file paths. Each
+// entry in the slices corresponds to a certificate and its respective paths for
+// the certificate and key files. Using this struct allows us to reduce
+// allocations.
+type certStorage struct {
+ // certs contains the list of TLS certificates. All elements must not be
+ // nil.
+ certs []*tls.Certificate
+
+ // paths contains corresponding file paths for certificate and key files.
+ // All elements must not be nil.
+ paths []*certPaths
+}
+
+// add saves the TLS certificate and its paths. Certificate paths must only be
+// added once, see [certStorage.contains]. cert and cp must not be nil.
+func (s *certStorage) add(cert *tls.Certificate, cp *certPaths) {
+ s.certs = append(s.certs, cert)
+ s.paths = append(s.paths, cp)
+}
+
+// contains returns true if the TLS certificate has already been added using the
+// provided file paths. cp must not be nil.
+func (s *certStorage) contains(cp *certPaths) (ok bool) {
+ return slices.ContainsFunc(s.paths, func(p *certPaths) (found bool) {
+ return *cp == *p
+ })
+}
+
+// count returns the number of saved TLS certificates.
+func (s *certStorage) count() (n int) {
+ return len(s.certs)
+}
+
+// certFor returns the TLS certificate for chi. chi must not be nil. cert must
+// not be modified.
+func (s *certStorage) certFor(chi *tls.ClientHelloInfo) (cert *tls.Certificate, err error) {
+ var errs []error
+ for _, c := range s.certs {
+ err = chi.SupportsCertificate(c)
+ if err == nil {
+ return c, nil
+ }
+
+ errs = append(errs, err)
+ }
+
+ return nil, errors.Join(errs...)
+}
+
+// rangeFn calls fn for each stored TLS certificate and its paths. fn must not
+// be nil. Neither cert nor cp must be modified.
+func (s *certStorage) rangeFn(fn func(cert *tls.Certificate, cp *certPaths) (cont bool)) {
+ for i, p := range s.paths {
+ if !fn(s.certs[i], p) {
+ return
+ }
+ }
+}
+
+// stored returns the list of saved TLS certificates.
+func (s *certStorage) stored() (certs []*tls.Certificate) {
+ return s.certs
+}
+
+// update updates the certificate corresponding to the paths. cp and c must not
+// be nil.
+//
+// TODO(a.garipov): Think of a better way to do this that doesn't involve code
+// that looks like iterator invalidation.
+func (s *certStorage) update(cp *certPaths, c *tls.Certificate) (ok bool) {
+ for i, p := range s.paths {
+ if *cp == *p {
+ s.certs[i] = c
+
+ return true
+ }
+ }
+
+ return false
+}
diff --git a/internal/tlsconfig/certstorage_internal_test.go b/internal/tlsconfig/certstorage_internal_test.go
new file mode 100644
index 0000000..8a0e3de
--- /dev/null
+++ b/internal/tlsconfig/certstorage_internal_test.go
@@ -0,0 +1,92 @@
+package tlsconfig
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestCertStorage(t *testing.T) {
+ const (
+ domainA = "a.com"
+ domainB = "b.org"
+ )
+
+ var (
+ pathsDomainA = &certPaths{
+ certPath: domainA + "_path",
+ keyPath: domainA + "_path",
+ }
+ pathsDomainB = &certPaths{
+ certPath: domainB + "_path",
+ keyPath: domainB + "_path",
+ }
+ nonAddedPaths = &certPaths{
+ certPath: "non_added_path",
+ keyPath: "non_added_path",
+ }
+ )
+
+ certDomainA := &tls.Certificate{Leaf: &x509.Certificate{
+ DNSNames: []string{domainA},
+ Version: tls.VersionTLS13,
+ }}
+
+ certDomainB := &tls.Certificate{Leaf: &x509.Certificate{
+ DNSNames: []string{domainB},
+ Version: tls.VersionTLS13,
+ }}
+
+ certWithPaths := []struct {
+ cert *tls.Certificate
+ paths *certPaths
+ }{{
+ cert: certDomainA,
+ paths: pathsDomainA,
+ }, {
+ cert: certDomainB,
+ paths: pathsDomainB,
+ }}
+
+ s := &certStorage{}
+ for _, cp := range certWithPaths {
+ s.add(cp.cert, cp.paths)
+ }
+
+ assert.True(t, s.contains(pathsDomainA))
+
+ copyPathsDomainsB := *pathsDomainB
+ assert.True(t, s.contains(©PathsDomainsB))
+ assert.False(t, s.contains(nonAddedPaths))
+ assert.Equal(t, len(certWithPaths), s.count())
+
+ got, err := s.certFor(&tls.ClientHelloInfo{
+ ServerName: domainA,
+ SupportedVersions: []uint16{tls.VersionTLS13},
+ })
+ require.NoError(t, err)
+
+ assert.Equal(t, certDomainA, got)
+
+ got, err = s.certFor(&tls.ClientHelloInfo{
+ ServerName: domainB,
+ SupportedVersions: []uint16{tls.VersionTLS13},
+ })
+ require.NoError(t, err)
+
+ assert.Equal(t, certDomainB, got)
+ assert.Equal(t, []*tls.Certificate{certDomainA, certDomainB}, s.stored())
+
+ i := 0
+ s.rangeFn(func(c *tls.Certificate, cp *certPaths) (cont bool) {
+ assert.Equal(t, certWithPaths[i].cert, c)
+ assert.Equal(t, certWithPaths[i].paths, cp)
+
+ i++
+
+ return true
+ })
+}
diff --git a/internal/tlsconfig/manager.go b/internal/tlsconfig/manager.go
index f6e35c0..9bbeaba 100644
--- a/internal/tlsconfig/manager.go
+++ b/internal/tlsconfig/manager.go
@@ -1,17 +1,13 @@
package tlsconfig
import (
- "cmp"
"context"
"crypto/tls"
"fmt"
"io"
"log/slog"
- "maps"
"os"
"path/filepath"
- "slices"
- "strings"
"sync"
"github.com/AdguardTeam/AdGuardDNS/internal/agdservice"
@@ -21,9 +17,15 @@ import (
// Manager stores and updates TLS configurations.
type Manager interface {
- // Add returns an initialized TLS configuration using the provided paths to
- // a certificate and a key. certPath and keyPath must not be empty.
- Add(ctx context.Context, certPath, keyPath string) (c *tls.Config, err error)
+ // Add saves an initialized TLS certificate using the provided paths to a
+ // certificate and a key. certPath and keyPath must not be empty.
+ Add(ctx context.Context, certPath, keyPath string) (err error)
+
+ // Clone returns the TLS configuration that contains saved TLS certificates.
+ Clone() (c *tls.Config)
+
+ // CloneWithMetrics is like [Manager.Clone] but it also sets metrics.
+ CloneWithMetrics(proto, srvName string, deviceDomains []string) (c *tls.Config)
}
// DefaultManagerConfig is the configuration structure for [DefaultManager].
@@ -33,11 +35,11 @@ type DefaultManagerConfig struct {
// Logger is used for logging the operation of the TLS manager.
Logger *slog.Logger
- // ErrColl is used to collect TLS related errors.
+ // ErrColl is used to collect TLS-related errors.
ErrColl errcoll.Interface
- // Metrics is used to collect TLS related statistics.
- Metrics RefreshMetrics
+ // Metrics is used to collect TLS-related statistics.
+ Metrics Metrics
// KeyLogFilename, if not empty, is the name of the TLS key log file.
KeyLogFilename string
@@ -46,33 +48,19 @@ type DefaultManagerConfig struct {
SessionTicketPaths []string
}
-// certWithKey contains a certificate path and a key path.
-type certWithKey struct {
- certPath string
- keyPath string
-}
-
-// compare is a comparison function for the certWithKey. It returns -1 if a
-// sorts before b, 1 if a sorts after b, and 0 if their relative sorting
-// position is the same. The sorting prioritizes certificate paths first, and
-// then key paths.
-func (a certWithKey) compare(b certWithKey) (r int) {
- return cmp.Or(
- strings.Compare(a.certPath, b.certPath),
- strings.Compare(a.keyPath, b.keyPath),
- )
-}
-
// DefaultManager is the default implementation of [Manager].
type DefaultManager struct {
- // mu prtotects configs, sessionTickets.
- mu *sync.Mutex
- logger *slog.Logger
- errColl errcoll.Interface
- metrics RefreshMetrics
- keyLogWriter io.Writer
- configs map[certWithKey]*tls.Config
- sessTicketPaths []string
+ // mu protects fields certStorage, clones, clonesWithMetrics,
+ // sessTicketPaths.
+ mu *sync.Mutex
+ logger *slog.Logger
+ errColl errcoll.Interface
+ metrics Metrics
+ certStorage *certStorage
+ original *tls.Config
+ clones []*tls.Config
+ clonesWithMetrics []*tls.Config
+ sessTicketPaths []string
}
// NewDefaultManager returns a new initialized *DefaultManager.
@@ -86,15 +74,23 @@ func NewDefaultManager(conf *DefaultManagerConfig) (m *DefaultManager, err error
}
}
- return &DefaultManager{
+ m = &DefaultManager{
mu: &sync.Mutex{},
logger: conf.Logger,
errColl: conf.ErrColl,
metrics: conf.Metrics,
- keyLogWriter: kl,
- configs: make(map[certWithKey]*tls.Config),
+ certStorage: &certStorage{},
sessTicketPaths: conf.SessionTicketPaths,
- }, nil
+ }
+
+ m.original = &tls.Config{
+ GetCertificate: m.getCertificate,
+ MinVersion: tls.VersionTLS12,
+ MaxVersion: tls.VersionTLS13,
+ KeyLogWriter: kl,
+ }
+
+ return m, nil
}
// type check
@@ -105,8 +101,8 @@ func (m *DefaultManager) Add(
ctx context.Context,
certPath string,
keyPath string,
-) (conf *tls.Config, err error) {
- ck := certWithKey{
+) (err error) {
+ cp := &certPaths{
certPath: certPath,
keyPath: keyPath,
}
@@ -114,17 +110,36 @@ func (m *DefaultManager) Add(
m.mu.Lock()
defer m.mu.Unlock()
- if conf = m.configs[ck]; conf != nil {
- return conf, nil
+ if m.certStorage.contains(cp) {
+ m.logger.InfoContext(
+ ctx,
+ "skipping already added certificate",
+ "cert", cp.certPath,
+ "key", cp.keyPath,
+ )
+
+ return nil
}
- return m.add(ctx, ck)
+ cert, err := m.load(ctx, cp)
+ if err != nil {
+ return fmt.Errorf("adding certificate: %w", err)
+ }
+
+ m.certStorage.add(cert, cp)
+
+ m.logger.InfoContext(ctx, "added certificate", "cert", cp.certPath, "key", cp.keyPath)
+
+ return nil
}
-// add returns a new TLS configuration from the provided certificate and key
-// paths. m.mu must be locked.
-func (m *DefaultManager) add(ctx context.Context, ck certWithKey) (conf *tls.Config, err error) {
- cert, err := tls.LoadX509KeyPair(ck.certPath, ck.keyPath)
+// load returns a new TLS configuration from the provided certificate and key
+// paths. m.mu must be locked. c must not be modified.
+func (m *DefaultManager) load(
+ ctx context.Context,
+ cp *certPaths,
+) (c *tls.Certificate, err error) {
+ cert, err := tls.LoadX509KeyPair(cp.certPath, cp.keyPath)
if err != nil {
return nil, fmt.Errorf("loading certificate: %w", err)
}
@@ -133,30 +148,58 @@ func (m *DefaultManager) add(ctx context.Context, ck certWithKey) (conf *tls.Con
subj := cert.Leaf.Subject.String()
m.metrics.SetCertificateInfo(ctx, authAlgo, subj, cert.Leaf.NotAfter)
- if conf = m.configs[ck]; conf != nil {
- conf.GetCertificate = func(h *tls.ClientHelloInfo) (c *tls.Certificate, err error) {
- return &cert, nil
- }
+ return &cert, nil
+}
- m.logger.InfoContext(ctx, "refreshed config", "cert", ck.certPath, "key", ck.keyPath)
+// Clone implements the [Manager] interface for *DefaultManager.
+func (m *DefaultManager) Clone() (clone *tls.Config) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
- return conf, nil
+ clone = m.original.Clone()
+ m.clones = append(m.clones, clone)
+
+ return clone
+}
+
+// getCertificate returns the TLS certificate for chi. See
+// [tls.Config.GetCertificate]. c must not be modified.
+func (m *DefaultManager) getCertificate(chi *tls.ClientHelloInfo) (c *tls.Certificate, err error) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ if m.certStorage.count() == 0 {
+ return nil, errors.Error("no certificates")
}
- conf = &tls.Config{
- GetCertificate: func(clientHello *tls.ClientHelloInfo) (c *tls.Certificate, err error) {
- return &cert, nil
- },
- MinVersion: tls.VersionTLS12,
- MaxVersion: tls.VersionTLS13,
- KeyLogWriter: m.keyLogWriter,
- }
+ return m.certStorage.certFor(chi)
+}
- m.configs[ck] = conf
+// CloneWithMetrics implements the [Manager] interface for *DefaultManager.
+func (m *DefaultManager) CloneWithMetrics(
+ proto string,
+ srvName string,
+ deviceDomains []string,
+) (conf *tls.Config) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
- m.logger.InfoContext(ctx, "added config", "cert", ck.certPath, "key", ck.keyPath)
+ clone := m.original.Clone()
- return conf, nil
+ clone.GetConfigForClient = m.metrics.BeforeHandshake(proto)
+
+ clone.GetCertificate = m.getCertificate
+
+ clone.VerifyConnection = m.metrics.AfterHandshake(
+ proto,
+ srvName,
+ deviceDomains,
+ m.certStorage.stored(),
+ )
+
+ m.clonesWithMetrics = append(m.clonesWithMetrics, clone)
+
+ return clone
}
// type check
@@ -177,17 +220,29 @@ func (m *DefaultManager) Refresh(ctx context.Context) (err error) {
defer m.mu.Unlock()
var errs []error
- for _, ck := range slices.SortedFunc(maps.Keys(m.configs), certWithKey.compare) {
- _, err = m.add(ctx, ck)
- errs = append(errs, err)
- }
+ m.certStorage.rangeFn(func(_ *tls.Certificate, cp *certPaths) (cont bool) {
+ cert, loadErr := m.load(ctx, cp)
+ if err != nil {
+ errs = append(errs, loadErr)
+
+ 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)
+ }
+
+ return true
+ })
err = errors.Join(errs...)
if err != nil {
return fmt.Errorf("refreshing tls certificates: %w", err)
}
- m.logger.InfoContext(ctx, "refresh successful", "num_configs", len(m.configs))
+ m.logger.InfoContext(ctx, "refresh successful", "num_configs", m.certStorage.count())
return nil
}
@@ -233,14 +288,18 @@ func (m *DefaultManager) RotateTickets(ctx context.Context) (err error) {
m.mu.Lock()
defer m.mu.Unlock()
- for _, conf := range m.configs {
+ for _, conf := range m.clones {
+ conf.SetSessionTicketKeys(tickets)
+ }
+
+ for _, conf := range m.clonesWithMetrics {
conf.SetSessionTicketKeys(tickets)
}
m.logger.InfoContext(
ctx,
"ticket rotation successful",
- "num_configs", len(m.configs),
+ "num_configs", m.certStorage.count(),
"num_tickets", len(tickets),
)
@@ -259,9 +318,9 @@ func readSessionTicketKey(fn string) (ticket sessionTicket, err error) {
}
tickLen := len(b)
- if tickLen != sessTickLen {
+ if tickLen < sessTickLen {
return ticket, fmt.Errorf(
- "session ticket in %s: bad len %d, want %d",
+ "session ticket in %q: bad len %d, want no less than %d",
fn,
tickLen,
sessTickLen,
diff --git a/internal/tlsconfig/manager_test.go b/internal/tlsconfig/manager_test.go
index 60d2bcc..5924ab1 100644
--- a/internal/tlsconfig/manager_test.go
+++ b/internal/tlsconfig/manager_test.go
@@ -98,6 +98,19 @@ func writeSessionKey(tb testing.TB, sessKeyPath string) {
require.NoError(tb, err)
}
+// assertCertSerialNumber is a helper function that checks serial number of the
+// TLS certificate.
+func assertCertSerialNumber(tb testing.TB, conf *tls.Config, wantSN int64) {
+ tb.Helper()
+
+ cert, err := conf.GetCertificate(&tls.ClientHelloInfo{
+ SupportedVersions: []uint16{tls.VersionTLS13},
+ })
+ require.NoError(tb, err)
+
+ assert.Equal(tb, wantSN, cert.Leaf.SerialNumber.Int64())
+}
+
func TestDefaultManager_Refresh(t *testing.T) {
t.Parallel()
@@ -109,7 +122,7 @@ func TestDefaultManager_Refresh(t *testing.T) {
m, err := tlsconfig.NewDefaultManager(&tlsconfig.DefaultManagerConfig{
Logger: slogutil.NewDiscardLogger(),
ErrColl: agdtest.NewErrorCollector(),
- Metrics: tlsconfig.EmptyRefreshMetrics{},
+ Metrics: tlsconfig.EmptyMetrics{},
})
require.NoError(t, err)
@@ -122,13 +135,14 @@ func TestDefaultManager_Refresh(t *testing.T) {
writeCertAndKey(t, certDER, certPath, key, keyPath)
ctx := testutil.ContextWithTimeout(t, testTimeout)
- conf, err := m.Add(ctx, certPath, keyPath)
+ err = m.Add(ctx, certPath, keyPath)
require.NoError(t, err)
- cert, err := conf.GetCertificate(&tls.ClientHelloInfo{})
- require.NoError(t, err)
+ conf := m.Clone()
+ confWithMetrics := m.CloneWithMetrics("", "", nil)
- assert.Equal(t, snBefore, cert.Leaf.SerialNumber.Int64())
+ assertCertSerialNumber(t, conf, snBefore)
+ assertCertSerialNumber(t, confWithMetrics, snBefore)
certDER, key = newCertAndKey(t, snAfter)
writeCertAndKey(t, certDER, certPath, key, keyPath)
@@ -136,10 +150,8 @@ func TestDefaultManager_Refresh(t *testing.T) {
err = m.Refresh(ctx)
require.NoError(t, err)
- cert, err = conf.GetCertificate(&tls.ClientHelloInfo{})
- require.NoError(t, err)
-
- assert.Equal(t, snAfter, cert.Leaf.SerialNumber.Int64())
+ assertCertSerialNumber(t, conf, snAfter)
+ assertCertSerialNumber(t, confWithMetrics, snAfter)
}
func TestDefaultManager_RotateTickets(t *testing.T) {
@@ -152,7 +164,7 @@ func TestDefaultManager_RotateTickets(t *testing.T) {
m, err := tlsconfig.NewDefaultManager(&tlsconfig.DefaultManagerConfig{
Logger: slogutil.NewDiscardLogger(),
ErrColl: agdtest.NewErrorCollector(),
- Metrics: tlsconfig.EmptyRefreshMetrics{},
+ Metrics: tlsconfig.EmptyMetrics{},
SessionTicketPaths: []string{sessKeyPath},
})
require.NoError(t, err)
@@ -165,7 +177,7 @@ func TestDefaultManager_RotateTickets(t *testing.T) {
writeCertAndKey(t, certDER, certPath, key, keyPath)
ctx := testutil.ContextWithTimeout(t, testTimeout)
- _, err = m.Add(ctx, certPath, keyPath)
+ err = m.Add(ctx, certPath, keyPath)
require.NoError(t, err)
err = m.RotateTickets(ctx)
diff --git a/internal/tlsconfig/metrics.go b/internal/tlsconfig/metrics.go
index 30b0096..0b4be1a 100644
--- a/internal/tlsconfig/metrics.go
+++ b/internal/tlsconfig/metrics.go
@@ -6,7 +6,7 @@ import (
"time"
)
-// Metrics is an interface that is used for the collection of the TLS related
+// Metrics is an interface that is used for the collection of the TLS-related
// statistics.
type Metrics interface {
// BeforeHandshake returns a function that needs to be passed to
@@ -19,18 +19,9 @@ type Metrics interface {
proto string,
srvName string,
devDomains []string,
- srvCerts []tls.Certificate,
+ srvCerts []*tls.Certificate,
) (f func(s tls.ConnectionState) (err error))
- // RefreshMetrics gathers statistics during updates.
- //
- // TODO(s.chzhen): Separate it.
- RefreshMetrics
-}
-
-// RefreshMetrics is an interface that is used to collect statistics during TLS
-// certificate and TLS session ticket updates.
-type RefreshMetrics interface {
// SetCertificateInfo sets the TLS certificate information.
SetCertificateInfo(ctx context.Context, algo, subj string, notAfter time.Time)
@@ -62,7 +53,7 @@ func (EmptyMetrics) AfterHandshake(
_ string,
_ string,
_ []string,
- _ []tls.Certificate,
+ _ []*tls.Certificate,
) (f func(s tls.ConnectionState) (err error)) {
return func(tls.ConnectionState) error {
return nil
@@ -75,18 +66,3 @@ func (EmptyMetrics) SetCertificateInfo(_ context.Context, _, _ string, _ time.Ti
// SetSessionTicketRotationStatus implements the [Metrics] interface for
// EmptyMetrics.
func (EmptyMetrics) SetSessionTicketRotationStatus(_ context.Context, _ bool) {}
-
-// EmptyRefreshMetrics is the implementation of the [RefreshMetrics] interface
-// that does nothing.
-type EmptyRefreshMetrics struct{}
-
-// type check
-var _ RefreshMetrics = EmptyRefreshMetrics{}
-
-// SetCertificateInfo implements the [RefreshMetrics] interface for
-// EmptyRefreshMetrics.
-func (EmptyRefreshMetrics) SetCertificateInfo(_ context.Context, _, _ string, _ time.Time) {}
-
-// SetSessionTicketRotationStatus implements the [RefreshMetrics] interface for
-// EmptyRefreshMetrics.
-func (EmptyRefreshMetrics) SetSessionTicketRotationStatus(_ context.Context, _ bool) {}
diff --git a/internal/tlsconfig/tlsconfig.go b/internal/tlsconfig/tlsconfig.go
index 83f34f4..7b66f2b 100644
--- a/internal/tlsconfig/tlsconfig.go
+++ b/internal/tlsconfig/tlsconfig.go
@@ -1,3 +1,3 @@
-// Package tlsconfig contains TLS related interfaces, helpers, and
+// Package tlsconfig contains TLS-related interfaces, helpers, and
// implementations.
package tlsconfig
diff --git a/internal/tools/go.mod b/internal/tools/go.mod
index 0a02d17..303103c 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.2
+go 1.23.4
require (
github.com/fzipp/gocyclo v0.6.0
@@ -10,10 +10,10 @@ require (
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.26.0
+ golang.org/x/tools v0.28.0
golang.org/x/vuln v1.1.3
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1
- google.golang.org/protobuf v1.35.1
+ google.golang.org/protobuf v1.35.2
honnef.co/go/tools v0.5.1
mvdan.cc/gofumpt v0.7.0
mvdan.cc/sh/v3 v3.10.0
@@ -22,49 +22,49 @@ require (
require (
cloud.google.com/go v0.116.0 // indirect
- cloud.google.com/go/ai v0.8.2 // indirect
- cloud.google.com/go/auth v0.9.9 // indirect
- cloud.google.com/go/auth/oauth2adapt v0.2.4 // 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.2 // indirect
+ cloud.google.com/go/longrunning v0.6.3 // 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-20210331224755-41bb18bfe9da // indirect
- github.com/google/generative-ai-go v0.18.0 // 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/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
- github.com/googleapis/gax-go/v2 v2.13.0 // indirect
+ github.com/googleapis/gax-go/v2 v2.14.0 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/rogpeppe/go-internal v1.13.1 // 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.56.0 // indirect
- go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect
- go.opentelemetry.io/otel v1.31.0 // indirect
- go.opentelemetry.io/otel/metric v1.31.0 // indirect
- go.opentelemetry.io/otel/trace v1.31.0 // indirect
- golang.org/x/crypto v0.28.0 // indirect
- golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
- golang.org/x/exp/typeparams v0.0.0-20241009180824-f66d83c29e7c // indirect
- golang.org/x/mod v0.21.0 // indirect
- golang.org/x/net v0.30.0 // indirect
- golang.org/x/oauth2 v0.23.0 // indirect
- golang.org/x/sync v0.8.0 // indirect
- golang.org/x/sys v0.26.0 // indirect
- golang.org/x/telemetry v0.0.0-20241017030730-50079b310951 // indirect
- golang.org/x/term v0.25.0 // indirect
- golang.org/x/text v0.19.0 // indirect
- golang.org/x/time v0.7.0 // indirect
- google.golang.org/api v0.203.0 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
- google.golang.org/grpc v1.67.1 // 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
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 779bc3f..ac81594 100644
--- a/internal/tools/go.sum
+++ b/internal/tools/go.sum
@@ -1,16 +1,16 @@
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.8.2 h1:LEaQwqBv+k2ybrcdTtCTc9OPZXoEdcQaGrfvDYS6Bnk=
-cloud.google.com/go/ai v0.8.2/go.mod h1:Wb3EUUGWwB6yHBaUf/+oxUq/6XbCaU1yh0GrwUS8lr4=
-cloud.google.com/go/auth v0.9.9 h1:BmtbpNQozo8ZwW2t7QJjnrQtdganSdmqeIBxHxNkEZQ=
-cloud.google.com/go/auth v0.9.9/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI=
-cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY=
-cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc=
+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.2 h1:xjDfh1pQcWPEvnfjZmwjKQEcHnpz6lHjfy7Fo0MK+hc=
-cloud.google.com/go/longrunning v0.6.2/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI=
+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=
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=
@@ -41,8 +41,8 @@ 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/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-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/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=
@@ -57,8 +57,8 @@ 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=
github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo=
-github.com/google/generative-ai-go v0.18.0 h1:6ybg9vOCLcI/UpBBYXOTVgvKmcUKFRNj+2Cj3GnebSo=
-github.com/google/generative-ai-go v0.18.0/go.mod h1:JYolL13VG7j79kM5BtHz4qwONHkeJQzOCkKXnpqtS/E=
+github.com/google/generative-ai-go v0.19.0 h1:R71szggh8wHMCUlEMsW2A/3T+5LdEIkiaHSYgSpUgdg=
+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=
@@ -83,8 +83,8 @@ 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.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s=
-github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A=
+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/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=
@@ -125,32 +125,32 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJu
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.56.0 h1:yMkBS9yViCc7U7yeLzJPM2XizlfdVvBRSmsQDWu6qc0=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0/go.mod h1:n8MR6/liuGB5EmTETUBeU5ZgqMOlqKRxUaqPQBOANZ8=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
-go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
-go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
-go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
-go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
-go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
-go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
+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=
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.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
-golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
+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-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
-golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
-golang.org/x/exp/typeparams v0.0.0-20241009180824-f66d83c29e7c h1:F/15/6p7LyGUSoP0GE5CB/U9+TNEER1foNOP5sWLLnI=
-golang.org/x/exp/typeparams v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
+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/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
-golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
+golang.org/x/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=
@@ -159,17 +159,17 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
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.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
-golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
+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.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
-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/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-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.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
-golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+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/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=
@@ -177,19 +177,19 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
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.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
-golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/telemetry v0.0.0-20241017030730-50079b310951 h1:VIsPFKAgj0HiIP3VzW4sSLxbZ9GnRitkOBt0bwqYl3Q=
-golang.org/x/telemetry v0.0.0-20241017030730-50079b310951/go.mod h1:uskmY3Y2C5OU/HAtQlc9Jq98qE2bf7H3kCPFgkab838=
+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/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
-golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
+golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
+golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
-golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
-golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
-golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/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.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=
@@ -197,32 +197,32 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
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.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
-golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
+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/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.203.0 h1:SrEeuwU3S11Wlscsn+LA1kb/Y5xT8uggJSkIhD08NAU=
-google.golang.org/api v0.203.0/go.mod h1:BuOVyCSYEPwJb3npWvDnNmFI92f3GeRnHNkETneT3SI=
+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-20241021214115-324edc3d5d38 h1:2oV8dfuIkM1Ti7DwXc0BJfnwr9csz4TDXI9EmiI+Rbw=
-google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38/go.mod h1:vuAjtvlwkDKF6L1GQ0SokiRLCGFfeBUXWr/aFFkHACc=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
+google.golang.org/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.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
-google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
+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/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=
@@ -234,8 +234,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
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.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
-google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/protobuf v1.35.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=
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=
diff --git a/internal/websvc/linkip.go b/internal/websvc/linkip.go
index 2eb470d..12b284f 100644
--- a/internal/websvc/linkip.go
+++ b/internal/websvc/linkip.go
@@ -78,11 +78,19 @@ func linkedIPHandler(
}
// Collect errors using our own error collector.
- errHdlr := func(_ http.ResponseWriter, r *http.Request, err error) {
+ handlerWithError := func(_ http.ResponseWriter, r *http.Request, err error) {
ctx := r.Context()
reqID, _ := agd.RequestIDFromContext(ctx)
- m, p := r.Method, r.URL.Path
- errcoll.Collectf(ctx, errColl, "%s: proxying %s %s: req %s: %w", logPrefix, m, p, reqID, err)
+ errcoll.Collectf(
+ ctx,
+ errColl,
+ "%s: proxying %s %s: req %s: %w",
+ logPrefix,
+ r.Method,
+ r.URL.Path,
+ reqID,
+ err,
+ )
}
return &linkedIPProxy{
@@ -91,7 +99,7 @@ func linkedIPHandler(
Transport: transport,
ErrorLog: log.StdLog(logPrefix, log.DEBUG),
ModifyResponse: modifyResponse,
- ErrorHandler: errHdlr,
+ ErrorHandler: handlerWithError,
},
errColl: errColl,
logPrefix: logPrefix,
diff --git a/scripts/backend/dns.go b/scripts/backend/dns.go
index 3c83b89..3df5b0e 100644
--- a/scripts/backend/dns.go
+++ b/scripts/backend/dns.go
@@ -183,8 +183,9 @@ func newDNSProfile() (dp *backendpb.DNSProfile) {
Devices: devices,
CustomRules: []string{"||example.org^"},
FilteredResponseTtl: durationpb.New(10 * time.Second),
- BlockPrivateRelay: true,
+ BlockChromePrefetch: true,
BlockFirefoxCanary: true,
+ BlockPrivateRelay: true,
BlockingMode: &backendpb.DNSProfile_BlockingModeCustomIp{
BlockingModeCustomIp: &backendpb.BlockingModeCustomIP{
Ipv4: []byte{1, 2, 3, 4},