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