From 87137bddcfae6cf17643a66705e3931645826b59 Mon Sep 17 00:00:00 2001 From: Andrey Meshkov Date: Fri, 8 Nov 2024 16:26:22 +0300 Subject: [PATCH] Sync v2.10.0 --- CHANGELOG.md | 88 + Makefile | 2 +- config.dist.yaml | 23 +- doc/configuration.md | 26 +- doc/development.md | 2 +- doc/environment.md | 43 +- doc/externalhttp.md | 20 + go.mod | 43 +- go.sum | 76 +- go.work | 2 +- go.work.sum | 14 + internal/agdhttp/client.go | 2 + internal/agdhttp/url.go | 44 +- internal/agdhttp/url_test.go | 16 +- internal/agdtest/agdtest.go | 36 +- internal/agdtest/interface.go | 130 +- internal/backendpb/backendpb.go | 6 +- internal/backendpb/backendpb_test.go | 36 + internal/backendpb/billstat.go | 2 +- internal/backendpb/dns.pb.go | 1648 ++++----- internal/backendpb/dns.proto | 57 + internal/backendpb/dns_grpc.pb.go | 268 +- internal/backendpb/profiledb.go | 2 +- internal/backendpb/ratelimiter.go | 103 + internal/backendpb/ratelimiter_test.go | 110 + internal/backendpb/remotekv.go | 96 + internal/backendpb/remotekv_test.go | 106 + internal/bindtodevice/connindex_linux.go | 30 +- internal/cmd/backend.go | 7 +- internal/cmd/builder.go | 190 +- internal/cmd/cache.go | 23 + internal/cmd/check.go | 65 +- internal/cmd/cmd.go | 2 + internal/cmd/env.go | 216 +- internal/cmd/filter.go | 44 +- internal/cmd/plugin/plugin.go | 14 + internal/cmd/ratelimit.go | 70 +- internal/cmd/server.go | 7 +- internal/cmd/servergroup.go | 14 +- internal/cmd/tickrot.go | 10 +- internal/cmd/tls.go | 25 +- internal/cmd/websvc.go | 35 +- internal/consul/allowlist.go | 19 +- internal/consul/allowlist_test.go | 3 + internal/consul/metrics.go | 26 + internal/debugsvc/debugsvc_test.go | 3 +- internal/dnscheck/remotekv.go | 9 +- internal/dnscheck/remotekv_test.go | 5 +- internal/dnsdb/http_test.go | 34 +- internal/dnsmsg/constructor.go | 217 +- internal/dnsmsg/constructor_test.go | 357 +- internal/dnsmsg/dnsmsg_test.go | 4 +- internal/dnsmsg/optcloner.go | 3 +- internal/dnsmsg/response.go | 228 ++ internal/dnsmsg/response_test.go | 416 +++ internal/dnsmsg/rrconstructor.go | 33 + internal/dnsmsg/structurederror.go | 143 + internal/dnsserver/cache/cache.go | 18 +- internal/dnsserver/cache/cache_test.go | 6 +- internal/dnsserver/dnsservertest/handler.go | 23 +- internal/dnsserver/dnsservertest/msg.go | 23 +- internal/dnsserver/forward/forward_test.go | 4 +- .../dnsserver/forward/healthcheck_test.go | 2 +- .../dnsserver/forward/upstreamplain_test.go | 4 +- internal/dnsserver/go.mod | 31 +- internal/dnsserver/go.sum | 59 +- internal/dnsserver/prometheus/cache_test.go | 2 +- internal/dnsserver/prometheus/forward_test.go | 2 +- .../dnsserver/prometheus/ratelimit_test.go | 17 +- internal/dnsserver/prometheus/server_test.go | 2 +- internal/dnsserver/ratelimit/backoff.go | 52 +- .../dnsserver/ratelimit/ratelimit_test.go | 13 +- internal/dnsserver/serverbase.go | 26 + internal/dnsserver/serverbench_test.go | 10 +- internal/dnsserver/serverdns_test.go | 30 +- internal/dnsserver/serverdnscrypt_test.go | 6 +- internal/dnsserver/serverhttps.go | 5 +- internal/dnsserver/serverhttps_test.go | 10 +- internal/dnsserver/serverhttpsjson.go | 125 +- internal/dnsserver/serverquic_test.go | 8 +- internal/dnsserver/servertls_test.go | 10 +- internal/dnssvc/config.go | 228 ++ internal/dnssvc/context.go | 35 + internal/dnssvc/dnssvc.go | 727 ++-- internal/dnssvc/dnssvc_test.go | 60 +- internal/dnssvc/handler.go | 211 ++ internal/dnssvc/handler_test.go | 188 ++ internal/dnssvc/integration_test.go | 163 +- .../internal/devicefinder/device_test.go | 165 +- .../internal/devicefinder/devicedata_test.go | 157 +- .../internal/devicefinder/humanid_test.go | 44 +- .../dnssvc/internal/dnssvctest/dnssvctest.go | 12 +- .../dnssvc/internal/initial/specialdomain.go | 46 +- .../internal/mainmw/debug_internal_test.go | 2 + internal/dnssvc/internal/mainmw/filter.go | 4 +- internal/dnssvc/internal/mainmw/mainmw.go | 58 +- .../dnssvc/internal/mainmw/mainmw_test.go | 60 +- internal/dnssvc/internal/mainmw/record.go | 5 +- .../internal/mainmw/record_internal_test.go | 22 +- .../dnssvc/internal/preservice/preservice.go | 52 +- .../internal/preservice/preservice_test.go | 15 +- .../internal/preupstream/preupstream.go | 107 +- .../internal/preupstream/preupstream_test.go | 174 +- .../internal/ratelimitmw/access_test.go | 31 +- .../internal/ratelimitmw/ratelimitmw.go | 15 +- .../internal/ratelimitmw/requestinfo.go | 2 + internal/dnssvc/reexport.go | 16 + internal/ecscache/cache.go | 20 +- internal/ecscache/cache_internal_test.go | 5 +- internal/ecscache/ecsblocklist.go | 2939 ++++++++--------- internal/ecscache/ecsblocklist_generate.go | 40 +- internal/ecscache/ecscache.go | 80 +- internal/ecscache/ecscache_test.go | 41 +- internal/filter/filter_test.go | 1 - internal/filter/hashprefix/filter.go | 13 +- internal/filter/hashprefix/filter_test.go | 4 +- internal/filter/index.go | 101 +- internal/filter/index_internal_test.go | 37 + .../composite/composite_internal_test.go | 8 +- internal/filter/internal/refreshable.go | 20 +- internal/filter/internal/refreshable_test.go | 4 +- .../filter/internal/rulelist/dnsrewrite.go | 15 +- .../filter/internal/rulelist/refreshable.go | 5 +- internal/filter/storage.go | 13 +- internal/geoip/asntops.go | 867 ++--- internal/geoip/asntops_generate.go | 27 +- internal/geoip/country.go | 2 - internal/geoip/country_generate.go | 29 +- internal/metrics/consul.go | 130 +- internal/metrics/metrics_test.go | 4 + internal/metrics/tls.go | 270 +- internal/metrics/tls_test.go | 23 +- internal/optlog/optlog.go | 28 - internal/optslog/optslog.go | 28 +- .../internal/filecachepb/filecache.pb.go | 336 +- internal/querylog/querylog_test.go | 6 - internal/tlsconfig/manager.go | 286 ++ internal/tlsconfig/manager_test.go | 175 + internal/tlsconfig/metrics.go | 92 + internal/tlsconfig/tlsconfig.go | 3 + internal/tools/go.mod | 52 +- internal/tools/go.sum | 100 +- internal/websvc/blockpage_test.go | 3 +- internal/websvc/handler_test.go | 5 +- internal/websvc/linkip_internal_test.go | 7 +- internal/websvc/websvc_test.go | 5 +- scripts/backend/dns.go | 202 ++ scripts/backend/main.go | 204 +- scripts/backend/ratelimiter.go | 51 + scripts/backend/remotekv.go | 83 + 150 files changed, 8610 insertions(+), 6094 deletions(-) create mode 100644 internal/backendpb/ratelimiter.go create mode 100644 internal/backendpb/ratelimiter_test.go create mode 100644 internal/backendpb/remotekv.go create mode 100644 internal/backendpb/remotekv_test.go create mode 100644 internal/consul/metrics.go create mode 100644 internal/dnsmsg/response.go create mode 100644 internal/dnsmsg/response_test.go create mode 100644 internal/dnsmsg/structurederror.go create mode 100644 internal/dnssvc/config.go create mode 100644 internal/dnssvc/context.go create mode 100644 internal/dnssvc/handler.go create mode 100644 internal/dnssvc/handler_test.go create mode 100644 internal/dnssvc/reexport.go create mode 100644 internal/filter/index_internal_test.go create mode 100644 internal/tlsconfig/manager.go create mode 100644 internal/tlsconfig/manager_test.go create mode 100644 internal/tlsconfig/metrics.go create mode 100644 internal/tlsconfig/tlsconfig.go create mode 100644 scripts/backend/dns.go create mode 100644 scripts/backend/ratelimiter.go create mode 100644 scripts/backend/remotekv.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 1525252..9c4ef7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,94 @@ 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-2484/ Build 886 + +- Property `type` of the `ratelimit` object has been moved to the underlying `allowlist` object. So replace this: + + ```yaml + ratelimit: + type: 'consul' + # … + allowlist: + # … + ``` + + with this: + + ```yaml + ratelimit: + # … + allowlist: + type: 'consul' + # … + ``` + +## AGDNS-2443 / Build 877 + +- The object `filters` has new properties: `ede_enabled`, and `sde_enabled`. So replace this: + + ```yaml + filters: + # … + ``` + + with this: + + ```yaml + filters: + # … + ede_enabled: true + sde_enabled: true + ``` + +## AGDNS-2456 / Build 873 + +- The environment variables `BACKEND_RATELIMIT_URL` and `BACKEND_RATELIMIT_API_KEY` have been added. + +- Added the `type` property within the `ratelimit` object. So add it: + + ```yaml + ratelimit: + type: 'consul' + # … + ``` + +## AGDNS-2431 / Build 872 + +- The objects `ratelimit.ipv4` and `ratelimit.ipv6` have been modified. Its `rps` properties have been replaced with the new properties `count` and `interval`. So replace this: + + ```yaml + ratelimit: + # … + ipv4: + rps: 30 + ipv6: + rps: 300 + ``` + + with this: + + ```yaml + ratelimit: + # … + ipv4: + # … + count: 300 + interval: 10s + ipv6: + # … + count: 3000 + interval: 10s + ``` + + Adjust the value and add new ones, if necessary. + +## AGDNS-2457 / Build 871 + +- The environment variables `DNSCHECK_REMOTEKV_URL` and `DNSCHECK_REMOTEKV_API_KEY` have been added. + +- The property `kv.type` within the `check` object now supports the `backend` value. + ## AGDNS-2468 / Build 869 - The environment variable `PROFILES_MAX_RESP_SIZE` has been added. It sets the maximum size of the response from the profiles endpoint of the backend API. The default value is `8MB`. diff --git a/Makefile b/Makefile index d75f046..cbdf6a4 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.1 +GOTOOLCHAIN = go1.23.2 RACE = 0 REVISION = $${REVISION:-$$(git rev-parse --short HEAD)} VERSION = 0 diff --git a/config.dist.yaml b/config.dist.yaml index 170a4ed..c12d370 100644 --- a/config.dist.yaml +++ b/config.dist.yaml @@ -10,15 +10,19 @@ ratelimit: response_size_estimate: 1KB # Rate limit options for IPv4 addresses. ipv4: - # Rate of requests per second for one subnet for IPv4 addresses. - rps: 30 + # Requests per configured interval for one subnet for IPv4 addresses. + count: 300 + # The time during which to count the number of requests. + interval: 10s # The lengths of the subnet prefixes used to calculate rate limiter # bucket keys for IPv4 addresses. subnet_key_len: 24 # Rate limit options for IPv6 addresses. ipv6: - # Rate of requests per second for one subnet for IPv6 addresses. - rps: 300 + # Requests per configured interval for one subnet for IPv6 addresses. + count: 3000 + # The time during which to count the number of requests. + interval: 10s # The lengths of the subnet prefixes used to calculate rate limiter # bucket keys for IPv6 addresses. subnet_key_len: 48 @@ -40,6 +44,9 @@ ratelimit: - '127.0.0.1/24' # Time between two updates of allow list. refresh_interval: 1h + # Defines where the rate limiting settings are received from. Allowed + # values are "backend" and "consul". + type: 'consul' # Configuration for the stream connection limiting. connection_limit: @@ -167,8 +174,8 @@ geoip: check: # Domains to use for DNS checking. kv: - # Defines the type of remote kay-value storage. Allowed values are - # "consul" and "redis". + # Defines the type of remote key-value storage. Allowed values are + # "backend", "consul", and "redis". type: 'consul' # For how long to keep the information about the client. ttl: 30s @@ -313,6 +320,10 @@ filters: enabled: true # The size of the LRU cache of rule-list filtering results. size: 10000 + # Enable the Extended DNS Errors feature. + ede_enabled: true + # Enable the Structured DNS Errors feature. Requires ede_enabled: true. + sde_enabled: true # Filtering groups are a set of different filtering configurations. These # filtering configurations are then used by server_groups. diff --git a/doc/configuration.md b/doc/configuration.md index c1655f5..34e45e3 100644 --- a/doc/configuration.md +++ b/doc/configuration.md @@ -104,9 +104,13 @@ The `ratelimit` object has the following properties: - `ipv4`: The ipv4 configuration object. It has the following fields: - - `rps`: The rate of requests per second for one subnet. Requests above this are counted in the backoff count. + - `count`: Requests per configured interval for one subnet for IPv4 addresses. Requests above this are counted in the backoff count. - **Example:** `30`. + **Example:** `300`. + + - `interval`: The time during which to count the number of requests. + + **Example:** `10s`. - `ipv4-subnet_key_len`: The length of the subnet prefix used to calculate rate limiter bucket keys. @@ -134,7 +138,11 @@ The `ratelimit` object has the following properties: **Example:** `30s`. -For example, if `backoff_period` is `1m`, `backoff_count` is `10`, and `ipv4-rps` is `5`, a client (meaning all IP addresses within the subnet defined by `ipv4-subnet_key_len`) that made 15 requests in one second or 6 requests (one above `rps`) every second for 10 seconds within one minute, the client is blocked for `backoff_duration`. + - `type`: Defines where the rate limit settings are received from. Allowed values are `backend` and `consul`. + + **Example:** `consul`. + +For example, if `backoff_period` is `1m`, `backoff_count` is `10`, `ipv4-count` is `5`, and `ipv4-interval` is `1s`, a client (meaning all IP addresses within the subnet defined by `ipv4-subnet_key_len`) that made 15 requests in one second or 6 requests (one above `rps`) every second for 10 seconds within one minute, the client is blocked for `backoff_duration`. ### Stream connection limit @@ -370,12 +378,14 @@ 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 `consul` and `redis`. + - `type`: Type of the remote KV storage. Allowed values are `backend`, `consul`, and `redis`. **Example:** `consul`. - `ttl`: For how long to keep the information about a single user in remote KV, as a human-readable duration. + For `backend`, the TTL must be greater than `0s`. + 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`. @@ -592,6 +602,14 @@ The `filters` object has the following properties: **Example:** `10000`. +- `ede_enabled`: Shows if Extended DNS Error codes should be added. + + **Example:** `true`. + +- `sde_enabled`: Shows if the experimental Structured DNS Errors feature should be enabled. `ede_enabled` must be `true` to enable SDE. + + **Example:** `true`. + [env-blocked_services]: environment.md#BLOCKED_SERVICE_INDEX_URL ## Filtering groups diff --git a/doc/development.md b/doc/development.md index 385c433..f118d11 100644 --- a/doc/development.md +++ b/doc/development.md @@ -159,7 +159,7 @@ You'll need to supply the following: See the [external HTTP API documentation][externalhttp]. -You may use `go run ./scripts/backend` to start mock GRPC server for `BILLSTAT_URL` and `PROFILES_URL` endpoints. +You may use `go run ./scripts/backend` to start mock GRPC server for `BACKEND_PROFILES_URL`, `BILLSTAT_URL`, `DNSCHECK_REMOTEKV_URL`, and `PROFILES_URL` endpoints. You may need to change the listen ports in `config.yaml` which are less than 1024 to some other ports. Otherwise, `sudo` or `doas` is required to run `AdGuardDNS`. diff --git a/doc/environment.md b/doc/environment.md index cda8b27..76c5d49 100644 --- a/doc/environment.md +++ b/doc/environment.md @@ -6,6 +6,8 @@ AdGuard DNS uses [environment variables][wiki-env] to store some of the more sen - [`ADULT_BLOCKING_ENABLED`](#ADULT_BLOCKING_ENABLED) - [`ADULT_BLOCKING_URL`](#ADULT_BLOCKING_URL) +- [`BACKEND_RATELIMIT_API_KEY`](#BACKEND_RATELIMIT_API_KEY) +- [`BACKEND_RATELIMIT_URL`](#BACKEND_RATELIMIT_URL) - [`BILLSTAT_API_KEY`](#BILLSTAT_API_KEY) - [`BILLSTAT_URL`](#BILLSTAT_URL) - [`BLOCKED_SERVICE_ENABLED`](#BLOCKED_SERVICE_ENABLED) @@ -14,6 +16,8 @@ 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_REMOTEKV_API_KEY`](#DNSCHECK_REMOTEKV_API_KEY) +- [`DNSCHECK_REMOTEKV_URL`](#DNSCHECK_REMOTEKV_URL) - [`FILTER_CACHE_PATH`](#FILTER_CACHE_PATH) - [`FILTER_INDEX_URL`](#FILTER_INDEX_URL) - [`GENERAL_SAFE_ENABLED`](#GENERAL_SAFE_SEARCH_ENABLED) @@ -62,6 +66,21 @@ The HTTP(S) URL of source list of rules for adult blocking filter. **Default:** No default value, the variable is required if `ADULT_BLOCKING_ENABLED` is set to `1`. +## `BACKEND_RATELIMIT_API_KEY` + +The API key to use when authenticating requests to the backend rate limiter API, if any. The API key should be valid as defined by [RFC 6750]. + +**Default:** **Unset.** + +## `BACKEND_RATELIMIT_URL` + +The base backend URL for backend rate limiter. Supports gRPC(S) (`grpc://` and `grpcs://`) URLs. See the [external API requirements section][ext-backend-ratelimit]. + +**Default:** No default value, the variable is required if the [type][conf-ratelimit-type] of rate limiter is `backend` in the configuration file. + +[conf-ratelimit-type]: configuration.md#ratelimit-type +[ext-backend-ratelimit]: externalhttp.md#backend-ratelimit + ## `BILLSTAT_API_KEY` The API key to use when authenticating queries to the billing statistics API, if any. The API key should be valid as defined by [RFC 6750]. @@ -72,7 +91,7 @@ The API key to use when authenticating queries to the billing statistics API, if ## `BILLSTAT_URL` -The base backend URL for backend billing statistics uploader API. Supports gRPC(S) (`grpc://` and`grpcs://`) URLs. See the [external HTTP API requirements section][ext-billstat]. +The base backend URL for backend billing statistics uploader API. Supports gRPC(S) (`grpc://` and `grpcs://`) URLs. See the [external HTTP API requirements section][ext-billstat]. **Default:** No default value, the variable is required if there is at least one [server group][conf-sg] with profiles enabled. @@ -103,7 +122,7 @@ The path to the configuration file. The HTTP(S) URL of the Consul instance serving the dynamic part of the rate-limit allowlist. See the [external HTTP API requirements section][ext-consul] on the expected format of the response. -**Default:** No default value, the variable is **required.** +**Default:** No default value, the variable is required if the [type][conf-ratelimit-type] of rate limiter is `consul` in the configuration file. [ext-consul]: externalhttp.md#consul @@ -123,6 +142,20 @@ 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_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]. + +**Default:** **Unset.** + +## `DNSCHECK_REMOTEKV_URL` + +The base backend URL used as a key-value database for the DNS server checking. Supports gRPC(S) (`grpc://` and`grpcs://`) URLs. See the [external API requirements section][ext-backend-dnscheck]. + +**Default:** **Unset.** + +[ext-backend-dnscheck]: externalhttp.md#backend-dnscheck + ## `FILTER_CACHE_PATH` The path to the directory used to store the cached version of all filters and filter indexes. @@ -238,11 +271,11 @@ The profile cache is read on start and is later updated on every [full refresh][ The maximum size of the response from the profiles API in a human-readable format. -**Default:** `8MB`. +**Default:** `64MB`. ## `PROFILES_URL` -The base backend URL for profiles API. Supports gRPC(S) (`grpc://` and`grpcs://`) URLs. See the [external API requirements section][ext-profiles]. +The base backend URL for profiles API. Supports gRPC(S) (`grpc://` and `grpcs://`) URLs. See the [external API requirements section][ext-profiles]. **Default:** No default value, the variable is required if there is at least one [server group][conf-sg] with profiles enabled. @@ -252,7 +285,7 @@ The base backend URL for profiles API. Supports gRPC(S) (`grpc://` and`grpcs://` Redis server address. Can be an IP address or a hostname. -**Default:** No default value, the variable if required if the [type][conf-check-kv-type] of remote KV storage for DNS server checking is `redis` in the configuration file. +**Default:** No default value, the variable is required if the [type][conf-check-kv-type] of remote KV storage for DNS server checking is `redis` in the configuration file. [conf-check-kv-type]: configuration.md#check-kv-type diff --git a/doc/externalhttp.md b/doc/externalhttp.md index d50f09e..364b069 100644 --- a/doc/externalhttp.md +++ b/doc/externalhttp.md @@ -10,7 +10,9 @@ AdGuard DNS uses information from external HTTP APIs for filtering and other pie ## Contents - [Backend billing statistics](#backend-billstat) +- [Backend DNSCheck service](#backend-dnscheck) - [Backend profiles service](#backend-profiles) +- [Backend ratelimit service](#backend-ratelimit) - [Consul key-value storage](#consul) - [Filtering](#filters) - [Blocked services](#filters-blocked-services) @@ -28,6 +30,15 @@ This service is disabled when all server groups have property [`profiles_enabled [env-billstat_url]: environment.md#BILLSTAT_URL [conf-srvgrp-prof]: configuration.md#sg-*-profiles_enabled +## Backend DNSCheck service + +This is the service to which the [`DNSCHECK_REMOTEKV_URL`][env-dnscheck_remotekv_url] environment variable points. Supports gRPC(s) URLs. The service must correspond to `./internal/backendpb/dns.proto`. + +This service is only enabled when the `check.kv` object has the [`type`][conf-check-kv-type] property set to `backend`. + +[env-dnscheck_remotekv_url]: environment.md#DNSCHECK_REMOTEKV_URL +[conf-check-kv-type]: configuration.md#check-kv-type + ## Backend profiles service This is the service to which the [`PROFILES_URL`][env-profiles_url] environment variable points. Supports gRPC(s) URLs. The service must correspond to `./internal/backendpb/dns.proto`. @@ -36,6 +47,15 @@ This service is disabled when all server groups have property [`profiles_enabled [env-profiles_url]: environment.md#PROFILES_URL +## Backend ratelimit_service + +This is the service to which the [`BACKEND_RATELIMIT_URL`][env-backend_ratelimit_url] environment variable points. Supports gRPC(s) URLs. The service must correspond to `./internal/backendpb/dns.proto`. + +This service is only enabled when the `ratelimit` object has the [`type`][conf-ratelimit-type] property set to `backend`. + +[conf-ratelimit-type]: configuration.md#ratelimit-type +[env-backend_ratelimit_url]: environment.md#BACKEND_RATELIMIT_URL + ## Consul key-value storage A [Consul][consul-io] service can be used for the DNS server check and dynamic rate-limit allowlist features. Currently used endpoints can be seen in the documentation of the [`CONSUL_ALLOWLIST_URL`][env-consul-allowlist], [`CONSUL_DNSCHECK_KV_URL`][env-consul-dnscheck-kv], and [`CONSUL_DNSCHECK_SESSION_URL`][env-consul-dnscheck-session] environment variables. diff --git a/go.mod b/go.mod index f502ad6..2279a23 100644 --- a/go.mod +++ b/go.mod @@ -1,34 +1,34 @@ module github.com/AdguardTeam/AdGuardDNS -go 1.23.1 +go 1.23.2 require ( github.com/AdguardTeam/AdGuardDNS/internal/dnsserver v0.0.0-20240607112746-5690301129fe - github.com/AdguardTeam/golibs v0.28.0 - github.com/AdguardTeam/urlfilter v0.19.0 + github.com/AdguardTeam/golibs v0.30.1 + github.com/AdguardTeam/urlfilter v0.20.0 github.com/ameshkov/dnscrypt/v2 v2.3.0 github.com/axiomhq/hyperloglog v0.2.0 github.com/bluele/gcache v0.0.2 github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 github.com/caarlos0/env/v7 v7.1.0 - github.com/getsentry/sentry-go v0.28.1 + github.com/getsentry/sentry-go v0.29.1 github.com/gomodule/redigo v1.9.2 github.com/google/renameio/v2 v2.0.0 github.com/miekg/dns v1.1.62 github.com/oschwald/maxminddb-golang v1.13.1 github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible - github.com/prometheus/client_golang v1.20.1 + github.com/prometheus/client_golang v1.20.5 github.com/prometheus/client_model v0.6.1 - github.com/prometheus/common v0.55.0 - github.com/quic-go/quic-go v0.47.0 + github.com/prometheus/common v0.60.0 + github.com/quic-go/quic-go v0.48.1 github.com/stretchr/testify v1.9.0 - golang.org/x/crypto v0.27.0 - golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 - golang.org/x/net v0.29.0 - golang.org/x/sys v0.25.0 - golang.org/x/time v0.6.0 - google.golang.org/grpc v1.65.0 - google.golang.org/protobuf v1.34.2 + 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 + google.golang.org/protobuf v1.35.1 gopkg.in/yaml.v2 v2.4.0 ) @@ -41,24 +41,21 @@ 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-20240929191954-255acd752d31 // indirect - github.com/klauspost/compress v1.17.9 // indirect + github.com/google/pprof v0.0.0-20241023014458-598669927662 // 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/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.4.0 // 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.18.0 // indirect - golang.org/x/tools v0.25.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // 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 gopkg.in/yaml.v3 v3.0.1 // indirect ) replace github.com/AdguardTeam/AdGuardDNS/internal/dnsserver => ./internal/dnsserver - -// TODO(a.garipov): Remove once https://github.com/quic-go/quic-go/pull/4685 is merged. -replace github.com/quic-go/quic-go => github.com/ainar-g/quic-go v0.0.0-20240930125330-446bd86056fd diff --git a/go.sum b/go.sum index 5ba8856..125acca 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,11 @@ -github.com/AdguardTeam/golibs v0.28.0 h1:SK1q8SqkkJ/61pp2abTmio90S4QpteYK9rtgROfnrb4= -github.com/AdguardTeam/golibs v0.28.0/go.mod h1:iWdjXPCwmK2g2FKIb/OwEPnovSXeMqRhI8FWLxF5oxE= -github.com/AdguardTeam/urlfilter v0.19.0 h1:q7eH13+yNETlpD/VD3u5rLQOripcUdEktqZFy+KiQLk= -github.com/AdguardTeam/urlfilter v0.19.0/go.mod h1:+N54ZvxqXYLnXuvpaUhK2exDQW+djZBRSb6F6j0rkBY= +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/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= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw= github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us= -github.com/ainar-g/quic-go v0.0.0-20240930125330-446bd86056fd h1:mw4LqrCiv3vcKuCxBRg7kA17xfHKM+9hZgFWmyhe/AY= -github.com/ainar-g/quic-go v0.0.0-20240930125330-446bd86056fd/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= github.com/ameshkov/dnscrypt/v2 v2.3.0 h1:pDXDF7eFa6Lw+04C0hoMh8kCAQM8NwUdFEllSP2zNLs= github.com/ameshkov/dnscrypt/v2 v2.3.0/go.mod h1:N5hDwgx2cNb4Ay7AhvOSKst+eUiOZ/vbKRO9qMpQttE= github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo= @@ -29,8 +27,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0= github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= -github.com/getsentry/sentry-go v0.28.1 h1:zzaSm/vHmGllRM6Tpx1492r0YDzauArdBfkJRtY6P5k= -github.com/getsentry/sentry-go v0.28.1/go.mod h1:1fQZ+7l7eeJ3wYi82q5Hg8GqAPgefRq+FP/QhafYVgg= +github.com/getsentry/sentry-go v0.29.1 h1:DyZuChN8Hz3ARxGVV8ePaNXh1dQ7d76AiB117xcREwA= +github.com/getsentry/sentry-go v0.29.1/go.mod h1:x3AtIzN01d6SiWkderzaH28Tm0lgkafpJ5Bm3li39O0= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= @@ -43,12 +41,12 @@ 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-20240929191954-255acd752d31 h1:LcRdQWywSgfi5jPsYZ1r2avbbs5IQ5wtyhMBCcokyo4= -github.com/google/pprof v0.0.0-20240929191954-255acd752d31/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +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/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.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -79,16 +77,18 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/prometheus/client_golang v1.20.1 h1:IMJXHOD6eARkQpxo8KkhgEVFlBNm+nkrFUyGlIu7Na8= -github.com/prometheus/client_golang v1.20.1/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +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.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= +github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/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/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= @@ -109,33 +109,33 @@ github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+F github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= -go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +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.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= -golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= -golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd h1:6TEm2ZxXoQmFWFlt1vNxvVOa1Q0dXFQD1m/rYjXmS0E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +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= +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= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/go.work b/go.work index 71f59a2..b86714e 100644 --- a/go.work +++ b/go.work @@ -1,4 +1,4 @@ -go 1.23.1 +go 1.23.2 use ( . diff --git a/go.work.sum b/go.work.sum index 3cec561..1d88e00 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,5 +1,6 @@ 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= 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= @@ -45,6 +46,7 @@ cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGB cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= cloud.google.com/go/contactcenterinsights v1.13.0/go.mod h1:ieq5d5EtHsu8vhe2y3amtZ+BE+AQwX5qAy7cpo0POsI= cloud.google.com/go/container v1.31.0/go.mod h1:7yABn5s3Iv3lmw7oMmyGbeV6tQj86njcTijkkGuvdZA= cloud.google.com/go/containeranalysis v0.11.4/go.mod h1:cVZT7rXYBS9NG1rhQbWL9pWbXCKHWJPYraE8/FTSYPE= @@ -156,6 +158,9 @@ github.com/AdguardTeam/golibs v0.19.0/go.mod h1:3WunclLLfrVAq7fYQRhd6f168FHOEMss github.com/AdguardTeam/golibs v0.25.2/go.mod h1:HaTyS2wCbxFudjht9N/+/Qf1b5cMad2BAYSwe7DPCXI= github.com/AdguardTeam/golibs v0.25.3 h1:A06JZGSuAhAC0uq/s7IlNsv/V8TyNJfLalB0vhkd1vA= github.com/AdguardTeam/golibs v0.25.3/go.mod h1:HaTyS2wCbxFudjht9N/+/Qf1b5cMad2BAYSwe7DPCXI= +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/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= @@ -245,6 +250,7 @@ github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50 h1:DBmgJDC9dTfkVyGgipa github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50/go.mod h1:5e1+Vvlzido69INQaVO6d87Qn543Xr6nooe9Kz7oBFM= 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/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= @@ -262,12 +268,14 @@ github.com/envoyproxy/go-control-plane v0.11.1 h1:wSUXTlLfiAQRWs2F+p+EKOY9rUyis1 github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= github.com/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI= github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= +github.com/envoyproxy/go-control-plane v0.13.0/go.mod h1:GRaKG3dwvFoTg4nj7aXdZnvMg4d7nvT/wl9WgVXn3Q8= github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= +github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= @@ -338,6 +346,7 @@ github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -576,6 +585,7 @@ github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwb github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= @@ -677,6 +687,7 @@ github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0 github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +github.com/urfave/negroni/v3 v3.1.1/go.mod h1:jWvnX03kcSjDBl/ShB0iHvx5uOs7mAzZXW+JvJ5XYAs= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.40.0 h1:CRq/00MfruPGFLTQKY8b+8SfdK60TxNztjRMnH0t1Yc= @@ -828,6 +839,7 @@ golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852 h1:xYq6+9AtI+xP3M4r0N1hCkHrInHDBohhquRgx9Kk6gI= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -912,6 +924,7 @@ golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= 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/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= @@ -1000,6 +1013,7 @@ google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1: google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= 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/bytestream v0.0.0-20240304161311-37d4d3c04a78/go.mod h1:vh/N7795ftP0AkN1w8XKqN4w1OdUKXW5Eummda+ofv8= google.golang.org/genproto/googleapis/rpc v0.0.0-20240304161311-37d4d3c04a78/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= diff --git a/internal/agdhttp/client.go b/internal/agdhttp/client.go index 85a22a6..93010cc 100644 --- a/internal/agdhttp/client.go +++ b/internal/agdhttp/client.go @@ -10,6 +10,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/golibs/httphdr" + "github.com/AdguardTeam/golibs/netutil/urlutil" ) // Client is a wrapper around http.Client. @@ -93,6 +94,7 @@ func (c *Client) do( req.Header.Set(httphdr.UserAgent, c.userAgent) resp, err = c.http.Do(req) + urlutil.RedactUserinfoInURLError(u, err) if err != nil && resp != nil && resp.Header != nil { // A non-nil Response with a non-nil error only occurs when // CheckRedirect fails. diff --git a/internal/agdhttp/url.go b/internal/agdhttp/url.go index 7745036..d4c014c 100644 --- a/internal/agdhttp/url.go +++ b/internal/agdhttp/url.go @@ -5,51 +5,13 @@ import ( "net/url" "github.com/AdguardTeam/golibs/errors" + "github.com/AdguardTeam/golibs/netutil/urlutil" ) -// Known scheme constants. -// -// TODO(a.garipov): Move to agdurl or golibs. -// -// TODO(a.garipov): Use more. -const ( - SchemeFile = "file" - SchemeGRPC = "grpc" - SchemeGRPCS = "grpcs" - SchemeHTTP = "http" - SchemeHTTPS = "https" -) - -// CheckGRPCURLScheme returns true if s is a valid gRPC URL scheme. That is, -// [SchemeGRPC] or [SchemeGRPCS] -// -// TODO(a.garipov): Move to golibs? -func CheckGRPCURLScheme(s string) (ok bool) { - switch s { - case SchemeGRPC, SchemeGRPCS: - return true - default: - return false - } -} - -// CheckHTTPURLScheme returns true if s is a valid HTTP URL scheme. That is, -// [SchemeHTTP] or [SchemeHTTPS] -// -// TODO(a.garipov): Move to golibs? -func CheckHTTPURLScheme(s string) (ok bool) { - switch s { - case SchemeHTTP, SchemeHTTPS: - return true - default: - return false - } -} - // ParseHTTPURL parses an absolute URL and makes sure that it is a valid HTTP(S) // URL. All returned errors will have the underlying type [*url.Error]. // -// TODO(a.garipov): Define as a type? +// TODO(a.garipov): Define as a type? func ParseHTTPURL(s string) (u *url.URL, err error) { u, err = url.Parse(s) if err != nil { @@ -63,7 +25,7 @@ func ParseHTTPURL(s string) (u *url.URL, err error) { URL: s, Err: errors.Error("empty host"), } - case !CheckHTTPURLScheme(u.Scheme): + case !urlutil.IsValidHTTPURLScheme(u.Scheme): return nil, &url.Error{ Op: "parse", URL: s, diff --git a/internal/agdhttp/url_test.go b/internal/agdhttp/url_test.go index c9255f9..0694e67 100644 --- a/internal/agdhttp/url_test.go +++ b/internal/agdhttp/url_test.go @@ -6,12 +6,19 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp" "github.com/AdguardTeam/golibs/netutil" + "github.com/AdguardTeam/golibs/netutil/urlutil" "github.com/AdguardTeam/golibs/testutil" "github.com/stretchr/testify/assert" ) +// Common user credentials for tests. +const ( + testUsername = "user" + testPassword = "pass" +) + func TestParseHTTPURL(t *testing.T) { - goodURL := testURL() + goodURL := testURL(url.UserPassword(testUsername, testPassword)) badSchemeURL := netutil.CloneURL(goodURL) badSchemeURL.Scheme = "ftp" @@ -61,10 +68,11 @@ func TestParseHTTPURL(t *testing.T) { } } -func testURL() (u *url.URL) { +// testURL is a helper function that returns an url with dummy values. +func testURL(info *url.Userinfo) (u *url.URL) { return &url.URL{ - Scheme: agdhttp.SchemeHTTP, - User: url.UserPassword("user", "pass"), + Scheme: urlutil.SchemeHTTP, + User: info, Host: "example.com", Path: "/a/b/c/", RawQuery: "d=e", diff --git a/internal/agdtest/agdtest.go b/internal/agdtest/agdtest.go index 2364a84..6816907 100644 --- a/internal/agdtest/agdtest.go +++ b/internal/agdtest/agdtest.go @@ -3,6 +3,7 @@ package agdtest import ( + "net/url" "testing" "time" @@ -18,7 +19,7 @@ const FilteredResponseTTL = FilteredResponseTTLSec * time.Second // number to simplify message creation. const FilteredResponseTTLSec = 10 -// NewConstructorWithTTL returns a standard dnsmsg.Constructor for tests, using +// NewConstructorWithTTL returns a standard *dnsmsg.Constructor for tests, using // ttl as the TTL for filtered responses. func NewConstructorWithTTL(tb testing.TB, ttl time.Duration) (c *dnsmsg.Constructor) { tb.Helper() @@ -26,28 +27,57 @@ func NewConstructorWithTTL(tb testing.TB, ttl time.Duration) (c *dnsmsg.Construc c, err := dnsmsg.NewConstructor(&dnsmsg.ConstructorConfig{ Cloner: NewCloner(), BlockingMode: &dnsmsg.BlockingModeNullIP{}, + StructuredErrors: NewSDEConfig(true), FilteredResponseTTL: ttl, + EDEEnabled: true, }) require.NoError(tb, err) return c } -// NewConstructor returns a standard dnsmsg.Constructor for tests, using -// [FilteredResponseTTL] as the TTL for filtered responses. +// NewConstructor returns a standard *dnsmsg.Constructor for tests, using +// [FilteredResponseTTL] as the TTL for filtered responses. The returned +// constructor also has the Structured DNS Errors feature enabled. func NewConstructor(tb testing.TB) (c *dnsmsg.Constructor) { tb.Helper() c, err := dnsmsg.NewConstructor(&dnsmsg.ConstructorConfig{ Cloner: NewCloner(), BlockingMode: &dnsmsg.BlockingModeNullIP{}, + StructuredErrors: NewSDEConfig(true), FilteredResponseTTL: FilteredResponseTTL, + EDEEnabled: true, }) require.NoError(tb, err) return c } +// SDEText is a test Structured DNS Error text. +// +// NOTE: Keep in sync with [NewSDEConfig]. +// +// TODO(e.burkov): Add some helper when this message becomes configurable. +const SDEText = `{` + + `"j":"Filtering",` + + `"o":"Test Org",` + + `"c":["mailto:support@dns.example"]` + + `}` + +// NewSDEConfig returns a standard *dnsmsg.StructuredDNSErrorsConfig for tests. +func NewSDEConfig(enabled bool) (c *dnsmsg.StructuredDNSErrorsConfig) { + return &dnsmsg.StructuredDNSErrorsConfig{ + Contact: []*url.URL{{ + Scheme: "mailto", + Opaque: "support@dns.example", + }}, + Justification: "Filtering", + Organization: "Test Org", + Enabled: enabled, + } +} + // NewCloner returns a standard dnsmsg.Cloner for tests. func NewCloner() (c *dnsmsg.Cloner) { return dnsmsg.NewCloner(dnsmsg.EmptyClonerStat{}) diff --git a/internal/agdtest/interface.go b/internal/agdtest/interface.go index 93d9d5a..e492594 100644 --- a/internal/agdtest/interface.go +++ b/internal/agdtest/interface.go @@ -116,7 +116,7 @@ func (r *Refresher) Refresh(ctx context.Context) (err error) { // type check var _ billstat.Recorder = (*BillStatRecorder)(nil) -// BillStatRecorder is a billstat.Recorder for tests. +// BillStatRecorder is a [billstat.Recorder] for tests. type BillStatRecorder struct { OnRecord func( ctx context.Context, @@ -128,7 +128,7 @@ type BillStatRecorder struct { ) } -// Record implements the billstat.Recorder interface for *BillStatRecorder. +// Record implements the [billstat.Recorder] interface for *BillStatRecorder. func (r *BillStatRecorder) Record( ctx context.Context, id agd.DeviceID, @@ -143,12 +143,12 @@ func (r *BillStatRecorder) Record( // type check var _ billstat.Uploader = (*BillStatUploader)(nil) -// BillStatUploader is a billstat.Uploader for tests. +// BillStatUploader is a [billstat.Uploader] for tests. type BillStatUploader struct { OnUpload func(ctx context.Context, records billstat.Records) (err error) } -// Upload implements the billstat.Uploader interface for *BillStatUploader. +// Upload implements the [billstat.Uploader] interface for *BillStatUploader. func (b *BillStatUploader) Upload(ctx context.Context, records billstat.Records) (err error) { return b.OnUpload(ctx, records) } @@ -158,7 +158,7 @@ func (b *BillStatUploader) Upload(ctx context.Context, records billstat.Records) // type check var _ dnscheck.Interface = (*DNSCheck)(nil) -// DNSCheck is a dnscheck.Interface for tests. +// DNSCheck is a [dnscheck.Interface] for tests. type DNSCheck struct { OnCheck func(ctx context.Context, req *dns.Msg, ri *agd.RequestInfo) (reqp *dns.Msg, err error) } @@ -177,12 +177,12 @@ func (db *DNSCheck) Check( // type check var _ dnsdb.Interface = (*DNSDB)(nil) -// DNSDB is a dnsdb.Interface for tests. +// DNSDB is a [dnsdb.Interface] for tests. type DNSDB struct { OnRecord func(ctx context.Context, resp *dns.Msg, ri *agd.RequestInfo) } -// Record implements the dnsdb.Interface interface for *DNSDB. +// Record implements the [dnsdb.Interface] interface for *DNSDB. func (db *DNSDB) Record(ctx context.Context, resp *dns.Msg, ri *agd.RequestInfo) { db.OnRecord(ctx, resp, ri) } @@ -204,7 +204,7 @@ func (c *ErrorCollector) Collect(ctx context.Context, err error) { c.OnCollect(ctx, err) } -// NewErrorCollector returns a new [ErrorCollector] all methods of which panic. +// NewErrorCollector returns a new *ErrorCollector all methods of which panic. func NewErrorCollector() (c *ErrorCollector) { return &ErrorCollector{ OnCollect: func(_ context.Context, err error) { @@ -297,21 +297,38 @@ func (s *FilterStorage) HasListID(id agd.FilterListID) (ok bool) { // type check var _ geoip.Interface = (*GeoIP)(nil) -// GeoIP is a geoip.Interface for tests. +// GeoIP is a [geoip.Interface] for tests. type GeoIP struct { - OnSubnetByLocation func(l *geoip.Location, fam netutil.AddrFamily) (n netip.Prefix, err error) OnData func(host string, ip netip.Addr) (l *geoip.Location, err error) + OnSubnetByLocation func(l *geoip.Location, fam netutil.AddrFamily) (n netip.Prefix, err error) } -// SubnetByLocation implements the geoip.Interface interface for *GeoIP. -func (g *GeoIP) SubnetByLocation(l *geoip.Location, fam netutil.AddrFamily, +// Data implements the [geoip.Interface] interface for *GeoIP. +func (g *GeoIP) Data(host string, ip netip.Addr) (l *geoip.Location, err error) { + return g.OnData(host, ip) +} + +// SubnetByLocation implements the [geoip.Interface] interface for *GeoIP. +func (g *GeoIP) SubnetByLocation( + l *geoip.Location, + fam netutil.AddrFamily, ) (n netip.Prefix, err error) { return g.OnSubnetByLocation(l, fam) } -// Data implements the geoip.Interface interface for *GeoIP. -func (g *GeoIP) Data(host string, ip netip.Addr) (l *geoip.Location, err error) { - return g.OnData(host, ip) +// NewGeoIP returns a new *GeoIP all methods of which panic. +func NewGeoIP() (c *GeoIP) { + return &GeoIP{ + OnData: func(host string, ip netip.Addr) (l *geoip.Location, err error) { + panic(fmt.Errorf("unexpected call to GeoIP.Data(%v, %v)", host, ip)) + }, + OnSubnetByLocation: func( + l *geoip.Location, + fam netutil.AddrFamily, + ) (n netip.Prefix, err error) { + panic(fmt.Errorf("unexpected call to GeoIP.SubnetByLocation(%v, %v)", l, fam)) + }, + } } // Package profiledb @@ -398,6 +415,58 @@ func (db *ProfileDB) ProfileByLinkedIP( return db.OnProfileByLinkedIP(ctx, ip) } +// NewProfileDB returns a new *ProfileDB all methods of which panic. +func NewProfileDB() (db *ProfileDB) { + return &ProfileDB{ + OnCreateAutoDevice: func( + _ context.Context, + id agd.ProfileID, + humanID agd.HumanID, + devType agd.DeviceType, + ) (p *agd.Profile, d *agd.Device, err error) { + panic(fmt.Errorf( + "unexpected call to ProfileDB.CreateAutoDevice(%v, %v, %v)", + id, + humanID, + devType, + )) + }, + + OnProfileByDedicatedIP: func( + _ context.Context, + ip netip.Addr, + ) (p *agd.Profile, d *agd.Device, err error) { + panic(fmt.Errorf("unexpected call to ProfileDB.ProfileByDedicatedIP(%v)", ip)) + }, + + OnProfileByDeviceID: func( + _ context.Context, + id agd.DeviceID, + ) (p *agd.Profile, d *agd.Device, err error) { + panic(fmt.Errorf("unexpected call to ProfileDB.ProfileByDeviceID(%v)", id)) + }, + + OnProfileByHumanID: func( + _ context.Context, + profID agd.ProfileID, + humanID agd.HumanIDLower, + ) (p *agd.Profile, d *agd.Device, err error) { + panic(fmt.Errorf( + "unexpected call to ProfileDB.ProfileByHumanID(%v, %v)", + profID, + humanID, + )) + }, + + OnProfileByLinkedIP: func( + _ context.Context, + ip netip.Addr, + ) (p *agd.Profile, d *agd.Device, err error) { + panic(fmt.Errorf("unexpected call to ProfileDB.ProfileByLinkedIP(%v)", ip)) + }, + } +} + // type check var _ profiledb.Storage = (*ProfileStorage)(nil) @@ -436,12 +505,12 @@ func (s *ProfileStorage) Profiles( // type check var _ querylog.Interface = (*QueryLog)(nil) -// QueryLog is a querylog.Interface for tests. +// QueryLog is a [querylog.Interface] for tests. type QueryLog struct { OnWrite func(ctx context.Context, e *querylog.Entry) (err error) } -// Write implements the querylog.Interface interface for *QueryLog. +// Write implements the [querylog.Interface] interface for *QueryLog. func (ql *QueryLog) Write(ctx context.Context, e *querylog.Entry) (err error) { return ql.OnWrite(ctx, e) } @@ -451,12 +520,12 @@ func (ql *QueryLog) Write(ctx context.Context, e *querylog.Entry) (err error) { // type check var _ rulestat.Interface = (*RuleStat)(nil) -// RuleStat is a rulestat.Interface for tests. +// RuleStat is a [rulestat.Interface] for tests. type RuleStat struct { OnCollect func(ctx context.Context, id agd.FilterListID, text agd.FilterRuleText) } -// Collect implements the rulestat.Interface interface for *RuleStat. +// Collect implements the [rulestat.Interface] interface for *RuleStat. func (s *RuleStat) Collect(ctx context.Context, id agd.FilterListID, text agd.FilterRuleText) { s.OnCollect(ctx, id, text) } @@ -501,7 +570,7 @@ func (c *ListenConfig) ListenPacket( // type check var _ ratelimit.Interface = (*RateLimit)(nil) -// RateLimit is a ratelimit.Interface for tests. +// RateLimit is a [ratelimit.Interface] for tests. type RateLimit struct { OnIsRateLimited func( ctx context.Context, @@ -511,7 +580,7 @@ type RateLimit struct { OnCountResponses func(ctx context.Context, resp *dns.Msg, ip netip.Addr) } -// IsRateLimited implements the ratelimit.Interface interface for *RateLimit. +// IsRateLimited implements the [ratelimit.Interface] interface for *RateLimit. func (l *RateLimit) IsRateLimited( ctx context.Context, req *dns.Msg, @@ -520,12 +589,27 @@ func (l *RateLimit) IsRateLimited( return l.OnIsRateLimited(ctx, req, ip) } -// CountResponses implements the ratelimit.Interface interface for -// *RateLimit. +// CountResponses implements the [ratelimit.Interface] interface for *RateLimit. func (l *RateLimit) CountResponses(ctx context.Context, req *dns.Msg, ip netip.Addr) { l.OnCountResponses(ctx, req, ip) } +// NewRateLimit returns a new *RateLimit all methods of which panic. +func NewRateLimit() (c *RateLimit) { + return &RateLimit{ + OnIsRateLimited: func( + _ context.Context, + req *dns.Msg, + addr netip.Addr, + ) (shouldDrop, isAllowlisted bool, err error) { + panic(fmt.Errorf("unexpected call to RateLimit.IsRateLimited(%v, %v)", req, addr)) + }, + OnCountResponses: func(_ context.Context, resp *dns.Msg, addr netip.Addr) { + panic(fmt.Errorf("unexpected call to RateLimit.CountResponses(%v, %v)", resp, addr)) + }, + } +} + // RemoteKV is an [remotekv.Interface] implementation for tests. type RemoteKV struct { OnGet func(ctx context.Context, key string) (val []byte, ok bool, err error) diff --git a/internal/backendpb/backendpb.go b/internal/backendpb/backendpb.go index 0173310..9b1fdd5 100644 --- a/internal/backendpb/backendpb.go +++ b/internal/backendpb/backendpb.go @@ -16,8 +16,8 @@ import ( "google.golang.org/grpc/metadata" ) -// newClient returns new properly initialized DNSServiceClient. -func newClient(apiURL *url.URL) (client DNSServiceClient, err error) { +// newClient returns new properly initialized gRPC connection to the API server. +func newClient(apiURL *url.URL) (client *grpc.ClientConn, err error) { var creds credentials.TransportCredentials switch s := apiURL.Scheme; s { case "grpc": @@ -38,7 +38,7 @@ func newClient(apiURL *url.URL) (client DNSServiceClient, err error) { // called right before the initial refresh. conn.Connect() - return NewDNSServiceClient(conn), nil + return conn, nil } // reportf is a helper method for reporting non-critical errors. diff --git a/internal/backendpb/backendpb_test.go b/internal/backendpb/backendpb_test.go index 2f0d438..fe36a10 100644 --- a/internal/backendpb/backendpb_test.go +++ b/internal/backendpb/backendpb_test.go @@ -65,3 +65,39 @@ func (s *testDNSServiceServer) SaveDevicesBillingStat( ) (err error) { return s.OnSaveDevicesBillingStat(srv) } + +// testRemoteKVServiceServer is the [backendpb.RemoteKVServiceServer] for tests. +type testRemoteKVServiceServer struct { + backendpb.UnimplementedRemoteKVServiceServer + + OnGet func( + ctx context.Context, + req *backendpb.RemoteKVGetRequest, + ) (resp *backendpb.RemoteKVGetResponse, err error) + + OnSet func( + ctx context.Context, + req *backendpb.RemoteKVSetRequest, + ) (resp *backendpb.RemoteKVSetResponse, err error) +} + +// type check +var _ backendpb.RemoteKVServiceServer = (*testRemoteKVServiceServer)(nil) + +// Get implements the [backendpb.RemoteKVServiceServer] interface for +// *testRemoteKVServiceServer. +func (s *testRemoteKVServiceServer) Get( + ctx context.Context, + req *backendpb.RemoteKVGetRequest, +) (resp *backendpb.RemoteKVGetResponse, err error) { + return s.OnGet(ctx, req) +} + +// Set implements the [backendpb.RemoteKVServiceServer] interface for +// *testRemoteKVServiceServer. +func (s *testRemoteKVServiceServer) Set( + ctx context.Context, + req *backendpb.RemoteKVSetRequest, +) (resp *backendpb.RemoteKVSetResponse, err error) { + return s.OnSet(ctx, req) +} diff --git a/internal/backendpb/billstat.go b/internal/backendpb/billstat.go index 18456e4..3547bed 100644 --- a/internal/backendpb/billstat.go +++ b/internal/backendpb/billstat.go @@ -42,7 +42,7 @@ func NewBillStat(c *BillStatConfig) (b *BillStat, err error) { return &BillStat{ errColl: c.ErrColl, metrics: c.Metrics, - client: client, + client: NewDNSServiceClient(client), apiKey: c.APIKey, }, nil } diff --git a/internal/backendpb/dns.pb.go b/internal/backendpb/dns.pb.go index f9f093b..aa71698 100644 --- a/internal/backendpb/dns.pb.go +++ b/internal/backendpb/dns.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.34.2 -// protoc v5.27.1 +// protoc-gen-go v1.35.1 +// protoc v5.28.3 // source: dns.proto package backendpb @@ -93,6 +93,87 @@ func (DeviceType) EnumDescriptor() ([]byte, []int) { return file_dns_proto_rawDescGZIP(), []int{0} } +type RateLimitSettingsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *RateLimitSettingsRequest) Reset() { + *x = RateLimitSettingsRequest{} + mi := &file_dns_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RateLimitSettingsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RateLimitSettingsRequest) ProtoMessage() {} + +func (x *RateLimitSettingsRequest) ProtoReflect() protoreflect.Message { + mi := &file_dns_proto_msgTypes[0] + 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 RateLimitSettingsRequest.ProtoReflect.Descriptor instead. +func (*RateLimitSettingsRequest) Descriptor() ([]byte, []int) { + return file_dns_proto_rawDescGZIP(), []int{0} +} + +type RateLimitSettingsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AllowedSubnets []*CidrRange `protobuf:"bytes,1,rep,name=allowed_subnets,json=allowedSubnets,proto3" json:"allowed_subnets,omitempty"` +} + +func (x *RateLimitSettingsResponse) Reset() { + *x = RateLimitSettingsResponse{} + mi := &file_dns_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RateLimitSettingsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RateLimitSettingsResponse) ProtoMessage() {} + +func (x *RateLimitSettingsResponse) ProtoReflect() protoreflect.Message { + mi := &file_dns_proto_msgTypes[1] + 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 RateLimitSettingsResponse.ProtoReflect.Descriptor instead. +func (*RateLimitSettingsResponse) Descriptor() ([]byte, []int) { + return file_dns_proto_rawDescGZIP(), []int{1} +} + +func (x *RateLimitSettingsResponse) GetAllowedSubnets() []*CidrRange { + if x != nil { + return x.AllowedSubnets + } + return nil +} + type DNSProfilesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -103,11 +184,9 @@ type DNSProfilesRequest struct { func (x *DNSProfilesRequest) Reset() { *x = DNSProfilesRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *DNSProfilesRequest) String() string { @@ -117,8 +196,8 @@ func (x *DNSProfilesRequest) String() string { func (*DNSProfilesRequest) ProtoMessage() {} func (x *DNSProfilesRequest) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[2] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -130,7 +209,7 @@ func (x *DNSProfilesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DNSProfilesRequest.ProtoReflect.Descriptor instead. func (*DNSProfilesRequest) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{0} + return file_dns_proto_rawDescGZIP(), []int{2} } func (x *DNSProfilesRequest) GetSyncTime() *timestamppb.Timestamp { @@ -172,11 +251,9 @@ type DNSProfile struct { func (x *DNSProfile) Reset() { *x = DNSProfile{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *DNSProfile) String() string { @@ -186,8 +263,8 @@ func (x *DNSProfile) String() string { func (*DNSProfile) ProtoMessage() {} func (x *DNSProfile) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[3] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -199,7 +276,7 @@ func (x *DNSProfile) ProtoReflect() protoreflect.Message { // Deprecated: Use DNSProfile.ProtoReflect.Descriptor instead. func (*DNSProfile) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{1} + return file_dns_proto_rawDescGZIP(), []int{3} } func (x *DNSProfile) GetDnsId() string { @@ -389,11 +466,9 @@ type SafeBrowsingSettings struct { func (x *SafeBrowsingSettings) Reset() { *x = SafeBrowsingSettings{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *SafeBrowsingSettings) String() string { @@ -403,8 +478,8 @@ func (x *SafeBrowsingSettings) String() string { func (*SafeBrowsingSettings) ProtoMessage() {} func (x *SafeBrowsingSettings) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[4] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -416,7 +491,7 @@ func (x *SafeBrowsingSettings) ProtoReflect() protoreflect.Message { // Deprecated: Use SafeBrowsingSettings.ProtoReflect.Descriptor instead. func (*SafeBrowsingSettings) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{2} + return file_dns_proto_rawDescGZIP(), []int{4} } func (x *SafeBrowsingSettings) GetEnabled() bool { @@ -457,11 +532,9 @@ type DeviceSettings struct { func (x *DeviceSettings) Reset() { *x = DeviceSettings{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *DeviceSettings) String() string { @@ -471,8 +544,8 @@ func (x *DeviceSettings) String() string { func (*DeviceSettings) ProtoMessage() {} func (x *DeviceSettings) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[5] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -484,7 +557,7 @@ func (x *DeviceSettings) ProtoReflect() protoreflect.Message { // Deprecated: Use DeviceSettings.ProtoReflect.Descriptor instead. func (*DeviceSettings) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{3} + return file_dns_proto_rawDescGZIP(), []int{5} } func (x *DeviceSettings) GetId() string { @@ -551,11 +624,9 @@ type ParentalSettings struct { func (x *ParentalSettings) Reset() { *x = ParentalSettings{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ParentalSettings) String() string { @@ -565,8 +636,8 @@ func (x *ParentalSettings) String() string { func (*ParentalSettings) ProtoMessage() {} func (x *ParentalSettings) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[6] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -578,7 +649,7 @@ func (x *ParentalSettings) ProtoReflect() protoreflect.Message { // Deprecated: Use ParentalSettings.ProtoReflect.Descriptor instead. func (*ParentalSettings) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{4} + return file_dns_proto_rawDescGZIP(), []int{6} } func (x *ParentalSettings) GetEnabled() bool { @@ -634,11 +705,9 @@ type ScheduleSettings struct { func (x *ScheduleSettings) Reset() { *x = ScheduleSettings{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ScheduleSettings) String() string { @@ -648,8 +717,8 @@ func (x *ScheduleSettings) String() string { func (*ScheduleSettings) ProtoMessage() {} func (x *ScheduleSettings) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[7] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -661,7 +730,7 @@ func (x *ScheduleSettings) ProtoReflect() protoreflect.Message { // Deprecated: Use ScheduleSettings.ProtoReflect.Descriptor instead. func (*ScheduleSettings) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{5} + return file_dns_proto_rawDescGZIP(), []int{7} } func (x *ScheduleSettings) GetTmz() string { @@ -694,11 +763,9 @@ type WeeklyRange struct { func (x *WeeklyRange) Reset() { *x = WeeklyRange{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *WeeklyRange) String() string { @@ -708,8 +775,8 @@ func (x *WeeklyRange) String() string { func (*WeeklyRange) ProtoMessage() {} func (x *WeeklyRange) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[8] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -721,7 +788,7 @@ func (x *WeeklyRange) ProtoReflect() protoreflect.Message { // Deprecated: Use WeeklyRange.ProtoReflect.Descriptor instead. func (*WeeklyRange) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{6} + return file_dns_proto_rawDescGZIP(), []int{8} } func (x *WeeklyRange) GetMon() *DayRange { @@ -784,11 +851,9 @@ type DayRange struct { func (x *DayRange) Reset() { *x = DayRange{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *DayRange) String() string { @@ -798,8 +863,8 @@ func (x *DayRange) String() string { func (*DayRange) ProtoMessage() {} func (x *DayRange) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[9] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -811,7 +876,7 @@ func (x *DayRange) ProtoReflect() protoreflect.Message { // Deprecated: Use DayRange.ProtoReflect.Descriptor instead. func (*DayRange) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{7} + return file_dns_proto_rawDescGZIP(), []int{9} } func (x *DayRange) GetStart() *durationpb.Duration { @@ -839,11 +904,9 @@ type RuleListsSettings struct { func (x *RuleListsSettings) Reset() { *x = RuleListsSettings{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RuleListsSettings) String() string { @@ -853,8 +916,8 @@ func (x *RuleListsSettings) String() string { func (*RuleListsSettings) ProtoMessage() {} func (x *RuleListsSettings) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[10] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -866,7 +929,7 @@ func (x *RuleListsSettings) ProtoReflect() protoreflect.Message { // Deprecated: Use RuleListsSettings.ProtoReflect.Descriptor instead. func (*RuleListsSettings) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{8} + return file_dns_proto_rawDescGZIP(), []int{10} } func (x *RuleListsSettings) GetEnabled() bool { @@ -894,11 +957,9 @@ type BlockingModeCustomIP struct { func (x *BlockingModeCustomIP) Reset() { *x = BlockingModeCustomIP{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *BlockingModeCustomIP) String() string { @@ -908,8 +969,8 @@ func (x *BlockingModeCustomIP) String() string { func (*BlockingModeCustomIP) ProtoMessage() {} func (x *BlockingModeCustomIP) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[11] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -921,7 +982,7 @@ func (x *BlockingModeCustomIP) ProtoReflect() protoreflect.Message { // Deprecated: Use BlockingModeCustomIP.ProtoReflect.Descriptor instead. func (*BlockingModeCustomIP) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{9} + return file_dns_proto_rawDescGZIP(), []int{11} } func (x *BlockingModeCustomIP) GetIpv4() []byte { @@ -946,11 +1007,9 @@ type BlockingModeNXDOMAIN struct { func (x *BlockingModeNXDOMAIN) Reset() { *x = BlockingModeNXDOMAIN{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *BlockingModeNXDOMAIN) String() string { @@ -960,8 +1019,8 @@ func (x *BlockingModeNXDOMAIN) String() string { func (*BlockingModeNXDOMAIN) ProtoMessage() {} func (x *BlockingModeNXDOMAIN) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[12] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -973,7 +1032,7 @@ func (x *BlockingModeNXDOMAIN) ProtoReflect() protoreflect.Message { // Deprecated: Use BlockingModeNXDOMAIN.ProtoReflect.Descriptor instead. func (*BlockingModeNXDOMAIN) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{10} + return file_dns_proto_rawDescGZIP(), []int{12} } type BlockingModeNullIP struct { @@ -984,11 +1043,9 @@ type BlockingModeNullIP struct { func (x *BlockingModeNullIP) Reset() { *x = BlockingModeNullIP{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *BlockingModeNullIP) String() string { @@ -998,8 +1055,8 @@ func (x *BlockingModeNullIP) String() string { func (*BlockingModeNullIP) ProtoMessage() {} func (x *BlockingModeNullIP) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[13] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1011,7 +1068,7 @@ func (x *BlockingModeNullIP) ProtoReflect() protoreflect.Message { // Deprecated: Use BlockingModeNullIP.ProtoReflect.Descriptor instead. func (*BlockingModeNullIP) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{11} + return file_dns_proto_rawDescGZIP(), []int{13} } type BlockingModeREFUSED struct { @@ -1022,11 +1079,9 @@ type BlockingModeREFUSED struct { func (x *BlockingModeREFUSED) Reset() { *x = BlockingModeREFUSED{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *BlockingModeREFUSED) String() string { @@ -1036,8 +1091,8 @@ func (x *BlockingModeREFUSED) String() string { func (*BlockingModeREFUSED) ProtoMessage() {} func (x *BlockingModeREFUSED) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[12] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[14] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1049,7 +1104,7 @@ func (x *BlockingModeREFUSED) ProtoReflect() protoreflect.Message { // Deprecated: Use BlockingModeREFUSED.ProtoReflect.Descriptor instead. func (*BlockingModeREFUSED) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{12} + return file_dns_proto_rawDescGZIP(), []int{14} } type DeviceBillingStat struct { @@ -1068,11 +1123,9 @@ type DeviceBillingStat struct { func (x *DeviceBillingStat) Reset() { *x = DeviceBillingStat{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *DeviceBillingStat) String() string { @@ -1082,8 +1135,8 @@ func (x *DeviceBillingStat) String() string { func (*DeviceBillingStat) ProtoMessage() {} func (x *DeviceBillingStat) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[13] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[15] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1095,7 +1148,7 @@ func (x *DeviceBillingStat) ProtoReflect() protoreflect.Message { // Deprecated: Use DeviceBillingStat.ProtoReflect.Descriptor instead. func (*DeviceBillingStat) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{13} + return file_dns_proto_rawDescGZIP(), []int{15} } func (x *DeviceBillingStat) GetLastActivityTime() *timestamppb.Timestamp { @@ -1155,11 +1208,9 @@ type AccessSettings struct { func (x *AccessSettings) Reset() { *x = AccessSettings{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AccessSettings) String() string { @@ -1169,8 +1220,8 @@ func (x *AccessSettings) String() string { func (*AccessSettings) ProtoMessage() {} func (x *AccessSettings) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[14] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[16] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1182,7 +1233,7 @@ func (x *AccessSettings) ProtoReflect() protoreflect.Message { // Deprecated: Use AccessSettings.ProtoReflect.Descriptor instead. func (*AccessSettings) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{14} + return file_dns_proto_rawDescGZIP(), []int{16} } func (x *AccessSettings) GetAllowlistCidr() []*CidrRange { @@ -1238,11 +1289,9 @@ type CidrRange struct { func (x *CidrRange) Reset() { *x = CidrRange{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *CidrRange) String() string { @@ -1252,8 +1301,8 @@ func (x *CidrRange) String() string { func (*CidrRange) ProtoMessage() {} func (x *CidrRange) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[15] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[17] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1265,7 +1314,7 @@ func (x *CidrRange) ProtoReflect() protoreflect.Message { // Deprecated: Use CidrRange.ProtoReflect.Descriptor instead. func (*CidrRange) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{15} + return file_dns_proto_rawDescGZIP(), []int{17} } func (x *CidrRange) GetAddress() []byte { @@ -1296,11 +1345,9 @@ type AuthenticationSettings struct { func (x *AuthenticationSettings) Reset() { *x = AuthenticationSettings{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[16] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AuthenticationSettings) String() string { @@ -1310,8 +1357,8 @@ func (x *AuthenticationSettings) String() string { func (*AuthenticationSettings) ProtoMessage() {} func (x *AuthenticationSettings) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[16] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[18] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1323,7 +1370,7 @@ func (x *AuthenticationSettings) ProtoReflect() protoreflect.Message { // Deprecated: Use AuthenticationSettings.ProtoReflect.Descriptor instead. func (*AuthenticationSettings) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{16} + return file_dns_proto_rawDescGZIP(), []int{18} } func (x *AuthenticationSettings) GetDohAuthOnly() bool { @@ -1369,11 +1416,9 @@ type CreateDeviceRequest struct { func (x *CreateDeviceRequest) Reset() { *x = CreateDeviceRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[17] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *CreateDeviceRequest) String() string { @@ -1383,8 +1428,8 @@ func (x *CreateDeviceRequest) String() string { func (*CreateDeviceRequest) ProtoMessage() {} func (x *CreateDeviceRequest) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[17] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[19] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1396,7 +1441,7 @@ func (x *CreateDeviceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateDeviceRequest.ProtoReflect.Descriptor instead. func (*CreateDeviceRequest) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{17} + return file_dns_proto_rawDescGZIP(), []int{19} } func (x *CreateDeviceRequest) GetDnsId() string { @@ -1430,11 +1475,9 @@ type CreateDeviceResponse struct { func (x *CreateDeviceResponse) Reset() { *x = CreateDeviceResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[18] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *CreateDeviceResponse) String() string { @@ -1444,8 +1487,8 @@ func (x *CreateDeviceResponse) String() string { func (*CreateDeviceResponse) ProtoMessage() {} func (x *CreateDeviceResponse) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[18] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[20] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1457,7 +1500,7 @@ func (x *CreateDeviceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateDeviceResponse.ProtoReflect.Descriptor instead. func (*CreateDeviceResponse) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{18} + return file_dns_proto_rawDescGZIP(), []int{20} } func (x *CreateDeviceResponse) GetDevice() *DeviceSettings { @@ -1478,11 +1521,9 @@ type RateLimitedError struct { func (x *RateLimitedError) Reset() { *x = RateLimitedError{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[19] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RateLimitedError) String() string { @@ -1492,8 +1533,8 @@ func (x *RateLimitedError) String() string { func (*RateLimitedError) ProtoMessage() {} func (x *RateLimitedError) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[19] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[21] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1505,7 +1546,7 @@ func (x *RateLimitedError) ProtoReflect() protoreflect.Message { // Deprecated: Use RateLimitedError.ProtoReflect.Descriptor instead. func (*RateLimitedError) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{19} + return file_dns_proto_rawDescGZIP(), []int{21} } func (x *RateLimitedError) GetMessage() string { @@ -1532,11 +1573,9 @@ type DeviceQuotaExceededError struct { func (x *DeviceQuotaExceededError) Reset() { *x = DeviceQuotaExceededError{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[20] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *DeviceQuotaExceededError) String() string { @@ -1546,8 +1585,8 @@ func (x *DeviceQuotaExceededError) String() string { func (*DeviceQuotaExceededError) ProtoMessage() {} func (x *DeviceQuotaExceededError) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[20] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[22] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1559,7 +1598,7 @@ func (x *DeviceQuotaExceededError) ProtoReflect() protoreflect.Message { // Deprecated: Use DeviceQuotaExceededError.ProtoReflect.Descriptor instead. func (*DeviceQuotaExceededError) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{20} + return file_dns_proto_rawDescGZIP(), []int{22} } func (x *DeviceQuotaExceededError) GetMessage() string { @@ -1579,11 +1618,9 @@ type BadRequestError struct { func (x *BadRequestError) Reset() { *x = BadRequestError{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[21] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *BadRequestError) String() string { @@ -1593,8 +1630,8 @@ func (x *BadRequestError) String() string { func (*BadRequestError) ProtoMessage() {} func (x *BadRequestError) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[21] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[23] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1606,7 +1643,7 @@ func (x *BadRequestError) ProtoReflect() protoreflect.Message { // Deprecated: Use BadRequestError.ProtoReflect.Descriptor instead. func (*BadRequestError) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{21} + return file_dns_proto_rawDescGZIP(), []int{23} } func (x *BadRequestError) GetMessage() string { @@ -1626,11 +1663,9 @@ type AuthenticationFailedError struct { func (x *AuthenticationFailedError) Reset() { *x = AuthenticationFailedError{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[22] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AuthenticationFailedError) String() string { @@ -1640,8 +1675,8 @@ func (x *AuthenticationFailedError) String() string { func (*AuthenticationFailedError) ProtoMessage() {} func (x *AuthenticationFailedError) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[22] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[24] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1653,7 +1688,7 @@ func (x *AuthenticationFailedError) ProtoReflect() protoreflect.Message { // Deprecated: Use AuthenticationFailedError.ProtoReflect.Descriptor instead. func (*AuthenticationFailedError) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{22} + return file_dns_proto_rawDescGZIP(), []int{24} } func (x *AuthenticationFailedError) GetMessage() string { @@ -1675,11 +1710,9 @@ type RateLimitSettings struct { func (x *RateLimitSettings) Reset() { *x = RateLimitSettings{} - if protoimpl.UnsafeEnabled { - mi := &file_dns_proto_msgTypes[23] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_dns_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RateLimitSettings) String() string { @@ -1689,8 +1722,8 @@ func (x *RateLimitSettings) String() string { func (*RateLimitSettings) ProtoMessage() {} func (x *RateLimitSettings) ProtoReflect() protoreflect.Message { - mi := &file_dns_proto_msgTypes[23] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_dns_proto_msgTypes[25] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1702,7 +1735,7 @@ func (x *RateLimitSettings) ProtoReflect() protoreflect.Message { // Deprecated: Use RateLimitSettings.ProtoReflect.Descriptor instead. func (*RateLimitSettings) Descriptor() ([]byte, []int) { - return file_dns_proto_rawDescGZIP(), []int{23} + return file_dns_proto_rawDescGZIP(), []int{25} } func (x *RateLimitSettings) GetEnabled() bool { @@ -1726,6 +1759,227 @@ func (x *RateLimitSettings) GetClientCidr() []*CidrRange { return nil } +type RemoteKVGetRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` +} + +func (x *RemoteKVGetRequest) Reset() { + *x = RemoteKVGetRequest{} + mi := &file_dns_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RemoteKVGetRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoteKVGetRequest) ProtoMessage() {} + +func (x *RemoteKVGetRequest) ProtoReflect() protoreflect.Message { + mi := &file_dns_proto_msgTypes[26] + 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 RemoteKVGetRequest.ProtoReflect.Descriptor instead. +func (*RemoteKVGetRequest) Descriptor() ([]byte, []int) { + return file_dns_proto_rawDescGZIP(), []int{26} +} + +func (x *RemoteKVGetRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +type RemoteKVGetResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Value: + // + // *RemoteKVGetResponse_Data + // *RemoteKVGetResponse_Empty + Value isRemoteKVGetResponse_Value `protobuf_oneof:"value"` +} + +func (x *RemoteKVGetResponse) Reset() { + *x = RemoteKVGetResponse{} + mi := &file_dns_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RemoteKVGetResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoteKVGetResponse) ProtoMessage() {} + +func (x *RemoteKVGetResponse) ProtoReflect() protoreflect.Message { + mi := &file_dns_proto_msgTypes[27] + 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 RemoteKVGetResponse.ProtoReflect.Descriptor instead. +func (*RemoteKVGetResponse) Descriptor() ([]byte, []int) { + return file_dns_proto_rawDescGZIP(), []int{27} +} + +func (m *RemoteKVGetResponse) GetValue() isRemoteKVGetResponse_Value { + if m != nil { + return m.Value + } + return nil +} + +func (x *RemoteKVGetResponse) GetData() []byte { + if x, ok := x.GetValue().(*RemoteKVGetResponse_Data); ok { + return x.Data + } + return nil +} + +func (x *RemoteKVGetResponse) GetEmpty() *emptypb.Empty { + if x, ok := x.GetValue().(*RemoteKVGetResponse_Empty); ok { + return x.Empty + } + return nil +} + +type isRemoteKVGetResponse_Value interface { + isRemoteKVGetResponse_Value() +} + +type RemoteKVGetResponse_Data struct { + Data []byte `protobuf:"bytes,1,opt,name=data,proto3,oneof"` +} + +type RemoteKVGetResponse_Empty struct { + Empty *emptypb.Empty `protobuf:"bytes,2,opt,name=empty,proto3,oneof"` +} + +func (*RemoteKVGetResponse_Data) isRemoteKVGetResponse_Value() {} + +func (*RemoteKVGetResponse_Empty) isRemoteKVGetResponse_Value() {} + +type RemoteKVSetRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + Ttl *durationpb.Duration `protobuf:"bytes,3,opt,name=ttl,proto3" json:"ttl,omitempty"` +} + +func (x *RemoteKVSetRequest) Reset() { + *x = RemoteKVSetRequest{} + mi := &file_dns_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RemoteKVSetRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoteKVSetRequest) ProtoMessage() {} + +func (x *RemoteKVSetRequest) ProtoReflect() protoreflect.Message { + mi := &file_dns_proto_msgTypes[28] + 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 RemoteKVSetRequest.ProtoReflect.Descriptor instead. +func (*RemoteKVSetRequest) Descriptor() ([]byte, []int) { + return file_dns_proto_rawDescGZIP(), []int{28} +} + +func (x *RemoteKVSetRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *RemoteKVSetRequest) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *RemoteKVSetRequest) GetTtl() *durationpb.Duration { + if x != nil { + return x.Ttl + } + return nil +} + +type RemoteKVSetResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *RemoteKVSetResponse) Reset() { + *x = RemoteKVSetResponse{} + mi := &file_dns_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RemoteKVSetResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoteKVSetResponse) ProtoMessage() {} + +func (x *RemoteKVSetResponse) ProtoReflect() protoreflect.Message { + mi := &file_dns_proto_msgTypes[29] + 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 RemoteKVSetResponse.ProtoReflect.Descriptor instead. +func (*RemoteKVSetResponse) Descriptor() ([]byte, []int) { + return file_dns_proto_rawDescGZIP(), []int{29} +} + var File_dns_proto protoreflect.FileDescriptor var file_dns_proto_rawDesc = []byte{ @@ -1735,264 +1989,301 @@ var file_dns_proto_rawDesc = []byte{ 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, - 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x4d, 0x0a, 0x12, 0x44, 0x4e, 0x53, - 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x37, 0x0a, 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, 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, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x69, 0x6e, 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, 0x03, 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, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x64, 0x12, 0x3a, 0x0a, 0x0d, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x62, 0x72, 0x6f, 0x77, 0x73, 0x69, - 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 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, 0x2d, 0x0a, - 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x11, 0x2e, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, - 0x67, 0x73, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x12, 0x31, 0x0a, 0x0a, - 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x12, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x53, 0x65, 0x74, 0x74, - 0x69, 0x6e, 0x67, 0x73, 0x52, 0x09, 0x72, 0x75, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x12, - 0x29, 0x0a, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x0f, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, - 0x73, 0x52, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x75, - 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x09, 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, 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, 0x2e, 0x0a, 0x13, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, - 0x6c, 0x61, 0x79, 0x18, 0x0b, 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, 0x6f, 0x63, 0x6b, 0x5f, 0x66, 0x69, 0x72, 0x65, 0x66, 0x6f, 0x78, 0x5f, 0x63, 0x61, - 0x6e, 0x61, 0x72, 0x79, 0x18, 0x0c, 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, 0x4e, - 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, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x15, 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, 0x4d, - 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, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, - 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, 0x48, 0x0a, - 0x15, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x6e, - 0x75, 0x6c, 0x6c, 0x5f, 0x69, 0x70, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 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, 0x4a, 0x0a, 0x15, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x72, 0x65, 0x66, 0x75, 0x73, 0x65, 0x64, - 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 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, 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, 0x27, 0x0a, 0x06, 0x61, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x41, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x06, 0x61, 0x63, 0x63, 0x65, - 0x73, 0x73, 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, 0x13, 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, 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, + 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1a, 0x0a, 0x18, 0x52, 0x61, 0x74, + 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x50, 0x0a, 0x19, 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, 0x12, 0x33, 0x0a, 0x0f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x73, 0x75, + 0x62, 0x6e, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x69, + 0x64, 0x72, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, + 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x73, 0x22, 0x4d, 0x0a, 0x12, 0x44, 0x4e, 0x53, 0x50, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x37, 0x0a, + 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, + 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, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x69, + 0x6e, 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, 0x03, + 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, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, + 0x3a, 0x0a, 0x0d, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x62, 0x72, 0x6f, 0x77, 0x73, 0x69, 0x6e, 0x67, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 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, 0x2d, 0x0a, 0x08, 0x70, + 0x61, 0x72, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, + 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, + 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x12, 0x31, 0x0a, 0x0a, 0x72, 0x75, + 0x6c, 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x52, 0x09, 0x72, 0x75, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x12, 0x29, 0x0a, + 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, + 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, + 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x75, 0x73, 0x74, + 0x6f, 0x6d, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x09, 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, 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, 0x2e, 0x0a, 0x13, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, + 0x79, 0x18, 0x0b, 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, + 0x6f, 0x63, 0x6b, 0x5f, 0x66, 0x69, 0x72, 0x65, 0x66, 0x6f, 0x78, 0x5f, 0x63, 0x61, 0x6e, 0x61, + 0x72, 0x79, 0x18, 0x0c, 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, 0x4e, 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, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 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, 0x4d, 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, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 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, 0x48, 0x0a, 0x15, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x75, 0x6c, + 0x6c, 0x5f, 0x69, 0x70, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 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, 0x4a, 0x0a, 0x15, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x69, 0x6e, + 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x72, 0x65, 0x66, 0x75, 0x73, 0x65, 0x64, 0x18, 0x10, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 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, 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, 0x27, 0x0a, 0x06, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x06, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 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, 0x13, 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, 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, + 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, 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, 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, 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, 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, + 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, + 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 ( @@ -2008,80 +2299,95 @@ func file_dns_proto_rawDescGZIP() []byte { } var file_dns_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_dns_proto_msgTypes = make([]protoimpl.MessageInfo, 24) +var file_dns_proto_msgTypes = make([]protoimpl.MessageInfo, 30) var file_dns_proto_goTypes = []any{ (DeviceType)(0), // 0: DeviceType - (*DNSProfilesRequest)(nil), // 1: DNSProfilesRequest - (*DNSProfile)(nil), // 2: DNSProfile - (*SafeBrowsingSettings)(nil), // 3: SafeBrowsingSettings - (*DeviceSettings)(nil), // 4: DeviceSettings - (*ParentalSettings)(nil), // 5: ParentalSettings - (*ScheduleSettings)(nil), // 6: ScheduleSettings - (*WeeklyRange)(nil), // 7: WeeklyRange - (*DayRange)(nil), // 8: DayRange - (*RuleListsSettings)(nil), // 9: RuleListsSettings - (*BlockingModeCustomIP)(nil), // 10: BlockingModeCustomIP - (*BlockingModeNXDOMAIN)(nil), // 11: BlockingModeNXDOMAIN - (*BlockingModeNullIP)(nil), // 12: BlockingModeNullIP - (*BlockingModeREFUSED)(nil), // 13: BlockingModeREFUSED - (*DeviceBillingStat)(nil), // 14: DeviceBillingStat - (*AccessSettings)(nil), // 15: AccessSettings - (*CidrRange)(nil), // 16: CidrRange - (*AuthenticationSettings)(nil), // 17: AuthenticationSettings - (*CreateDeviceRequest)(nil), // 18: CreateDeviceRequest - (*CreateDeviceResponse)(nil), // 19: CreateDeviceResponse - (*RateLimitedError)(nil), // 20: RateLimitedError - (*DeviceQuotaExceededError)(nil), // 21: DeviceQuotaExceededError - (*BadRequestError)(nil), // 22: BadRequestError - (*AuthenticationFailedError)(nil), // 23: AuthenticationFailedError - (*RateLimitSettings)(nil), // 24: RateLimitSettings - (*timestamppb.Timestamp)(nil), // 25: google.protobuf.Timestamp - (*durationpb.Duration)(nil), // 26: google.protobuf.Duration - (*emptypb.Empty)(nil), // 27: google.protobuf.Empty + (*RateLimitSettingsRequest)(nil), // 1: RateLimitSettingsRequest + (*RateLimitSettingsResponse)(nil), // 2: RateLimitSettingsResponse + (*DNSProfilesRequest)(nil), // 3: DNSProfilesRequest + (*DNSProfile)(nil), // 4: DNSProfile + (*SafeBrowsingSettings)(nil), // 5: SafeBrowsingSettings + (*DeviceSettings)(nil), // 6: DeviceSettings + (*ParentalSettings)(nil), // 7: ParentalSettings + (*ScheduleSettings)(nil), // 8: ScheduleSettings + (*WeeklyRange)(nil), // 9: WeeklyRange + (*DayRange)(nil), // 10: DayRange + (*RuleListsSettings)(nil), // 11: RuleListsSettings + (*BlockingModeCustomIP)(nil), // 12: BlockingModeCustomIP + (*BlockingModeNXDOMAIN)(nil), // 13: BlockingModeNXDOMAIN + (*BlockingModeNullIP)(nil), // 14: BlockingModeNullIP + (*BlockingModeREFUSED)(nil), // 15: BlockingModeREFUSED + (*DeviceBillingStat)(nil), // 16: DeviceBillingStat + (*AccessSettings)(nil), // 17: AccessSettings + (*CidrRange)(nil), // 18: CidrRange + (*AuthenticationSettings)(nil), // 19: AuthenticationSettings + (*CreateDeviceRequest)(nil), // 20: CreateDeviceRequest + (*CreateDeviceResponse)(nil), // 21: CreateDeviceResponse + (*RateLimitedError)(nil), // 22: RateLimitedError + (*DeviceQuotaExceededError)(nil), // 23: DeviceQuotaExceededError + (*BadRequestError)(nil), // 24: BadRequestError + (*AuthenticationFailedError)(nil), // 25: AuthenticationFailedError + (*RateLimitSettings)(nil), // 26: RateLimitSettings + (*RemoteKVGetRequest)(nil), // 27: RemoteKVGetRequest + (*RemoteKVGetResponse)(nil), // 28: RemoteKVGetResponse + (*RemoteKVSetRequest)(nil), // 29: RemoteKVSetRequest + (*RemoteKVSetResponse)(nil), // 30: RemoteKVSetResponse + (*timestamppb.Timestamp)(nil), // 31: google.protobuf.Timestamp + (*durationpb.Duration)(nil), // 32: google.protobuf.Duration + (*emptypb.Empty)(nil), // 33: google.protobuf.Empty } var file_dns_proto_depIdxs = []int32{ - 25, // 0: DNSProfilesRequest.sync_time:type_name -> google.protobuf.Timestamp - 3, // 1: DNSProfile.safe_browsing:type_name -> SafeBrowsingSettings - 5, // 2: DNSProfile.parental:type_name -> ParentalSettings - 9, // 3: DNSProfile.rule_lists:type_name -> RuleListsSettings - 4, // 4: DNSProfile.devices:type_name -> DeviceSettings - 26, // 5: DNSProfile.filtered_response_ttl:type_name -> google.protobuf.Duration - 10, // 6: DNSProfile.blocking_mode_custom_ip:type_name -> BlockingModeCustomIP - 11, // 7: DNSProfile.blocking_mode_nxdomain:type_name -> BlockingModeNXDOMAIN - 12, // 8: DNSProfile.blocking_mode_null_ip:type_name -> BlockingModeNullIP - 13, // 9: DNSProfile.blocking_mode_refused:type_name -> BlockingModeREFUSED - 15, // 10: DNSProfile.access:type_name -> AccessSettings - 24, // 11: DNSProfile.rate_limit:type_name -> RateLimitSettings - 17, // 12: DeviceSettings.authentication:type_name -> AuthenticationSettings - 6, // 13: ParentalSettings.schedule:type_name -> ScheduleSettings - 7, // 14: ScheduleSettings.weeklyRange:type_name -> WeeklyRange - 8, // 15: WeeklyRange.mon:type_name -> DayRange - 8, // 16: WeeklyRange.tue:type_name -> DayRange - 8, // 17: WeeklyRange.wed:type_name -> DayRange - 8, // 18: WeeklyRange.thu:type_name -> DayRange - 8, // 19: WeeklyRange.fri:type_name -> DayRange - 8, // 20: WeeklyRange.sat:type_name -> DayRange - 8, // 21: WeeklyRange.sun:type_name -> DayRange - 26, // 22: DayRange.start:type_name -> google.protobuf.Duration - 26, // 23: DayRange.end:type_name -> google.protobuf.Duration - 25, // 24: DeviceBillingStat.last_activity_time:type_name -> google.protobuf.Timestamp - 16, // 25: AccessSettings.allowlist_cidr:type_name -> CidrRange - 16, // 26: AccessSettings.blocklist_cidr:type_name -> CidrRange - 0, // 27: CreateDeviceRequest.device_type:type_name -> DeviceType - 4, // 28: CreateDeviceResponse.device:type_name -> DeviceSettings - 26, // 29: RateLimitedError.retry_delay:type_name -> google.protobuf.Duration - 16, // 30: RateLimitSettings.client_cidr:type_name -> CidrRange - 1, // 31: DNSService.getDNSProfiles:input_type -> DNSProfilesRequest - 14, // 32: DNSService.saveDevicesBillingStat:input_type -> DeviceBillingStat - 18, // 33: DNSService.createDeviceByHumanId:input_type -> CreateDeviceRequest - 2, // 34: DNSService.getDNSProfiles:output_type -> DNSProfile - 27, // 35: DNSService.saveDevicesBillingStat:output_type -> google.protobuf.Empty - 19, // 36: DNSService.createDeviceByHumanId:output_type -> CreateDeviceResponse - 34, // [34:37] is the sub-list for method output_type - 31, // [31:34] is the sub-list for method input_type - 31, // [31:31] is the sub-list for extension type_name - 31, // [31:31] is the sub-list for extension extendee - 0, // [0:31] is the sub-list for field type_name + 18, // 0: RateLimitSettingsResponse.allowed_subnets:type_name -> CidrRange + 31, // 1: DNSProfilesRequest.sync_time:type_name -> google.protobuf.Timestamp + 5, // 2: DNSProfile.safe_browsing:type_name -> SafeBrowsingSettings + 7, // 3: DNSProfile.parental:type_name -> ParentalSettings + 11, // 4: DNSProfile.rule_lists:type_name -> RuleListsSettings + 6, // 5: DNSProfile.devices:type_name -> DeviceSettings + 32, // 6: DNSProfile.filtered_response_ttl:type_name -> google.protobuf.Duration + 12, // 7: DNSProfile.blocking_mode_custom_ip:type_name -> BlockingModeCustomIP + 13, // 8: DNSProfile.blocking_mode_nxdomain:type_name -> BlockingModeNXDOMAIN + 14, // 9: DNSProfile.blocking_mode_null_ip:type_name -> BlockingModeNullIP + 15, // 10: DNSProfile.blocking_mode_refused:type_name -> BlockingModeREFUSED + 17, // 11: DNSProfile.access:type_name -> AccessSettings + 26, // 12: DNSProfile.rate_limit:type_name -> RateLimitSettings + 19, // 13: DeviceSettings.authentication:type_name -> AuthenticationSettings + 8, // 14: ParentalSettings.schedule:type_name -> ScheduleSettings + 9, // 15: ScheduleSettings.weeklyRange:type_name -> WeeklyRange + 10, // 16: WeeklyRange.mon:type_name -> DayRange + 10, // 17: WeeklyRange.tue:type_name -> DayRange + 10, // 18: WeeklyRange.wed:type_name -> DayRange + 10, // 19: WeeklyRange.thu:type_name -> DayRange + 10, // 20: WeeklyRange.fri:type_name -> DayRange + 10, // 21: WeeklyRange.sat:type_name -> DayRange + 10, // 22: WeeklyRange.sun:type_name -> DayRange + 32, // 23: DayRange.start:type_name -> google.protobuf.Duration + 32, // 24: DayRange.end:type_name -> google.protobuf.Duration + 31, // 25: DeviceBillingStat.last_activity_time:type_name -> google.protobuf.Timestamp + 18, // 26: AccessSettings.allowlist_cidr:type_name -> CidrRange + 18, // 27: AccessSettings.blocklist_cidr:type_name -> CidrRange + 0, // 28: CreateDeviceRequest.device_type:type_name -> DeviceType + 6, // 29: CreateDeviceResponse.device:type_name -> DeviceSettings + 32, // 30: RateLimitedError.retry_delay:type_name -> google.protobuf.Duration + 18, // 31: RateLimitSettings.client_cidr:type_name -> CidrRange + 33, // 32: RemoteKVGetResponse.empty:type_name -> google.protobuf.Empty + 32, // 33: RemoteKVSetRequest.ttl:type_name -> google.protobuf.Duration + 3, // 34: DNSService.getDNSProfiles:input_type -> DNSProfilesRequest + 16, // 35: DNSService.saveDevicesBillingStat:input_type -> DeviceBillingStat + 20, // 36: DNSService.createDeviceByHumanId:input_type -> CreateDeviceRequest + 1, // 37: RateLimitService.getRateLimitSettings:input_type -> RateLimitSettingsRequest + 27, // 38: RemoteKVService.get:input_type -> RemoteKVGetRequest + 29, // 39: RemoteKVService.set:input_type -> RemoteKVSetRequest + 4, // 40: DNSService.getDNSProfiles:output_type -> DNSProfile + 33, // 41: DNSService.saveDevicesBillingStat:output_type -> google.protobuf.Empty + 21, // 42: DNSService.createDeviceByHumanId:output_type -> CreateDeviceResponse + 2, // 43: RateLimitService.getRateLimitSettings:output_type -> RateLimitSettingsResponse + 28, // 44: RemoteKVService.get:output_type -> RemoteKVGetResponse + 30, // 45: RemoteKVService.set:output_type -> RemoteKVSetResponse + 40, // [40:46] is the sub-list for method output_type + 34, // [34:40] is the sub-list for method input_type + 34, // [34:34] is the sub-list for extension type_name + 34, // [34:34] is the sub-list for extension extendee + 0, // [0:34] is the sub-list for field type_name } func init() { file_dns_proto_init() } @@ -2089,314 +2395,28 @@ func file_dns_proto_init() { if File_dns_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_dns_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*DNSProfilesRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[1].Exporter = func(v any, i int) any { - switch v := v.(*DNSProfile); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[2].Exporter = func(v any, i int) any { - switch v := v.(*SafeBrowsingSettings); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[3].Exporter = func(v any, i int) any { - switch v := v.(*DeviceSettings); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[4].Exporter = func(v any, i int) any { - switch v := v.(*ParentalSettings); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[5].Exporter = func(v any, i int) any { - switch v := v.(*ScheduleSettings); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[6].Exporter = func(v any, i int) any { - switch v := v.(*WeeklyRange); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[7].Exporter = func(v any, i int) any { - switch v := v.(*DayRange); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[8].Exporter = func(v any, i int) any { - switch v := v.(*RuleListsSettings); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[9].Exporter = func(v any, i int) any { - switch v := v.(*BlockingModeCustomIP); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[10].Exporter = func(v any, i int) any { - switch v := v.(*BlockingModeNXDOMAIN); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[11].Exporter = func(v any, i int) any { - switch v := v.(*BlockingModeNullIP); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[12].Exporter = func(v any, i int) any { - switch v := v.(*BlockingModeREFUSED); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[13].Exporter = func(v any, i int) any { - switch v := v.(*DeviceBillingStat); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[14].Exporter = func(v any, i int) any { - switch v := v.(*AccessSettings); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[15].Exporter = func(v any, i int) any { - switch v := v.(*CidrRange); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[16].Exporter = func(v any, i int) any { - switch v := v.(*AuthenticationSettings); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[17].Exporter = func(v any, i int) any { - switch v := v.(*CreateDeviceRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[18].Exporter = func(v any, i int) any { - switch v := v.(*CreateDeviceResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[19].Exporter = func(v any, i int) any { - switch v := v.(*RateLimitedError); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[20].Exporter = func(v any, i int) any { - switch v := v.(*DeviceQuotaExceededError); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[21].Exporter = func(v any, i int) any { - switch v := v.(*BadRequestError); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[22].Exporter = func(v any, i int) any { - switch v := v.(*AuthenticationFailedError); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_dns_proto_msgTypes[23].Exporter = func(v any, i int) any { - switch v := v.(*RateLimitSettings); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - file_dns_proto_msgTypes[1].OneofWrappers = []any{ + file_dns_proto_msgTypes[3].OneofWrappers = []any{ (*DNSProfile_BlockingModeCustomIp)(nil), (*DNSProfile_BlockingModeNxdomain)(nil), (*DNSProfile_BlockingModeNullIp)(nil), (*DNSProfile_BlockingModeRefused)(nil), } - file_dns_proto_msgTypes[16].OneofWrappers = []any{ + file_dns_proto_msgTypes[18].OneofWrappers = []any{ (*AuthenticationSettings_PasswordHashBcrypt)(nil), } + file_dns_proto_msgTypes[27].OneofWrappers = []any{ + (*RemoteKVGetResponse_Data)(nil), + (*RemoteKVGetResponse_Empty)(nil), + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_dns_proto_rawDesc, NumEnums: 1, - NumMessages: 24, + NumMessages: 30, NumExtensions: 0, - NumServices: 1, + NumServices: 3, }, GoTypes: file_dns_proto_goTypes, DependencyIndexes: file_dns_proto_depIdxs, diff --git a/internal/backendpb/dns.proto b/internal/backendpb/dns.proto index 4653e9d..4c14fb3 100644 --- a/internal/backendpb/dns.proto +++ b/internal/backendpb/dns.proto @@ -45,6 +45,42 @@ service DNSService { rpc createDeviceByHumanId(CreateDeviceRequest) returns (CreateDeviceResponse); } +service RateLimitService { + + /* + Gets rate limit settings. + */ + rpc getRateLimitSettings(RateLimitSettingsRequest) returns (RateLimitSettingsResponse); +} + +service RemoteKVService { + + /** + Get the value for the specified key. + + This method may return the following errors: + - AuthenticationFailedError: If the authentication failed. + */ + rpc get(RemoteKVGetRequest) returns (RemoteKVGetResponse); + + /** + Set the value for the specified key. + + This method may return the following errors: + - AuthenticationFailedError: If the authentication failed. + - BadRequestError: If the request is invalid: value size exceeds the 512kb. + */ + rpc set(RemoteKVSetRequest) returns (RemoteKVSetResponse); +} + +message RateLimitSettingsRequest { + +} + +message RateLimitSettingsResponse { + repeated CidrRange allowed_subnets = 1; +} + message DNSProfilesRequest { google.protobuf.Timestamp sync_time = 1; } @@ -212,3 +248,24 @@ message RateLimitSettings { uint32 rps = 2; repeated CidrRange client_cidr = 3; } + +message RemoteKVGetRequest { + string key = 1; +} + +message RemoteKVGetResponse { + oneof value { + bytes data = 1; + google.protobuf.Empty empty = 2; + } +} + +message RemoteKVSetRequest { + string key = 1; + bytes data = 2; + google.protobuf.Duration ttl = 3; +} + +message RemoteKVSetResponse { + +} diff --git a/internal/backendpb/dns_grpc.pb.go b/internal/backendpb/dns_grpc.pb.go index 4ff7dff..945451c 100644 --- a/internal/backendpb/dns_grpc.pb.go +++ b/internal/backendpb/dns_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v5.27.1 +// - protoc v5.28.3 // source: dns.proto package backendpb @@ -235,3 +235,269 @@ var DNSService_ServiceDesc = grpc.ServiceDesc{ }, Metadata: "dns.proto", } + +const ( + RateLimitService_GetRateLimitSettings_FullMethodName = "/RateLimitService/getRateLimitSettings" +) + +// RateLimitServiceClient is the client API for RateLimitService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type RateLimitServiceClient interface { + // Gets rate limit settings. + GetRateLimitSettings(ctx context.Context, in *RateLimitSettingsRequest, opts ...grpc.CallOption) (*RateLimitSettingsResponse, error) +} + +type rateLimitServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewRateLimitServiceClient(cc grpc.ClientConnInterface) RateLimitServiceClient { + return &rateLimitServiceClient{cc} +} + +func (c *rateLimitServiceClient) GetRateLimitSettings(ctx context.Context, in *RateLimitSettingsRequest, opts ...grpc.CallOption) (*RateLimitSettingsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(RateLimitSettingsResponse) + err := c.cc.Invoke(ctx, RateLimitService_GetRateLimitSettings_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// RateLimitServiceServer is the server API for RateLimitService service. +// All implementations must embed UnimplementedRateLimitServiceServer +// for forward compatibility. +type RateLimitServiceServer interface { + // Gets rate limit settings. + GetRateLimitSettings(context.Context, *RateLimitSettingsRequest) (*RateLimitSettingsResponse, error) + mustEmbedUnimplementedRateLimitServiceServer() +} + +// UnimplementedRateLimitServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedRateLimitServiceServer struct{} + +func (UnimplementedRateLimitServiceServer) GetRateLimitSettings(context.Context, *RateLimitSettingsRequest) (*RateLimitSettingsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetRateLimitSettings not implemented") +} +func (UnimplementedRateLimitServiceServer) mustEmbedUnimplementedRateLimitServiceServer() {} +func (UnimplementedRateLimitServiceServer) testEmbeddedByValue() {} + +// UnsafeRateLimitServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to RateLimitServiceServer will +// result in compilation errors. +type UnsafeRateLimitServiceServer interface { + mustEmbedUnimplementedRateLimitServiceServer() +} + +func RegisterRateLimitServiceServer(s grpc.ServiceRegistrar, srv RateLimitServiceServer) { + // If the following call pancis, it indicates UnimplementedRateLimitServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&RateLimitService_ServiceDesc, srv) +} + +func _RateLimitService_GetRateLimitSettings_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RateLimitSettingsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RateLimitServiceServer).GetRateLimitSettings(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: RateLimitService_GetRateLimitSettings_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RateLimitServiceServer).GetRateLimitSettings(ctx, req.(*RateLimitSettingsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// RateLimitService_ServiceDesc is the grpc.ServiceDesc for RateLimitService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var RateLimitService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "RateLimitService", + HandlerType: (*RateLimitServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "getRateLimitSettings", + Handler: _RateLimitService_GetRateLimitSettings_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "dns.proto", +} + +const ( + RemoteKVService_Get_FullMethodName = "/RemoteKVService/get" + RemoteKVService_Set_FullMethodName = "/RemoteKVService/set" +) + +// RemoteKVServiceClient is the client API for RemoteKVService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type RemoteKVServiceClient interface { + // * + // Get the value for the specified key. + // + // This method may return the following errors: + // - AuthenticationFailedError: If the authentication failed. + Get(ctx context.Context, in *RemoteKVGetRequest, opts ...grpc.CallOption) (*RemoteKVGetResponse, error) + // * + // Set the value for the specified key. + // + // This method may return the following errors: + // - AuthenticationFailedError: If the authentication failed. + // - BadRequestError: If the request is invalid: value size exceeds the 512kb. + Set(ctx context.Context, in *RemoteKVSetRequest, opts ...grpc.CallOption) (*RemoteKVSetResponse, error) +} + +type remoteKVServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewRemoteKVServiceClient(cc grpc.ClientConnInterface) RemoteKVServiceClient { + return &remoteKVServiceClient{cc} +} + +func (c *remoteKVServiceClient) Get(ctx context.Context, in *RemoteKVGetRequest, opts ...grpc.CallOption) (*RemoteKVGetResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(RemoteKVGetResponse) + err := c.cc.Invoke(ctx, RemoteKVService_Get_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *remoteKVServiceClient) Set(ctx context.Context, in *RemoteKVSetRequest, opts ...grpc.CallOption) (*RemoteKVSetResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(RemoteKVSetResponse) + err := c.cc.Invoke(ctx, RemoteKVService_Set_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// RemoteKVServiceServer is the server API for RemoteKVService service. +// All implementations must embed UnimplementedRemoteKVServiceServer +// for forward compatibility. +type RemoteKVServiceServer interface { + // * + // Get the value for the specified key. + // + // This method may return the following errors: + // - AuthenticationFailedError: If the authentication failed. + Get(context.Context, *RemoteKVGetRequest) (*RemoteKVGetResponse, error) + // * + // Set the value for the specified key. + // + // This method may return the following errors: + // - AuthenticationFailedError: If the authentication failed. + // - BadRequestError: If the request is invalid: value size exceeds the 512kb. + Set(context.Context, *RemoteKVSetRequest) (*RemoteKVSetResponse, error) + mustEmbedUnimplementedRemoteKVServiceServer() +} + +// UnimplementedRemoteKVServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedRemoteKVServiceServer struct{} + +func (UnimplementedRemoteKVServiceServer) Get(context.Context, *RemoteKVGetRequest) (*RemoteKVGetResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") +} +func (UnimplementedRemoteKVServiceServer) Set(context.Context, *RemoteKVSetRequest) (*RemoteKVSetResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Set not implemented") +} +func (UnimplementedRemoteKVServiceServer) mustEmbedUnimplementedRemoteKVServiceServer() {} +func (UnimplementedRemoteKVServiceServer) testEmbeddedByValue() {} + +// UnsafeRemoteKVServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to RemoteKVServiceServer will +// result in compilation errors. +type UnsafeRemoteKVServiceServer interface { + mustEmbedUnimplementedRemoteKVServiceServer() +} + +func RegisterRemoteKVServiceServer(s grpc.ServiceRegistrar, srv RemoteKVServiceServer) { + // If the following call pancis, it indicates UnimplementedRemoteKVServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&RemoteKVService_ServiceDesc, srv) +} + +func _RemoteKVService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoteKVGetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RemoteKVServiceServer).Get(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: RemoteKVService_Get_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RemoteKVServiceServer).Get(ctx, req.(*RemoteKVGetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _RemoteKVService_Set_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoteKVSetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RemoteKVServiceServer).Set(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: RemoteKVService_Set_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RemoteKVServiceServer).Set(ctx, req.(*RemoteKVSetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// RemoteKVService_ServiceDesc is the grpc.ServiceDesc for RemoteKVService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var RemoteKVService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "RemoteKVService", + HandlerType: (*RemoteKVServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "get", + Handler: _RemoteKVService_Get_Handler, + }, + { + MethodName: "set", + Handler: _RemoteKVService_Set_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "dns.proto", +} diff --git a/internal/backendpb/profiledb.go b/internal/backendpb/profiledb.go index 5a301cd..25d75e9 100644 --- a/internal/backendpb/profiledb.go +++ b/internal/backendpb/profiledb.go @@ -81,7 +81,7 @@ func NewProfileStorage(c *ProfileStorageConfig) (s *ProfileStorage, err error) { return &ProfileStorage{ bindSet: c.BindSet, errColl: c.ErrColl, - client: client, + client: NewDNSServiceClient(client), logger: c.Logger, metrics: c.Metrics, apiKey: c.APIKey, diff --git a/internal/backendpb/ratelimiter.go b/internal/backendpb/ratelimiter.go new file mode 100644 index 0000000..fa67d14 --- /dev/null +++ b/internal/backendpb/ratelimiter.go @@ -0,0 +1,103 @@ +package backendpb + +import ( + "context" + "fmt" + "log/slog" + "net/url" + + "github.com/AdguardTeam/AdGuardDNS/internal/agdservice" + "github.com/AdguardTeam/AdGuardDNS/internal/consul" + "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit" + "github.com/AdguardTeam/AdGuardDNS/internal/errcoll" +) + +// RateLimiterConfig is the configuration structure for the business logic +// backend rate limiter. +type RateLimiterConfig struct { + // Logger is used for logging the operation of the rate limiter. It must + // not be nil. + Logger *slog.Logger + + // GRPCMetrics is used for the collection of the protobuf errors. + GRPCMetrics Metrics + + // Metrics is used to collect allowlist statistics. + Metrics consul.Metrics + + // Allowlist is the allowlist to update. + Allowlist *ratelimit.DynamicAllowlist + + // ErrColl is used to collect errors during refreshes. + ErrColl errcoll.Interface + + // Endpoint is the backend API URL. The scheme should be either "grpc" or + // "grpcs". It must not be nil. + Endpoint *url.URL + + // APIKey is the API key used for authentication, if any. If empty, no + // authentication is performed. + APIKey string +} + +// RateLimiter is the implementation of the [agdservice.Refresher] interface +// that retrieves the rate limit settings from the business logic backend. +type RateLimiter struct { + logger *slog.Logger + grpcMetrics Metrics + metrics consul.Metrics + allowlist *ratelimit.DynamicAllowlist + errColl errcoll.Interface + client RateLimitServiceClient + apiKey string +} + +// NewRateLimiter creates a new properly initialized rate limiter. c must not +// be nil. +func NewRateLimiter(c *RateLimiterConfig) (l *RateLimiter, err error) { + client, err := newClient(c.Endpoint) + if err != nil { + // Don't wrap the error, because it's informative enough as is. + return nil, err + } + + return &RateLimiter{ + logger: c.Logger, + grpcMetrics: c.GRPCMetrics, + metrics: c.Metrics, + allowlist: c.Allowlist, + errColl: c.ErrColl, + client: NewRateLimitServiceClient(client), + apiKey: c.APIKey, + }, nil +} + +// type check +var _ agdservice.Refresher = (*RateLimiter)(nil) + +// Refresh implements the [agdservice.Refresher] interface for *RateLimiter. +func (l *RateLimiter) Refresh(ctx context.Context) (err error) { + l.logger.InfoContext(ctx, "refresh started") + defer l.logger.InfoContext(ctx, "refresh finished") + + defer func() { l.metrics.SetStatus(ctx, err) }() + + ctx = ctxWithAuthentication(ctx, l.apiKey) + backendResp, err := l.client.GetRateLimitSettings(ctx, &RateLimitSettingsRequest{}) + if err != nil { + return fmt.Errorf( + "loading backend rate limit settings: %w", + fixGRPCError(ctx, l.grpcMetrics, err), + ) + } + + allowedSubnets := backendResp.AllowedSubnets + prefixes := cidrRangeToInternal(ctx, l.errColl, allowedSubnets) + l.allowlist.Update(prefixes) + + l.logger.InfoContext(ctx, "refresh successful", "num_records", len(prefixes)) + + l.metrics.SetSize(ctx, len(prefixes)) + + return nil +} diff --git a/internal/backendpb/ratelimiter_test.go b/internal/backendpb/ratelimiter_test.go new file mode 100644 index 0000000..dc86e7e --- /dev/null +++ b/internal/backendpb/ratelimiter_test.go @@ -0,0 +1,110 @@ +package backendpb_test + +import ( + "context" + "net" + "net/netip" + "net/url" + "testing" + "time" + + "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" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +// testRateLimitServiceServer is the [backendpb.RateLimitServiceServer] for +// tests. +type testRateLimitServiceServer struct { + backendpb.UnimplementedRateLimitServiceServer + + OnGetRateLimitSettings func( + ctx context.Context, + req *backendpb.RateLimitSettingsRequest, + ) (resp *backendpb.RateLimitSettingsResponse, err error) +} + +// type check +var _ backendpb.DNSServiceServer = (*testDNSServiceServer)(nil) + +// GetRateLimitSettings implements the [backendpb.RateLimitServiceServer] +// interface for *testRateLimitServiceServer. +func (s *testRateLimitServiceServer) GetRateLimitSettings( + ctx context.Context, + req *backendpb.RateLimitSettingsRequest, +) (resp *backendpb.RateLimitSettingsResponse, err error) { + return s.OnGetRateLimitSettings(ctx, req) +} + +func TestRateLimiter_Refresh(t *testing.T) { + var ( + allowedIP = netip.MustParseAddr("1.2.3.4") + notAllowedIP = netip.MustParseAddr("4.3.2.1") + + cidr = &backendpb.CidrRange{ + Address: allowedIP.AsSlice(), + Prefix: 32, + } + ) + + srv := &testRateLimitServiceServer{ + OnGetRateLimitSettings: func( + ctx context.Context, + req *backendpb.RateLimitSettingsRequest, + ) (resp *backendpb.RateLimitSettingsResponse, err error) { + return &backendpb.RateLimitSettingsResponse{ + AllowedSubnets: []*backendpb.CidrRange{cidr}, + }, nil + }, + } + + ln, err := net.Listen("tcp", "localhost:0") + require.NoError(t, err) + + grpcSrv := grpc.NewServer( + grpc.ConnectionTimeout(1*time.Second), + grpc.Creds(insecure.NewCredentials()), + ) + backendpb.RegisterRateLimitServiceServer(grpcSrv, srv) + + go func() { + pt := testutil.PanicT{} + + srvErr := grpcSrv.Serve(ln) + require.NoError(pt, srvErr) + }() + t.Cleanup(grpcSrv.GracefulStop) + + allowlist := ratelimit.NewDynamicAllowlist(nil, nil) + l, err := backendpb.NewRateLimiter(&backendpb.RateLimiterConfig{ + Logger: slogutil.NewDiscardLogger(), + Metrics: consul.EmptyMetrics{}, + GRPCMetrics: backendpb.EmptyMetrics{}, + Allowlist: allowlist, + Endpoint: &url.URL{ + Scheme: "grpc", + Host: ln.Addr().String(), + }, + }) + require.NoError(t, err) + + ctx := testutil.ContextWithTimeout(t, testTimeout) + err = l.Refresh(ctx) + require.NoError(t, err) + + ok, err := allowlist.IsAllowed(ctx, allowedIP) + require.NoError(t, err) + + assert.True(t, ok) + + ok, err = allowlist.IsAllowed(ctx, notAllowedIP) + require.NoError(t, err) + + assert.False(t, ok) +} diff --git a/internal/backendpb/remotekv.go b/internal/backendpb/remotekv.go new file mode 100644 index 0000000..09810d4 --- /dev/null +++ b/internal/backendpb/remotekv.go @@ -0,0 +1,96 @@ +package backendpb + +import ( + "context" + "fmt" + "net/url" + "time" + + "github.com/AdguardTeam/AdGuardDNS/internal/remotekv" + "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 + // 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 + + // Endpoint is the backend API URL. The scheme should be either "grpc" or + // "grpcs". + Endpoint *url.URL + + // APIKey is the API key used for authentication, if any. + APIKey string + + // TTL is the TTL of the values in the storage. + TTL time.Duration +} + +// RemoteKV is the implementation of the [remotekv.Interface] interface that +// 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 +} + +// NewRemoteKV returns a new [RemoteKV] that retrieves information from the +// business logic backend. +func NewRemoteKV(c *RemoteKVConfig) (kv *RemoteKV, err error) { + client, err := newClient(c.Endpoint) + if err != nil { + // Don't wrap the error, because it's informative enough as is. + return nil, err + } + + return &RemoteKV{ + metrics: c.Metrics, + client: NewRemoteKVServiceClient(client), + apiKey: c.APIKey, + ttl: c.TTL, + }, nil +} + +// type check +var _ remotekv.Interface = (*RemoteKV)(nil) + +// Get implements the [remotekv.Interface] interface for *RemoteKV. +func (kv *RemoteKV) Get(ctx context.Context, key string) (val []byte, ok bool, err error) { + req := &RemoteKVGetRequest{ + Key: key, + } + + ctx = ctxWithAuthentication(ctx, kv.apiKey) + 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)) + } + + val = resp.GetData() + + return val, val != nil, nil +} + +// Set implements the [remotekv.Interface] interface for *RemoteKV. +func (kv *RemoteKV) Set(ctx context.Context, key string, val []byte) (err error) { + req := &RemoteKVSetRequest{ + Key: key, + Data: val, + Ttl: durationpb.New(kv.ttl), + } + + ctx = ctxWithAuthentication(ctx, kv.apiKey) + _, err = kv.client.Set(ctx, req) + if err != nil { + return fmt.Errorf("setting %q key: %w", key, fixGRPCError(ctx, kv.metrics, err)) + } + + return nil +} diff --git a/internal/backendpb/remotekv_test.go b/internal/backendpb/remotekv_test.go new file mode 100644 index 0000000..612ae7e --- /dev/null +++ b/internal/backendpb/remotekv_test.go @@ -0,0 +1,106 @@ +package backendpb_test + +import ( + "context" + "net" + "net/url" + "testing" + "time" + + "github.com/AdguardTeam/AdGuardDNS/internal/backendpb" + "github.com/AdguardTeam/golibs/testutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +func TestRemoteKV_Get(t *testing.T) { + const testTTL = 10 * time.Second + + pt := &testutil.PanicT{} + + strg := map[string][]byte{} + srv := &testRemoteKVServiceServer{ + OnGet: func( + ctx context.Context, + req *backendpb.RemoteKVGetRequest, + ) (resp *backendpb.RemoteKVGetResponse, err error) { + resp = &backendpb.RemoteKVGetResponse{ + Value: &backendpb.RemoteKVGetResponse_Empty{}, + } + + if val, ok := strg[req.Key]; ok { + resp.Value = &backendpb.RemoteKVGetResponse_Data{Data: val} + } + + return resp, nil + }, + + OnSet: func( + ctx context.Context, + req *backendpb.RemoteKVSetRequest, + ) (resp *backendpb.RemoteKVSetResponse, err error) { + require.Equal(pt, testTTL, req.Ttl.AsDuration()) + + strg[req.Key] = req.Data + + return &backendpb.RemoteKVSetResponse{}, nil + }, + } + + l, err := net.Listen("tcp", "localhost:0") + require.NoError(t, err) + + grpcSrv := grpc.NewServer( + grpc.ConnectionTimeout(1*time.Second), + grpc.Creds(insecure.NewCredentials()), + ) + backendpb.RegisterRemoteKVServiceServer(grpcSrv, srv) + + go func() { + srvErr := grpcSrv.Serve(l) + require.NoError(pt, srvErr) + }() + t.Cleanup(grpcSrv.GracefulStop) + + kv, err := backendpb.NewRemoteKV(&backendpb.RemoteKVConfig{ + Metrics: backendpb.EmptyMetrics{}, + Endpoint: &url.URL{ + Scheme: "grpc", + Host: l.Addr().String(), + }, + APIKey: "apikey", + TTL: testTTL, + }) + require.NoError(t, err) + + const ( + keyWithData = "key" + keyNoData = "unknown" + ) + + t.Run("success", func(t *testing.T) { + val := []byte("value") + ctx := testutil.ContextWithTimeout(t, testTimeout) + + setErr := kv.Set(ctx, keyWithData, val) + require.NoError(t, setErr) + + gotVal, ok, getErr := kv.Get(ctx, keyWithData) + require.NoError(t, getErr) + require.True(t, ok) + + assert.Equal(t, val, gotVal) + }) + + t.Run("not_found", func(t *testing.T) { + ctx := testutil.ContextWithTimeout(t, testTimeout) + + val, ok, getErr := kv.Get(ctx, keyNoData) + require.NoError(t, getErr) + require.False(t, ok) + + assert.Nil(t, val) + }) +} diff --git a/internal/bindtodevice/connindex_linux.go b/internal/bindtodevice/connindex_linux.go index 70614a3..765d85f 100644 --- a/internal/bindtodevice/connindex_linux.go +++ b/internal/bindtodevice/connindex_linux.go @@ -20,23 +20,19 @@ type connIndex struct { } // subnetCompare is a comparison function for the two subnets. It returns -1 if -// x sorts before y, 1 if x sorts after y, and 0 if their relative sorting +// a sorts before b, 1 if a sorts after b, and 0 if their relative sorting // position is the same. -func subnetCompare(x, y netip.Prefix) (cmp int) { - if x == y { - return 0 - } +func subnetCompare(a, b netip.Prefix) (cmp int) { + aAddr, aBits := a.Addr(), a.Bits() + bAddr, bBits := b.Addr(), b.Bits() - xAddr, xBits := x.Addr(), x.Bits() - yAddr, yBits := y.Addr(), y.Bits() - if xBits == yBits { - return xAddr.Compare(yAddr) - } - - if xBits > yBits { + switch { + case aBits > bBits: return -1 - } else { + case aBits < bBits: return 1 + default: + return aAddr.Compare(bAddr) } } @@ -45,8 +41,8 @@ func subnetCompare(x, y netip.Prefix) (cmp int) { // // TODO(a.garipov): Merge with [addListenerChannel]. func (idx *connIndex) addPacketConn(c *chanPacketConn) (err error) { - cmpFunc := func(x, y *chanPacketConn) (cmp int) { - return subnetCompare(x.subnet, y.subnet) + cmpFunc := func(a, b *chanPacketConn) (cmp int) { + return subnetCompare(a.subnet, b.subnet) } newIdx, ok := slices.BinarySearchFunc(idx.packetConns, c, cmpFunc) @@ -67,8 +63,8 @@ func (idx *connIndex) addPacketConn(c *chanPacketConn) (err error) { // // TODO(a.garipov): Merge with [addPacketConnChannel]. func (idx *connIndex) addListener(l *chanListener) (err error) { - cmpFunc := func(x, y *chanListener) (cmp int) { - return subnetCompare(x.subnet, y.subnet) + cmpFunc := func(a, b *chanListener) (cmp int) { + return subnetCompare(a.subnet, b.subnet) } newIdx, ok := slices.BinarySearchFunc(idx.listeners, l, cmpFunc) diff --git a/internal/cmd/backend.go b/internal/cmd/backend.go index 181456f..d37728e 100644 --- a/internal/cmd/backend.go +++ b/internal/cmd/backend.go @@ -6,7 +6,6 @@ import ( "log/slog" "time" - "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp" "github.com/AdguardTeam/AdGuardDNS/internal/backendpb" "github.com/AdguardTeam/AdGuardDNS/internal/billstat" "github.com/AdguardTeam/AdGuardDNS/internal/errcoll" @@ -14,6 +13,7 @@ import ( "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" ) @@ -100,8 +100,9 @@ func newBillStatUploader( mtrc backendpb.Metrics, ) (s billstat.Uploader, err error) { apiURL := netutil.CloneURL(&envs.BillStatURL.URL) - if !agdhttp.CheckGRPCURLScheme(apiURL.Scheme) { - return nil, fmt.Errorf("invalid backend api url: %s", apiURL) + err = urlutil.ValidateGRPCURL(apiURL) + if err != nil { + return nil, fmt.Errorf("billstat api url: %w", err) } return backendpb.NewBillStat(&backendpb.BillStatConfig{ diff --git a/internal/cmd/builder.go b/internal/cmd/builder.go index 7904eb7..a081d7e 100644 --- a/internal/cmd/builder.go +++ b/internal/cmd/builder.go @@ -6,6 +6,7 @@ import ( "log/slog" "maps" "net/netip" + "net/url" "path" "path/filepath" "slices" @@ -14,7 +15,6 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/access" "github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/agdcache" - "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp" "github.com/AdguardTeam/AdGuardDNS/internal/agdservice" "github.com/AdguardTeam/AdGuardDNS/internal/backendpb" "github.com/AdguardTeam/AdGuardDNS/internal/billstat" @@ -38,9 +38,11 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/profiledb" "github.com/AdguardTeam/AdGuardDNS/internal/querylog" "github.com/AdguardTeam/AdGuardDNS/internal/rulestat" + "github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig" "github.com/AdguardTeam/AdGuardDNS/internal/websvc" "github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/netutil" + "github.com/AdguardTeam/golibs/netutil/urlutil" "github.com/AdguardTeam/golibs/osutil" "github.com/AdguardTeam/golibs/service" "github.com/c2h5oh/datasize" @@ -112,6 +114,8 @@ type builder struct { ruleStat rulestat.Interface safeBrowsing *hashprefix.Filter safeBrowsingHashes *hashprefix.Storage + sdeConf *dnsmsg.StructuredDNSErrorsConfig + tlsMtrc tlsconfig.Metrics webSvc *websvc.Service // The fields below are initialized later, just like with the fields above, @@ -556,12 +560,42 @@ func (b *builder) initBindToDevice(ctx context.Context) (err error) { return nil } +// Constants for the experimental Structured DNS Errors feature. +// +// TODO(a.garipov): Make configurable. +const ( + sdeJustification = "Filtered by AdGuard DNS" + sdeOrganization = "AdGuard DNS" +) + +// Variables for the experimental Structured DNS Errors feature. +// +// TODO(a.garipov): Make configurable. +var ( + sdeContactURL = &url.URL{ + Scheme: "mailto", + Opaque: "support@adguard-dns.io", + } +) + // initMsgConstructor initializes the common DNS message constructor. func (b *builder) initMsgConstructor(ctx context.Context) (err error) { + fltConf := b.conf.Filters + b.sdeConf = &dnsmsg.StructuredDNSErrorsConfig{ + Contact: []*url.URL{ + sdeContactURL, + }, + Justification: sdeJustification, + Organization: sdeOrganization, + Enabled: fltConf.SDEEnabled, + } + b.messages, err = dnsmsg.NewConstructor(&dnsmsg.ConstructorConfig{ Cloner: b.cloner, BlockingMode: &dnsmsg.BlockingModeNullIP{}, - FilteredResponseTTL: b.conf.Filters.ResponseTTL.Duration, + StructuredErrors: b.sdeConf, + FilteredResponseTTL: fltConf.ResponseTTL.Duration, + EDEEnabled: fltConf.EDEEnabled, }) if err != nil { return fmt.Errorf("creating dns message constructor: %w", err) @@ -579,8 +613,17 @@ func (b *builder) initMsgConstructor(ctx context.Context) (err error) { // - [builder.initFilteringGroups] // - [builder.initMsgConstructor] 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.filteringGroups, @@ -671,7 +714,7 @@ func (b *builder) initTLS(ctx context.Context) (err error) { } } - tickRot := newTicketRotator(b.baseLogger, b.errColl, b.serverGroups) + tickRot := newTicketRotator(b.baseLogger, b.errColl, b.tlsMtrc, b.serverGroups) err = tickRot.Refresh(ctx) if err != nil { return fmt.Errorf("initial session ticket refresh: %w", err) @@ -700,6 +743,29 @@ func (b *builder) initTLS(ctx context.Context) (err error) { return nil } +// initGRPCMetrics initializes the gRPC metrics if necessary. +func (b *builder) initGRPCMetrics(ctx context.Context) (err error) { + switch { + case + b.profilesEnabled, + b.conf.Check.RemoteKV.Type == kvModeBackend, + b.conf.RateLimit.Allowlist.Type == rlAllowlistTypeBackend: + // Go on. + default: + // Don't initialize the metrics if no protobuf backend is used. + return nil + } + + b.backendGRPCMtrc, err = metrics.NewBackendPB(b.mtrcNamespace, b.promRegisterer) + if err != nil { + return fmt.Errorf("registering backendbp metrics: %w", err) + } + + b.logger.DebugContext(ctx, "initialized grpc metrics") + + return nil +} + // initBillStat initializes the billing-statistics recorder if necessary. It // also adds the refresher with ID [debugIDBillStat] to the debug refreshers. func (b *builder) initBillStat(ctx context.Context) (err error) { @@ -709,11 +775,6 @@ func (b *builder) initBillStat(ctx context.Context) (err error) { return nil } - b.backendGRPCMtrc, err = metrics.NewBackendPB(b.mtrcNamespace, b.promRegisterer) - if err != nil { - return fmt.Errorf("registering backendbp metrics: %w", err) - } - upl, err := newBillStatUploader(b.env, b.errColl, b.backendGRPCMtrc) if err != nil { return fmt.Errorf("creating billstat uploader: %w", err) @@ -760,9 +821,9 @@ func (b *builder) initBillStat(ctx context.Context) (err error) { // initProfileDB initializes the profile database if necessary. // -// [builder.initBillStat] and [builder.initServerGroups] must be called before -// this method. It also adds the refresher with ID [debugIDProfileDB] to the -// debug refreshers. +// [builder.initGRPCMetrics] and [builder.initServerGroups] 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{} @@ -771,8 +832,9 @@ func (b *builder) initProfileDB(ctx context.Context) (err error) { } apiURL := netutil.CloneURL(&b.env.ProfilesURL.URL) - if !agdhttp.CheckGRPCURLScheme(apiURL.Scheme) { - return fmt.Errorf("invalid backend api url: %s", apiURL) + err = urlutil.ValidateGRPCURL(apiURL) + if err != nil { + return fmt.Errorf("profile api url: %w", err) } respSzEst := b.conf.RateLimit.ResponseSizeEstimate @@ -842,7 +904,8 @@ func (b *builder) initProfileDB(ctx context.Context) (err error) { // initDNSCheck initializes the DNS checker. // -// [builder.initMsgConstructor] must be called before this method. +// [builder.initGRPCMetrics] and [builder.initMsgConstructor] must be called +// before this method. func (b *builder) initDNSCheck(ctx context.Context) (err error) { b.dnsCheck = b.plugins.DNSCheck() if b.dnsCheck != nil { @@ -853,7 +916,14 @@ func (b *builder) initDNSCheck(ctx context.Context) (err error) { c := b.conf.Check - checkConf, err := c.toInternal(b.env, b.messages, b.errColl, b.mtrcNamespace, b.promRegisterer) + checkConf, err := c.toInternal( + b.env, + b.messages, + b.errColl, + b.mtrcNamespace, + b.promRegisterer, + b.backendGRPCMtrc, + ) if err != nil { return fmt.Errorf("initializing dnscheck: %w", err) } @@ -911,18 +981,44 @@ func (b *builder) initRuleStat(ctx context.Context) (err error) { // well as starts and registers the rate-limiter refresher in the signal // handler. It also adds the refresher with ID [debugIDAllowlist] to the debug // refreshers. +// +// [builder.initGRPCMetrics] must be called before this method. func (b *builder) initRateLimiter(ctx context.Context) (err error) { c := b.conf.RateLimit allowSubnets := netutil.UnembedPrefixes(c.Allowlist.List) allowlist := ratelimit.NewDynamicAllowlist(allowSubnets, nil) - updater := consul.NewAllowlistUpdater(&consul.AllowlistUpdaterConfig{ - Logger: b.baseLogger.With(slogutil.KeyPrefix, "ratelimit_allowlist_updater"), - Allowlist: allowlist, - ConsulURL: &b.env.ConsulAllowlistURL.URL, - ErrColl: b.errColl, - // TODO(a.garipov): Make configurable. - Timeout: 15 * time.Second, - }) + + typ := b.conf.RateLimit.Allowlist.Type + mtrc, err := metrics.NewAllowlist(b.mtrcNamespace, b.promRegisterer, typ) + if err != nil { + return fmt.Errorf("ratelimit metrics: %w", err) + } + + var updater agdservice.Refresher + if typ == rlAllowlistTypeBackend { + updater, err = backendpb.NewRateLimiter(&backendpb.RateLimiterConfig{ + Logger: b.baseLogger.With(slogutil.KeyPrefix, "backend_ratelimiter"), + Metrics: mtrc, + GRPCMetrics: b.backendGRPCMtrc, + Allowlist: allowlist, + Endpoint: &b.env.BackendRateLimitURL.URL, + ErrColl: b.errColl, + APIKey: b.env.BackendRateLimitAPIKey, + }) + if err != nil { + return fmt.Errorf("ratelimit: %w", err) + } + } else { + updater = consul.NewAllowlistUpdater(&consul.AllowlistUpdaterConfig{ + Logger: b.baseLogger.With(slogutil.KeyPrefix, "ratelimit_allowlist_updater"), + Allowlist: allowlist, + ConsulURL: &b.env.ConsulAllowlistURL.URL, + ErrColl: b.errColl, + Metrics: mtrc, + // TODO(a.garipov): Make configurable. + Timeout: 15 * time.Second, + }) + } err = updater.Refresh(ctx) if err != nil { @@ -956,9 +1052,11 @@ 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. func (b *builder) initWeb(ctx context.Context) (err error) { c := b.conf.Web - webConf, err := c.toInternal(b.env, b.dnsCheck, b.errColl) + webConf, err := c.toInternal(ctx, b.env, b.dnsCheck, b.errColl, b.tlsMtrc) if err != nil { return fmt.Errorf("converting web configuration: %w", err) } @@ -1057,45 +1155,55 @@ 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) - cacheConf := b.conf.Cache - dnsConf := &dnssvc.Config{ + dnsHdlrsConf := &dnssvc.HandlersConfig{ BaseLogger: b.baseLogger, - Messages: b.messages, + Cache: b.conf.Cache.toInternal(), Cloner: b.cloner, - ControlConf: b.controlConf, - ConnLimiter: b.connLimit, HumanIDParser: agd.NewHumanIDParser(), + Messages: b.messages, PluginRegistry: b.plugins, + StructuredErrors: b.sdeConf, AccessManager: b.access, - SafeBrowsing: b.hashMatcher, BillStat: b.billStat, CacheManager: b.cacheManager, - ProfileDB: b.profileDB, - PrometheusRegisterer: b.promRegisterer, DNSCheck: b.dnsCheck, - NonDNS: b.webSvc, DNSDB: b.dnsDB, ErrColl: b.errColl, FilterStorage: b.filterStorage, GeoIP: b.geoIP, Handler: b.fwdHandler, + HashMatcher: b.hashMatcher, + ProfileDB: b.profileDB, + PrometheusRegisterer: b.promRegisterer, QueryLog: b.queryLog(), - RuleStat: b.ruleStat, RateLimit: b.rateLimit, + RuleStat: b.ruleStat, MetricsNamespace: b.mtrcNamespace, FilteringGroups: b.filteringGroups, ServerGroups: b.serverGroups, - HandleTimeout: b.conf.DNS.HandleTimeout.Duration, - CacheSize: cacheConf.Size, - ECSCacheSize: cacheConf.ECSSize, - CacheMinTTL: cacheConf.TTLOverride.Min.Duration, - UseCacheTTLOverride: cacheConf.TTLOverride.Enabled, - UseECSCache: cacheConf.Type == cacheTypeECS, + EDEEnabled: b.conf.Filters.EDEEnabled, + } + + dnsHdlrs, err := dnssvc.NewHandlers(ctx, dnsHdlrsConf) + if err != nil { + return fmt.Errorf("dns handlers: %w", err) + } + + dnsConf := &dnssvc.Config{ + Handlers: dnsHdlrs, + Cloner: b.cloner, + ControlConf: b.controlConf, + ConnLimiter: b.connLimit, + NonDNS: b.webSvc, + ErrColl: b.errColl, + MetricsNamespace: b.mtrcNamespace, + ServerGroups: b.serverGroups, + HandleTimeout: b.conf.DNS.HandleTimeout.Duration, } b.dnsSvc, err = dnssvc.New(dnsConf) if err != nil { - return fmt.Errorf("initializing dns: %w", err) + return fmt.Errorf("dns service: %w", err) } b.logger.DebugContext(ctx, "initialized dns") diff --git a/internal/cmd/cache.go b/internal/cmd/cache.go index fd9d483..ef222a7 100644 --- a/internal/cmd/cache.go +++ b/internal/cmd/cache.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" + "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc" "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/timeutil" ) @@ -43,6 +44,28 @@ const ( cacheTypeSimple = "simple" ) +// toInternal converts c to the cache configuration for the DNS server. c must +// be valid. +func (c *cacheConfig) toInternal() (cacheConf *dnssvc.CacheConfig) { + var typ dnssvc.CacheType + if c.Size == 0 { + // TODO(a.garipov): Add as a type in the configuration file. + typ = dnssvc.CacheTypeNone + } else if c.Type == cacheTypeSimple { + typ = dnssvc.CacheTypeSimple + } else { + typ = dnssvc.CacheTypeECS + } + + return &dnssvc.CacheConfig{ + MinTTL: c.TTLOverride.Min.Duration, + ECSCount: c.ECSSize, + NoECSCount: c.Size, + Type: typ, + OverrideCacheTTL: c.TTLOverride.Enabled, + } +} + // type check var _ validator = (*cacheConfig)(nil) diff --git a/internal/cmd/check.go b/internal/cmd/check.go index 5075cf8..142f771 100644 --- a/internal/cmd/check.go +++ b/internal/cmd/check.go @@ -7,6 +7,7 @@ import ( "time" "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp" + "github.com/AdguardTeam/AdGuardDNS/internal/backendpb" "github.com/AdguardTeam/AdGuardDNS/internal/dnscheck" "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "github.com/AdguardTeam/AdGuardDNS/internal/errcoll" @@ -54,8 +55,9 @@ func (c *checkConfig) toInternal( errColl errcoll.Interface, namespace string, reg prometheus.Registerer, + backendMtrc backendpb.Metrics, ) (conf *dnscheck.RemoteKVConfig, err error) { - kv, err := newDNSCheckKV(c, envs, namespace, reg) + kv, err := newRemoteKV(c.RemoteKV, envs, namespace, reg, backendMtrc) if err != nil { // Don't wrap the error, because it's informative enough as is. return nil, err @@ -85,21 +87,33 @@ const maxRespSize = 1 * datasize.MB // [remotekv.KeyNamespace]. const keyNamespaceCheck = "check" -// newDNSCheckKV returns a new properly initialized remote key-value storage. -func newDNSCheckKV( - conf *checkConfig, +// newRemoteKV returns a new properly initialized remote key-value storage. +func newRemoteKV( + c *remoteKVConfig, envs *environment, namespace string, reg prometheus.Registerer, + backendMtrc backendpb.Metrics, ) (kv remotekv.Interface, err error) { - if conf.RemoteKV.Type == kvModeRedis { + switch c.Type { + case kvModeBackend: + kv, err = backendpb.NewRemoteKV(&backendpb.RemoteKVConfig{ + Metrics: backendMtrc, + Endpoint: &envs.DNSCheckRemoteKVURL.URL, + APIKey: envs.DNSCheckRemoteKVAPIKey, + TTL: c.TTL.Duration, + }) + if err != nil { + return nil, fmt.Errorf("initializing backend dnscheck kv: %w", err) + } + case kvModeRedis: var redisKVMtrc rediskv.Metrics redisKVMtrc, err = metrics.NewRedisKV(namespace, reg) if err != nil { return nil, fmt.Errorf("registering redis kv metrics: %w", err) } - kv := rediskv.NewRedisKV(&rediskv.RedisKVConfig{ + redisKV := rediskv.NewRedisKV(&rediskv.RedisKVConfig{ Metrics: redisKVMtrc, Addr: &netutil.HostPort{ Host: envs.RedisAddr, @@ -108,18 +122,20 @@ func newDNSCheckKV( MaxActive: envs.RedisMaxActive, MaxIdle: envs.RedisMaxIdle, IdleTimeout: envs.RedisIdleTimeout.Duration, - TTL: conf.RemoteKV.TTL.Duration, + TTL: c.TTL.Duration, }) - return remotekv.NewKeyNamespace(&remotekv.KeyNamespaceConfig{ - KV: kv, + kv = remotekv.NewKeyNamespace(&remotekv.KeyNamespaceConfig{ + KV: redisKV, Prefix: fmt.Sprintf("%s:%s:", envs.RedisKeyPrefix, keyNamespaceCheck), - }), nil - } + }) + case kvModeConsul: + consulKVURL := envs.ConsulDNSCheckKVURL + consulSessionURL := envs.ConsulDNSCheckSessionURL + if consulKVURL == nil || consulSessionURL == nil { + return remotekv.Empty{}, nil + } - consulKVURL := envs.ConsulDNSCheckKVURL - consulSessionURL := envs.ConsulDNSCheckSessionURL - if consulKVURL != nil && consulSessionURL != nil { kv, err = consulkv.NewKV(&consulkv.Config{ URL: &consulKVURL.URL, SessionURL: &consulSessionURL.URL, @@ -129,14 +145,14 @@ func newDNSCheckKV( }), // TODO(ameshkov): Consider making configurable. Limiter: rate.NewLimiter(rate.Limit(200)/60, 1), - TTL: conf.RemoteKV.TTL.Duration, + TTL: c.TTL.Duration, MaxRespSize: maxRespSize, }) if err != nil { - return nil, fmt.Errorf("initializing consul dnscheck: %w", err) + return nil, fmt.Errorf("initializing consul dnscheck kv: %w", err) } - } else { - kv = remotekv.Empty{} + default: + return remotekv.Empty{}, nil } return kv, nil @@ -221,15 +237,16 @@ func validateNonNilIPs(ips []netip.Addr, fam netutil.AddrFamily) (err error) { // DNSCheck key-value database modes. const ( - kvModeConsul = "consul" - kvModeRedis = "redis" + kvModeBackend = "backend" + kvModeConsul = "consul" + kvModeRedis = "redis" ) // remoteKVConfig is remote key-value store configuration for DNS server // checking. type remoteKVConfig struct { // Type defines the type of remote key-value store. Allowed values are - // [kvModeConsul] and [kvModeRedis]. + // [kvModeBackend], [kvModeConsul] and [kvModeRedis]. Type string `yaml:"type"` // TTL defines, for how long to keep the information about a single client. @@ -248,6 +265,10 @@ func (c *remoteKVConfig) validate() (err error) { ttl := c.TTL switch c.Type { + case kvModeBackend: + if ttl.Duration <= 0 { + return newNotPositiveError("ttl", ttl) + } case kvModeConsul: if ttl.Duration < consulkv.MinTTL || ttl.Duration > consulkv.MaxTTL { return fmt.Errorf( @@ -270,7 +291,7 @@ func (c *remoteKVConfig) validate() (err error) { case "": return fmt.Errorf("type: %w", errors.ErrEmptyValue) default: - return fmt.Errorf("type: %q: %w", c.Type, errors.ErrBadEnumValue) + return fmt.Errorf("type: %w: %q", errors.ErrBadEnumValue, c.Type) } return nil diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go index ad342fc..f66aba5 100644 --- a/internal/cmd/cmd.go +++ b/internal/cmd/cmd.go @@ -100,6 +100,8 @@ func Main(plugins *plugin.Registry) { errors.Check(b.initTLS(ctx)) + errors.Check(b.initGRPCMetrics(ctx)) + errors.Check(b.initBillStat(ctx)) errors.Check(b.initProfileDB(ctx)) diff --git a/internal/cmd/env.go b/internal/cmd/env.go index a469de2..ff5f027 100644 --- a/internal/cmd/env.go +++ b/internal/cmd/env.go @@ -6,9 +6,10 @@ import ( "math" "net" "net/http" + "net/url" "os" + "strings" - "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp" "github.com/AdguardTeam/AdGuardDNS/internal/debugsvc" "github.com/AdguardTeam/AdGuardDNS/internal/dnsdb" "github.com/AdguardTeam/AdGuardDNS/internal/errcoll" @@ -25,13 +26,17 @@ import ( ) // environment represents the configuration that is kept in the environment. +// +// TODO(e.burkov, a.garipov): Name variables more consistently. type environment struct { AdultBlockingURL *urlutil.URL `env:"ADULT_BLOCKING_URL"` + BackendRateLimitURL *urlutil.URL `env:"BACKEND_RATELIMIT_URL"` BillStatURL *urlutil.URL `env:"BILLSTAT_URL"` BlockedServiceIndexURL *urlutil.URL `env:"BLOCKED_SERVICE_INDEX_URL"` - ConsulAllowlistURL *urlutil.URL `env:"CONSUL_ALLOWLIST_URL,notEmpty"` + ConsulAllowlistURL *urlutil.URL `env:"CONSUL_ALLOWLIST_URL"` ConsulDNSCheckKVURL *urlutil.URL `env:"CONSUL_DNSCHECK_KV_URL"` ConsulDNSCheckSessionURL *urlutil.URL `env:"CONSUL_DNSCHECK_SESSION_URL"` + DNSCheckRemoteKVURL *urlutil.URL `env:"DNSCHECK_REMOTEKV_URL"` FilterIndexURL *urlutil.URL `env:"FILTER_INDEX_URL,notEmpty"` GeneralSafeSearchURL *urlutil.URL `env:"GENERAL_SAFE_SEARCH_URL"` LinkedIPTargetURL *urlutil.URL `env:"LINKED_IP_TARGET_URL"` @@ -41,23 +46,25 @@ type environment struct { SafeBrowsingURL *urlutil.URL `env:"SAFE_BROWSING_URL"` YoutubeSafeSearchURL *urlutil.URL `env:"YOUTUBE_SAFE_SEARCH_URL"` - BillStatAPIKey string `env:"BILLSTAT_API_KEY"` - ConfPath string `env:"CONFIG_PATH" envDefault:"./config.yaml"` - FilterCachePath string `env:"FILTER_CACHE_PATH" envDefault:"./filters/"` - GeoIPASNPath string `env:"GEOIP_ASN_PATH" envDefault:"./asn.mmdb"` - GeoIPCountryPath string `env:"GEOIP_COUNTRY_PATH" envDefault:"./country.mmdb"` - ProfilesAPIKey string `env:"PROFILES_API_KEY"` - ProfilesCachePath string `env:"PROFILES_CACHE_PATH" envDefault:"./profilecache.pb"` - RedisAddr string `env:"REDIS_ADDR"` - RedisKeyPrefix string `env:"REDIS_KEY_PREFIX" envDefault:"agdns"` - QueryLogPath string `env:"QUERYLOG_PATH" envDefault:"./querylog.jsonl"` - SSLKeyLogFile string `env:"SSL_KEY_LOG_FILE"` - SentryDSN string `env:"SENTRY_DSN" envDefault:"stderr"` - WebStaticDir string `env:"WEB_STATIC_DIR"` + BackendRateLimitAPIKey string `env:"BACKEND_RATELIMIT_API_KEY"` + BillStatAPIKey string `env:"BILLSTAT_API_KEY"` + ConfPath string `env:"CONFIG_PATH" envDefault:"./config.yaml"` + DNSCheckRemoteKVAPIKey string `env:"DNSCHECK_REMOTEKV_API_KEY"` + FilterCachePath string `env:"FILTER_CACHE_PATH" envDefault:"./filters/"` + GeoIPASNPath string `env:"GEOIP_ASN_PATH" envDefault:"./asn.mmdb"` + GeoIPCountryPath string `env:"GEOIP_COUNTRY_PATH" envDefault:"./country.mmdb"` + ProfilesAPIKey string `env:"PROFILES_API_KEY"` + ProfilesCachePath string `env:"PROFILES_CACHE_PATH" envDefault:"./profilecache.pb"` + RedisAddr string `env:"REDIS_ADDR"` + RedisKeyPrefix string `env:"REDIS_KEY_PREFIX" envDefault:"agdns"` + QueryLogPath string `env:"QUERYLOG_PATH" envDefault:"./querylog.jsonl"` + SSLKeyLogFile string `env:"SSL_KEY_LOG_FILE"` + SentryDSN string `env:"SENTRY_DSN" envDefault:"stderr"` + WebStaticDir string `env:"WEB_STATIC_DIR"` ListenAddr net.IP `env:"LISTEN_ADDR" envDefault:"127.0.0.1"` - ProfilesMaxRespSize datasize.ByteSize `env:"PROFILES_MAX_RESP_SIZE" envDefault:"8MB"` + ProfilesMaxRespSize datasize.ByteSize `env:"PROFILES_MAX_RESP_SIZE" envDefault:"64MB"` RedisIdleTimeout timeutil.Duration `env:"REDIS_IDLE_TIMEOUT" envDefault:"30s"` @@ -100,7 +107,8 @@ func (envs *environment) validate() (err error) { errs = envs.validateHTTPURLs(errs) - if s := envs.FilterIndexURL.Scheme; s != agdhttp.SchemeFile && !agdhttp.CheckHTTPURLScheme(s) { + if s := envs.FilterIndexURL.Scheme; !strings.EqualFold(s, urlutil.SchemeFile) && + !urlutil.IsValidHTTPURLScheme(s) { errs = append(errs, fmt.Errorf( "env %s: not a valid http(s) url or file uri", "FILTER_INDEX_URL", @@ -140,10 +148,6 @@ func (envs *environment) validateHTTPURLs(errs []error) (res []error) { url: envs.BlockedServiceIndexURL, name: "BLOCKED_SERVICE_INDEX_URL", isRequired: bool(envs.BlockedServiceEnabled), - }, { - url: envs.ConsulAllowlistURL, - name: "CONSUL_ALLOWLIST_URL", - isRequired: true, }, { url: envs.ConsulDNSCheckKVURL, name: "CONSUL_DNSCHECK_KV_URL", @@ -184,15 +188,14 @@ func (envs *environment) validateHTTPURLs(errs []error) (res []error) { continue } - u := urlData.url - if u == nil { - res = append(res, fmt.Errorf("env %s: %w", urlData.name, errors.ErrEmptyValue)) - - continue + var u *url.URL + if urlData.url != nil { + u = &urlData.url.URL } - if !agdhttp.CheckHTTPURLScheme(u.Scheme) { - res = append(res, fmt.Errorf("env %s: not a valid http(s) url", urlData.name)) + err := urlutil.ValidateHTTPURL(u) + if err != nil { + res = append(res, fmt.Errorf("env %s: %w", urlData.name, err)) } } @@ -227,59 +230,86 @@ func (envs *environment) validateWebStaticDir() (err error) { // validateFromValidConfig returns an error if environment variables that depend // on configuration properties contain errors. conf is expected to be valid. func (envs *environment) validateFromValidConfig(conf *configuration) (err error) { - err = envs.validateRedis(conf) - if err != nil { - // Don't wrap the error, because it's informative enough as is. - return err - } - - if !conf.isProfilesEnabled() { - return nil - } - - if envs.ProfilesMaxRespSize > math.MaxInt { - return fmt.Errorf( - "PROFILES_MAX_RESP_SIZE: %w: must be less than or equal to %s, got %s", - errors.ErrOutOfRange, - datasize.ByteSize(math.MaxInt), - envs.ProfilesMaxRespSize, - ) - } - - return envs.validateProfilesURLs() -} - -// validateRedis returns an error if environment variables for Redis as a remote -// key-value store for DNS server checking contain errors. -func (envs *environment) validateRedis(conf *configuration) (err error) { - if conf.Check.RemoteKV.Type != kvModeRedis { - return nil - } - var errs []error - if envs.RedisAddr == "" { - errs = append(errs, fmt.Errorf("REDIS_ADDR: %q", errors.ErrEmptyValue)) + + switch typ := conf.Check.RemoteKV.Type; typ { + case kvModeRedis: + errs = envs.validateRedis(errs) + case kvModeBackend: + errs = envs.validateBackendKV(errs) + default: + // Probably consul. } - if envs.RedisIdleTimeout.Duration <= 0 { - errs = append(errs, newNotPositiveError("REDIS_IDLE_TIMEOUT", envs.RedisIdleTimeout)) + if conf.isProfilesEnabled() { + errs = envs.validateProfilesURLs(errs) + + if envs.ProfilesMaxRespSize > math.MaxInt { + errs = append(errs, fmt.Errorf( + "PROFILES_MAX_RESP_SIZE: %w: must be less than or equal to %s, got %s", + errors.ErrOutOfRange, + datasize.ByteSize(math.MaxInt), + envs.ProfilesMaxRespSize, + )) + } } - if envs.RedisMaxActive < 0 { - errs = append(errs, newNegativeError("REDIS_MAX_ACTIVE", envs.RedisMaxActive)) - } - - if envs.RedisMaxIdle < 0 { - errs = append(errs, newNegativeError("REDIS_MAX_IDLE", envs.RedisMaxIdle)) - } + errs = envs.validateRateLimitURLs(conf, errs) return errors.Join(errs...) } +// 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 + + if envs.RedisAddr == "" { + err := fmt.Errorf("REDIS_ADDR: %q", errors.ErrEmptyValue) + withRedis = append(withRedis, err) + } + + if envs.RedisIdleTimeout.Duration <= 0 { + err := newNotPositiveError("REDIS_IDLE_TIMEOUT", envs.RedisIdleTimeout) + withRedis = append(withRedis, err) + } + + if envs.RedisMaxActive < 0 { + err := newNegativeError("REDIS_MAX_ACTIVE", envs.RedisMaxActive) + withRedis = append(withRedis, err) + } + + if envs.RedisMaxIdle < 0 { + err := newNegativeError("REDIS_MAX_IDLE", envs.RedisMaxIdle) + withRedis = append(withRedis, err) + } + + return withRedis +} + +// 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 + + var u *url.URL + if envs.DNSCheckRemoteKVURL != nil { + u = &envs.DNSCheckRemoteKVURL.URL + } + + err := urlutil.ValidateGRPCURL(u) + if err != nil { + withKV = append(withKV, fmt.Errorf("env DNSCHECK_REMOTEKV_URL: %w", err)) + } + + return withKV +} + // validateProfilesURLs appends validation errors to the given errs if profiles -// URLs in environment variables are invalid. All errors are appended to errs -// and returned as res. -func (envs *environment) validateProfilesURLs() (err error) { +// URLs in environment variables are invalid. +func (envs *environment) validateProfilesURLs(errs []error) (withURLs []error) { + withURLs = errs + grpcOnlyURLs := []*urlEnvData{{ url: envs.BillStatURL, name: "BILLSTAT_URL", @@ -290,24 +320,52 @@ func (envs *environment) validateProfilesURLs() (err error) { isRequired: true, }} - var res []error for _, urlData := range grpcOnlyURLs { if !urlData.isRequired { continue } - if urlData.url == nil { - res = append(res, fmt.Errorf("env %s: %w", urlData.name, errors.ErrEmptyValue)) - - continue + var u *url.URL + if urlData.url != nil { + u = &urlData.url.URL } - if !agdhttp.CheckGRPCURLScheme(urlData.url.Scheme) { - res = append(res, fmt.Errorf("env %s: not a valid grpc(s) url", urlData.name)) + err := urlutil.ValidateGRPCURL(u) + if err != nil { + withURLs = append(withURLs, fmt.Errorf("env %s: %w", urlData.name, err)) } } - return errors.Join(res...) + return withURLs +} + +// validateRateLimitURLs appends validation errors to the given errs if rate +// limit URLs in environment variables are invalid. +func (envs *environment) validateRateLimitURLs( + conf *configuration, + errs []error, +) (withURLs []error) { + rlURL := envs.BackendRateLimitURL + rlEnv := "BACKEND_RATELIMIT_URL" + validateFunc := urlutil.ValidateGRPCURL + + if conf.RateLimit.Allowlist.Type == rlAllowlistTypeConsul { + rlURL = envs.ConsulAllowlistURL + rlEnv = "CONSUL_ALLOWLIST_URL" + validateFunc = urlutil.ValidateHTTPURL + } + + var u *url.URL + if rlURL != nil { + u = &rlURL.URL + } + + err := validateFunc(u) + if err != nil { + return append(errs, fmt.Errorf("env %s: %w", rlEnv, err)) + } + + return errs } // configureLogs sets the configuration for the plain text logs. It also diff --git a/internal/cmd/filter.go b/internal/cmd/filter.go index c7e8acf..ee27585 100644 --- a/internal/cmd/filter.go +++ b/internal/cmd/filter.go @@ -58,6 +58,12 @@ type filtersConfig struct { // MaxSize is the maximum size of the downloadable filtering rule-list. MaxSize datasize.ByteSize `yaml:"max_size"` + + // EDEEnabled enables the Extended DNS Errors feature. + EDEEnabled bool `yaml:"ede_enabled"` + + // SDEEnabled enables the experimental Structured DNS Errors feature. + SDEEnabled bool `yaml:"sde_enabled"` } // toInternal converts c to the filter storage configuration for the DNS server. @@ -117,33 +123,31 @@ var _ validator = (*filtersConfig)(nil) // validate implements the [validator] interface for *filtersConfig. func (c *filtersConfig) validate() (err error) { - switch { - case c == nil: + if c == nil { return errors.ErrNoValue - case c.SafeSearchCacheSize <= 0: - return newNotPositiveError("safe_search_cache_size", c.SafeSearchCacheSize) - case c.ResponseTTL.Duration <= 0: - return newNotPositiveError("response_ttl", c.ResponseTTL) - case c.RefreshIvl.Duration <= 0: - return newNotPositiveError("refresh_interval", c.RefreshIvl) - case c.RefreshTimeout.Duration <= 0: - return newNotPositiveError("refresh_timeout", c.RefreshTimeout) - case c.IndexRefreshTimeout.Duration <= 0: - return newNotPositiveError("index_refresh_timeout", c.IndexRefreshTimeout) - case c.RuleListRefreshTimeout.Duration <= 0: - return newNotPositiveError("rule_list_refresh_timeout", c.RuleListRefreshTimeout) - case c.MaxSize <= 0: - return newNotPositiveError("max_size", c.MaxSize) - default: - // Go on. + } + + errs := []error{ + validatePositive("custom_filter_cache_size", c.CustomFilterCacheSize), + validatePositive("safe_search_cache_size", c.SafeSearchCacheSize), + validatePositive("response_ttl", c.ResponseTTL), + validatePositive("refresh_interval", c.RefreshIvl), + validatePositive("refresh_timeout", c.RefreshTimeout), + validatePositive("index_refresh_timeout", c.IndexRefreshTimeout), + validatePositive("rule_list_refresh_timeout", c.RuleListRefreshTimeout), + validatePositive("max_size", c.MaxSize), + } + + if !c.EDEEnabled && c.SDEEnabled { + errs = append(errs, errors.Error("ede must be enabled to enable sde")) } err = c.RuleListCache.validate() if err != nil { - return fmt.Errorf("rule_list_cache: %w", err) + errs = append(errs, fmt.Errorf("rule_list_cache: %w", err)) } - return nil + return errors.Join(errs...) } // fltRuleListCache contains filtering rule-list cache configuration. diff --git a/internal/cmd/plugin/plugin.go b/internal/cmd/plugin/plugin.go index 1405247..e3f2b1b 100644 --- a/internal/cmd/plugin/plugin.go +++ b/internal/cmd/plugin/plugin.go @@ -4,6 +4,7 @@ package plugin import ( "github.com/AdguardTeam/AdGuardDNS/internal/dnscheck" + "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver" "github.com/AdguardTeam/AdGuardDNS/internal/metrics" ) @@ -13,16 +14,19 @@ import ( type Registry struct { dnscheck dnscheck.Interface mainMwMtrc metrics.MainMiddleware + postInitMw dnsserver.Middleware } // NewRegistry returns a new registry with the given custom implementations. func NewRegistry( dnsCk dnscheck.Interface, mainMwMtrc metrics.MainMiddleware, + postInitMw dnsserver.Middleware, ) (r *Registry) { return &Registry{ dnscheck: dnsCk, mainMwMtrc: mainMwMtrc, + postInitMw: postInitMw, } } @@ -44,3 +48,13 @@ func (r *Registry) MainMiddlewareMetrics() (mainMwMtrc metrics.MainMiddleware) { return r.mainMwMtrc } + +// PostInitialMiddleware returns a custom implementation of the post-initial +// middleware, if any. +func (r *Registry) PostInitialMiddleware() (postInitMw dnsserver.Middleware) { + if r == nil { + return nil + } + + return r.postInitMw +} diff --git a/internal/cmd/ratelimit.go b/internal/cmd/ratelimit.go index 9a14929..b107f20 100644 --- a/internal/cmd/ratelimit.go +++ b/internal/cmd/ratelimit.go @@ -15,6 +15,12 @@ import ( "github.com/c2h5oh/datasize" ) +// Constants for rate limit settings endpoints. +const ( + rlAllowlistTypeBackend = "backend" + rlAllowlistTypeConsul = "consul" +) + // rateLimitConfig is the configuration of the instance's rate limiting. type rateLimitConfig struct { // AllowList is the allowlist of clients. @@ -57,20 +63,14 @@ type rateLimitConfig struct { RefuseANY bool `yaml:"refuse_any"` } -// allowListConfig is the consul allow list configuration. -type allowListConfig struct { - // List contains IPs and CIDRs. - List []netutil.Prefix `yaml:"list"` - - // RefreshIvl time between two updates of allow list from the Consul URL. - RefreshIvl timeutil.Duration `yaml:"refresh_interval"` -} - // rateLimitOptions allows define maximum number of requests for IPv4 or IPv6 // addresses. type rateLimitOptions struct { - // RPS is the maximum number of requests per second. - RPS uint `yaml:"rps"` + // Count is the maximum number of requests per interval. + Count uint `yaml:"count"` + + // Interval is the time during which to count the number of requests. + Interval timeutil.Duration `yaml:"interval"` // SubnetKeyLen is the length of the subnet prefix used to calculate // rate limiter bucket keys. @@ -87,7 +87,8 @@ func (o *rateLimitOptions) validate() (err error) { } return cmp.Or( - validatePositive("rps", o.RPS), + validatePositive("count", o.Count), + validatePositive("interval", o.Interval), validatePositive("subnet_key_len", o.SubnetKeyLen), ) } @@ -100,9 +101,11 @@ func (c *rateLimitConfig) toInternal(al ratelimit.Allowlist) (conf *ratelimit.Ba ResponseSizeEstimate: c.ResponseSizeEstimate, Duration: c.BackoffDuration.Duration, Period: c.BackoffPeriod.Duration, - IPv4RPS: c.IPv4.RPS, + IPv4Count: c.IPv4.Count, + IPv4Interval: c.IPv4.Interval.Duration, IPv4SubnetKeyLen: c.IPv4.SubnetKeyLen, - IPv6RPS: c.IPv6.RPS, + IPv6Count: c.IPv6.Count, + IPv6Interval: c.IPv6.Interval.Duration, IPv6SubnetKeyLen: c.IPv6.SubnetKeyLen, Count: c.BackoffCount, RefuseANY: c.RefuseANY, @@ -114,14 +117,12 @@ var _ validator = (*rateLimitConfig)(nil) // validate implements the [validator] interface for *rateLimitConfig. func (c *rateLimitConfig) validate() (err error) { - switch { - case c == nil: + if c == nil { return errors.ErrNoValue - case c.Allowlist == nil: - return fmt.Errorf("allowlist: %w", errors.ErrNoValue) } return cmp.Or( + validateProp("allowlist", c.Allowlist.validate), validateProp("connection_limit", c.ConnectionLimit.validate), validateProp("ipv4", c.IPv4.validate), validateProp("ipv6", c.IPv6.validate), @@ -131,10 +132,41 @@ func (c *rateLimitConfig) validate() (err error) { validatePositive("backoff_duration", c.BackoffDuration), validatePositive("backoff_period", c.BackoffPeriod), validatePositive("response_size_estimate", c.ResponseSizeEstimate), - validatePositive("allowlist.refresh_interval", c.Allowlist.RefreshIvl), ) } +// allowListConfig is the consul allow list configuration. +type allowListConfig struct { + // Type defines where the rate limit settings are received from. Allowed + // values are [rlAllowlistTypeBackend] and [rlAllowlistTypeConsul]. + Type string `yaml:"type"` + + // List contains IPs and CIDRs. + List []netutil.Prefix `yaml:"list"` + + // RefreshIvl time between two updates of allow list from the Consul URL. + RefreshIvl timeutil.Duration `yaml:"refresh_interval"` +} + +// type check +var _ validator = (*allowListConfig)(nil) + +// validate implements the [validator] interface for *allowListConfig. +func (c *allowListConfig) validate() (err error) { + if c == nil { + return errors.ErrNoValue + } + + switch c.Type { + case rlAllowlistTypeBackend, rlAllowlistTypeConsul: + // Go on. + default: + return fmt.Errorf("type: %w: %q", errors.ErrBadEnumValue, c.Type) + } + + return validatePositive("refresh_interval", c.RefreshIvl) +} + // connLimitConfig is the configuration structure for the stream-connection // limiter. type connLimitConfig struct { diff --git a/internal/cmd/server.go b/internal/cmd/server.go index 334f530..11bb88a 100644 --- a/internal/cmd/server.go +++ b/internal/cmd/server.go @@ -6,7 +6,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/bindtodevice" - "github.com/AdguardTeam/AdGuardDNS/internal/metrics" + "github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig" "github.com/AdguardTeam/golibs/container" "github.com/AdguardTeam/golibs/errors" ) @@ -14,6 +14,7 @@ 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, ratelimitConf *rateLimitConfig, @@ -68,8 +69,8 @@ func (srvs servers) toInternal( tlsConf := tlsConfig.Conf.Clone() // Attach the functions that will count TLS handshake metrics. - tlsConf.GetConfigForClient = metrics.TLSMetricsBeforeHandshake(string(srv.Protocol)) - tlsConf.VerifyConnection = metrics.TLSMetricsAfterHandshake( + tlsConf.GetConfigForClient = mtrc.BeforeHandshake(string(srv.Protocol)) + tlsConf.VerifyConnection = mtrc.AfterHandshake( string(srv.Protocol), srv.Name, tlsConfig.DeviceDomains, diff --git a/internal/cmd/servergroup.go b/internal/cmd/servergroup.go index 3ad9b52..6c9dea4 100644 --- a/internal/cmd/servergroup.go +++ b/internal/cmd/servergroup.go @@ -1,11 +1,13 @@ package cmd import ( + "context" "fmt" "github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/bindtodevice" "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" + "github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig" "github.com/AdguardTeam/golibs/container" "github.com/AdguardTeam/golibs/errors" ) @@ -17,6 +19,8 @@ type serverGroups []*serverGroup // toInternal returns the configuration for all server groups in the DNS // 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, fltGrps map[agd.FilteringGroupID]*agd.FilteringGroup, @@ -32,7 +36,7 @@ func (srvGrps serverGroups) toInternal( } var tlsConf *agd.TLS - tlsConf, err = g.TLS.toInternal() + tlsConf, err = g.TLS.toInternal(ctx, mtrc) if err != nil { return nil, fmt.Errorf("tls: %w", err) } @@ -45,7 +49,13 @@ func (srvGrps serverGroups) toInternal( ProfilesEnabled: g.ProfilesEnabled, } - svcSrvGrps[i].Servers, err = g.Servers.toInternal(tlsConf, btdMgr, ratelimitConf, dnsConf) + svcSrvGrps[i].Servers, err = g.Servers.toInternal( + mtrc, + tlsConf, + btdMgr, + ratelimitConf, + dnsConf, + ) if err != nil { return nil, fmt.Errorf("server group %q: %w", g.Name, err) } diff --git a/internal/cmd/tickrot.go b/internal/cmd/tickrot.go index e134dd1..fe6431b 100644 --- a/internal/cmd/tickrot.go +++ b/internal/cmd/tickrot.go @@ -10,7 +10,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/agdservice" "github.com/AdguardTeam/AdGuardDNS/internal/errcoll" - "github.com/AdguardTeam/AdGuardDNS/internal/metrics" + "github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig" "github.com/AdguardTeam/golibs/logutil/slogutil" ) @@ -19,6 +19,7 @@ import ( type ticketRotator struct { logger *slog.Logger errColl errcoll.Interface + mtrc tlsconfig.Metrics confs map[*tls.Config][]string } @@ -29,6 +30,7 @@ type ticketRotator struct { func newTicketRotator( logger *slog.Logger, errColl errcoll.Interface, + mtrc tlsconfig.Metrics, grps []*agd.ServerGroup, ) (tr *ticketRotator) { confs := map[*tls.Config][]string{} @@ -49,6 +51,7 @@ func newTicketRotator( return &ticketRotator{ logger: logger.With(slogutil.KeyPrefix, "tickrot"), errColl: errColl, + mtrc: mtrc, confs: confs, } } @@ -81,7 +84,7 @@ func (r *ticketRotator) Refresh(ctx context.Context) (err error) { var key [sessTickLen]byte key, err = readSessionTicketKey(fileName) if err != nil { - metrics.TLSSessionTicketsRotateStatus.Set(0) + r.mtrc.SetSessionTicketRotationStatus(ctx, false) return fmt.Errorf("session ticket for srv %s: %w", conf.ServerName, err) } @@ -96,8 +99,7 @@ func (r *ticketRotator) Refresh(ctx context.Context) (err error) { conf.SetSessionTicketKeys(keys) } - metrics.TLSSessionTicketsRotateStatus.Set(1) - metrics.TLSSessionTicketsRotateTime.SetToCurrentTime() + r.mtrc.SetSessionTicketRotationStatus(ctx, true) return nil } diff --git a/internal/cmd/tls.go b/internal/cmd/tls.go index 9624436..4692140 100644 --- a/internal/cmd/tls.go +++ b/internal/cmd/tls.go @@ -1,6 +1,7 @@ package cmd import ( + "context" "crypto/tls" "crypto/x509" "fmt" @@ -9,10 +10,9 @@ import ( "strings" "github.com/AdguardTeam/AdGuardDNS/internal/agd" - "github.com/AdguardTeam/AdGuardDNS/internal/metrics" + "github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig" "github.com/AdguardTeam/golibs/container" "github.com/AdguardTeam/golibs/errors" - "github.com/prometheus/client_golang/prometheus" ) // tlsConfig are the TLS settings of a DNS server, if any. @@ -37,12 +37,15 @@ type tlsConfig struct { // toInternal converts c to the TLS configuration for a DNS server. c must be // valid. -func (c *tlsConfig) toInternal() (conf *agd.TLS, err error) { +func (c *tlsConfig) toInternal( + ctx context.Context, + mtrc tlsconfig.Metrics, +) (conf *agd.TLS, err error) { if c == nil { return nil, nil } - tlsConf, err := c.Certificates.toInternal() + tlsConf, err := c.Certificates.toInternal(ctx, mtrc) if err != nil { return nil, fmt.Errorf("certificates: %w", err) } @@ -123,7 +126,10 @@ type tlsConfigCert struct { type tlsConfigCerts []*tlsConfigCert // toInternal converts certs to a TLS configuration. certs must be valid. -func (certs tlsConfigCerts) toInternal() (conf *tls.Config, err error) { +func (certs tlsConfigCerts) toInternal( + ctx context.Context, + mtrc tlsconfig.Metrics, +) (conf *tls.Config, err error) { if len(certs) == 0 { return nil, nil } @@ -146,13 +152,8 @@ func (certs tlsConfigCerts) toInternal() (conf *tls.Config, err error) { tlsCerts[i] = cert authAlgo, subj := leaf.PublicKeyAlgorithm.String(), leaf.Subject.String() - metrics.TLSCertificateInfo.With(prometheus.Labels{ - "auth_algo": authAlgo, - "subject": subj, - }).Set(1) - metrics.TLSCertificateNotAfter.With(prometheus.Labels{ - "subject": subj, - }).Set(float64(leaf.NotAfter.Unix())) + + mtrc.SetCertificateInfo(ctx, authAlgo, subj, leaf.NotAfter) } return &tls.Config{ diff --git a/internal/cmd/websvc.go b/internal/cmd/websvc.go index 614b310..64b38d1 100644 --- a/internal/cmd/websvc.go +++ b/internal/cmd/websvc.go @@ -1,6 +1,7 @@ package cmd import ( + "context" "encoding/base64" "fmt" "maps" @@ -13,6 +14,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/dnscheck" "github.com/AdguardTeam/AdGuardDNS/internal/errcoll" + "github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig" "github.com/AdguardTeam/AdGuardDNS/internal/websvc" "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/httphdr" @@ -62,9 +64,11 @@ type webConfig struct { // toInternal converts c to the AdGuardDNS web service configuration. c must be // valid. func (c *webConfig) toInternal( + ctx context.Context, envs *environment, dnsCk dnscheck.Interface, errColl errcoll.Interface, + mtrc tlsconfig.Metrics, ) (conf *websvc.Config, err error) { if c == nil { return nil, nil @@ -83,7 +87,7 @@ func (c *webConfig) toInternal( conf.RootRedirectURL = netutil.CloneURL(&c.RootRedirectURL.URL) } - conf.LinkedIP, err = c.LinkedIP.toInternal(envs.LinkedIPTargetURL) + conf.LinkedIP, err = c.LinkedIP.toInternal(ctx, mtrc, envs.LinkedIPTargetURL) if err != nil { return nil, fmt.Errorf("converting linked_ip: %w", err) } @@ -107,7 +111,7 @@ func (c *webConfig) toInternal( }} for _, bp := range blockPages { - *bp.webConfPtr, err = bp.conf.toInternal() + *bp.webConfPtr, err = bp.conf.toInternal(ctx, mtrc) if err != nil { return nil, fmt.Errorf("%s: %w", bp.name, err) } @@ -119,7 +123,7 @@ func (c *webConfig) toInternal( return nil, err } - conf.NonDoHBind, err = c.NonDoHBind.toInternal() + conf.NonDoHBind, err = c.NonDoHBind.toInternal(ctx, mtrc) if err != nil { return nil, fmt.Errorf("converting non_doh_bind: %w", err) } @@ -225,6 +229,8 @@ 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, targetURL *urlutil.URL, ) (srv *websvc.LinkedIPServer, err error) { if s == nil { @@ -232,7 +238,7 @@ func (s *linkedIPServer) toInternal( } srv = &websvc.LinkedIPServer{} - srv.Bind, err = s.Bind.toInternal() + srv.Bind, err = s.Bind.toInternal(ctx, mtrc) if err != nil { return nil, fmt.Errorf("converting bind: %w", err) } @@ -280,7 +286,10 @@ type blockPageServer struct { } // toInternal converts s to a block page server configuration. s must be valid. -func (s *blockPageServer) toInternal() (conf *websvc.BlockPageServerConfig, err error) { +func (s *blockPageServer) toInternal( + ctx context.Context, + mtrc tlsconfig.Metrics, +) (conf *websvc.BlockPageServerConfig, err error) { if s == nil { return nil, nil } @@ -289,7 +298,7 @@ func (s *blockPageServer) toInternal() (conf *websvc.BlockPageServerConfig, err ContentFilePath: s.BlockPage, } - conf.Bind, err = s.Bind.toInternal() + conf.Bind, err = s.Bind.toInternal(ctx, mtrc) if err != nil { return nil, fmt.Errorf("converting bind: %w", err) } @@ -326,11 +335,14 @@ type bindData []*bindItem // toInternal converts bd to bind data for the AdGuard DNS web service. bd must // be valid. -func (bd bindData) toInternal() (data []*websvc.BindData, err error) { +func (bd bindData) toInternal( + ctx context.Context, + mtrc tlsconfig.Metrics, +) (data []*websvc.BindData, err error) { data = make([]*websvc.BindData, len(bd)) for i, d := range bd { - data[i], err = d.toInternal() + data[i], err = d.toInternal(ctx, mtrc) if err != nil { return nil, fmt.Errorf("bind data at index %d: %w", i, err) } @@ -369,8 +381,11 @@ type bindItem struct { // toInternal converts i to bind data for the AdGuard DNS web service. i must // be valid. -func (i *bindItem) toInternal() (data *websvc.BindData, err error) { - tlsConf, err := i.Certificates.toInternal() +func (i *bindItem) toInternal( + ctx context.Context, + mtrc tlsconfig.Metrics, +) (data *websvc.BindData, err error) { + tlsConf, err := i.Certificates.toInternal(ctx, mtrc) if err != nil { return nil, fmt.Errorf("certificates: %w", err) } diff --git a/internal/consul/allowlist.go b/internal/consul/allowlist.go index 3c64098..a1d2023 100644 --- a/internal/consul/allowlist.go +++ b/internal/consul/allowlist.go @@ -15,7 +15,6 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/agdservice" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit" "github.com/AdguardTeam/AdGuardDNS/internal/errcoll" - "github.com/AdguardTeam/AdGuardDNS/internal/metrics" "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/netutil/urlutil" ) @@ -28,6 +27,7 @@ type AllowlistUpdater struct { http *agdhttp.Client url *url.URL errColl errcoll.Interface + metrics Metrics } // AllowlistUpdaterConfig is the configuration structure for the allowlist @@ -45,6 +45,9 @@ type AllowlistUpdaterConfig struct { // ErrColl is used to collect errors during refreshes. ErrColl errcoll.Interface + // Metrics is used to collect allowlist statistics. + Metrics Metrics + // Timeout is the timeout for Consul queries. Timeout time.Duration } @@ -60,6 +63,7 @@ func NewAllowlistUpdater(c *AllowlistUpdaterConfig) (upd *AllowlistUpdater) { }), url: c.ConsulURL, errColl: c.ErrColl, + metrics: c.Metrics, } } @@ -72,10 +76,7 @@ func (upd *AllowlistUpdater) Refresh(ctx context.Context) (err error) { upd.logger.InfoContext(ctx, "refresh started") defer upd.logger.InfoContext(ctx, "refresh finished") - defer func() { - metrics.ConsulAllowlistUpdateTime.SetToCurrentTime() - metrics.SetStatusGauge(metrics.ConsulAllowlistUpdateStatus, err) - }() + defer func() { upd.metrics.SetStatus(ctx, err) }() consulNets, err := upd.loadConsul(ctx) if err != nil { @@ -89,13 +90,11 @@ func (upd *AllowlistUpdater) Refresh(ctx context.Context) (err error) { ctx, "refresh successful", "num_records", len(consulNets), - "url", &urlutil.URL{ - URL: *upd.url, - }, + "url", urlutil.RedactUserinfo(upd.url), ) upd.allowlist.Update(consulNets) - metrics.ConsulAllowlistSize.Set(float64(len(consulNets))) + upd.metrics.SetSize(ctx, len(consulNets)) return nil } @@ -107,7 +106,7 @@ type consulRecord struct { // loadConsul fetches, decodes, and returns the list of IP networks from consul. func (upd *AllowlistUpdater) loadConsul(ctx context.Context) (nets []netip.Prefix, err error) { - defer func() { err = errors.Annotate(err, "loading allowlist nets from %s: %w", upd.url) }() + defer func() { err = errors.Annotate(err, "loading allowlist nets: %w") }() httpResp, err := upd.http.Get(ctx, upd.url) if err != nil { diff --git a/internal/consul/allowlist_test.go b/internal/consul/allowlist_test.go index c315466..d6faf8a 100644 --- a/internal/consul/allowlist_test.go +++ b/internal/consul/allowlist_test.go @@ -83,6 +83,7 @@ func TestNewAllowlistUpdater(t *testing.T) { Allowlist: al, ConsulURL: u, ErrColl: agdtest.NewErrorCollector(), + Metrics: consul.EmptyMetrics{}, Timeout: testTimeout, }) @@ -128,6 +129,7 @@ func TestNewAllowlistUpdater(t *testing.T) { Allowlist: al, ConsulURL: u, ErrColl: errColl, + Metrics: consul.EmptyMetrics{}, Timeout: testTimeout, }) @@ -161,6 +163,7 @@ func TestAllowlistUpdater_Refresh_deadline(t *testing.T) { Allowlist: al, ConsulURL: u, ErrColl: errColl, + Metrics: consul.EmptyMetrics{}, Timeout: testTimeout, }) diff --git a/internal/consul/metrics.go b/internal/consul/metrics.go new file mode 100644 index 0000000..4b7d8eb --- /dev/null +++ b/internal/consul/metrics.go @@ -0,0 +1,26 @@ +package consul + +import "context" + +// Metrics is an interface that is used for the collection of the allowlist +// statistics. +type Metrics interface { + // SetSize sets the number of received subnets. + SetSize(ctx context.Context, n int) + + // SetStatus sets the status and time of the allowlist refresh attempt. + SetStatus(ctx context.Context, err error) +} + +// EmptyMetrics is the implementation of the [Metrics] interface that does +// nothing. +type EmptyMetrics struct{} + +// type check +var _ Metrics = EmptyMetrics{} + +// SetSize implements the [Metrics] interface for EmptyMetrics. +func (EmptyMetrics) SetSize(_ context.Context, _ int) {} + +// SetStatus plements the [Metrics] interface for EmptyMetrics. +func (EmptyMetrics) SetStatus(_ context.Context, _ error) {} diff --git a/internal/debugsvc/debugsvc_test.go b/internal/debugsvc/debugsvc_test.go index 4c51646..9e8a4bf 100644 --- a/internal/debugsvc/debugsvc_test.go +++ b/internal/debugsvc/debugsvc_test.go @@ -15,6 +15,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/debugsvc" "github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/netutil/httputil" + "github.com/AdguardTeam/golibs/netutil/urlutil" "github.com/AdguardTeam/golibs/testutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -92,7 +93,7 @@ func TestService_Start(t *testing.T) { }) srvURL := &url.URL{ - Scheme: agdhttp.SchemeHTTP, + Scheme: urlutil.SchemeHTTP, Host: addr, } diff --git a/internal/dnscheck/remotekv.go b/internal/dnscheck/remotekv.go index c104dbf..4e94aa5 100644 --- a/internal/dnscheck/remotekv.go +++ b/internal/dnscheck/remotekv.go @@ -201,18 +201,21 @@ func (cc *RemoteKV) newInfo(ri *agd.RequestInfo) (inf *info) { } // resp returns the corresponding response. +// +// 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) { qt := ri.QType if qt != dns.TypeA && qt != dns.TypeAAAA { - return ri.Messages.NewMsgNODATA(req), nil + return ri.Messages.NewRespRCode(req, dns.RcodeSuccess), nil } if qt == dns.TypeA { - return cc.messages.NewIPRespMsg(req, cc.ipv4...) + return cc.messages.NewRespIP(req, cc.ipv4...) } - return cc.messages.NewIPRespMsg(req, cc.ipv6...) + return cc.messages.NewRespIP(req, cc.ipv6...) } // type check diff --git a/internal/dnscheck/remotekv_test.go b/internal/dnscheck/remotekv_test.go index 0add623..700d78a 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/netutil/urlutil" "github.com/miekg/dns" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -86,7 +87,7 @@ func TestConsul_ServeHTTP(t *testing.T) { t.Run("hit", func(t *testing.T) { r := httptest.NewRequest(http.MethodGet, (&url.URL{ - Scheme: "http", + Scheme: urlutil.SchemeHTTP, Host: randomid + "-" + checkDomain, Path: "/dnscheck/test", }).String(), strings.NewReader("")) @@ -103,7 +104,7 @@ func TestConsul_ServeHTTP(t *testing.T) { t.Run("miss", func(t *testing.T) { r := httptest.NewRequest(http.MethodGet, (&url.URL{ - Scheme: "http", + Scheme: urlutil.SchemeHTTP, Host: "non" + randomid + "-" + checkDomain, Path: "/dnscheck/test", }).String(), strings.NewReader("")) diff --git a/internal/dnsdb/http_test.go b/internal/dnsdb/http_test.go index 1fd6b7d..ebe3713 100644 --- a/internal/dnsdb/http_test.go +++ b/internal/dnsdb/http_test.go @@ -18,13 +18,14 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/dnsdb" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest" "github.com/AdguardTeam/golibs/httphdr" + "github.com/AdguardTeam/golibs/netutil/urlutil" "github.com/miekg/dns" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestDefault_ServeHTTP(t *testing.T) { - const dname = "some-domain.name" + const domain = "domain.example" testIP := netip.MustParseAddr("1.2.3.4") @@ -39,7 +40,7 @@ func TestDefault_ServeHTTP(t *testing.T) { rcode, dnsservertest.NewReq(name, qtype, dns.ClassINET), dnsservertest.SectionAnswer{ - dnsservertest.NewA(dname, 0, testIP), + dnsservertest.NewA(domain, 0, testIP), }, ) } @@ -52,38 +53,38 @@ func TestDefault_ServeHTTP(t *testing.T) { }{{ name: "single", msgs: []*dns.Msg{ - newMsg(dns.RcodeSuccess, dname, dns.TypeA), + newMsg(dns.RcodeSuccess, domain, dns.TypeA), }, wantHdr: successHdr, - wantResp: [][]byte{[]byte(dname + `,A,NOERROR,` + testIP.String() + `,1`)}, + wantResp: [][]byte{[]byte(domain + `,A,NOERROR,` + testIP.String() + `,1`)}, }, { name: "existing", msgs: []*dns.Msg{ - newMsg(dns.RcodeSuccess, dname, dns.TypeA), - newMsg(dns.RcodeSuccess, dname, dns.TypeA), + newMsg(dns.RcodeSuccess, domain, dns.TypeA), + newMsg(dns.RcodeSuccess, domain, dns.TypeA), }, wantHdr: successHdr, - wantResp: [][]byte{[]byte(dname + `,A,NOERROR,` + testIP.String() + `,2`)}, + wantResp: [][]byte{[]byte(domain + `,A,NOERROR,` + testIP.String() + `,2`)}, }, { name: "different", msgs: []*dns.Msg{ - newMsg(dns.RcodeSuccess, dname, dns.TypeA), - newMsg(dns.RcodeSuccess, "sub."+dname, dns.TypeA), + newMsg(dns.RcodeSuccess, domain, dns.TypeA), + newMsg(dns.RcodeSuccess, "sub."+domain, dns.TypeA), }, wantHdr: successHdr, wantResp: [][]byte{ - []byte("sub." + dname + `,A,NOERROR,` + testIP.String() + `,1`), - []byte(dname + `,A,NOERROR,` + testIP.String() + `,1`), + []byte("sub." + domain + `,A,NOERROR,` + testIP.String() + `,1`), + []byte(domain + `,A,NOERROR,` + testIP.String() + `,1`), }, }, { name: "non-recordable", msgs: []*dns.Msg{ // Not NOERROR. - newMsg(dns.RcodeBadName, dname, dns.TypeA), + newMsg(dns.RcodeBadName, domain, dns.TypeA), // Not A/AAAA. - newMsg(dns.RcodeSuccess, dname, dns.TypeSRV), + newMsg(dns.RcodeSuccess, domain, dns.TypeSRV), // Android metrics. - newMsg(dns.RcodeSuccess, dname+"-dnsotls-ds.metric.gstatic.com.", dns.TypeA), + newMsg(dns.RcodeSuccess, domain+"-dnsotls-ds.metric.gstatic.com.", dns.TypeA), }, wantHdr: successHdr, wantResp: [][]byte{}, @@ -109,7 +110,10 @@ func TestDefault_ServeHTTP(t *testing.T) { r := httptest.NewRequest( http.MethodGet, - (&url.URL{Scheme: "http", Host: "example.com"}).String(), + (&url.URL{ + Scheme: urlutil.SchemeHTTP, + Host: "dnsdb.example", + }).String(), nil, ) r.Header.Add(httphdr.AcceptEncoding, agdhttp.HdrValGzip) diff --git a/internal/dnsmsg/constructor.go b/internal/dnsmsg/constructor.go index 869185c..3ed4b36 100644 --- a/internal/dnsmsg/constructor.go +++ b/internal/dnsmsg/constructor.go @@ -15,6 +15,11 @@ type ConstructorConfig struct { // Cloner used to clone DNS messages. It must not be nil. Cloner *Cloner + // StructuredErrors is the configuration for the experimental Structured DNS + // Errors feature. It must not be nil. If enabled, + // [ConstructorConfig.Enabled] should also be true. + StructuredErrors *StructuredDNSErrorsConfig + // BlockingMode is the blocking mode to use in // [Constructor.NewBlockedRespMsg]. It must not be nil. BlockingMode BlockingMode @@ -22,6 +27,9 @@ type ConstructorConfig struct { // FilteredResponseTTL is the time-to-live value used for responses created // by this message constructor. It must be non-negative. FilteredResponseTTL time.Duration + + // EDEEnabled enables the addition of the Extended DNS Error (EDE) codes. + EDEEnabled bool } // validate checks the configuration for errors. @@ -33,13 +41,19 @@ func (conf *ConstructorConfig) validate() (err error) { errs = append(errs, err) } + err = conf.StructuredErrors.validate(conf.EDEEnabled) + if err != nil { + err = fmt.Errorf("structured errors: %w", err) + errs = append(errs, err) + } + if conf.BlockingMode == nil { err = fmt.Errorf("blocking mode: %w", errors.ErrNoValue) errs = append(errs, err) } if conf.FilteredResponseTTL < 0 { - err = fmt.Errorf("filtered response TTL: %w", errors.ErrNegative) + err = fmt.Errorf("filtered response ttl: %w", errors.ErrNegative) errs = append(errs, err) } @@ -51,7 +65,9 @@ func (conf *ConstructorConfig) validate() (err error) { type Constructor struct { cloner *Cloner blockingMode BlockingMode + sde string fltRespTTL time.Duration + edeEnabled bool } // NewConstructor returns a properly initialized constructor using conf. @@ -60,10 +76,17 @@ func NewConstructor(conf *ConstructorConfig) (c *Constructor, err error) { return nil, fmt.Errorf("configuration: %w", err) } + var sde string + if sdeConf := conf.StructuredErrors; sdeConf.Enabled { + sde = sdeConf.iJSON() + } + return &Constructor{ cloner: conf.Cloner, blockingMode: conf.BlockingMode, + sde: sde, fltRespTTL: conf.FilteredResponseTTL, + edeEnabled: conf.EDEEnabled, }, nil } @@ -72,156 +95,6 @@ func (c *Constructor) Cloner() (cloner *Cloner) { return c.cloner } -// FilteredResponseTTL returns the TTL that the constructor uses to build -// blocked responses. -func (c *Constructor) FilteredResponseTTL() (ttl time.Duration) { - return c.fltRespTTL -} - -// NewBlockedRespMsg returns a blocked DNS response message based on the -// constructor's blocking mode. -func (c *Constructor) NewBlockedRespMsg(req *dns.Msg) (msg *dns.Msg, err error) { - switch m := c.blockingMode.(type) { - case *BlockingModeCustomIP: - return c.newBlockedCustomIPRespMsg(req, m) - case *BlockingModeNullIP: - switch qt := req.Question[0].Qtype; qt { - case dns.TypeA, dns.TypeAAAA: - return c.NewIPRespMsg(req, netip.Addr{}) - default: - return c.NewMsgNODATA(req), nil - } - case *BlockingModeNXDOMAIN: - return c.NewMsgNXDOMAIN(req), nil - case *BlockingModeREFUSED: - return c.NewMsgREFUSED(req), nil - default: - // Consider unhandled sum type members as unrecoverable programmer - // errors. - panic(fmt.Errorf("unexpected type %T", c.blockingMode)) - } -} - -// newBlockedCustomIPRespMsg returns a blocked DNS response message with either -// the custom IPs from the blocking mode options or a NODATA one. -func (c *Constructor) newBlockedCustomIPRespMsg( - req *dns.Msg, - m *BlockingModeCustomIP, -) (msg *dns.Msg, err error) { - switch qt := req.Question[0].Qtype; qt { - case dns.TypeA: - if len(m.IPv4) > 0 { - return c.NewIPRespMsg(req, m.IPv4...) - } - case dns.TypeAAAA: - if len(m.IPv6) > 0 { - return c.NewIPRespMsg(req, m.IPv6...) - } - default: - // Go on. - } - - return c.NewMsgNODATA(req), nil -} - -// NewIPRespMsg returns a DNS A or AAAA response message with the given IP -// addresses. If any IP address is nil, it is replaced by an unspecified (aka -// null) IP. The TTL is also set to c.FilteredResponseTTL. -func (c *Constructor) NewIPRespMsg(req *dns.Msg, ips ...netip.Addr) (msg *dns.Msg, err error) { - switch qt := req.Question[0].Qtype; qt { - case dns.TypeA: - return c.newMsgA(req, ips...) - case dns.TypeAAAA: - return c.newMsgAAAA(req, ips...) - default: - return nil, fmt.Errorf("bad qtype for a or aaaa resp: %d", qt) - } -} - -// NewCNAMEWithIPs generates a filtered response to req with CNAME record and -// provided ips. cname is the fully-qualified name and must not be empty, ips -// must be of the same family. -func (c *Constructor) NewCNAMEWithIPs( - req *dns.Msg, - cname string, - ips ...netip.Addr, -) (resp *dns.Msg, err error) { - resp = c.NewRespMsg(req) - - resp.Answer = make([]dns.RR, 0, len(ips)+1) - resp.Answer = append(resp.Answer, c.NewAnswerCNAME(req, cname)) - - var ans dns.RR - for i, ip := range ips { - switch qt := req.Question[0].Qtype; qt { - case dns.TypeA: - ans, err = c.NewAnswerA(cname, ip) - case dns.TypeAAAA: - ans, err = c.NewAnswerAAAA(cname, ip) - default: - return nil, fmt.Errorf("bad qtype for a or aaaa resp: %d", qt) - } - - if err != nil { - return nil, fmt.Errorf("bad ip at idx %d: %w", i, err) - } - - resp.Answer = append(resp.Answer, ans) - } - - return resp, err -} - -// NewMsgFORMERR returns a properly initialized FORMERR response. -func (c *Constructor) NewMsgFORMERR(req *dns.Msg) (resp *dns.Msg) { - return c.newMsgRCode(req, dns.RcodeFormatError) -} - -// NewMsgNXDOMAIN returns a properly initialized NXDOMAIN response. -func (c *Constructor) NewMsgNXDOMAIN(req *dns.Msg) (resp *dns.Msg) { - return c.newMsgRCode(req, dns.RcodeNameError) -} - -// NewMsgREFUSED returns a properly initialized REFUSED response. -func (c *Constructor) NewMsgREFUSED(req *dns.Msg) (resp *dns.Msg) { - return c.newMsgRCode(req, dns.RcodeRefused) -} - -// NewMsgSERVFAIL returns a properly initialized SERVFAIL response. -func (c *Constructor) NewMsgSERVFAIL(req *dns.Msg) (resp *dns.Msg) { - return c.newMsgRCode(req, dns.RcodeServerFailure) -} - -// NewMsgNODATA returns a properly initialized NODATA response. -// -// See https://www.rfc-editor.org/rfc/rfc2308#section-2.2. -func (c *Constructor) NewMsgNODATA(req *dns.Msg) (resp *dns.Msg) { - return c.newMsgRCode(req, dns.RcodeSuccess) -} - -// newMsgRCode returns a properly initialized response with the given RCode. -func (c *Constructor) newMsgRCode(req *dns.Msg, rc RCode) (resp *dns.Msg) { - resp = (&dns.Msg{}).SetRcode(req, int(rc)) - resp.Ns = c.newSOARecords(req) - resp.RecursionAvailable = true - - return resp -} - -// NewTXTRespMsg returns a DNS TXT response message with the given strings as -// content. The TTL is also set to c.FilteredResponseTTL. -func (c *Constructor) NewTXTRespMsg(req *dns.Msg, strs ...string) (msg *dns.Msg, err error) { - ans, err := c.NewAnswerTXT(req, strs) - if err != nil { - return nil, err - } - - msg = c.NewRespMsg(req) - msg.Answer = append(msg.Answer, ans) - - return msg, nil -} - // AppendDebugExtra appends to response message a DNS TXT extra with CHAOS // class. func (c *Constructor) AppendDebugExtra(req, resp *dns.Msg, str string) (err error) { @@ -386,26 +259,23 @@ func (c *Constructor) newSOARecords(req *dns.Msg) (soaRecs []dns.RR) { zone = req.Question[0].Name } - // TODO(a.garipov): A lot of this is copied from AdGuard Home and needs - // to be inspected and refactored. + // TODO(a.garipov): A lot of this is copied from AdGuard Home and needs to + // be inspected and refactored. soa := &dns.SOA{ - // values copied from verisign's nonexistent .com domain - // their exact values are not important in our use case because they are used for domain transfers between primary/secondary DNS servers + // Use values from verisign's nonexistent.com domain. Their exact + // values are not important in our use case because they are used for + // domain transfers between primary/secondary DNS servers. Refresh: 1800, Retry: 900, Expire: 604800, Minttl: 86400, - // copied from AdGuard DNS + // Copied from AdGuard DNS. Ns: "fake-for-negative-caching.adguard.com.", Serial: 100500, - // rest is request-specific - Hdr: dns.RR_Header{ - Name: zone, - Rrtype: dns.TypeSOA, - Ttl: uint32(c.fltRespTTL.Seconds()), - Class: dns.ClassINET, - }, - Mbox: "hostmaster.", // zone will be appended later if it's not empty or "." + // Rest is request-specific. + Hdr: c.newHdrWithClass(zone, dns.TypeSOA, dns.ClassINET), + // Zone will be appended later if it's not empty or ".". + Mbox: "hostmaster.", } if len(zone) > 0 && zone[0] != '.' { @@ -415,25 +285,10 @@ func (c *Constructor) newSOARecords(req *dns.Msg) (soaRecs []dns.RR) { return []dns.RR{soa} } -// NewRespMsg creates a DNS response for req and sets all necessary flags and -// fields. It also guarantees that req.Question will be not empty. -func (c *Constructor) NewRespMsg(req *dns.Msg) (resp *dns.Msg) { - resp = &dns.Msg{ - MsgHdr: dns.MsgHdr{ - RecursionAvailable: true, - }, - Compress: true, - } - - resp.SetReply(req) - - return resp -} - // newMsgA returns a new DNS response with the given IPv4 addresses. If any IP // address is nil, it is replaced by an unspecified (aka null) IP, 0.0.0.0. func (c *Constructor) newMsgA(req *dns.Msg, ips ...netip.Addr) (msg *dns.Msg, err error) { - msg = c.NewRespMsg(req) + msg = c.NewResp(req) for i, ip := range ips { var ans dns.RR ans, err = c.NewAnswerA(req.Question[0].Name, ip) @@ -450,7 +305,7 @@ func (c *Constructor) newMsgA(req *dns.Msg, ips ...netip.Addr) (msg *dns.Msg, er // newMsgAAAA returns a new DNS response with the given IPv6 addresses. If any // IP address is nil, it is replaced by an unspecified (aka null) IP, [::]. func (c *Constructor) newMsgAAAA(req *dns.Msg, ips ...netip.Addr) (msg *dns.Msg, err error) { - msg = c.NewRespMsg(req) + msg = c.NewResp(req) for i, ip := range ips { var ans dns.RR ans, err = c.NewAnswerAAAA(req.Question[0].Name, ip) diff --git a/internal/dnsmsg/constructor_test.go b/internal/dnsmsg/constructor_test.go index 941dca1..74e3be4 100644 --- a/internal/dnsmsg/constructor_test.go +++ b/internal/dnsmsg/constructor_test.go @@ -1,18 +1,16 @@ package dnsmsg_test import ( - "net" - "net/netip" + "net/url" "strings" "testing" "github.com/AdguardTeam/AdGuardDNS/internal/agdtest" "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" - "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest" + "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/testutil" "github.com/miekg/dns" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) // newTXTExtra is a helper constructor of the expected extra data. @@ -27,311 +25,88 @@ func newTXTExtra(ttl uint32, strs ...string) (extra []dns.RR) { }} } -// newConstructor returns a new dnsmsg.Constructor with [testFltRespTTL]. -func newConstructor(tb testing.TB) (c *dnsmsg.Constructor) { - msgs, err := dnsmsg.NewConstructor(&dnsmsg.ConstructorConfig{ - Cloner: dnsmsg.NewCloner(dnsmsg.EmptyClonerStat{}), - BlockingMode: &dnsmsg.BlockingModeNullIP{}, - FilteredResponseTTL: agdtest.FilteredResponseTTL, - }) - require.NoError(tb, err) - - return msgs -} - -func TestConstructor_NewBlockedRespMsg_nullIP(t *testing.T) { - t.Parallel() - - msgs := newConstructor(t) - - testCases := []struct { - name string - wantAnsNum int - qt dnsmsg.RRType - }{{ - name: "a", - wantAnsNum: 1, - qt: dns.TypeA, - }, { - name: "aaaa", - wantAnsNum: 1, - qt: dns.TypeAAAA, - }, { - name: "txt", - wantAnsNum: 0, - qt: dns.TypeTXT, - }} - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - - req := dnsservertest.NewReq(testFQDN, tc.qt, dns.ClassINET) - - resp, respErr := msgs.NewBlockedRespMsg(req) - require.NoError(t, respErr) - require.NotNil(t, resp) - - assert.Equal(t, dns.RcodeSuccess, resp.Rcode) - - if tc.wantAnsNum == 0 { - assert.Empty(t, resp.Answer) - - require.Len(t, resp.Ns, 1) - - nsTTL := resp.Ns[0].Header().Ttl - assert.Equal(t, uint32(agdtest.FilteredResponseTTLSec), nsTTL) - } else { - require.Len(t, resp.Answer, 1) - - ansTTL := resp.Answer[0].Header().Ttl - assert.Equal(t, uint32(agdtest.FilteredResponseTTLSec), ansTTL) - } - }) - } -} - -func TestConstructor_NewBlockedRespMsg_customIP(t *testing.T) { +func TestNewConstructor(t *testing.T) { t.Parallel() cloner := agdtest.NewCloner() - - testCases := []struct { - blockingMode dnsmsg.BlockingMode - name string - wantA bool - wantAAAA bool - }{{ - blockingMode: &dnsmsg.BlockingModeCustomIP{ - IPv4: []netip.Addr{testIPv4}, - IPv6: []netip.Addr{testIPv6}, - }, - name: "both", - wantA: true, - wantAAAA: true, - }, { - blockingMode: &dnsmsg.BlockingModeCustomIP{ - IPv4: []netip.Addr{testIPv4}, - }, - name: "ipv4_only", - wantA: true, - wantAAAA: false, - }, { - blockingMode: &dnsmsg.BlockingModeCustomIP{ - IPv6: []netip.Addr{testIPv6}, - }, - name: "ipv6_only", - wantA: false, - wantAAAA: true, - }, { - blockingMode: &dnsmsg.BlockingModeCustomIP{ - IPv4: []netip.Addr{}, - IPv6: []netip.Addr{}, - }, - name: "empty", - wantA: false, - wantAAAA: false, - }} - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - - msgs, err := dnsmsg.NewConstructor(&dnsmsg.ConstructorConfig{ - Cloner: cloner, - BlockingMode: tc.blockingMode, - FilteredResponseTTL: agdtest.FilteredResponseTTL, - }) - require.NoError(t, err) - - reqA := dnsservertest.NewReq(testFQDN, dns.TypeA, dns.ClassINET) - respA, err := msgs.NewBlockedRespMsg(reqA) - require.NoError(t, err) - require.NotNil(t, respA) - - assert.Equal(t, dns.RcodeSuccess, respA.Rcode) - - if tc.wantA { - require.Len(t, respA.Answer, 1) - - a := testutil.RequireTypeAssert[*dns.A](t, respA.Answer[0]) - assert.Equal(t, net.IP(testIPv4.AsSlice()), a.A) - } else { - assert.Empty(t, respA.Answer) - } - - reqAAAA := dnsservertest.NewReq(testFQDN, dns.TypeAAAA, dns.ClassINET) - respAAAA, err := msgs.NewBlockedRespMsg(reqAAAA) - require.NoError(t, err) - require.NotNil(t, respAAAA) - - assert.Equal(t, dns.RcodeSuccess, respAAAA.Rcode) - - if tc.wantAAAA { - require.Len(t, respAAAA.Answer, 1) - - aaaa := testutil.RequireTypeAssert[*dns.AAAA](t, respAAAA.Answer[0]) - assert.Equal(t, net.IP(testIPv6.AsSlice()), aaaa.AAAA) - } else { - assert.Empty(t, respAAAA.Answer) - } - }) - } -} - -func TestConstructor_NewBlockedRespMsg_noAnswer(t *testing.T) { - t.Parallel() - - req := dnsservertest.NewReq(testFQDN, dns.TypeA, dns.ClassINET) - cloner := agdtest.NewCloner() - - testCases := []struct { - blockingMode dnsmsg.BlockingMode - name string - rcode dnsmsg.RCode - }{{ - blockingMode: &dnsmsg.BlockingModeNXDOMAIN{}, - name: "nxdomain", - rcode: dns.RcodeNameError, - }, { - blockingMode: &dnsmsg.BlockingModeREFUSED{}, - name: "refused", - rcode: dns.RcodeRefused, - }} - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - - msgs, err := dnsmsg.NewConstructor(&dnsmsg.ConstructorConfig{ - Cloner: cloner, - BlockingMode: tc.blockingMode, - FilteredResponseTTL: agdtest.FilteredResponseTTL, - }) - require.NoError(t, err) - - resp, err := msgs.NewBlockedRespMsg(req) - require.NoError(t, err) - require.NotNil(t, resp) - - assert.Equal(t, tc.rcode, dnsmsg.RCode(resp.Rcode)) - assert.Empty(t, resp.Answer) - - require.Len(t, resp.Ns, 1) - - nsTTL := resp.Ns[0].Header().Ttl - assert.Equal(t, uint32(agdtest.FilteredResponseTTLSec), nsTTL) - }) - } -} - -func TestConstructor_noAnswerMethods(t *testing.T) { - t.Parallel() - - msgs := newConstructor(t) - - req := dnsservertest.NewReq(testFQDN, dns.TypeA, dns.ClassINET) - - testCases := []struct { - method func(req *dns.Msg) (resp *dns.Msg) - name string - want dnsmsg.RCode - }{{ - method: msgs.NewMsgFORMERR, - name: "formerr", - want: dns.RcodeFormatError, - }, { - method: msgs.NewMsgNXDOMAIN, - name: "nxdomain", - want: dns.RcodeNameError, - }, { - method: msgs.NewMsgREFUSED, - name: "refused", - want: dns.RcodeRefused, - }, { - method: msgs.NewMsgSERVFAIL, - name: "servfail", - want: dns.RcodeServerFailure, - }, { - method: msgs.NewMsgNODATA, - name: "nodata", - want: dns.RcodeSuccess, - }} - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - - resp := tc.method(req) - require.NotNil(t, resp) - require.Len(t, resp.Ns, 1) - - assert.Empty(t, resp.Answer) - assert.Equal(t, tc.want, dnsmsg.RCode(resp.Rcode)) - - nsTTL := resp.Ns[0].Header().Ttl - assert.Equal(t, uint32(agdtest.FilteredResponseTTLSec), nsTTL) - }) - } -} - -func TestConstructor_NewTXTRespMsg(t *testing.T) { - t.Parallel() - - msgs := newConstructor(t) - - req := dnsservertest.NewReq(testFQDN, dns.TypeTXT, dns.ClassINET) - tooLong := strings.Repeat("1", dnsmsg.MaxTXTStringLen+1) + badContactURL := errors.Must(url.Parse("invalid-scheme://devteam@adguard.com")) testCases := []struct { name string + conf *dnsmsg.ConstructorConfig wantErrMsg string - strs []string }{{ - name: "success", + name: "good", + conf: &dnsmsg.ConstructorConfig{ + Cloner: cloner, + StructuredErrors: agdtest.NewSDEConfig(true), + BlockingMode: &dnsmsg.BlockingModeNullIP{}, + FilteredResponseTTL: agdtest.FilteredResponseTTL, + EDEEnabled: true, + }, wantErrMsg: "", - strs: []string{"111"}, }, { - name: "success_many", - wantErrMsg: "", - strs: []string{"111", "222"}, + name: "all_bad", + conf: &dnsmsg.ConstructorConfig{ + FilteredResponseTTL: -1, + }, + wantErrMsg: "configuration: " + + "cloner: no value\n" + + "structured errors: no value\n" + + "blocking mode: no value\n" + + "filtered response ttl: negative value", }, { - name: "success_nil", - wantErrMsg: "", - strs: nil, + name: "sde_enabled", + conf: &dnsmsg.ConstructorConfig{ + Cloner: cloner, + StructuredErrors: agdtest.NewSDEConfig(true), + BlockingMode: &dnsmsg.BlockingModeNullIP{}, + FilteredResponseTTL: agdtest.FilteredResponseTTL, + EDEEnabled: false, + }, + wantErrMsg: "configuration: structured errors: " + + "ede must be enabled to enable sde", }, { - name: "success_empty", - wantErrMsg: "", - strs: []string{}, + name: "sde_empty", + conf: &dnsmsg.ConstructorConfig{ + Cloner: cloner, + StructuredErrors: &dnsmsg.StructuredDNSErrorsConfig{ + Enabled: true, + }, + BlockingMode: &dnsmsg.BlockingModeNullIP{}, + FilteredResponseTTL: agdtest.FilteredResponseTTL, + EDEEnabled: true, + }, + wantErrMsg: "configuration: structured errors: " + + "contact data: empty value\n" + + "justification: empty value", }, { - name: "too_long", - wantErrMsg: "txt string at index 0: too long: got 256 bytes, max 255", - strs: []string{tooLong}, + name: "sde_bad", + conf: &dnsmsg.ConstructorConfig{ + Cloner: cloner, + StructuredErrors: &dnsmsg.StructuredDNSErrorsConfig{ + Enabled: true, + Contact: []*url.URL{badContactURL, nil}, + Justification: "\uFFFE", + Organization: "\uFFFE", + }, + BlockingMode: &dnsmsg.BlockingModeNullIP{}, + FilteredResponseTTL: agdtest.FilteredResponseTTL, + EDEEnabled: true, + }, + wantErrMsg: "configuration: structured errors: " + + `contact data: at index 0: scheme: bad enum value: "invalid-scheme"` + "\n" + + "contact data: at index 1: no value\n" + + "justification: bad code point at index 0\n" + + "organization: bad code point at index 0", }} for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { t.Parallel() - resp, respErr := msgs.NewTXTRespMsg(req, tc.strs...) - testutil.AssertErrorMsg(t, tc.wantErrMsg, respErr) - - if tc.wantErrMsg != "" { - return - } - - require.NotNil(t, resp) - - assert.Equal(t, dns.RcodeSuccess, resp.Rcode) - - require.Len(t, resp.Answer, 1) - - ans := resp.Answer[0] - ansTTL := ans.Header().Ttl - assert.Equal(t, uint32(agdtest.FilteredResponseTTLSec), ansTTL) - - txt := testutil.RequireTypeAssert[*dns.TXT](t, ans) - assert.Equal(t, tc.strs, txt.Txt) + _, err := dnsmsg.NewConstructor(tc.conf) + testutil.AssertErrorMsg(t, tc.wantErrMsg, err) }) } } @@ -339,7 +114,7 @@ func TestConstructor_NewTXTRespMsg(t *testing.T) { func TestConstructor_AppendDebugExtra(t *testing.T) { t.Parallel() - msgs := newConstructor(t) + msgs := agdtest.NewConstructor(t) shortText := "This is a short test text" longText := strings.Repeat("a", 2*dnsmsg.MaxTXTStringLen) diff --git a/internal/dnsmsg/dnsmsg_test.go b/internal/dnsmsg/dnsmsg_test.go index b382846..83dea9b 100644 --- a/internal/dnsmsg/dnsmsg_test.go +++ b/internal/dnsmsg/dnsmsg_test.go @@ -78,7 +78,9 @@ func TestClone(t *testing.T) { }, name: "empty_slice_ans", }, { - msg: dnsservertest.NewReq(testFQDN, dns.TypeA, dns.ClassINET), + msg: dnsservertest.NewReq(testFQDN, dns.TypeA, dns.ClassINET, dnsservertest.SectionExtra{ + dnsservertest.NewOPT(true, dns.MaxMsgSize, &dns.EDNS0_EDE{}), + }), name: "a", }} diff --git a/internal/dnsmsg/optcloner.go b/internal/dnsmsg/optcloner.go index 0627f0e..507eaa7 100644 --- a/internal/dnsmsg/optcloner.go +++ b/internal/dnsmsg/optcloner.go @@ -72,8 +72,7 @@ func (c *optCloner) clone(rr *dns.OPT) (clone *dns.OPT, full bool) { optClone = opt case *dns.EDNS0_EDE: opt := c.ede.Get() - opt.InfoCode = orig.InfoCode - opt.ExtraText = orig.ExtraText + *opt = *orig optClone = opt case *dns.EDNS0_SUBNET: diff --git a/internal/dnsmsg/response.go b/internal/dnsmsg/response.go new file mode 100644 index 0000000..bf4f7ce --- /dev/null +++ b/internal/dnsmsg/response.go @@ -0,0 +1,228 @@ +package dnsmsg + +import ( + "fmt" + "net/netip" + + "github.com/miekg/dns" +) + +// NewResp creates a response DNS message for req and sets all necessary flags +// and fields. resp contains no resource records. +func (c *Constructor) NewResp(req *dns.Msg) (resp *dns.Msg) { + return (&dns.Msg{ + MsgHdr: dns.MsgHdr{ + RecursionAvailable: true, + }, + Compress: true, + }).SetReply(req) +} + +// NewBlockedResp returns a blocked response DNS message based on the +// constructor's blocking mode. +func (c *Constructor) NewBlockedResp(req *dns.Msg) (msg *dns.Msg, err error) { + switch m := c.blockingMode.(type) { + case *BlockingModeCustomIP: + return c.newBlockedCustomIPResp(req, m) + case *BlockingModeNullIP: + switch qt := req.Question[0].Qtype; qt { + case dns.TypeA, dns.TypeAAAA: + return c.NewBlockedNullIPResp(req) + default: + msg = c.NewBlockedRespRCode(req, dns.RcodeSuccess) + msg.Ns = c.newSOARecords(req) + } + case *BlockingModeNXDOMAIN: + msg = c.NewBlockedRespRCode(req, dns.RcodeNameError) + msg.Ns = c.newSOARecords(req) + case *BlockingModeREFUSED: + msg = c.NewBlockedRespRCode(req, dns.RcodeRefused) + msg.Ns = c.newSOARecords(req) + default: + // Consider unhandled sum type members as unrecoverable programmer + // errors. + panic(fmt.Errorf("unexpected type %T", c.blockingMode)) + } + + return msg, nil +} + +// NewRespRCode returns a response DNS message with given response code and a +// predefined authority section. +// +// Use [dns.RcodeSuccess] for a proper NODATA response, see +// https://www.rfc-editor.org/rfc/rfc2308#section-2.2. +func (c *Constructor) NewRespRCode(req *dns.Msg, rc RCode) (resp *dns.Msg) { + resp = c.NewResp(req) + resp.Rcode = int(rc) + + resp.Ns = c.newSOARecords(req) + + return resp +} + +// NewBlockedRespRCode returns a blocked response DNS message with given +// response code. +// +// TODO(e.burkov): Add SOA records to the response, like in +// [Constructor.NewRespRCode]. +func (c *Constructor) NewBlockedRespRCode(req *dns.Msg, rc RCode) (resp *dns.Msg) { + resp = c.NewResp(req) + resp.Rcode = int(rc) + + c.AddEDE(req, resp, dns.ExtendedErrorCodeFiltered) + + return resp +} + +// NewRespTXT returns a DNS TXT response message with the given strings as +// content. The TTL of the TXT answer is set to c.FilteredResponseTTL. +func (c *Constructor) NewRespTXT(req *dns.Msg, strs ...string) (msg *dns.Msg, err error) { + ans, err := c.NewAnswerTXT(req, strs) + if err != nil { + return nil, err + } + + msg = c.NewResp(req) + msg.Answer = append(msg.Answer, ans) + + return msg, nil +} + +// NewRespIP returns an A or AAAA DNS response message with the given IP +// addresses. If any IP address is nil, it is replaced by an unspecified (aka +// null) IP. The TTL is also set to c.FilteredResponseTTL. +func (c *Constructor) NewRespIP(req *dns.Msg, ips ...netip.Addr) (msg *dns.Msg, err error) { + switch qt := req.Question[0].Qtype; qt { + case dns.TypeA: + return c.newMsgA(req, ips...) + case dns.TypeAAAA: + return c.newMsgAAAA(req, ips...) + default: + return nil, fmt.Errorf("bad qtype for a or aaaa resp: %d", qt) + } +} + +// NewBlockedRespIP returns an A or AAAA DNS response message with the given IP +// addresses. The TTL of each record is set to c.FilteredResponseTTL. ips +// should not contain zero values due to the extended error code semantics, use +// [NewBlockedNullIPResp] for this case. +// +// TODO(a.garipov): Consider merging with [NewRespIP] if AddEDE with the Forged +// Answer code isn't used again. +func (c *Constructor) NewBlockedRespIP(req *dns.Msg, ips ...netip.Addr) (msg *dns.Msg, err error) { + switch qt := req.Question[0].Qtype; qt { + case dns.TypeA: + msg, err = c.newMsgA(req, ips...) + case dns.TypeAAAA: + msg, err = c.newMsgAAAA(req, ips...) + default: + return nil, fmt.Errorf("bad qtype for an ip resp: %d", qt) + } + + if err != nil { + return nil, err + } + + return msg, nil +} + +// NewBlockedNullIPResp returns a blocked A or AAAA DNS response message with an +// unspecified (aka null) IP address. The TTL of the record is set to the +// constructor's FilteredResponseTTL. +func (c *Constructor) NewBlockedNullIPResp(req *dns.Msg) (resp *dns.Msg, err error) { + switch qt := req.Question[0].Qtype; qt { + case dns.TypeA: + resp, err = c.newMsgA(req, netip.Addr{}) + case dns.TypeAAAA: + resp, err = c.newMsgAAAA(req, netip.Addr{}) + default: + err = fmt.Errorf("bad qtype for an ip resp: %d", qt) + } + + if err != nil { + return nil, err + } + + c.AddEDE(req, resp, dns.ExtendedErrorCodeFiltered) + + return resp, nil +} + +// AddEDE adds an Extended DNS Error (EDE) option to the blocked response +// message, if the feature is enabled in the Constructor and the request +// indicates EDNS support. It does not overwrite EDE if there already is one. +// req and resp must not be nil. +func (c *Constructor) AddEDE(req, resp *dns.Msg, code uint16) { + if !c.edeEnabled { + return + } + + reqOpt := req.IsEdns0() + if reqOpt == nil { + // Requestor doesn't implement EDNS, see + // https://datatracker.ietf.org/doc/html/rfc6891#section-7. + return + } + + respOpt := resp.IsEdns0() + if respOpt == nil { + respOpt = newOPT(c.cloner, reqOpt.UDPSize(), reqOpt.Do()) + resp.Extra = append(resp.Extra, respOpt) + } else if findEDE(respOpt) != nil { + // Do not add an EDE option if there already is one. + return + } + + sdeText := c.sdeForReqOpt(reqOpt) + + respOpt.Option = append(respOpt.Option, newEDNS0EDE(c.cloner, code, sdeText)) +} + +// findEDE returns the EDE option if there is one. opt must not be nil. +func findEDE(opt *dns.OPT) (ede *dns.EDNS0_EDE) { + for _, o := range opt.Option { + var ok bool + if ede, ok = o.(*dns.EDNS0_EDE); ok { + return ede + } + } + + return nil +} + +// sdeForReqOpt returns either the configured SDE text or empty string depending +// on the request's EDNS options. +func (c *Constructor) sdeForReqOpt(reqOpt *dns.OPT) (sde string) { + ede := findEDE(reqOpt) + if ede != nil && ede.InfoCode == 0 && ede.ExtraText == "" { + return c.sde + } + + return "" +} + +// newBlockedCustomIPResp returns a blocked DNS response message with either the +// custom IPs from the blocking mode options or a NODATA one. +func (c *Constructor) newBlockedCustomIPResp( + req *dns.Msg, + m *BlockingModeCustomIP, +) (msg *dns.Msg, err error) { + switch qt := req.Question[0].Qtype; qt { + case dns.TypeA: + if len(m.IPv4) > 0 { + return c.NewBlockedRespIP(req, m.IPv4...) + } + case dns.TypeAAAA: + if len(m.IPv6) > 0 { + return c.NewBlockedRespIP(req, m.IPv6...) + } + default: + // Go on. + } + + msg = c.NewBlockedRespRCode(req, dns.RcodeSuccess) + msg.Ns = c.newSOARecords(req) + + return msg, nil +} diff --git a/internal/dnsmsg/response_test.go b/internal/dnsmsg/response_test.go new file mode 100644 index 0000000..c9663f3 --- /dev/null +++ b/internal/dnsmsg/response_test.go @@ -0,0 +1,416 @@ +package dnsmsg_test + +import ( + "net/netip" + "strings" + "testing" + + "github.com/AdguardTeam/AdGuardDNS/internal/agdtest" + "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" + "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest" + "github.com/AdguardTeam/golibs/testutil" + "github.com/miekg/dns" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestConstructor_NewBlockedResp_nullIP(t *testing.T) { + t.Parallel() + + msgs := agdtest.NewConstructor(t) + reqExtra := dnsservertest.SectionExtra{ + dnsservertest.NewOPT(true, dns.MaxMsgSize, &dns.EDNS0_EDE{}), + } + + filteredSDE := dnsservertest.NewOPT(true, dns.MaxMsgSize, &dns.EDNS0_EDE{ + InfoCode: dns.ExtendedErrorCodeFiltered, + ExtraText: agdtest.SDEText, + }) + + testCases := []struct { + name string + wantAns []dns.RR + wantExtra []dns.RR + qt dnsmsg.RRType + }{{ + name: "a", + wantAns: []dns.RR{dnsservertest.NewA( + testFQDN, agdtest.FilteredResponseTTLSec, netip.IPv4Unspecified(), + )}, + wantExtra: []dns.RR{filteredSDE}, + qt: dns.TypeA, + }, { + name: "aaaa", + wantAns: []dns.RR{dnsservertest.NewAAAA( + testFQDN, agdtest.FilteredResponseTTLSec, netip.IPv6Unspecified(), + )}, + wantExtra: []dns.RR{filteredSDE}, + qt: dns.TypeAAAA, + }, { + name: "txt", + wantAns: nil, + wantExtra: []dns.RR{filteredSDE}, + qt: dns.TypeTXT, + }} + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + req := dnsservertest.NewReq(testFQDN, tc.qt, dns.ClassINET, reqExtra) + + resp, respErr := msgs.NewBlockedResp(req) + require.NoError(t, respErr) + require.NotNil(t, resp) + + assert.Equal(t, dns.RcodeSuccess, resp.Rcode) + assert.Equal(t, tc.wantAns, resp.Answer) + assert.Equal(t, tc.wantExtra, resp.Extra) + }) + } +} + +func TestConstructor_NewBlockedResp_customIP(t *testing.T) { + t.Parallel() + + cloner := agdtest.NewCloner() + + // TODO(a.garipov): Test the forged extra as well if the EDE with that code + // is used again. + reqExtra := dnsservertest.SectionExtra{ + dnsservertest.NewOPT(true, dns.MaxMsgSize, &dns.EDNS0_EDE{}), + } + filteredExtra := dnsservertest.NewOPT(true, dns.MaxMsgSize, &dns.EDNS0_EDE{ + InfoCode: dns.ExtendedErrorCodeFiltered, + ExtraText: agdtest.SDEText, + }) + + ansA := dnsservertest.NewA(testFQDN, agdtest.FilteredResponseTTLSec, testIPv4) + ansAAAA := dnsservertest.NewAAAA(testFQDN, agdtest.FilteredResponseTTLSec, testIPv6) + + testCases := []struct { + blockingMode dnsmsg.BlockingMode + name string + wantAnsA []dns.RR + wantAnsAAAA []dns.RR + wantExtraA []dns.RR + wantExtraAAAA []dns.RR + }{{ + blockingMode: &dnsmsg.BlockingModeCustomIP{ + IPv4: []netip.Addr{testIPv4}, + IPv6: []netip.Addr{testIPv6}, + }, + name: "both", + wantAnsA: []dns.RR{ansA}, + wantAnsAAAA: []dns.RR{ansAAAA}, + wantExtraA: nil, + wantExtraAAAA: nil, + }, { + blockingMode: &dnsmsg.BlockingModeCustomIP{ + IPv4: []netip.Addr{testIPv4}, + }, + name: "ipv4_only", + wantAnsA: []dns.RR{ansA}, + wantAnsAAAA: nil, + wantExtraA: nil, + wantExtraAAAA: []dns.RR{filteredExtra}, + }, { + blockingMode: &dnsmsg.BlockingModeCustomIP{ + IPv6: []netip.Addr{testIPv6}, + }, + name: "ipv6_only", + wantAnsA: nil, + wantAnsAAAA: []dns.RR{ansAAAA}, + wantExtraA: []dns.RR{filteredExtra}, + wantExtraAAAA: nil, + }, { + blockingMode: &dnsmsg.BlockingModeCustomIP{ + IPv4: []netip.Addr{}, + IPv6: []netip.Addr{}, + }, + name: "empty", + wantAnsA: nil, + wantAnsAAAA: nil, + wantExtraA: []dns.RR{filteredExtra}, + wantExtraAAAA: []dns.RR{filteredExtra}, + }} + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + msgs, err := dnsmsg.NewConstructor(&dnsmsg.ConstructorConfig{ + Cloner: cloner, + BlockingMode: tc.blockingMode, + StructuredErrors: agdtest.NewSDEConfig(true), + FilteredResponseTTL: agdtest.FilteredResponseTTL, + EDEEnabled: true, + }) + require.NoError(t, err) + + t.Run("a", func(t *testing.T) { + t.Parallel() + + req := dnsservertest.NewReq(testFQDN, dns.TypeA, dns.ClassINET, reqExtra) + resp, respErr := msgs.NewBlockedResp(req) + require.NoError(t, respErr) + require.NotNil(t, resp) + + assert.Equal(t, dns.RcodeSuccess, resp.Rcode) + assert.Equal(t, tc.wantAnsA, resp.Answer) + assert.Equal(t, tc.wantExtraA, resp.Extra) + }) + + t.Run("aaaa", func(t *testing.T) { + t.Parallel() + + req := dnsservertest.NewReq(testFQDN, dns.TypeAAAA, dns.ClassINET, reqExtra) + resp, respErr := msgs.NewBlockedResp(req) + require.NoError(t, respErr) + require.NotNil(t, resp) + + assert.Equal(t, dns.RcodeSuccess, resp.Rcode) + assert.Equal(t, tc.wantAnsAAAA, resp.Answer) + assert.Equal(t, tc.wantExtraAAAA, resp.Extra) + }) + }) + } +} + +func TestConstructor_NewBlockedResp_nodata(t *testing.T) { + t.Parallel() + + req := dnsservertest.NewReq(testFQDN, dns.TypeA, dns.ClassINET, dnsservertest.SectionExtra{ + dnsservertest.NewOPT(true, dns.MaxMsgSize, &dns.EDNS0_EDE{}), + }) + cloner := agdtest.NewCloner() + + wantExtra := []dns.RR{dnsservertest.NewOPT(true, dns.MaxMsgSize, &dns.EDNS0_EDE{ + InfoCode: dns.ExtendedErrorCodeFiltered, + ExtraText: agdtest.SDEText, + })} + + testCases := []struct { + blockingMode dnsmsg.BlockingMode + name string + rcode dnsmsg.RCode + }{{ + blockingMode: &dnsmsg.BlockingModeNXDOMAIN{}, + name: "nxdomain", + rcode: dns.RcodeNameError, + }, { + blockingMode: &dnsmsg.BlockingModeREFUSED{}, + name: "refused", + rcode: dns.RcodeRefused, + }} + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + msgs, err := dnsmsg.NewConstructor(&dnsmsg.ConstructorConfig{ + Cloner: cloner, + BlockingMode: tc.blockingMode, + StructuredErrors: agdtest.NewSDEConfig(true), + FilteredResponseTTL: agdtest.FilteredResponseTTL, + EDEEnabled: true, + }) + require.NoError(t, err) + + resp, err := msgs.NewBlockedResp(req) + require.NoError(t, err) + require.NotNil(t, resp) + + assert.Equal(t, tc.rcode, dnsmsg.RCode(resp.Rcode)) + assert.Empty(t, resp.Answer) + + require.Len(t, resp.Ns, 1) + + nsTTL := resp.Ns[0].Header().Ttl + assert.Equal(t, uint32(agdtest.FilteredResponseTTLSec), nsTTL) + + assert.Equal(t, wantExtra, resp.Extra) + }) + } +} + +func TestConstructor_NewBlockedResp_sde(t *testing.T) { + t.Parallel() + + reqEDNS := dnsservertest.NewReq(testFQDN, dns.TypeA, dns.ClassINET, dnsservertest.SectionExtra{ + dnsservertest.NewOPT(true, dns.MaxMsgSize, &dns.EDNS0_EDE{}), + }) + reqNoEDNS := dnsservertest.NewReq(testFQDN, dns.TypeA, dns.ClassINET) + + wantAns := []dns.RR{ + dnsservertest.NewA(testFQDN, agdtest.FilteredResponseTTLSec, netip.IPv4Unspecified()), + } + + testCases := []struct { + req *dns.Msg + sde *dnsmsg.StructuredDNSErrorsConfig + name string + wantExtra []dns.RR + ede bool + }{{ + req: reqEDNS, + sde: agdtest.NewSDEConfig(true), + name: "ede_sde", + wantExtra: []dns.RR{ + dnsservertest.NewOPT(true, dns.MaxMsgSize, &dns.EDNS0_EDE{ + InfoCode: dns.ExtendedErrorCodeFiltered, + ExtraText: agdtest.SDEText, + }), + }, + ede: true, + }, { + req: reqEDNS, + sde: agdtest.NewSDEConfig(false), + name: "ede_no_sde", + wantExtra: []dns.RR{ + dnsservertest.NewOPT(true, dns.MaxMsgSize, &dns.EDNS0_EDE{ + InfoCode: dns.ExtendedErrorCodeFiltered, + }), + }, + ede: true, + }, { + req: reqEDNS, + sde: agdtest.NewSDEConfig(false), + name: "no_ede", + wantExtra: nil, + ede: false, + }, { + req: reqNoEDNS, + sde: agdtest.NewSDEConfig(true), + name: "unsupported_ede_sde", + wantExtra: nil, + ede: true, + }, { + req: reqNoEDNS, + sde: agdtest.NewSDEConfig(false), + name: "unsupported_ede_no_sde", + wantExtra: nil, + ede: true, + }, { + req: reqNoEDNS, + sde: agdtest.NewSDEConfig(false), + name: "unsupported_no_ede", + wantExtra: nil, + ede: false, + }} + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + msgs, err := dnsmsg.NewConstructor(&dnsmsg.ConstructorConfig{ + Cloner: agdtest.NewCloner(), + BlockingMode: &dnsmsg.BlockingModeNullIP{}, + StructuredErrors: tc.sde, + FilteredResponseTTL: agdtest.FilteredResponseTTL, + EDEEnabled: tc.ede, + }) + require.NoError(t, err) + + resp, err := msgs.NewBlockedResp(tc.req) + require.NoError(t, err) + require.NotNil(t, resp) + + assert.Equal(t, dns.RcodeSuccess, resp.Rcode) + assert.Equal(t, wantAns, resp.Answer) + assert.Equal(t, tc.wantExtra, resp.Extra) + }) + } +} + +func TestConstructor_NewRespRCode(t *testing.T) { + t.Parallel() + + msgs := agdtest.NewConstructor(t) + req := dnsservertest.NewReq(testFQDN, dns.TypeA, dns.ClassINET, dnsservertest.SectionExtra{ + dnsservertest.NewOPT(true, dns.MaxMsgSize, &dns.EDNS0_EDE{}), + }) + + for rcode, name := range dns.RcodeToString { + t.Run(name, func(t *testing.T) { + t.Parallel() + + resp := msgs.NewRespRCode(req, dnsmsg.RCode(rcode)) + require.NotNil(t, resp) + require.Empty(t, resp.Answer) + + assert.Equal(t, rcode, resp.Rcode) + + require.Len(t, resp.Ns, 1) + + nsTTL := resp.Ns[0].Header().Ttl + assert.Equal(t, uint32(agdtest.FilteredResponseTTLSec), nsTTL) + + assert.Empty(t, resp.Extra) + }) + } +} + +func TestConstructor_NewRespTXT(t *testing.T) { + t.Parallel() + + msgs := agdtest.NewConstructor(t) + + req := dnsservertest.NewReq(testFQDN, dns.TypeTXT, dns.ClassINET, dnsservertest.SectionExtra{ + dnsservertest.NewOPT(true, dns.MaxMsgSize, &dns.EDNS0_EDE{}), + }) + tooLong := strings.Repeat("1", dnsmsg.MaxTXTStringLen+1) + + testCases := []struct { + name string + wantErrMsg string + strs []string + }{{ + name: "success", + wantErrMsg: "", + strs: []string{"111"}, + }, { + name: "success_many", + wantErrMsg: "", + strs: []string{"111", "222"}, + }, { + name: "success_nil", + wantErrMsg: "", + strs: nil, + }, { + name: "success_empty", + wantErrMsg: "", + strs: []string{}, + }, { + name: "too_long", + wantErrMsg: "txt string at index 0: too long: got 256 bytes, max 255", + strs: []string{tooLong}, + }} + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + resp, respErr := msgs.NewRespTXT(req, tc.strs...) + testutil.AssertErrorMsg(t, tc.wantErrMsg, respErr) + + if tc.wantErrMsg != "" { + return + } + + require.NotNil(t, resp) + + assert.Equal(t, dns.RcodeSuccess, resp.Rcode) + + require.Len(t, resp.Answer, 1) + + ans := resp.Answer[0] + txt := testutil.RequireTypeAssert[*dns.TXT](t, ans) + + assert.Equal(t, uint32(agdtest.FilteredResponseTTLSec), txt.Hdr.Ttl) + assert.Equal(t, tc.strs, txt.Txt) + + assert.Empty(t, resp.Extra) + }) + } +} diff --git a/internal/dnsmsg/rrconstructor.go b/internal/dnsmsg/rrconstructor.go index 0cb49ee..732bfd0 100644 --- a/internal/dnsmsg/rrconstructor.go +++ b/internal/dnsmsg/rrconstructor.go @@ -140,3 +140,36 @@ func newTXT(c *Cloner, txt []string) (rr *dns.TXT) { return rr } + +// newOPT constructs a new resource record of type OPT, optionally using c to +// allocate the structure. +func newOPT(c *Cloner, udpSize uint16, doBit bool) (opt *dns.OPT) { + if c == nil { + opt = &dns.OPT{} + } else { + opt = c.opt.rr.Get() + opt.Option = opt.Option[:0] + } + + opt.Hdr.Name = "." + opt.Hdr.Rrtype = dns.TypeOPT + opt.SetUDPSize(udpSize) + opt.SetDo(doBit) + + return opt +} + +// newEDNS0EDE constructs a new resource record of type EDNS0_EDE, optionally +// using c to allocate the structure. +func newEDNS0EDE(c *Cloner, infoCode uint16, extraText string) (opt *dns.EDNS0_EDE) { + if c == nil { + opt = &dns.EDNS0_EDE{} + } else { + opt = c.opt.ede.Get() + } + + opt.InfoCode = infoCode + opt.ExtraText = extraText + + return opt +} diff --git a/internal/dnsmsg/structurederror.go b/internal/dnsmsg/structurederror.go new file mode 100644 index 0000000..6d87943 --- /dev/null +++ b/internal/dnsmsg/structurederror.go @@ -0,0 +1,143 @@ +package dnsmsg + +import ( + "encoding/json" + "fmt" + "net/url" + "strings" + "unicode" + + "github.com/AdguardTeam/golibs/errors" +) + +// StructuredDNSErrorsConfig is the configuration structure for the experimental +// Structured DNS Errors feature. +// +// See https://www.ietf.org/archive/id/draft-ietf-dnsop-structured-dns-error-09.html. +// +// TODO(a.garipov): Add sub-error? +type StructuredDNSErrorsConfig struct { + // Justification for this particular DNS filtering. It must not be empty. + Justification string + + // Organization is an optional description of the organization. + Organization string + + // Contact information for the DNS service. It must not be empty. All + // items must not be nil and must be valid mailto, sips, or tel URLs. + Contact []*url.URL + + // Enabled, if true, enables the experimental Structured DNS Errors feature. + Enabled bool +} + +// iJSON returns the I-JSON representation of this configuration. c must be +// valid. +func (c *StructuredDNSErrorsConfig) iJSON() (s string) { + data := &structuredDNSErrorData{ + Justification: c.Justification, + Organization: c.Organization, + } + + for _, cont := range c.Contact { + data.Contact = append(data.Contact, cont.String()) + } + + // The only error that could be returned here is a type error from JSON + // encoding, and these should never happen. + b := errors.Must(json.Marshal(data)) + + return string(b) +} + +// structuredDNSErrorData is the structure for the JSON representation of the +// SDE data. +// +// TODO(a.garipov): Add sub-error? +type structuredDNSErrorData struct { + Justification string `json:"j"` + Organization string `json:"o,omitempty"` + Contact []string `json:"c"` +} + +// forbiddenRanges contains the ranges of forbidden code points for structured +// DNS errors according to the I-JSON specification. +// +// See https://datatracker.ietf.org/doc/html/rfc7493#section-2.1. +var forbiddenRanges = []*unicode.RangeTable{unicode.Cs, unicode.Noncharacter_Code_Point} + +// isSurrogateOrNonCharacter returns true if r is a surrogate or a non-character +// code point. +func isSurrogateOrNonCharacter(r rune) (ok bool) { + return unicode.IsOneOf(forbiddenRanges, r) +} + +// validateSDEString returns an error if s contains a surrogate or a +// non-character code point. It always returns nil for an empty string. +func validateSDEString(s string) (err error) { + if i := strings.IndexFunc(s, isSurrogateOrNonCharacter); i >= 0 { + return fmt.Errorf("bad code point at index %d", i) + } + + return nil +} + +// validate checks the configuration for errors. +func (c *StructuredDNSErrorsConfig) validate(edeEnabled bool) (err error) { + if c == nil { + return errors.ErrNoValue + } + + if !c.Enabled { + return nil + } else if !edeEnabled { + return errors.Error("ede must be enabled to enable sde") + } + + var errs []error + if len(c.Contact) == 0 { + err = fmt.Errorf("contact data: %w", errors.ErrEmptyValue) + errs = append(errs, err) + } + + for i, cont := range c.Contact { + err = validateSDEContactURL(cont) + if err != nil { + err = fmt.Errorf("contact data: at index %d: %w", i, err) + errs = append(errs, err) + } + } + + if c.Justification == "" { + err = fmt.Errorf("justification: %w", errors.ErrEmptyValue) + errs = append(errs, err) + } else if err = validateSDEString(c.Justification); err != nil { + err = fmt.Errorf("justification: %w", err) + errs = append(errs, err) + } + + if err = validateSDEString(c.Organization); err != nil { + err = fmt.Errorf("organization: %w", err) + errs = append(errs, err) + } + + return errors.Join(errs...) +} + +// validateSDEContactURL returns an error if u is not a valid SDE contact URL. +// It doesn't check for bad code points in the URL since [url.URL.String] +// escapes them. +func validateSDEContactURL(u *url.URL) (err error) { + if u == nil { + return errors.ErrNoValue + } + + switch strings.ToLower(u.Scheme) { + case "mailto", "sips", "tel": + // TODO(a.garipov): Consider more thorough validations for each scheme. + default: + return fmt.Errorf("scheme: %w: %q", errors.ErrBadEnumValue, u.Scheme) + } + + return nil +} diff --git a/internal/dnsserver/cache/cache.go b/internal/dnsserver/cache/cache.go index 8dcf89a..85ed4d6 100644 --- a/internal/dnsserver/cache/cache.go +++ b/internal/dnsserver/cache/cache.go @@ -31,8 +31,8 @@ type Middleware struct { // cacheMinTTL is the minimum supported TTL for cache items. cacheMinTTL time.Duration - // useTTLOverride shows if the TTL overrides logic should be used. - useTTLOverride bool + // overrideTTL shows if the TTL overrides logic should be used. + overrideTTL bool } // MiddlewareConfig is the configuration structure for NewMiddleware. @@ -49,8 +49,8 @@ type MiddlewareConfig struct { // MinTTL is the minimum supported TTL for cache items. MinTTL time.Duration - // UseTTLOverride shows if the TTL overrides logic should be used. - UseTTLOverride bool + // OverrideTTL shows if the TTL overrides logic should be used. + OverrideTTL bool } // NewMiddleware initializes a new LRU caching middleware. c must not be nil. @@ -63,10 +63,10 @@ func NewMiddleware(c *MiddlewareConfig) (m *Middleware) { } return &Middleware{ - metrics: metrics, - cache: gcache.New(c.Size).LRU().Build(), - cacheMinTTL: c.MinTTL, - useTTLOverride: c.UseTTLOverride, + metrics: metrics, + cache: gcache.New(c.Size).LRU().Build(), + cacheMinTTL: c.MinTTL, + overrideTTL: c.OverrideTTL, } } @@ -150,7 +150,7 @@ func (m *Middleware) set(msg *dns.Msg) (err error) { } exp := time.Duration(ttl) * time.Second - if m.useTTLOverride && msg.Rcode != dns.RcodeServerFailure { + if m.overrideTTL && msg.Rcode != dns.RcodeServerFailure { exp = max(exp, m.cacheMinTTL) setMinTTL(msg, uint32(exp.Seconds())) } diff --git a/internal/dnsserver/cache/cache_test.go b/internal/dnsserver/cache/cache_test.go index 6a47f54..aa4b291 100644 --- a/internal/dnsserver/cache/cache_test.go +++ b/internal/dnsserver/cache/cache_test.go @@ -186,9 +186,9 @@ func TestMiddleware_Wrap(t *testing.T) { withCache := dnsserver.WithMiddlewares( handler, cache.NewMiddleware(&cache.MiddlewareConfig{ - Size: 100, - MinTTL: minTTL, - UseTTLOverride: tc.minTTL != nil, + Size: 100, + MinTTL: minTTL, + OverrideTTL: tc.minTTL != nil, }), ) diff --git a/internal/dnsserver/dnsservertest/handler.go b/internal/dnsserver/dnsservertest/handler.go index ca0c6b3..940afe0 100644 --- a/internal/dnsserver/dnsservertest/handler.go +++ b/internal/dnsserver/dnsservertest/handler.go @@ -2,6 +2,7 @@ package dnsservertest import ( "context" + "fmt" "time" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver" @@ -13,9 +14,15 @@ import ( // AnswerTTL is the default TTL of the test handler's answers. const AnswerTTL time.Duration = 100 * time.Second -// CreateTestHandler creates a [dnsserver.Handler] with the specified +// NewDefaultHandler returns a simple handler that always returns a response +// with a single A record. +func NewDefaultHandler() (handler dnsserver.Handler) { + return NewDefaultHandlerWithCount(1) +} + +// NewDefaultHandlerWithCount creates a [dnsserver.Handler] with the specified // parameters. All responses will have the [TestAnsTTL] TTL. -func CreateTestHandler(recordsCount int) (h dnsserver.Handler) { +func NewDefaultHandlerWithCount(recordsCount int) (h dnsserver.Handler) { f := func(ctx context.Context, rw dnsserver.ResponseWriter, req *dns.Msg) (err error) { // Check that necessary context keys are set. si := dnsserver.MustServerInfoFromContext(ctx) @@ -49,8 +56,12 @@ func CreateTestHandler(recordsCount int) (h dnsserver.Handler) { return dnsserver.HandlerFunc(f) } -// DefaultHandler returns a simple handler that always returns a response with -// a single A record. -func DefaultHandler() (handler dnsserver.Handler) { - return CreateTestHandler(1) +// NewPanicHandler returns a DNS handler that panics with an error. +func NewPanicHandler() (handler dnsserver.Handler) { + f := func(ctx context.Context, rw dnsserver.ResponseWriter, req *dns.Msg) (err error) { + // TODO(a.garipov): Add a helper for these kinds of errors to golibs. + panic(fmt.Errorf("unexpected call to ServeDNS(%v, %v)", rw, req)) + } + + return dnsserver.HandlerFunc(f) } diff --git a/internal/dnsserver/dnsservertest/msg.go b/internal/dnsserver/dnsservertest/msg.go index e2c144a..e3cc89f 100644 --- a/internal/dnsserver/dnsservertest/msg.go +++ b/internal/dnsserver/dnsservertest/msg.go @@ -273,6 +273,22 @@ func NewNS(name string, ttl uint32, ns string) (rr dns.RR) { } } +// NewOPT constructs the new resource record of type OPT. +func NewOPT(do bool, udpSize uint16, opts ...dns.EDNS0) (rr dns.RR) { + opt := &dns.OPT{ + Hdr: dns.RR_Header{ + Name: ".", + Rrtype: dns.TypeOPT, + }, + Option: opts, + } + + opt.SetDo(do) + opt.SetUDPSize(udpSize) + + return opt +} + // NewECSExtra constructs a new OPT RR for the extra section. func NewECSExtra(ip net.IP, fam uint16, mask, scope uint8) (extra dns.RR) { return &dns.OPT{ @@ -300,11 +316,8 @@ func NewEDNS0Padding(msgLen int, UDPBufferSize uint16) (extra dns.RR) { padLen := requestPaddingBlockSize - msgLen%requestPaddingBlockSize // Truncate padding to fit in UDP buffer. - if msgLen+padLen > int(UDPBufferSize) { - padLen = int(UDPBufferSize) - msgLen - if padLen < 0 { - padLen = 0 - } + if bufSzInt := int(UDPBufferSize); msgLen+padLen > bufSzInt { + padLen = max(bufSzInt-msgLen, 0) } return &dns.OPT{ diff --git a/internal/dnsserver/forward/forward_test.go b/internal/dnsserver/forward/forward_test.go index 94215fa..5e9cb4e 100644 --- a/internal/dnsserver/forward/forward_test.go +++ b/internal/dnsserver/forward/forward_test.go @@ -23,7 +23,7 @@ func TestMain(m *testing.M) { const testTimeout = 1 * time.Second func TestHandler_ServeDNS(t *testing.T) { - srv, addr := dnsservertest.RunDNSServer(t, dnsservertest.DefaultHandler()) + srv, addr := dnsservertest.RunDNSServer(t, dnsservertest.NewDefaultHandler()) // No-fallbacks handler. handler := forward.NewHandler(&forward.HandlerConfig{ @@ -47,7 +47,7 @@ func TestHandler_ServeDNS(t *testing.T) { } func TestHandler_ServeDNS_fallbackNetError(t *testing.T) { - srv, _ := dnsservertest.RunDNSServer(t, dnsservertest.DefaultHandler()) + srv, _ := dnsservertest.RunDNSServer(t, dnsservertest.NewDefaultHandler()) handler := forward.NewHandler(&forward.HandlerConfig{ UpstreamsAddresses: []*forward.UpstreamPlainConfig{{ Network: forward.NetworkAny, diff --git a/internal/dnsserver/forward/healthcheck_test.go b/internal/dnsserver/forward/healthcheck_test.go index 3b014ef..254a336 100644 --- a/internal/dnsserver/forward/healthcheck_test.go +++ b/internal/dnsserver/forward/healthcheck_test.go @@ -20,7 +20,7 @@ func TestHandler_Refresh(t *testing.T) { var upstreamIsUp atomic.Bool var upstreamRequestsCount atomic.Int64 - defaultHandler := dnsservertest.DefaultHandler() + defaultHandler := dnsservertest.NewDefaultHandler() // This handler writes an empty message if upstreamUp flag is false. handlerFunc := dnsserver.HandlerFunc(func( diff --git a/internal/dnsserver/forward/upstreamplain_test.go b/internal/dnsserver/forward/upstreamplain_test.go index 860266b..b3123d6 100644 --- a/internal/dnsserver/forward/upstreamplain_test.go +++ b/internal/dnsserver/forward/upstreamplain_test.go @@ -36,7 +36,7 @@ func TestUpstreamPlain_Exchange(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - _, addr := dnsservertest.RunDNSServer(t, dnsservertest.DefaultHandler()) + _, addr := dnsservertest.RunDNSServer(t, dnsservertest.NewDefaultHandler()) ups := forward.NewUpstreamPlain(&forward.UpstreamPlainConfig{ Network: tc.network, Address: netip.MustParseAddrPort(addr), @@ -65,7 +65,7 @@ func TestUpstreamPlain_Exchange_truncated(t *testing.T) { rw.LocalAddr(), rw.RemoteAddr(), ) - handler := dnsservertest.DefaultHandler() + handler := dnsservertest.NewDefaultHandler() err = handler.ServeDNS(ctx, nrw, req) if err != nil { return err diff --git a/internal/dnsserver/go.mod b/internal/dnsserver/go.mod index 61ba79a..a9d8c73 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.1 +go 1.23.2 require ( - github.com/AdguardTeam/golibs v0.28.0 + github.com/AdguardTeam/golibs v0.30.1 github.com/ameshkov/dnscrypt/v2 v2.3.0 github.com/ameshkov/dnsstamps v1.0.3 github.com/bluele/gcache v0.0.2 @@ -11,12 +11,12 @@ require ( github.com/miekg/dns v1.1.62 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.1 - github.com/quic-go/quic-go v0.47.0 + github.com/prometheus/client_golang v1.20.5 + github.com/quic-go/quic-go v0.48.1 github.com/stretchr/testify v1.9.0 - golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 - golang.org/x/net v0.29.0 - golang.org/x/sys v0.25.0 + golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c + golang.org/x/net v0.30.0 + golang.org/x/sys v0.26.0 ) require ( @@ -26,22 +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-20240929191954-255acd752d31 // indirect + github.com/google/pprof v0.0.0-20241023014458-598669927662 // 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/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/common v0.60.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/quic-go/qpack v0.5.1 // indirect - go.uber.org/mock v0.4.0 // indirect - golang.org/x/crypto v0.27.0 // 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.18.0 // indirect - golang.org/x/time v0.6.0 // indirect - golang.org/x/tools v0.25.0 // indirect - google.golang.org/protobuf v1.34.2 // 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 + 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 e27a1c6..870bc08 100644 --- a/internal/dnsserver/go.sum +++ b/internal/dnsserver/go.sum @@ -1,4 +1,5 @@ -github.com/AdguardTeam/golibs v0.28.0 h1:SK1q8SqkkJ/61pp2abTmio90S4QpteYK9rtgROfnrb4= +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/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= @@ -25,10 +26,10 @@ 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-20240929191954-255acd752d31 h1:LcRdQWywSgfi5jPsYZ1r2avbbs5IQ5wtyhMBCcokyo4= -github.com/google/pprof v0.0.0-20240929191954-255acd752d31/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +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/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -47,18 +48,18 @@ github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.20.1 h1:IMJXHOD6eARkQpxo8KkhgEVFlBNm+nkrFUyGlIu7Na8= -github.com/prometheus/client_golang v1.20.1/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +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.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= +github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/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.47.0 h1:yXs3v7r2bm1wmPTYNLKAAJTHMYkPEsfYJmTazXrCZ7Y= -github.com/quic-go/quic-go v0.47.0/go.mod h1:3bCapYsJvXGZcipOHuu7plYtaV6tnF+z7wIFsU0WK9E= +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/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= @@ -69,29 +70,29 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= -go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +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.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= -golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= -golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +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/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= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/internal/dnsserver/prometheus/cache_test.go b/internal/dnsserver/prometheus/cache_test.go index 0b80841..05545ac 100644 --- a/internal/dnsserver/prometheus/cache_test.go +++ b/internal/dnsserver/prometheus/cache_test.go @@ -23,7 +23,7 @@ func TestCacheMetricsListener_integration_cache(t *testing.T) { }) handlerWithMiddleware := dnsserver.WithMiddlewares( - dnsservertest.DefaultHandler(), + dnsservertest.NewDefaultHandler(), cacheMiddleware, ) diff --git a/internal/dnsserver/prometheus/forward_test.go b/internal/dnsserver/prometheus/forward_test.go index e1bacf5..5913fa6 100644 --- a/internal/dnsserver/prometheus/forward_test.go +++ b/internal/dnsserver/prometheus/forward_test.go @@ -18,7 +18,7 @@ import ( // normal unit test, we create a forward handler, emulate a query and then // check if prom metrics were incremented. func TestForwardMetricsListener_integration_request(t *testing.T) { - srv, addr := dnsservertest.RunDNSServer(t, dnsservertest.DefaultHandler()) + srv, addr := dnsservertest.RunDNSServer(t, dnsservertest.NewDefaultHandler()) // Initialize a new forward.Handler and set the metrics listener. handler := forward.NewHandler(&forward.HandlerConfig{ diff --git a/internal/dnsserver/prometheus/ratelimit_test.go b/internal/dnsserver/prometheus/ratelimit_test.go index 1706794..97a126c 100644 --- a/internal/dnsserver/prometheus/ratelimit_test.go +++ b/internal/dnsserver/prometheus/ratelimit_test.go @@ -19,16 +19,21 @@ import ( // normal unit test, we create a cache middleware, emulate a query and then // check if prom metrics were incremented. func TestRateLimiterMetricsListener_integration_cache(t *testing.T) { - rps := 5 + const ( + count = 5 + ivl = time.Second + ) rl := ratelimit.NewBackoff(&ratelimit.BackoffConfig{ Allowlist: ratelimit.NewDynamicAllowlist([]netip.Prefix{}, []netip.Prefix{}), Period: time.Minute, Duration: time.Minute, - Count: uint(rps), + Count: count, ResponseSizeEstimate: 1 * datasize.KB, - IPv4RPS: uint(rps), - IPv6RPS: uint(rps), + IPv4Count: count, + IPv4Interval: ivl, + IPv6Count: count, + IPv6Interval: ivl, RefuseANY: true, }) rlMw, err := ratelimit.NewMiddleware(&ratelimit.MiddlewareConfig{ @@ -38,7 +43,7 @@ func TestRateLimiterMetricsListener_integration_cache(t *testing.T) { require.NoError(t, err) handlerWithMiddleware := dnsserver.WithMiddlewares( - dnsservertest.DefaultHandler(), + dnsservertest.NewDefaultHandler(), rlMw, ) @@ -55,7 +60,7 @@ func TestRateLimiterMetricsListener_integration_cache(t *testing.T) { err = handlerWithMiddleware.ServeDNS(ctx, nrw, req) require.NoError(t, err) - if i < rps { + if i < count { dnsservertest.RequireResponse(t, req, nrw.Msg(), 1, dns.RcodeSuccess, false) } else { require.Nil(t, nrw.Msg()) diff --git a/internal/dnsserver/prometheus/server_test.go b/internal/dnsserver/prometheus/server_test.go index 23c6910..f52a3b1 100644 --- a/internal/dnsserver/prometheus/server_test.go +++ b/internal/dnsserver/prometheus/server_test.go @@ -24,7 +24,7 @@ func TestServerMetricsListener_integration_requestLifetime(t *testing.T) { ConfigBase: dnsserver.ConfigBase{ Name: "test", Addr: "127.0.0.1:0", - Handler: dnsservertest.DefaultHandler(), + Handler: dnsservertest.NewDefaultHandler(), Metrics: prometheus.NewServerMetricsListener(testNamespace), }, } diff --git a/internal/dnsserver/ratelimit/backoff.go b/internal/dnsserver/ratelimit/backoff.go index 27fb89b..09d0ec0 100644 --- a/internal/dnsserver/ratelimit/backoff.go +++ b/internal/dnsserver/ratelimit/backoff.go @@ -36,19 +36,29 @@ type BackoffConfig struct { // as several responses. ResponseSizeEstimate datasize.ByteSize - // IPv4RPS is the maximum number of requests per second allowed from a - // single subnet for IPv4 addresses. Any requests above this rate are - // counted as the client's backoff count. RPS must be greater than zero. - IPv4RPS uint + // IPv4Count is the maximum number of requests per a specified interval + // allowed from a single subnet for IPv4 addresses. Any requests above this + // rate are counted as the client's backoff count. It must be greater than + // zero. + IPv4Count uint + + // IPv4Interval is the time during which to count the number of requests + // for IPv4 addresses. + IPv4Interval time.Duration // IPv4SubnetKeyLen is the length of the subnet prefix used to calculate // rate limiter bucket keys for IPv4 addresses. Must be greater than zero. IPv4SubnetKeyLen int - // IPv6RPS is the maximum number of requests per second allowed from a - // single subnet for IPv6 addresses. Any requests above this rate are - // counted as the client's backoff count. RPS must be greater than zero. - IPv6RPS uint + // IPv6Count is the maximum number of requests per a specified interval + // allowed from a single subnet for IPv6 addresses. Any requests above this + // rate are counted as the client's backoff count. It must be greater than + // zero. + IPv6Count uint + + // IPv6Interval is the time during which to count the number of requests + // for IPv6 addresses. + IPv6Interval time.Duration // IPv6SubnetKeyLen is the length of the subnet prefix used to calculate // rate limiter bucket keys for IPv6 addresses. Must be greater than zero. @@ -75,9 +85,11 @@ type Backoff struct { allowlist Allowlist respSzEst datasize.ByteSize count uint - ipv4rps uint + ipv4Count uint + ipv4Interval time.Duration ipv4SubnetKeyLen int - ipv6rps uint + ipv6Count uint + ipv6Interval time.Duration ipv6SubnetKeyLen int refuseANY bool } @@ -93,9 +105,11 @@ func NewBackoff(c *BackoffConfig) (l *Backoff) { allowlist: c.Allowlist, respSzEst: c.ResponseSizeEstimate, count: c.Count, - ipv4rps: c.IPv4RPS, + ipv4Count: c.IPv4Count, + ipv4Interval: c.IPv4Interval, ipv4SubnetKeyLen: c.IPv4SubnetKeyLen, - ipv6rps: c.IPv6RPS, + ipv6Count: c.IPv6Count, + ipv6Interval: c.IPv6Interval, ipv6SubnetKeyLen: c.IPv6SubnetKeyLen, refuseANY: c.RefuseANY, } @@ -133,12 +147,12 @@ func (l *Backoff) IsRateLimited( return true, false, nil } - rps := l.ipv4rps + count, ivl := l.ipv4Count, l.ipv4Interval if ip.Is6() { - rps = l.ipv6rps + count, ivl = l.ipv6Count, l.ipv6Interval } - return l.hasHitRateLimit(key, rps), false, nil + return l.hasHitRateLimit(key, count, ivl), false, nil } // validateAddr returns an error if addr is not a valid IPv4 or IPv6 address. @@ -198,15 +212,15 @@ func (l *Backoff) incBackoff(key string) { l.hitCounters.SetDefault(key, counter) } -// hasHitRateLimit checks value for a subnet with rps as a maximum number -// requests per second. -func (l *Backoff) hasHitRateLimit(subnetIPStr string, rps uint) (ok bool) { +// hasHitRateLimit checks if the value of requests for given subnet hit the +// maximum count of requests per given interval. +func (l *Backoff) hasHitRateLimit(subnetIPStr string, count uint, ivl time.Duration) (ok bool) { var r *RequestCounter rVal, ok := l.reqCounters.Get(subnetIPStr) if ok { r = rVal.(*RequestCounter) } else { - r = NewRequestCounter(rps, time.Second) + r = NewRequestCounter(count, ivl) l.reqCounters.SetDefault(subnetIPStr, r) } diff --git a/internal/dnsserver/ratelimit/ratelimit_test.go b/internal/dnsserver/ratelimit/ratelimit_test.go index 8b2168d..adf5f17 100644 --- a/internal/dnsserver/ratelimit/ratelimit_test.go +++ b/internal/dnsserver/ratelimit/ratelimit_test.go @@ -22,7 +22,10 @@ func TestMain(m *testing.M) { } func TestRatelimitMiddleware(t *testing.T) { - const rps = 10 + const ( + rps = 10 + ivl = time.Second + ) persistent := []netip.Prefix{ netip.MustParsePrefix("4.3.2.1/8"), @@ -99,9 +102,11 @@ func TestRatelimitMiddleware(t *testing.T) { Duration: time.Minute, Count: rps, ResponseSizeEstimate: 128 * datasize.B, - IPv4RPS: rps, + IPv4Count: rps, + IPv4Interval: ivl, IPv4SubnetKeyLen: 24, - IPv6RPS: rps, + IPv6Count: rps, + IPv6Interval: ivl, IPv6SubnetKeyLen: 48, RefuseANY: true, }) @@ -112,7 +117,7 @@ func TestRatelimitMiddleware(t *testing.T) { require.NoError(t, err) withMw := dnsserver.WithMiddlewares( - dnsservertest.CreateTestHandler(tc.respCount), + dnsservertest.NewDefaultHandlerWithCount(tc.respCount), rlMw, ) diff --git a/internal/dnsserver/serverbase.go b/internal/dnsserver/serverbase.go index 686bcec..5f54f1e 100644 --- a/internal/dnsserver/serverbase.go +++ b/internal/dnsserver/serverbase.go @@ -309,6 +309,10 @@ func (s *ServerBase) serveDNSMsgInternal( s.metrics.OnError(ctx, err) resp = genErrorResponse(req, dns.RcodeServerFailure) + if isNonCriticalNetError(err) { + addEDE(req, resp, dns.ExtendedErrorCodeNetworkError, "") + } + err = rw.WriteMsg(ctx, req, resp) if err != nil { log.Debug("[%d]: error writing a response: %s", req.Id, err) @@ -316,6 +320,28 @@ func (s *ServerBase) serveDNSMsgInternal( } } +// addEDE adds an Extended DNS Error (EDE) option to the blocked response +// message, if the request indicates EDNS support. +func addEDE(req, resp *dns.Msg, code uint16, text string) { + reqOpt := req.IsEdns0() + if reqOpt == nil { + // Requestor doesn't implement EDNS, see + // https://datatracker.ietf.org/doc/html/rfc6891#section-7. + return + } + + respOpt := resp.IsEdns0() + if respOpt == nil { + resp.SetEdns0(reqOpt.UDPSize(), reqOpt.Do()) + respOpt = resp.Extra[len(resp.Extra)-1].(*dns.OPT) + } + + respOpt.Option = append(respOpt.Option, &dns.EDNS0_EDE{ + InfoCode: code, + ExtraText: text, + }) +} + // acceptMsg checks if we should process the incoming DNS query. func (s *ServerBase) acceptMsg(m *dns.Msg) (action dns.MsgAcceptAction) { if m.Response { diff --git a/internal/dnsserver/serverbench_test.go b/internal/dnsserver/serverbench_test.go index c1c5bb1..b245661 100644 --- a/internal/dnsserver/serverbench_test.go +++ b/internal/dnsserver/serverbench_test.go @@ -37,7 +37,7 @@ func BenchmarkServeDNS(b *testing.B) { for _, tc := range testCases { b.Run(tc.name, func(b *testing.B) { - _, addr := dnsservertest.RunDNSServer(b, dnsservertest.DefaultHandler()) + _, addr := dnsservertest.RunDNSServer(b, dnsservertest.NewDefaultHandler()) // Prepare a test message. m := new(dns.Msg) @@ -105,7 +105,7 @@ func readMsg(resBuf []byte, network dnsserver.Network, conn net.Conn) (err error func BenchmarkServeTLS(b *testing.B) { tlsConfig := dnsservertest.CreateServerTLSConfig("example.org") - addr := dnsservertest.RunTLSServer(b, dnsservertest.DefaultHandler(), tlsConfig) + addr := dnsservertest.RunTLSServer(b, dnsservertest.NewDefaultHandler(), tlsConfig) // Prepare a test message m := new(dns.Msg) @@ -171,7 +171,7 @@ func BenchmarkServeDoH(b *testing.B) { for _, tc := range testCases { b.Run(tc.name, func(b *testing.B) { srv, err := dnsservertest.RunLocalHTTPSServer( - dnsservertest.DefaultHandler(), + dnsservertest.NewDefaultHandler(), tc.tlsConfig, nil, ) @@ -250,7 +250,7 @@ func BenchmarkServeDNSCrypt(b *testing.B) { Net: string(tc.network), } - s := dnsservertest.RunDNSCryptServer(b, dnsservertest.DefaultHandler()) + s := dnsservertest.RunDNSCryptServer(b, dnsservertest.NewDefaultHandler()) stamp := dnsstamps.ServerStamp{ ServerAddrStr: s.ServerAddr, ServerPk: s.ResolverPk, @@ -282,7 +282,7 @@ func BenchmarkServeDNSCrypt(b *testing.B) { func BenchmarkServeQUIC(b *testing.B) { tlsConfig := dnsservertest.CreateServerTLSConfig("example.org") srv, addr, err := dnsservertest.RunLocalQUICServer( - dnsservertest.DefaultHandler(), + dnsservertest.NewDefaultHandler(), tlsConfig, ) require.NoError(b, err) diff --git a/internal/dnsserver/serverdns_test.go b/internal/dnsserver/serverdns_test.go index 0f89ce8..0a7634c 100644 --- a/internal/dnsserver/serverdns_test.go +++ b/internal/dnsserver/serverdns_test.go @@ -20,7 +20,7 @@ import ( ) func TestServerDNS_StartShutdown(t *testing.T) { - _, _ = dnsservertest.RunDNSServer(t, dnsservertest.DefaultHandler()) + _, _ = dnsservertest.RunDNSServer(t, dnsservertest.NewDefaultHandler()) } func TestServerDNS_integration_query(t *testing.T) { @@ -42,7 +42,7 @@ func TestServerDNS_integration_query(t *testing.T) { {Name: "example.org.", Qtype: dns.TypeA, Qclass: dns.ClassINET}, }, }, - handler: dnsservertest.DefaultHandler(), + handler: dnsservertest.NewDefaultHandler(), wantRecordsCount: 1, wantRCode: dns.RcodeSuccess, }, { @@ -54,7 +54,7 @@ func TestServerDNS_integration_query(t *testing.T) { {Name: "example.org.", Qtype: dns.TypeA, Qclass: dns.ClassINET}, }, }, - handler: dnsservertest.DefaultHandler(), + handler: dnsservertest.NewDefaultHandler(), wantRecordsCount: 1, wantRCode: dns.RcodeSuccess, }, { @@ -89,7 +89,7 @@ func TestServerDNS_integration_query(t *testing.T) { }, }, }, - handler: dnsservertest.DefaultHandler(), + handler: dnsservertest.NewDefaultHandler(), wantMsg: func(t *testing.T, m *dns.Msg) { opt := m.IsEdns0() require.NotNil(t, opt) @@ -109,7 +109,7 @@ func TestServerDNS_integration_query(t *testing.T) { {Name: "example.org.", Qtype: dns.TypeA, Qclass: dns.ClassINET}, }, }, - handler: dnsservertest.DefaultHandler(), + handler: dnsservertest.NewDefaultHandler(), wantRecordsCount: 0, wantRCode: dns.RcodeFormatError, }, { @@ -122,7 +122,7 @@ func TestServerDNS_integration_query(t *testing.T) { {Name: "eXaMplE.oRg.", Qtype: dns.TypeA, Qclass: dns.ClassINET}, }, }, - handler: dnsservertest.DefaultHandler(), + handler: dnsservertest.NewDefaultHandler(), wantRecordsCount: 1, wantRCode: dns.RcodeSuccess, }, { @@ -136,7 +136,7 @@ func TestServerDNS_integration_query(t *testing.T) { {Name: "example.org.", Qtype: dns.TypeA, Qclass: dns.ClassINET}, }, }, - handler: dnsservertest.DefaultHandler(), + handler: dnsservertest.NewDefaultHandler(), wantRecordsCount: 0, wantRCode: dns.RcodeNotImplemented, }, { @@ -170,7 +170,7 @@ func TestServerDNS_integration_query(t *testing.T) { {Name: "example.org.", Qtype: dns.TypeA, Qclass: dns.ClassINET}, }, }, - handler: dnsservertest.DefaultHandler(), + handler: dnsservertest.NewDefaultHandler(), wantRecordsCount: 1, wantRCode: dns.RcodeSuccess, }, { @@ -185,7 +185,7 @@ func TestServerDNS_integration_query(t *testing.T) { }, }, // Set a handler that generates a large response - handler: dnsservertest.CreateTestHandler(64), + handler: dnsservertest.NewDefaultHandlerWithCount(64), wantRecordsCount: 0, wantRCode: dns.RcodeSuccess, wantTruncated: true, @@ -210,7 +210,7 @@ func TestServerDNS_integration_query(t *testing.T) { }, }, // Set a handler that generates a large response - handler: dnsservertest.CreateTestHandler(64), + handler: dnsservertest.NewDefaultHandlerWithCount(64), wantRecordsCount: 64, wantRCode: dns.RcodeSuccess, wantTruncated: false, @@ -226,7 +226,7 @@ func TestServerDNS_integration_query(t *testing.T) { }, }, // Set a handler that generates a large response - handler: dnsservertest.CreateTestHandler(64), + handler: dnsservertest.NewDefaultHandlerWithCount(64), // No truncate wantRecordsCount: 64, wantRCode: dns.RcodeSuccess, @@ -257,7 +257,7 @@ func TestServerDNS_integration_query(t *testing.T) { }, }, }, - handler: dnsservertest.DefaultHandler(), + handler: dnsservertest.NewDefaultHandler(), wantRecordsCount: 1, wantRCode: dns.RcodeSuccess, }} @@ -304,7 +304,7 @@ func TestServerDNS_integration_tcpQueriesPipelining(t *testing.T) { // As per RFC 7766 we should support queries pipelining for TCP, that is // server must be able to process incoming queries in parallel and write // responses possibly out of order within the same connection. - _, addr := dnsservertest.RunDNSServer(t, dnsservertest.DefaultHandler()) + _, addr := dnsservertest.RunDNSServer(t, dnsservertest.NewDefaultHandler()) // Establish a connection. conn, err := net.Dial("tcp", addr) @@ -372,7 +372,7 @@ func TestServerDNS_integration_tcpQueriesPipelining(t *testing.T) { } func TestServerDNS_integration_udpMsgIgnore(t *testing.T) { - _, addr := dnsservertest.RunDNSServer(t, dnsservertest.DefaultHandler()) + _, addr := dnsservertest.RunDNSServer(t, dnsservertest.NewDefaultHandler()) conn, err := net.Dial("udp", addr) require.Nil(t, err) @@ -469,7 +469,7 @@ func TestServerDNS_integration_tcpMsgIgnore(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - _, addr := dnsservertest.RunDNSServer(t, dnsservertest.DefaultHandler()) + _, addr := dnsservertest.RunDNSServer(t, dnsservertest.NewDefaultHandler()) conn, err := net.Dial("tcp", addr) require.Nil(t, err) diff --git a/internal/dnsserver/serverdnscrypt_test.go b/internal/dnsserver/serverdnscrypt_test.go index 48adcb9..3365b69 100644 --- a/internal/dnsserver/serverdnscrypt_test.go +++ b/internal/dnsserver/serverdnscrypt_test.go @@ -49,7 +49,7 @@ func TestServerDNSCrypt_integration_query(t *testing.T) { name: "udp_truncate_response", network: dnsserver.NetworkUDP, // Set a handler that generates a large response - handler: dnsservertest.CreateTestHandler(64), + handler: dnsservertest.NewDefaultHandlerWithCount(64), // DNSCrypt server removes all records from a truncated response expectedRecordsCount: 0, expectedRCode: dns.RcodeSuccess, @@ -66,7 +66,7 @@ func TestServerDNSCrypt_integration_query(t *testing.T) { name: "udp_edns0_no_truncate", network: dnsserver.NetworkUDP, // Set a handler that generates a large response - handler: dnsservertest.CreateTestHandler(64), + handler: dnsservertest.NewDefaultHandlerWithCount(64), expectedRecordsCount: 64, expectedRCode: dns.RcodeSuccess, expectedTruncated: false, @@ -89,7 +89,7 @@ func TestServerDNSCrypt_integration_query(t *testing.T) { t.Run(tc.name, func(t *testing.T) { handler := tc.handler if tc.handler == nil { - handler = dnsservertest.DefaultHandler() + handler = dnsservertest.NewDefaultHandler() } s := dnsservertest.RunDNSCryptServer(t, handler) diff --git a/internal/dnsserver/serverhttps.go b/internal/dnsserver/serverhttps.go index d24e323..848d80b 100644 --- a/internal/dnsserver/serverhttps.go +++ b/internal/dnsserver/serverhttps.go @@ -19,6 +19,7 @@ import ( "github.com/AdguardTeam/golibs/httphdr" "github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/netutil" + "github.com/AdguardTeam/golibs/netutil/urlutil" "github.com/miekg/dns" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/http3" @@ -304,9 +305,9 @@ func (s *ServerHTTPS) serveHTTPS(ctx context.Context, hs *http.Server, l net.Lis // application won't be able to continue listening to DoH. defer s.handlePanicAndExit(ctx) - scheme := "https" + scheme := urlutil.SchemeHTTPS if s.conf.TLSConfig == nil { - scheme = "http" + scheme = urlutil.SchemeHTTP } u := &url.URL{ diff --git a/internal/dnsserver/serverhttps_test.go b/internal/dnsserver/serverhttps_test.go index f91a555..9b9c9c6 100644 --- a/internal/dnsserver/serverhttps_test.go +++ b/internal/dnsserver/serverhttps_test.go @@ -109,7 +109,7 @@ func TestServerHTTPS_integration_serveRequests(t *testing.T) { tlsConfig := dnsservertest.CreateServerTLSConfig("example.org") srv, err := dnsservertest.RunLocalHTTPSServer( - dnsservertest.DefaultHandler(), + dnsservertest.NewDefaultHandler(), tlsConfig, nil, ) @@ -146,7 +146,7 @@ func TestServerHTTPS_integration_nonDNSHandler(t *testing.T) { }) srv, err := dnsservertest.RunLocalHTTPSServer( - dnsservertest.DefaultHandler(), + dnsservertest.NewDefaultHandler(), nil, testHandler, ) @@ -321,7 +321,7 @@ func TestDNSMsgToJSONMsg(t *testing.T) { func TestServerHTTPS_integration_ENDS0Padding(t *testing.T) { tlsConfig := dnsservertest.CreateServerTLSConfig("example.org") srv, err := dnsservertest.RunLocalHTTPSServer( - dnsservertest.DefaultHandler(), + dnsservertest.NewDefaultHandler(), tlsConfig, nil, ) @@ -346,7 +346,7 @@ func TestServerHTTPS_integration_ENDS0Padding(t *testing.T) { func TestServerHTTPS_0RTT(t *testing.T) { tlsConfig := dnsservertest.CreateServerTLSConfig("example.org") srv, err := dnsservertest.RunLocalHTTPSServer( - dnsservertest.DefaultHandler(), + dnsservertest.NewDefaultHandler(), tlsConfig, nil, ) @@ -514,7 +514,7 @@ func createDoH3Client( tlsConfig = tlsConfig.Clone() tlsConfig.NextProtos = []string{http3.NextProtoH3} - transport := &http3.RoundTripper{ + transport := &http3.Transport{ DisableCompression: true, Dial: func( ctx context.Context, diff --git a/internal/dnsserver/serverhttpsjson.go b/internal/dnsserver/serverhttpsjson.go index 19a3db5..9415942 100644 --- a/internal/dnsserver/serverhttpsjson.go +++ b/internal/dnsserver/serverhttpsjson.go @@ -7,13 +7,18 @@ import ( "strconv" "strings" + "github.com/AdguardTeam/golibs/errors" "github.com/miekg/dns" ) // JSONMsg represents a *dns.Msg in the JSON format defined here: // https://developers.google.com/speed/public-dns/docs/doh/json#dns_response_in_json -// Note, that we do not implement some parts of it. There is no "Comment" field -// and there's no "edns_client_subnet". +// +// NOTE: This API differs from the Google one in the following ways: +// 1. The "Comment" field is not implemented. +// 2. The "edns_client_subnet" query parameter is not supported. +// 3. The "sde" query parameter is added and supported for the experimental +// Structured DNS Errors feature. type JSONMsg struct { Question []JSONQuestion `json:"Question"` Answer []JSONAnswer `json:"Answer"` @@ -26,13 +31,13 @@ type JSONMsg struct { Status int `json:"Status"` } -// JSONQuestion is a part of JSONMsg definition. +// JSONQuestion is a part of [JSONMsg] definition. type JSONQuestion struct { Name string `json:"name"` Type uint16 `json:"type"` } -// JSONAnswer is a part of JSONMsg definition. +// JSONAnswer is a part of [JSONMsg] definition. type JSONAnswer struct { Name string `json:"name"` Data string `json:"data"` @@ -41,7 +46,7 @@ type JSONAnswer struct { Class uint16 `json:"class"` } -// DNSMsgToJSONMsg converts the *dns.Msg to the JSON format (*JSONMsg). +// DNSMsgToJSONMsg converts the *dns.Msg to the JSON format. func DNSMsgToJSONMsg(m *dns.Msg) (msg *JSONMsg) { msg = &JSONMsg{ Status: m.Rcode, @@ -74,9 +79,9 @@ func DNSMsgToJSONMsg(m *dns.Msg) (msg *JSONMsg) { func rrToJSON(rr dns.RR) (j JSONAnswer) { hdr := rr.Header() - // Extracting the RR value is a bit tricky since miekg/dns does not - // expose the necessary methods. This way we can benefit from the - // proper string serialization code that's used inside miekg/dns. + // Extracting the RR value is a bit tricky since miekg/dns does not expose + // the necessary methods. This way we can benefit from the proper string + // serialization code that's used inside miekg/dns. hdrStr := hdr.String() valStr := rr.String() data := strings.TrimLeft(strings.TrimPrefix(valStr, hdrStr), " ") @@ -90,19 +95,17 @@ func rrToJSON(rr dns.RR) (j JSONAnswer) { } } -// dnsMsgToJSON converts the *dns.Msg to the JSON format (JSONMsg) and returns -// it in the serialized form. +// dnsMsgToJSON converts the *dns.Msg to the JSON format and returns it in the +// serialized form. func dnsMsgToJSON(m *dns.Msg) (b []byte, err error) { - msg := DNSMsgToJSONMsg(m) - return json.Marshal(msg) + return json.Marshal(DNSMsgToJSONMsg(m)) } // httpRequestToMsgJSON builds a DNS message from the request parameters. -// We use the same parameters as the ones defined here: -// https://developers.google.com/speed/public-dns/docs/doh/json#supported_parameters -// Some parameters are not supported: "ct", "edns_client_subnet". -func httpRequestToMsgJSON(req *http.Request) (b []byte, err error) { - q := req.URL.Query() +// +// See [JSONMsg]. +func httpRequestToMsgJSON(httpReq *http.Request) (b []byte, err error) { + q := httpReq.URL.Query() // Query name, the only required parameter. name := q.Get("name") @@ -111,71 +114,91 @@ func httpRequestToMsgJSON(req *http.Request) (b []byte, err error) { return nil, ErrInvalidArgument } - // RR type can be represented as a number in [1, 65535] or a - // canonical string (case-insensitive, such as A or AAAA). - var t uint16 - t, err = urlQueryParameterToUint16(q, "type", dns.TypeA, dns.StringToType) + // RR type can be represented as a number in [1, 65535] or a canonical + // string (case-insensitive, such as A or AAAA). + qt, err := urlQueryParameterToUint16(q, "type", dns.TypeA, dns.StringToType) if err != nil { + // Don't wrap the error, because it's informative enough as is. return nil, err } - // Query class can be represented as a number in [1, 65535] or a - // canonical string (case-insensitive). - var qc uint16 - qc, err = urlQueryParameterToUint16(q, "qc", dns.ClassINET, dns.StringToClass) + // Query class can be represented as a number in [1, 65535] or a canonical + // string (case-insensitive). + qc, err := urlQueryParameterToUint16(q, "qc", dns.ClassINET, dns.StringToClass) if err != nil { + // Don't wrap the error, because it's informative enough as is. return nil, err } - // The CD (Checking Disabled) flag. Use cd=1, or cd=true to disable - // DNSSEC validation; use cd=0, cd=false, or no cd parameter to - // enable DNSSEC validation. - var cd bool - cd, err = urlQueryParameterToBoolean(q, "cd", false) + // The CD (Checking Disabled) flag. Use cd=1, or cd=true to disable DNSSEC + // validation; use cd=0, cd=false, or no cd parameter to enable DNSSEC + // validation. + cd, err := urlQueryParameterToBoolean(q, "cd", false) if err != nil { + // Don't wrap the error, because it's informative enough as is. return nil, err } - // The DO (DNSSEC OK) flag. Use do=1, or do=true to include DNSSEC - // records (RRSIG, NSEC, NSEC3); use do=0, do=false, or no do parameter - // to omit DNSSEC records. - var do bool - do, err = urlQueryParameterToBoolean(q, "do", false) + // The DO (DNSSEC OK) flag. Use do=1 (or do=true) to include DNSSEC records + // (RRSIG, NSEC, NSEC3); use do=0 (do=false) or no do parameter to omit + // DNSSEC records. + do, err := urlQueryParameterToBoolean(q, "do", false) if err != nil { + // Don't wrap the error, because it's informative enough as is. + return nil, err + } + + // The experimental Structured DNS Errors feature. + sde, err := urlQueryParameterToBoolean(q, "sde", false) + if err != nil { + // Don't wrap the error, because it's informative enough as is. return nil, err } // Now build a DNS message with all those parameters - r := &dns.Msg{ + req := &dns.Msg{ MsgHdr: dns.MsgHdr{ Id: dns.Id(), CheckingDisabled: cd, RecursionDesired: true, }, - Question: []dns.Question{ - { - Name: dns.Fqdn(name), - Qtype: t, - Qclass: qc, - }, - }, + Question: []dns.Question{{ + Name: dns.Fqdn(name), + Qtype: qt, + Qclass: qc, + }}, } - if do { - r.SetEdns0(dns.MaxMsgSize, do) + setEDNSFromQuery(req, do, sde) + + return req.Pack() +} + +// setEDNSFromQuery sets the EDNS parameters on the request depending on the +// query parameters. +func setEDNSFromQuery(req *dns.Msg, do, sde bool) { + if !do && !sde { + return } - return r.Pack() + req.SetEdns0(dns.MaxMsgSize, do) + + if sde { + opt := req.Extra[0].(*dns.OPT) + opt.Option = append(opt.Option, &dns.EDNS0_EDE{}) + } } // urlQueryParameterToUint16 is a helper function that extracts a uint16 value -// from a query parameter. See httpRequestToMsgJSON to see how it's used. +// from a query parameter. func urlQueryParameterToUint16( q url.Values, name string, defaultValue uint16, strValuesMap map[string]uint16, ) (v uint16, err error) { + defer func() { err = errors.Annotate(err, "parameter %q: %w", name) }() + strValue := q.Get(name) uintValue, convErr := strconv.ParseUint(strValue, 10, 16) switch { @@ -199,12 +222,8 @@ func urlQueryParameterToUint16( } // urlQueryParameterToBoolean is a helper function that extracts a boolean value -// from a query parameter. See httpRequestToMsgJSON to see how it's used. -func urlQueryParameterToBoolean( - q url.Values, - name string, - defaultValue bool, -) (v bool, err error) { +// from a query parameter. +func urlQueryParameterToBoolean(q url.Values, name string, defaultValue bool) (v bool, err error) { strValue := q.Get(name) switch strValue { case "1", "true", "True": diff --git a/internal/dnsserver/serverquic_test.go b/internal/dnsserver/serverquic_test.go index b2e08bc..0eadfcd 100644 --- a/internal/dnsserver/serverquic_test.go +++ b/internal/dnsserver/serverquic_test.go @@ -25,7 +25,7 @@ import ( func TestServerQUIC_integration_query(t *testing.T) { tlsConfig := dnsservertest.CreateServerTLSConfig("example.org") srv, addr, err := dnsservertest.RunLocalQUICServer( - dnsservertest.DefaultHandler(), + dnsservertest.NewDefaultHandler(), tlsConfig, ) require.NoError(t, err) @@ -74,7 +74,7 @@ func TestServerQUIC_integration_query(t *testing.T) { func TestServerQUIC_integration_ENDS0Padding(t *testing.T) { tlsConfig := dnsservertest.CreateServerTLSConfig("example.org") srv, addr, err := dnsservertest.RunLocalQUICServer( - dnsservertest.DefaultHandler(), + dnsservertest.NewDefaultHandler(), tlsConfig, ) require.NoError(t, err) @@ -108,7 +108,7 @@ func TestServerQUIC_integration_ENDS0Padding(t *testing.T) { func TestServerQUIC_integration_0RTT(t *testing.T) { tlsConfig := dnsservertest.CreateServerTLSConfig("example.org") srv, addr, err := dnsservertest.RunLocalQUICServer( - dnsservertest.DefaultHandler(), + dnsservertest.NewDefaultHandler(), tlsConfig, ) require.NoError(t, err) @@ -146,7 +146,7 @@ func TestServerQUIC_integration_0RTT(t *testing.T) { func TestServerQUIC_integration_largeQuery(t *testing.T) { tlsConfig := dnsservertest.CreateServerTLSConfig("example.org") srv, addr, err := dnsservertest.RunLocalQUICServer( - dnsservertest.DefaultHandler(), + dnsservertest.NewDefaultHandler(), tlsConfig, ) require.NoError(t, err) diff --git a/internal/dnsserver/servertls_test.go b/internal/dnsserver/servertls_test.go index 5ab1a92..16af47c 100644 --- a/internal/dnsserver/servertls_test.go +++ b/internal/dnsserver/servertls_test.go @@ -17,7 +17,7 @@ import ( func TestServerTLS_integration_queryTLS(t *testing.T) { tlsConfig := dnsservertest.CreateServerTLSConfig("example.org") - addr := dnsservertest.RunTLSServer(t, dnsservertest.DefaultHandler(), tlsConfig) + addr := dnsservertest.RunTLSServer(t, dnsservertest.NewDefaultHandler(), tlsConfig) // Create a test message. req := new(dns.Msg) @@ -94,7 +94,7 @@ func TestServerTLS_integration_msgIgnore(t *testing.T) { t.Parallel() tlsConfig := dnsservertest.CreateServerTLSConfig("example.org") - h := dnsservertest.DefaultHandler() + h := dnsservertest.NewDefaultHandler() addr := dnsservertest.RunTLSServer(t, h, tlsConfig) conn, err := tls.Dial("tcp", addr.String(), tlsConfig) @@ -120,7 +120,7 @@ func TestServerTLS_integration_msgIgnore(t *testing.T) { func TestServerTLS_integration_noTruncateQuery(t *testing.T) { // Handler that writes a huge response which would not fit // into a UDP response, but it should fit a TCP response just okay. - handler := dnsservertest.CreateTestHandler(64) + handler := dnsservertest.NewDefaultHandlerWithCount(64) tlsConfig := dnsservertest.CreateServerTLSConfig("example.org") addr := dnsservertest.RunTLSServer(t, handler, tlsConfig) @@ -155,7 +155,7 @@ func TestServerTLS_integration_queriesPipelining(t *testing.T) { // i.e. we should be able to process incoming queries in parallel and // write responses out of order. tlsConfig := dnsservertest.CreateServerTLSConfig("example.org") - addr := dnsservertest.RunTLSServer(t, dnsservertest.DefaultHandler(), tlsConfig) + addr := dnsservertest.RunTLSServer(t, dnsservertest.NewDefaultHandler(), tlsConfig) // First - establish a connection conn, err := tls.Dial("tcp", addr.String(), tlsConfig) @@ -221,7 +221,7 @@ func TestServerTLS_integration_queriesPipelining(t *testing.T) { func TestServerTLS_integration_ENDS0Padding(t *testing.T) { tlsConfig := dnsservertest.CreateServerTLSConfig("example.org") - addr := dnsservertest.RunTLSServer(t, dnsservertest.DefaultHandler(), tlsConfig) + addr := dnsservertest.RunTLSServer(t, dnsservertest.NewDefaultHandler(), tlsConfig) req := dnsservertest.CreateMessage("example.org.", dns.TypeA) req.Extra = []dns.RR{dnsservertest.NewEDNS0Padding(req.Len(), dns.DefaultMsgSize)} diff --git a/internal/dnssvc/config.go b/internal/dnssvc/config.go new file mode 100644 index 0000000..1c01ad9 --- /dev/null +++ b/internal/dnssvc/config.go @@ -0,0 +1,228 @@ +package dnssvc + +import ( + "log/slog" + "net/http" + "time" + + "github.com/AdguardTeam/AdGuardDNS/internal/access" + "github.com/AdguardTeam/AdGuardDNS/internal/agd" + "github.com/AdguardTeam/AdGuardDNS/internal/agdcache" + "github.com/AdguardTeam/AdGuardDNS/internal/billstat" + "github.com/AdguardTeam/AdGuardDNS/internal/cmd/plugin" + "github.com/AdguardTeam/AdGuardDNS/internal/connlimiter" + "github.com/AdguardTeam/AdGuardDNS/internal/dnscheck" + "github.com/AdguardTeam/AdGuardDNS/internal/dnsdb" + "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" + "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver" + "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext" + "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit" + "github.com/AdguardTeam/AdGuardDNS/internal/errcoll" + "github.com/AdguardTeam/AdGuardDNS/internal/filter" + "github.com/AdguardTeam/AdGuardDNS/internal/geoip" + "github.com/AdguardTeam/AdGuardDNS/internal/profiledb" + "github.com/AdguardTeam/AdGuardDNS/internal/querylog" + "github.com/AdguardTeam/AdGuardDNS/internal/rulestat" + "github.com/prometheus/client_golang/prometheus" +) + +// Config is the configuration of the AdGuard DNS service. +type Config struct { + // Handlers are the handlers to use in this DNS service. + Handlers Handlers + + // NewListener, when set, is used instead of the package-level function + // [NewListener] when creating a DNS listener. + // + // TODO(a.garipov): This is only used for tests. Replace with a + // [netext.ListenConfig]. + NewListener NewListenerFunc + + // Cloner is used to clone messages more efficiently by disposing of parts + // of DNS responses for later reuse. It must not be nil. + Cloner *dnsmsg.Cloner + + // ControlConf is the configuration of socket options. + ControlConf *netext.ControlConfig + + // ConnLimiter, if not nil, is used to limit the number of simultaneously + // active stream-connections. + ConnLimiter *connlimiter.Limiter + + // ErrColl is the error collector that is used to collect critical and + // non-critical errors. It must not be nil. + ErrColl errcoll.Interface + + // NonDNS is the handler for non-DNS HTTP requests. It must not be nil. + NonDNS http.Handler + + // MetricsNamespace is a namespace for Prometheus metrics. It must be a + // valid Prometheus metric label. + MetricsNamespace string + + // ServerGroups are the DNS server groups. Each element must be non-nil. + ServerGroups []*agd.ServerGroup + + // HandleTimeout defines the timeout for the entire handling of a single + // query. It must be greater than zero. + HandleTimeout time.Duration +} + +// NewListenerFunc is the type for DNS listener constructors. +type NewListenerFunc func( + srv *agd.Server, + baseConf dnsserver.ConfigBase, + nonDNS http.Handler, +) (l Listener, err error) + +// Listener is a type alias for dnsserver.Server to make internal naming more +// consistent. +type Listener = dnsserver.Server + +// HandlersConfig is the configuration necessary to create or wrap the main DNS +// handler. +// +// TODO(a.garipov): Consider adding validation functions. +type HandlersConfig struct { + // BaseLogger is used to create loggers with custom prefixes for middlewares + // and the service itself. It must not be nil. + BaseLogger *slog.Logger + + // Cloner is used to clone messages more efficiently by disposing of parts + // of DNS responses for later reuse. It must not be nil. + Cloner *dnsmsg.Cloner + + // Cache is the configuration for the DNS cache. + Cache *CacheConfig + + // HumanIDParser is used to normalize and parse human-readable device + // identifiers. It must not be nil if at least one server group has + // profiles enabled. + HumanIDParser *agd.HumanIDParser + + // Messages is the message constructor used to create blocked and other + // messages for this DNS service. It must not be nil. + Messages *dnsmsg.Constructor + + // PluginRegistry is used to override configuration parameters. + PluginRegistry *plugin.Registry + + // StructuredErrors is the configuration for the experimental Structured DNS + // Errors feature in the profiles' message constructors. It must not be + // nil. + StructuredErrors *dnsmsg.StructuredDNSErrorsConfig + + // AccessManager is used to block requests. It must not be nil. + AccessManager access.Interface + + // BillStat is used to collect billing statistics. It must not be nil. + BillStat billstat.Recorder + + // CacheManager is the global cache manager. It must not be nil. + CacheManager agdcache.Manager + + // DNSCheck is used by clients to check if they use AdGuard DNS. It must + // not be nil. + DNSCheck dnscheck.Interface + + // DNSDB is used to update anonymous statistics about DNS queries. It must + // not be nil. + DNSDB dnsdb.Interface + + // ErrColl is the error collector that is used to collect critical and + // non-critical errors. It must not be nil. + ErrColl errcoll.Interface + + // FilterStorage is the storage of all filters. It must not be nil. + FilterStorage filter.Storage + + // GeoIP is the GeoIP database used to detect geographic data about IP + // addresses in requests and responses. It must not be nil. + GeoIP geoip.Interface + + // Handler is the ultimate handler of the DNS query to be wrapped by + // middlewares. It must not be nil. + Handler dnsserver.Handler + + // HashMatcher is the safe-browsing hash matcher for TXT queries. It must + // not be nil. + HashMatcher filter.HashMatcher + + // ProfileDB is the AdGuard DNS profile database used to fetch data about + // profiles, devices, and so on. It must not be nil if at least one server + // group has profiles enabled. + ProfileDB profiledb.Interface + + // PrometheusRegisterer is used to register Prometheus metrics. It must not + // be nil. + PrometheusRegisterer prometheus.Registerer + + // QueryLog is used to write the logs into. It must not be nil. + QueryLog querylog.Interface + + // RateLimit is used for allow or decline requests. It must not be nil. + RateLimit ratelimit.Interface + + // RuleStat is used to collect statistics about matched filtering rules and + // rule lists. It must not be nil. + RuleStat rulestat.Interface + + // MetricsNamespace is a namespace for Prometheus metrics. It must be a + // valid Prometheus metric label. + MetricsNamespace string + + // FilteringGroups are the DNS filtering groups. Each element must be + // non-nil. + FilteringGroups map[agd.FilteringGroupID]*agd.FilteringGroup + + // ServerGroups are the DNS server groups for which to build handlers. Each + // element and its servers must be non-nil. + ServerGroups []*agd.ServerGroup + + // EDEEnabled enables the addition of the Extended DNS Error (EDE) codes in + // the profiles' message constructors. + EDEEnabled bool +} + +// Handlers contains the map of handlers for each server of each server group. +// The pointers are the same as those passed in a [HandlersConfig] to +// [NewHandlers]. +type Handlers map[HandlerKey]dnsserver.Handler + +// HandlerKey is a key for the [Handlers] map. +type HandlerKey struct { + Server *agd.Server + ServerGroup *agd.ServerGroup +} + +// CacheConfig is the configuration for the DNS cache. +type CacheConfig struct { + // MinTTL is the minimum supported TTL for cache items. + MinTTL time.Duration + + // ECSCount is the size of the DNS cache for domain names that support + // ECS, in entries. It must be greater than zero if [CacheConfig.CacheType] + // is [CacheTypeECS]. + ECSCount int + + // NoECSCount is the size of the DNS cache for domain names that don't + // support ECS, in entries. It must be greater than zero if + // [CacheConfig.CacheType] is [CacheTypeSimple] or [CacheTypeECS]. + NoECSCount int + + // Type is the cache type. It must be valid. + Type CacheType + + // OverrideCacheTTL shows if the TTL overriding logic should be used. + OverrideCacheTTL bool +} + +// CacheType is the type of the cache to use. +type CacheType uint8 + +// CacheType constants. +const ( + CacheTypeNone CacheType = iota + 1 + CacheTypeSimple + CacheTypeECS +) diff --git a/internal/dnssvc/context.go b/internal/dnssvc/context.go new file mode 100644 index 0000000..3e054bc --- /dev/null +++ b/internal/dnssvc/context.go @@ -0,0 +1,35 @@ +package dnssvc + +import ( + "context" + "time" + + "github.com/AdguardTeam/AdGuardDNS/internal/agd" + "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver" +) + +// contextConstructor is a [dnsserver.ContextConstructor] implementation that +// returns a context with the given timeout as well as a new [agd.RequestID]. +type contextConstructor struct { + timeout time.Duration +} + +// newContextConstructor returns a new properly initialized *contextConstructor. +func newContextConstructor(timeout time.Duration) (c *contextConstructor) { + return &contextConstructor{ + timeout: timeout, + } +} + +// type check +var _ dnsserver.ContextConstructor = (*contextConstructor)(nil) + +// New implements the [dnsserver.ContextConstructor] interface for +// *contextConstructor. It returns a context with a new [agd.RequestID] as well +// as its timeout and the corresponding cancelation function. +func (c *contextConstructor) New() (ctx context.Context, cancel context.CancelFunc) { + ctx, cancel = context.WithTimeout(context.Background(), c.timeout) + ctx = agd.WithRequestID(ctx, agd.NewRequestID()) + + return ctx, cancel +} diff --git a/internal/dnssvc/dnssvc.go b/internal/dnssvc/dnssvc.go index b4714fa..5d8e314 100644 --- a/internal/dnssvc/dnssvc.go +++ b/internal/dnssvc/dnssvc.go @@ -7,289 +7,27 @@ package dnssvc import ( "context" "fmt" - "log/slog" "net/http" - "time" - "github.com/AdguardTeam/AdGuardDNS/internal/access" "github.com/AdguardTeam/AdGuardDNS/internal/agd" - "github.com/AdguardTeam/AdGuardDNS/internal/agdcache" - "github.com/AdguardTeam/AdGuardDNS/internal/billstat" - "github.com/AdguardTeam/AdGuardDNS/internal/cmd/plugin" "github.com/AdguardTeam/AdGuardDNS/internal/connlimiter" - "github.com/AdguardTeam/AdGuardDNS/internal/dnscheck" - "github.com/AdguardTeam/AdGuardDNS/internal/dnsdb" - "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext" dnssrvprom "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/prometheus" - "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/ratelimit" - "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/devicefinder" - "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/initial" - "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/mainmw" - "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/preservice" - "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/preupstream" - "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/ratelimitmw" - "github.com/AdguardTeam/AdGuardDNS/internal/errcoll" - "github.com/AdguardTeam/AdGuardDNS/internal/filter" - "github.com/AdguardTeam/AdGuardDNS/internal/geoip" - "github.com/AdguardTeam/AdGuardDNS/internal/metrics" - "github.com/AdguardTeam/AdGuardDNS/internal/profiledb" - "github.com/AdguardTeam/AdGuardDNS/internal/querylog" - "github.com/AdguardTeam/AdGuardDNS/internal/rulestat" "github.com/AdguardTeam/golibs/errors" - "github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/service" "github.com/miekg/dns" - "github.com/prometheus/client_golang/prometheus" ) -// Config is the configuration of the AdGuard DNS service. -type Config struct { - // BaseLogger is used to create loggers with custom prefixes for middlewares - // and the service itself. - BaseLogger *slog.Logger - - // Messages is the message constructor used to create blocked and other - // messages for this DNS service. - Messages *dnsmsg.Constructor - - // Cloner is used to clone messages more efficiently by disposing of parts - // of DNS responses for later reuse. - Cloner *dnsmsg.Cloner - - // ControlConf is the configuration of socket options. - ControlConf *netext.ControlConfig - - // ConnLimiter, if not nil, is used to limit the number of simultaneously - // active stream-connections. - ConnLimiter *connlimiter.Limiter - - // HumanIDParser is used to normalize and parse human-readable device - // identifiers. - HumanIDParser *agd.HumanIDParser - - // PluginRegistry is used to override configuration parameters. - PluginRegistry *plugin.Registry - - // AccessManager is used to block requests. - AccessManager access.Interface - - // SafeBrowsing is the safe browsing TXT hash matcher. - SafeBrowsing filter.HashMatcher - - // BillStat is used to collect billing statistics. - BillStat billstat.Recorder - - // CacheManager is the global cache manager. CacheManager must not be nil. - CacheManager agdcache.Manager - - // ProfileDB is the AdGuard DNS profile database used to fetch data about - // profiles, devices, and so on. - ProfileDB profiledb.Interface - - // PrometheusRegisterer is used to register Prometheus metrics. - PrometheusRegisterer prometheus.Registerer - - // DNSCheck is used by clients to check if they use AdGuard DNS. - DNSCheck dnscheck.Interface - - // NonDNS is the handler for non-DNS HTTP requests. - NonDNS http.Handler - - // DNSDB is used to update anonymous statistics about DNS queries. - DNSDB dnsdb.Interface - - // ErrColl is the error collector that is used to collect critical and - // non-critical errors. - ErrColl errcoll.Interface - - // FilterStorage is the storage of all filters. - FilterStorage filter.Storage - - // GeoIP is the GeoIP database used to detect geographic data about IP - // addresses in requests and responses. - GeoIP geoip.Interface - - // QueryLog is used to write the logs into. - QueryLog querylog.Interface - - // RuleStat is used to collect statistics about matched filtering rules and - // rule lists. - RuleStat rulestat.Interface - - // NewListener, when set, is used instead of the package-level function - // NewListener when creating a DNS listener. - // - // TODO(a.garipov): The handler and service logic should really not be - // intertwined in this way. See AGDNS-1327. - NewListener NewListenerFunc - - // Handler is used as the main DNS handler instead of a simple forwarder. - // It must not be nil. - // - // TODO(a.garipov): Think of a better way to make the DNS server logic more - // testable. - Handler dnsserver.Handler - - // RateLimit is used for allow or decline requests. - RateLimit ratelimit.Interface - - // MetricsNamespace is a namespace for Prometheus metrics. It must be a - // valid Prometheus metric label. - MetricsNamespace string - - // FilteringGroups are the DNS filtering groups. Each element must be - // non-nil. - FilteringGroups map[agd.FilteringGroupID]*agd.FilteringGroup - - // ServerGroups are the DNS server groups. Each element must be non-nil. - ServerGroups []*agd.ServerGroup - - // HandleTimeout defines the timeout for the entire handling of a single - // query. - HandleTimeout time.Duration - - // CacheSize is the size of the DNS cache for domain names that don't - // support ECS. - // - // TODO(a.garipov): Extract this and following fields to cache configuration - // struct. - CacheSize int - - // ECSCacheSize is the size of the DNS cache for domain names that support - // ECS. - ECSCacheSize int - - // CacheMinTTL is the minimum supported TTL for cache items. This setting - // is used when UseCacheTTLOverride set to true. - CacheMinTTL time.Duration - - // UseCacheTTLOverride shows if the TTL overrides logic should be used. - UseCacheTTLOverride bool - - // UseECSCache shows if the EDNS Client Subnet (ECS) aware cache should be - // used. - UseECSCache bool +// Service is the main DNS service of AdGuard DNS. +type Service struct { + groups []*serverGroup } -type ( - // MainMiddlewareMetrics is a re-export of the internal filtering-middleware - // metrics interface. - MainMiddlewareMetrics = mainmw.Metrics - - // RatelimitMiddlewareMetrics is a re-export of the metrics interface of the - // internal access and ratelimiting middleware. - RatelimitMiddlewareMetrics = ratelimitmw.Metrics -) - -// New returns a new DNS service. -func New(c *Config) (svc *Service, err error) { - // Use either the configured listener initializer or the default one. - newListener := c.NewListener - if newListener == nil { - newListener = NewListener - } - - // Configure the end of the request handling pipeline. - handler := c.Handler - if handler == nil { - return nil, errors.Error("handler in config must not be nil") - } - - // Configure the pre-upstream middleware common for all servers of all - // groups. - preUps := preupstream.New(&preupstream.Config{ - Cloner: c.Cloner, - CacheManager: c.CacheManager, - DB: c.DNSDB, - GeoIP: c.GeoIP, - CacheSize: c.CacheSize, - ECSCacheSize: c.ECSCacheSize, - UseECSCache: c.UseECSCache, - CacheMinTTL: c.CacheMinTTL, - UseCacheTTLOverride: c.UseCacheTTLOverride, - }) - handler = preUps.Wrap(handler) - - errCollListener := &errCollMetricsListener{ - errColl: c.ErrColl, - baseListener: dnssrvprom.NewServerMetricsListener(c.MetricsNamespace), - } - - // Configure the service itself. - groups := make([]*serverGroup, len(c.ServerGroups)) - svc = &Service{ - groups: groups, - } - - mainMwMtrc, err := newMainMiddlewareMetrics(c) - if err != nil { - // Don't wrap the error, because it's informative enough as is. - return nil, err - } - - rlMwMtrc, err := metrics.NewDefaultRatelimitMiddleware(c.MetricsNamespace, c.PrometheusRegisterer) - if err != nil { - // Don't wrap the error, because it's informative enough as is. - return nil, err - } - - for i, srvGrp := range c.ServerGroups { - // The Filtering Middlewares - // - // These are middlewares common to all filtering and server groups. - // They change the flow of request handling, so they are separated. - - dnsHdlr := dnsserver.WithMiddlewares( - handler, - preservice.New(&preservice.Config{ - Messages: c.Messages, - HashMatcher: c.SafeBrowsing, - Checker: c.DNSCheck, - }), - mainmw.New(&mainmw.Config{ - Metrics: mainMwMtrc, - Messages: c.Messages, - Cloner: c.Cloner, - BillStat: c.BillStat, - ErrColl: c.ErrColl, - FilterStorage: c.FilterStorage, - GeoIP: c.GeoIP, - QueryLog: c.QueryLog, - RuleStat: c.RuleStat, - }), - ) - - var servers []*server - servers, err = newServers(c, srvGrp, dnsHdlr, rlMwMtrc, errCollListener, newListener) - if err != nil { - return nil, fmt.Errorf("group %q: %w", srvGrp.Name, err) - } - - groups[i] = &serverGroup{ - name: srvGrp.Name, - servers: servers, - } - } - - return svc, nil -} - -// newMainMiddlewareMetrics returns a filtering-middleware metrics -// implementation from the config. -func newMainMiddlewareMetrics(c *Config) (mainMwMtrc MainMiddlewareMetrics, err error) { - mainMwMtrc = c.PluginRegistry.MainMiddlewareMetrics() - if mainMwMtrc != nil { - return mainMwMtrc, nil - } - - mainMwMtrc, err = metrics.NewDefaultMainMiddleware(c.MetricsNamespace, c.PrometheusRegisterer) - if err != nil { - return nil, fmt.Errorf("mainmw metrics: %w", err) - } - - return mainMwMtrc, nil +// serverGroup is a group of servers. +type serverGroup struct { + name agd.ServerGroupName + servers []*server } // server is a group of listeners. @@ -303,27 +41,171 @@ type server struct { listeners []*listener } -// serverGroup is a group of servers. -type serverGroup struct { - name agd.ServerGroupName - servers []*server +// listener is a Listener along with some of its associated data. +type listener struct { + Listener + + name string } -// Service is the main DNS service of AdGuard DNS. -type Service struct { - groups []*serverGroup -} - -// mustStartListener starts l and panics on any error. -func mustStartListener( - grp agd.ServerGroupName, - srv agd.ServerName, - l *listener, -) { - err := l.Start(context.Background()) - if err != nil { - panic(fmt.Errorf("group %q: server %q: starting %q: %w", grp, srv, l.name, err)) +// New returns a new DNS service. +func New(c *Config) (svc *Service, err error) { + // Use either the configured listener initializer or the default one. + newListener := c.NewListener + if newListener == nil { + newListener = NewListener } + + errCollListener := &errCollMetricsListener{ + errColl: c.ErrColl, + baseListener: dnssrvprom.NewServerMetricsListener(c.MetricsNamespace), + } + + // Configure the service itself. + groups := make([]*serverGroup, 0, len(c.ServerGroups)) + + for _, srvGrp := range c.ServerGroups { + g := &serverGroup{ + name: srvGrp.Name, + } + + g.servers, err = newServers(c, srvGrp, errCollListener, newListener) + if err != nil { + return nil, fmt.Errorf("group %q: %w", srvGrp.Name, err) + } + + groups = append(groups, g) + } + + svc = &Service{ + groups: groups, + } + + return svc, nil +} + +// newServers creates a slice of servers. +func newServers( + c *Config, + srvGrp *agd.ServerGroup, + errCollListener *errCollMetricsListener, + newListener NewListenerFunc, +) (servers []*server, err error) { + servers = make([]*server, 0, len(srvGrp.Servers)) + + for _, srv := range srvGrp.Servers { + k := HandlerKey{ + Server: srv, + ServerGroup: srvGrp, + } + handler, ok := c.Handlers[k] + if !ok { + return nil, fmt.Errorf("no handler for server %q of group %q", srv.Name, srvGrp.Name) + } + + s := &server{ + name: srv.Name, + handler: handler, + } + + s.listeners, err = newListeners(c, srv, handler, errCollListener, newListener) + if err != nil { + return nil, fmt.Errorf("server %q: %w", s.name, err) + } + + servers = append(servers, s) + } + + return servers, nil +} + +// newListeners creates a slice of listeners for a server. +func newListeners( + c *Config, + srv *agd.Server, + handler dnsserver.Handler, + errCollListener *errCollMetricsListener, + newListener NewListenerFunc, +) (listeners []*listener, err error) { + bindData := srv.BindData() + listeners = make([]*listener, 0, len(bindData)) + for i, bindData := range bindData { + var addr string + if bindData.PrefixAddr == nil { + addr = bindData.AddrPort.String() + } else { + addr = bindData.PrefixAddr.String() + } + + proto := srv.Protocol + + name := listenerName(srv.Name, addr, proto) + baseConf := dnsserver.ConfigBase{ + Network: dnsserver.NetworkAny, + Handler: handler, + Metrics: errCollListener, + Disposer: c.Cloner, + RequestContext: newContextConstructor(c.HandleTimeout), + ListenConfig: newListenConfig( + bindData.ListenConfig, + c.ControlConf, + c.ConnLimiter, + proto, + ), + Name: name, + Addr: addr, + } + + l := &listener{ + name: name, + } + + l.Listener, err = newListener(srv, baseConf, c.NonDNS) + if err != nil { + return nil, fmt.Errorf("bind data at index %d: %w", i, err) + } + + listeners = append(listeners, l) + } + + return listeners, nil +} + +// listenerName returns a standard name for a listener. +func listenerName(srvName agd.ServerName, addr string, proto agd.Protocol) (name string) { + return fmt.Sprintf("%s/%s/%s", srvName, proto, addr) +} + +// newListenConfig returns the netext.ListenConfig used by the plain-DNS +// servers. The resulting ListenConfig sets additional socket flags and +// processes the control messages of connections created with ListenPacket. +// Additionally, if l is not nil, it is used to limit the number of +// simultaneously active stream-connections. +func newListenConfig( + original netext.ListenConfig, + ctrlConf *netext.ControlConfig, + l *connlimiter.Limiter, + p agd.Protocol, +) (lc netext.ListenConfig) { + if original != nil { + if l == nil { + return original + } + + return connlimiter.NewListenConfig(original, l) + } + + if p == agd.ProtoDNS { + lc = netext.DefaultListenConfigWithOOB(ctrlConf) + } else { + lc = netext.DefaultListenConfig(ctrlConf) + } + + if l != nil { + lc = connlimiter.NewListenConfig(lc, l) + } + + return lc } // type check @@ -331,13 +213,13 @@ var _ service.Interface = (*Service)(nil) // Start implements the [service.Interface] interface for *Service. It panics // if one of the listeners could not start. -func (svc *Service) Start(_ context.Context) (err error) { +func (svc *Service) Start(ctx context.Context) (err error) { for _, g := range svc.groups { for _, s := range g.servers { for _, l := range s.listeners { // Consider inability to start any one DNS listener a fatal // error. - mustStartListener(g.name, s.name, l) + mustStartListener(ctx, g.name, s.name, l) } } } @@ -345,17 +227,17 @@ func (svc *Service) Start(_ context.Context) (err error) { return nil } -// shutdownListeners is a helper function that shuts down all listeners of a -// server. -func shutdownListeners(ctx context.Context, listeners []*listener) (err error) { - for _, l := range listeners { - err = l.Shutdown(ctx) - if err != nil { - return fmt.Errorf("shutting down listener %q: %w", l.name, err) - } +// mustStartListener starts l and panics on any error. +func mustStartListener( + ctx context.Context, + srvGrp agd.ServerGroupName, + srv agd.ServerName, + l *listener, +) { + err := l.Start(ctx) + if err != nil { + panic(fmt.Errorf("group %q: server %q: starting %q: %w", srvGrp, srv, l.name, err)) } - - return nil } // Shutdown implements the [service.Interface] interface for *Service. @@ -378,9 +260,22 @@ func (svc *Service) Shutdown(ctx context.Context) (err error) { return nil } +// shutdownListeners is a helper function that shuts down all listeners of a +// server. +func shutdownListeners(ctx context.Context, listeners []*listener) (err error) { + for _, l := range listeners { + err = l.Shutdown(ctx) + if err != nil { + return fmt.Errorf("shutting down listener %q: %w", l.name, err) + } + } + + return nil +} + // Handle is a simple helper to test the handling of DNS requests. // -// TODO(a.garipov): Remove once the mainmw refactoring is complete. +// TODO(a.garipov): Remove once the refactoring is complete. func (svc *Service) Handle( ctx context.Context, grpName agd.ServerGroupName, @@ -417,33 +312,10 @@ func (svc *Service) Handle( return srv.handler.ServeDNS(ctx, rw, r) } -// Listener is a type alias for dnsserver.Server to make internal naming more -// consistent. -type Listener = dnsserver.Server - -// NewListenerFunc is the type for DNS listener constructors. -type NewListenerFunc func( - s *agd.Server, - baseConf dnsserver.ConfigBase, - nonDNS http.Handler, -) (l Listener, err error) - -// listener is a Listener along with some of its associated data. -type listener struct { - Listener - - name string -} - -// listenerName returns a standard name for a listener. -func listenerName(srvName agd.ServerName, addr string, proto agd.Protocol) (name string) { - return fmt.Sprintf("%s/%s/%s", srvName, proto, addr) -} - // NewListener returns a new Listener. It is the default DNS listener // constructor. // -// TODO(a.garipov): Replace this in tests with [netext.ListenConfig]. +// TODO(a.garipov): Replace this in tests with [netext.ListenConfig]. func NewListener( s *agd.Server, baseConf dnsserver.ConfigBase, @@ -500,213 +372,8 @@ func NewListener( TLSConfig: s.TLS, }) default: - return nil, fmt.Errorf("bad protocol %v", p) + return nil, fmt.Errorf("protocol: %w: %d", errors.ErrBadEnumValue, p) } return l, nil } - -// contextConstructor is a [dnsserver.ContextConstructor] implementation that -// that returns a context with the given timeout as well as a new -// [agd.RequestID]. -type contextConstructor struct { - timeout time.Duration -} - -// newContextConstructor returns a new properly initialized *contextConstructor. -func newContextConstructor(timeout time.Duration) (c *contextConstructor) { - return &contextConstructor{ - timeout: timeout, - } -} - -// type check -var _ dnsserver.ContextConstructor = (*contextConstructor)(nil) - -// New implements the [dnsserver.ContextConstructor] interface for -// *contextConstructor. It returns a context with a new [agd.RequestID] as well -// as its timeout and the corresponding cancelation function. -func (c *contextConstructor) New() (ctx context.Context, cancel context.CancelFunc) { - ctx, cancel = context.WithTimeout(context.Background(), c.timeout) - ctx = agd.WithRequestID(ctx, agd.NewRequestID()) - - return ctx, cancel -} - -// newServers creates a slice of servers. -// -// TODO(a.garipov): Refactor this into a builder pattern. -func newServers( - c *Config, - srvGrp *agd.ServerGroup, - handler dnsserver.Handler, - rlMwMtrc ratelimitmw.Metrics, - errCollListener *errCollMetricsListener, - newListener NewListenerFunc, -) (servers []*server, err error) { - servers = make([]*server, len(srvGrp.Servers)) - - for i, s := range srvGrp.Servers { - // The Initial Middlewares - // - // These middlewares are either specific to the server or must be the - // furthest away from the handler and thus are the first to process - // a request. - - // Assume that all the validations have been made during the - // configuration validation step back in package cmd. If we ever get - // new ways of receiving configuration, remove this assumption and - // validate fg. - fg := c.FilteringGroups[srvGrp.FilteringGroup] - - df := newDeviceFinder(c, srvGrp, s) - rlm := ratelimitmw.New(&ratelimitmw.Config{ - Logger: c.BaseLogger.With(slogutil.KeyPrefix, "ratelimitmw"), - Messages: c.Messages, - FilteringGroup: fg, - ServerGroup: srvGrp, - Server: s, - AccessManager: c.AccessManager, - DeviceFinder: df, - ErrColl: c.ErrColl, - GeoIP: c.GeoIP, - Metrics: rlMwMtrc, - Limiter: c.RateLimit, - // Only apply rate-limiting logic to plain DNS. - Protocols: []agd.Protocol{agd.ProtoDNS}, - }) - if err != nil { - return nil, fmt.Errorf("ratelimit: %w", err) - } - - imw := initial.New(&initial.Config{ - Logger: c.BaseLogger.With(slogutil.KeyPrefix, "initmw"), - }) - - h := dnsserver.WithMiddlewares( - handler, - - // Keep the rate limiting and access middlewares as the outer ones - // to make sure that the application logic isn't touched if the - // request is ratelimited or blocked by access settings. - rlm, - imw, - ) - - srvName := s.Name - - var listeners []*listener - listeners, err = newListeners(c, s, h, errCollListener, newListener) - if err != nil { - return nil, fmt.Errorf("server %q: %w", srvName, err) - } - - servers[i] = &server{ - name: srvName, - handler: h, - listeners: listeners, - } - } - - return servers, nil -} - -// newDeviceFinder returns a new [agd.DeviceFinder] for a server based on the -// configuration. -func newDeviceFinder(c *Config, g *agd.ServerGroup, s *agd.Server) (df agd.DeviceFinder) { - if !g.ProfilesEnabled { - return agd.EmptyDeviceFinder{} - } - - return devicefinder.NewDefault(&devicefinder.Config{ - Logger: c.BaseLogger.With(slogutil.KeyPrefix, "devicefinder"), - ProfileDB: c.ProfileDB, - HumanIDParser: c.HumanIDParser, - Server: s, - DeviceDomains: g.TLS.DeviceDomains, - }) -} - -// newServers creates a slice of listeners for a server. -func newListeners( - c *Config, - srv *agd.Server, - handler dnsserver.Handler, - errCollListener *errCollMetricsListener, - newListener NewListenerFunc, -) (listeners []*listener, err error) { - bindData := srv.BindData() - listeners = make([]*listener, 0, len(bindData)) - for i, bindData := range bindData { - var addr string - if bindData.PrefixAddr == nil { - addr = bindData.AddrPort.String() - } else { - addr = bindData.PrefixAddr.String() - } - - proto := srv.Protocol - - name := listenerName(srv.Name, addr, proto) - baseConf := dnsserver.ConfigBase{ - Network: dnsserver.NetworkAny, - Handler: handler, - Metrics: errCollListener, - Disposer: c.Cloner, - RequestContext: newContextConstructor(c.HandleTimeout), - ListenConfig: newListenConfig( - bindData.ListenConfig, - c.ControlConf, - c.ConnLimiter, - proto, - ), - Name: name, - Addr: addr, - } - - var l Listener - l, err = newListener(srv, baseConf, c.NonDNS) - if err != nil { - return nil, fmt.Errorf("bind data at index %d: %w", i, err) - } - - listeners = append(listeners, &listener{ - name: name, - Listener: l, - }) - } - - return listeners, nil -} - -// newListenConfig returns the netext.ListenConfig used by the plain-DNS -// servers. The resulting ListenConfig sets additional socket flags and -// processes the control messages of connections created with ListenPacket. -// Additionally, if l is not nil, it is used to limit the number of -// simultaneously active stream-connections. -func newListenConfig( - original netext.ListenConfig, - ctrlConf *netext.ControlConfig, - l *connlimiter.Limiter, - p agd.Protocol, -) (lc netext.ListenConfig) { - if original != nil { - if l == nil { - return original - } - - return connlimiter.NewListenConfig(original, l) - } - - if p == agd.ProtoDNS { - lc = netext.DefaultListenConfigWithOOB(ctrlConf) - } else { - lc = netext.DefaultListenConfig(ctrlConf) - } - - if l != nil { - lc = connlimiter.NewListenConfig(lc, l) - } - - return lc -} diff --git a/internal/dnssvc/dnssvc_test.go b/internal/dnssvc/dnssvc_test.go index c0bfde6..6c91802 100644 --- a/internal/dnssvc/dnssvc_test.go +++ b/internal/dnssvc/dnssvc_test.go @@ -10,26 +10,17 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/agdservice" - "github.com/AdguardTeam/AdGuardDNS/internal/agdtest" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/forward" "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc" "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/dnssvctest" - "github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/testutil" "github.com/miekg/dns" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestMain(m *testing.M) { - testutil.DiscardLogOutput(m) -} - -// testSrvGrpName is the [agd.ServerGroupName] for tests. -const testSrvGrpName agd.ServerGroupName = "test_group" - // type check var _ agdservice.Refresher = (*forward.Handler)(nil) @@ -159,16 +150,23 @@ func TestService_Start(t *testing.T) { AddrPort: netip.MustParseAddrPort("127.0.0.1:53"), }) + srvGrp := &agd.ServerGroup{ + Name: dnssvctest.ServerGroupName, + Servers: []*agd.Server{srv}, + } + + k := dnssvc.HandlerKey{ + Server: srv, + ServerGroup: srvGrp, + } + c := &dnssvc.Config{ - BaseLogger: slogutil.NewDiscardLogger(), - NewListener: newTestListenerFunc(tl), - PrometheusRegisterer: agdtest.NewTestPrometheusRegisterer(), - Handler: dnsservertest.DefaultHandler(), - MetricsNamespace: "test_start", - ServerGroups: []*agd.ServerGroup{{ - Name: "test_group", - Servers: []*agd.Server{srv}, - }}, + NewListener: newTestListenerFunc(tl), + Handlers: dnssvc.Handlers{ + k: dnsservertest.NewDefaultHandler(), + }, + MetricsNamespace: "test_start", + ServerGroups: []*agd.ServerGroup{srvGrp}, } svc, err := dnssvc.New(c) @@ -206,15 +204,25 @@ func TestNew(t *testing.T) { }), } + srvGrp := &agd.ServerGroup{ + Name: dnssvctest.ServerGroupName, + Servers: srvs, + } + + handlers := dnssvc.Handlers{} + for _, srv := range srvs { + k := dnssvc.HandlerKey{ + Server: srv, + ServerGroup: srvGrp, + } + + handlers[k] = dnsservertest.NewDefaultHandler() + } + c := &dnssvc.Config{ - BaseLogger: slogutil.NewDiscardLogger(), - Handler: dnsservertest.DefaultHandler(), - PrometheusRegisterer: agdtest.NewTestPrometheusRegisterer(), - MetricsNamespace: "test_new", - ServerGroups: []*agd.ServerGroup{{ - Name: "test_group", - Servers: srvs, - }}, + Handlers: handlers, + MetricsNamespace: "test_new", + ServerGroups: []*agd.ServerGroup{srvGrp}, } svc, err := dnssvc.New(c) diff --git a/internal/dnssvc/handler.go b/internal/dnssvc/handler.go new file mode 100644 index 0000000..49f5f2c --- /dev/null +++ b/internal/dnssvc/handler.go @@ -0,0 +1,211 @@ +package dnssvc + +import ( + "context" + "fmt" + + "github.com/AdguardTeam/AdGuardDNS/internal/agd" + "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver" + "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/cache" + dnssrvprom "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/prometheus" + "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/devicefinder" + "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/initial" + "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/mainmw" + "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/preservice" + "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/preupstream" + "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/ratelimitmw" + "github.com/AdguardTeam/AdGuardDNS/internal/ecscache" + "github.com/AdguardTeam/AdGuardDNS/internal/metrics" + "github.com/AdguardTeam/golibs/errors" + "github.com/AdguardTeam/golibs/logutil/slogutil" +) + +// NewHandlers returns the main DNS handlers wrapped in all necessary +// middlewares. c must not be nil. +func NewHandlers(ctx context.Context, c *HandlersConfig) (handlers Handlers, err error) { + handler := wrapPreUpstreamMw(ctx, c) + + mainMwMtrc, err := newMainMiddlewareMetrics(c) + if err != nil { + // Don't wrap the error, because it's informative enough as is. + return nil, err + } + + mainMw := mainmw.New(&mainmw.Config{ + Cloner: c.Cloner, + Logger: c.BaseLogger.With(slogutil.KeyPrefix, "mainmw"), + Messages: c.Messages, + BillStat: c.BillStat, + ErrColl: c.ErrColl, + FilterStorage: c.FilterStorage, + GeoIP: c.GeoIP, + QueryLog: c.QueryLog, + Metrics: mainMwMtrc, + RuleStat: c.RuleStat, + }) + + handler = mainMw.Wrap(handler) + + preSvcMw := preservice.New(&preservice.Config{ + Logger: c.BaseLogger.With(slogutil.KeyPrefix, "presvcmw"), + Messages: c.Messages, + HashMatcher: c.HashMatcher, + Checker: c.DNSCheck, + }) + + handler = preSvcMw.Wrap(handler) + + postInitMw := c.PluginRegistry.PostInitialMiddleware() + if postInitMw != nil { + handler = postInitMw.Wrap(handler) + } + + initMw := initial.New(&initial.Config{ + Logger: c.BaseLogger.With(slogutil.KeyPrefix, "initmw"), + }) + + handler = initMw.Wrap(handler) + + return newHandlersForServers(c, handler) +} + +// wrapPreUpstreamMw returns the handler wrapped into the pre-upstream +// middlewares. +// +// TODO(a.garipov): Adapt the cache tests that previously were in package +// preupstream. +func wrapPreUpstreamMw(ctx context.Context, c *HandlersConfig) (wrapped dnsserver.Handler) { + // TODO(a.garipov): Use in other places if necessary. + l := c.BaseLogger.With(slogutil.KeyPrefix, "dnssvc") + + wrapped = c.Handler + switch conf := c.Cache; conf.Type { + case CacheTypeNone: + l.WarnContext(ctx, "cache disabled") + case CacheTypeSimple: + l.InfoContext(ctx, "plain cache enabled", "count", conf.NoECSCount) + + cacheMw := cache.NewMiddleware(&cache.MiddlewareConfig{ + // TODO(a.garipov): Do not use promauto and refactor. + MetricsListener: dnssrvprom.NewCacheMetricsListener(metrics.Namespace()), + Size: conf.NoECSCount, + MinTTL: conf.MinTTL, + OverrideTTL: conf.OverrideCacheTTL, + }) + + wrapped = cacheMw.Wrap(wrapped) + case CacheTypeECS: + l.InfoContext( + ctx, + "ecs cache enabled", + "ecs_count", conf.ECSCount, + "no_ecs_count", conf.NoECSCount, + ) + + cacheMw := ecscache.NewMiddleware(&ecscache.MiddlewareConfig{ + Cloner: c.Cloner, + Logger: c.BaseLogger.With(slogutil.KeyPrefix, "ecscache"), + CacheManager: c.CacheManager, + GeoIP: c.GeoIP, + NoECSCount: conf.NoECSCount, + ECSCount: conf.ECSCount, + MinTTL: conf.MinTTL, + OverrideTTL: conf.OverrideCacheTTL, + }) + + wrapped = cacheMw.Wrap(wrapped) + default: + panic(fmt.Errorf("cache type: %w: %d", errors.ErrBadEnumValue, conf.Type)) + } + + preUps := preupstream.New(ctx, &preupstream.Config{ + DB: c.DNSDB, + }) + + wrapped = preUps.Wrap(wrapped) + + return wrapped +} + +// newMainMiddlewareMetrics returns a filtering-middleware metrics +// implementation from the config. +func newMainMiddlewareMetrics(c *HandlersConfig) (mainMwMtrc MainMiddlewareMetrics, err error) { + mainMwMtrc = c.PluginRegistry.MainMiddlewareMetrics() + if mainMwMtrc != nil { + return mainMwMtrc, nil + } + + mainMwMtrc, err = metrics.NewDefaultMainMiddleware(c.MetricsNamespace, c.PrometheusRegisterer) + if err != nil { + return nil, fmt.Errorf("mainmw metrics: %w", err) + } + + return mainMwMtrc, nil +} + +// 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) + if err != nil { + return nil, fmt.Errorf("ratelimit middleware metrics: %w", err) + } + + handlers = Handlers{} + + rlMwLogger := c.BaseLogger.With(slogutil.KeyPrefix, "ratelimitmw") + for _, srvGrp := range c.ServerGroups { + fltGrp, ok := c.FilteringGroups[srvGrp.FilteringGroup] + if !ok { + return nil, fmt.Errorf( + "no filtering group %q for server group %q", + srvGrp.FilteringGroup, + srvGrp.Name, + ) + } + + for _, srv := range srvGrp.Servers { + rlMw := ratelimitmw.New(&ratelimitmw.Config{ + Logger: rlMwLogger, + Messages: c.Messages, + FilteringGroup: fltGrp, + ServerGroup: srvGrp, + Server: srv, + StructuredErrors: c.StructuredErrors, + AccessManager: c.AccessManager, + DeviceFinder: newDeviceFinder(c, srvGrp, srv), + ErrColl: c.ErrColl, + GeoIP: c.GeoIP, + Metrics: rlMwMtrc, + Limiter: c.RateLimit, + Protocols: []agd.Protocol{agd.ProtoDNS}, + EDEEnabled: c.EDEEnabled, + }) + + k := HandlerKey{ + Server: srv, + ServerGroup: srvGrp, + } + + handlers[k] = rlMw.Wrap(h) + } + } + + return handlers, nil +} + +// newDeviceFinder returns a new agd.DeviceFinder for a server based on the +// configuration. All arguments must not be nil. +func newDeviceFinder(c *HandlersConfig, g *agd.ServerGroup, s *agd.Server) (df agd.DeviceFinder) { + if !g.ProfilesEnabled { + return agd.EmptyDeviceFinder{} + } + + return devicefinder.NewDefault(&devicefinder.Config{ + Logger: c.BaseLogger.With(slogutil.KeyPrefix, "devicefinder"), + ProfileDB: c.ProfileDB, + HumanIDParser: c.HumanIDParser, + Server: s, + DeviceDomains: g.TLS.DeviceDomains, + }) +} diff --git a/internal/dnssvc/handler_test.go b/internal/dnssvc/handler_test.go new file mode 100644 index 0000000..39a7474 --- /dev/null +++ b/internal/dnssvc/handler_test.go @@ -0,0 +1,188 @@ +package dnssvc_test + +import ( + "context" + "net/netip" + "path" + "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/dnsserver/dnsservertest" + "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc" + "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/dnssvctest" + "github.com/AdguardTeam/AdGuardDNS/internal/filter" + "github.com/AdguardTeam/AdGuardDNS/internal/geoip" + "github.com/AdguardTeam/AdGuardDNS/internal/querylog" + "github.com/AdguardTeam/golibs/logutil/slogutil" + "github.com/AdguardTeam/golibs/testutil" + "github.com/miekg/dns" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNewHandlers(t *testing.T) { + t.Parallel() + + accessMgr := &agdtest.AccessManager{ + OnIsBlockedHost: func(host string, qt uint16) (blocked bool) { panic("not implemented") }, + OnIsBlockedIP: func(ip netip.Addr) (blocked bool) { panic("not implemented") }, + } + + billStat := &agdtest.BillStatRecorder{ + OnRecord: func( + _ context.Context, + _ agd.DeviceID, + _ geoip.Country, + _ geoip.ASN, + _ time.Time, + _ agd.Protocol, + ) { + panic("not implemented") + }, + } + + dnsCk := &agdtest.DNSCheck{ + OnCheck: func( + _ context.Context, + _ *dns.Msg, + _ *agd.RequestInfo, + ) (resp *dns.Msg, err error) { + panic("not implemented") + }, + } + + dnsDB := &agdtest.DNSDB{ + OnRecord: func(_ context.Context, _ *dns.Msg, _ *agd.RequestInfo) { + panic("not implemented") + }, + } + + fltGrps := map[agd.FilteringGroupID]*agd.FilteringGroup{ + dnssvctest.FilteringGroupID: { + ID: dnssvctest.FilteringGroupID, + RuleListIDs: []agd.FilterListID{dnssvctest.FilterListID1}, + RuleListsEnabled: true, + }, + } + + fltStrg := &agdtest.FilterStorage{ + OnFilterFromContext: func(_ context.Context, _ *agd.RequestInfo) (f filter.Interface) { + panic("not implemented") + }, + OnHasListID: func(_ agd.FilterListID) (ok bool) { panic("not implemented") }, + } + + hashMatcher := &agdtest.HashMatcher{ + OnMatchByPrefix: func( + _ context.Context, + _ string, + ) (hashes []string, matched bool, err error) { + panic("not implemented") + }, + } + + queryLog := &agdtest.QueryLog{ + OnWrite: func(_ context.Context, _ *querylog.Entry) (err error) { + panic("not implemented") + }, + } + + ruleStat := &agdtest.RuleStat{ + OnCollect: func(_ context.Context, _ agd.FilterListID, _ agd.FilterRuleText) { + panic("not implemented") + }, + } + + srv := dnssvctest.NewServer(dnssvctest.ServerName, agd.ProtoDoT, &agd.ServerBindData{ + AddrPort: dnssvctest.ServerAddrPort, + }) + + srvGrp := &agd.ServerGroup{ + DDR: &agd.DDR{ + Enabled: true, + }, + TLS: &agd.TLS{}, + Name: dnssvctest.ServerGroupName, + FilteringGroup: dnssvctest.FilteringGroupID, + Servers: []*agd.Server{srv}, + ProfilesEnabled: true, + } + + testCases := []struct { + cacheConf *dnssvc.CacheConfig + name string + }{{ + cacheConf: &dnssvc.CacheConfig{ + Type: dnssvc.CacheTypeNone, + }, + name: "no_cache", + }, { + cacheConf: &dnssvc.CacheConfig{ + MinTTL: 10 * time.Second, + NoECSCount: 100, + Type: dnssvc.CacheTypeSimple, + OverrideCacheTTL: true, + }, + name: "cache_simple", + }, { + cacheConf: &dnssvc.CacheConfig{ + MinTTL: 10 * time.Second, + ECSCount: 100, + NoECSCount: 100, + Type: dnssvc.CacheTypeECS, + OverrideCacheTTL: true, + }, + name: "cache_ecs", + }} + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + ctx := testutil.ContextWithTimeout(t, dnssvctest.Timeout) + handlers, err := dnssvc.NewHandlers(ctx, &dnssvc.HandlersConfig{ + BaseLogger: slogutil.NewDiscardLogger(), + Cloner: agdtest.NewCloner(), + Cache: tc.cacheConf, + HumanIDParser: agd.NewHumanIDParser(), + Messages: agdtest.NewConstructor(t), + PluginRegistry: nil, + StructuredErrors: agdtest.NewSDEConfig(true), + AccessManager: accessMgr, + BillStat: billStat, + // TODO(a.garipov): Create a test implementation? + CacheManager: agdcache.EmptyManager{}, + DNSCheck: dnsCk, + DNSDB: dnsDB, + ErrColl: agdtest.NewErrorCollector(), + FilterStorage: fltStrg, + GeoIP: agdtest.NewGeoIP(), + Handler: dnsservertest.NewPanicHandler(), + HashMatcher: hashMatcher, + ProfileDB: agdtest.NewProfileDB(), + PrometheusRegisterer: agdtest.NewTestPrometheusRegisterer(), + QueryLog: queryLog, + RateLimit: agdtest.NewRateLimit(), + RuleStat: ruleStat, + MetricsNamespace: path.Base(t.Name()), + FilteringGroups: fltGrps, + ServerGroups: []*agd.ServerGroup{srvGrp}, + EDEEnabled: true, + }) + require.NoError(t, err) + + assert.Len(t, handlers, 1) + + for k, v := range handlers { + assert.Same(t, srv, k.Server) + assert.Same(t, srvGrp, k.ServerGroup) + assert.NotNil(t, v) + + break + } + }) + } +} diff --git a/internal/dnssvc/integration_test.go b/internal/dnssvc/integration_test.go index 82a1623..dc63f64 100644 --- a/internal/dnssvc/integration_test.go +++ b/internal/dnssvc/integration_test.go @@ -11,6 +11,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/access" "github.com/AdguardTeam/AdGuardDNS/internal/agd" + "github.com/AdguardTeam/AdGuardDNS/internal/agdcache" "github.com/AdguardTeam/AdGuardDNS/internal/agdpasswd" "github.com/AdguardTeam/AdGuardDNS/internal/agdtest" "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" @@ -19,6 +20,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc" "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/dnssvctest" "github.com/AdguardTeam/AdGuardDNS/internal/filter" + "github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix" "github.com/AdguardTeam/AdGuardDNS/internal/geoip" "github.com/AdguardTeam/AdGuardDNS/internal/querylog" "github.com/AdguardTeam/golibs/logutil/slogutil" @@ -46,7 +48,7 @@ import ( // from the service. The channels must not be nil. Each sending to a channel // wrapped with [testutil.RequireSend] using [dnssvctest.Timeout]. // -// It also uses the [dnsservertest.DefaultHandler] to create the DNS handler. +// It also uses the [dnsservertest.NewDefaultHandler] to create the DNS handler. func newTestService( t testing.TB, flt filter.Interface, @@ -81,46 +83,14 @@ func newTestService( QueryLogEnabled: true, } - db := &agdtest.ProfileDB{ - OnCreateAutoDevice: func( - ctx context.Context, - id agd.ProfileID, - humanID agd.HumanID, - devType agd.DeviceType, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, + profDB := agdtest.NewProfileDB() + profDB.OnProfileByDeviceID = func( + _ context.Context, + id agd.DeviceID, + ) (p *agd.Profile, d *agd.Device, err error) { + testutil.RequireSend(pt, profileDBCh, id, dnssvctest.Timeout) - OnProfileByDedicatedIP: func( - _ context.Context, - _ netip.Addr, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByDeviceID: func( - _ context.Context, - id agd.DeviceID, - ) (p *agd.Profile, d *agd.Device, err error) { - testutil.RequireSend(pt, profileDBCh, id, dnssvctest.Timeout) - - return prof, dev, nil - }, - - OnProfileByHumanID: func( - _ context.Context, - _ agd.ProfileID, - _ agd.HumanIDLower, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByLinkedIP: func( - ctx context.Context, - ip netip.Addr, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, + return prof, dev, nil } accessManager := &agdtest.AccessManager{ @@ -145,18 +115,12 @@ func newTestService( Continent: geoip.ContinentEU, ASN: 42, } - geoIP := &agdtest.GeoIP{ - OnSubnetByLocation: func( - _ *geoip.Location, - _ netutil.AddrFamily, - ) (n netip.Prefix, err error) { - panic("not implemented") - }, - OnData: func(host string, _ netip.Addr) (l *geoip.Location, err error) { - testutil.RequireSend(pt, geoIPCh, host, dnssvctest.Timeout) - return loc, nil - }, + geoIP := agdtest.NewGeoIP() + geoIP.OnData = func(host string, _ netip.Addr) (l *geoip.Location, err error) { + testutil.RequireSend(pt, geoIPCh, host, dnssvctest.Timeout) + + return loc, nil } fltStrg := &agdtest.FilterStorage{ @@ -205,25 +169,38 @@ func newTestService( }, } - rl := &agdtest.RateLimit{ - OnIsRateLimited: func( - _ context.Context, - _ *dns.Msg, - _ netip.Addr, - ) (drop, allowlisted bool, err error) { - return true, false, nil - }, - OnCountResponses: func(_ context.Context, _ *dns.Msg, _ netip.Addr) { - panic("not implemented") - }, + rl := agdtest.NewRateLimit() + rl.OnIsRateLimited = func( + _ context.Context, + _ *dns.Msg, + _ netip.Addr, + ) (shouldDrop, isAllowlisted bool, err error) { + return true, false, nil } - testFltGrpID := agd.FilteringGroupID("1234") + srvGrps := []*agd.ServerGroup{{ + DDR: &agd.DDR{ + Enabled: true, + }, + TLS: &agd.TLS{ + DeviceDomains: []string{dnssvctest.DomainForDevices}, + }, + Name: dnssvctest.ServerGroupName, + FilteringGroup: dnssvctest.FilteringGroupID, + Servers: []*agd.Server{srv}, + ProfilesEnabled: true, + }} - c := &dnssvc.Config{ - BaseLogger: slogutil.NewDiscardLogger(), - AccessManager: accessManager, - Messages: agdtest.NewConstructor(t), + hdlrConf := &dnssvc.HandlersConfig{ + BaseLogger: slogutil.NewDiscardLogger(), + Cache: &dnssvc.CacheConfig{ + Type: dnssvc.CacheTypeNone, + }, + StructuredErrors: agdtest.NewSDEConfig(true), + Cloner: agdtest.NewCloner(), + HumanIDParser: agd.NewHumanIDParser(), + Messages: agdtest.NewConstructor(t), + AccessManager: accessManager, BillStat: &agdtest.BillStatRecorder{ OnRecord: func( _ context.Context, @@ -235,42 +212,47 @@ func newTestService( ) { }, }, - ProfileDB: db, - PrometheusRegisterer: agdtest.NewTestPrometheusRegisterer(), + CacheManager: agdcache.EmptyManager{}, DNSCheck: dnsCk, - NonDNS: http.NotFoundHandler(), DNSDB: dnsDB, ErrColl: errColl, FilterStorage: fltStrg, GeoIP: geoIP, + Handler: dnsservertest.NewDefaultHandler(), + HashMatcher: hashprefix.NewMatcher(nil), + ProfileDB: profDB, + PrometheusRegisterer: agdtest.NewTestPrometheusRegisterer(), QueryLog: ql, - RuleStat: ruleStat, - NewListener: newTestListenerFunc(tl), - Handler: dnsservertest.DefaultHandler(), RateLimit: rl, + RuleStat: ruleStat, MetricsNamespace: path.Base(t.Name()), FilteringGroups: map[agd.FilteringGroupID]*agd.FilteringGroup{ - testFltGrpID: { - ID: testFltGrpID, + dnssvctest.FilteringGroupID: { + ID: dnssvctest.FilteringGroupID, RuleListIDs: []agd.FilterListID{dnssvctest.FilterListID1}, RuleListsEnabled: true, }, }, - ServerGroups: []*agd.ServerGroup{{ - DDR: &agd.DDR{ - Enabled: true, - }, - TLS: &agd.TLS{ - DeviceDomains: []string{dnssvctest.DomainForDevices}, - }, - Name: testSrvGrpName, - FilteringGroup: testFltGrpID, - Servers: []*agd.Server{srv}, - ProfilesEnabled: true, - }}, + ServerGroups: srvGrps, + EDEEnabled: true, } - svc, err := dnssvc.New(c) + ctx := context.Background() + handlers, err := dnssvc.NewHandlers(ctx, hdlrConf) + require.NoError(t, err) + + c := &dnssvc.Config{ + Handlers: handlers, + NewListener: newTestListenerFunc(tl), + Cloner: agdtest.NewCloner(), + ErrColl: errColl, + NonDNS: http.NotFoundHandler(), + MetricsNamespace: path.Base(t.Name()), + ServerGroups: srvGrps, + HandleTimeout: dnssvctest.Timeout, + } + + svc, err = dnssvc.New(c) require.NoError(t, err) require.NotNil(t, svc) @@ -283,6 +265,7 @@ func newTestService( return svc, srvAddr } +// TODO(a.garipov): Refactor to test handlers separately from the service. func TestService_Wrap(t *testing.T) { profileDBCh := make(chan agd.DeviceID, 1) querylogCh := make(chan *querylog.Entry, 1) @@ -346,7 +329,7 @@ func TestService_Wrap(t *testing.T) { TLSServerName: dnssvctest.DeviceIDSrvName, }) - err := svc.Handle(ctx, testSrvGrpName, dnssvctest.ServerName, rw, req) + err := svc.Handle(ctx, dnssvctest.ServerGroupName, dnssvctest.ServerName, rw, req) require.NoError(t, err) resp := rw.Msg() @@ -420,7 +403,7 @@ func TestService_Wrap(t *testing.T) { TLSServerName: dnssvctest.DeviceIDSrvName, }) - err := svc.Handle(ctx, testSrvGrpName, dnssvctest.ServerName, rw, req) + err := svc.Handle(ctx, dnssvctest.ServerGroupName, dnssvctest.ServerName, rw, req) require.NoError(t, err) resp := rw.Msg() diff --git a/internal/dnssvc/internal/devicefinder/device_test.go b/internal/dnssvc/internal/devicefinder/device_test.go index 4f097bf..e558de7 100644 --- a/internal/dnssvc/internal/devicefinder/device_test.go +++ b/internal/dnssvc/internal/devicefinder/device_test.go @@ -80,35 +80,9 @@ func TestDefault_Find_plainAddrs(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - profDB := &agdtest.ProfileDB{ - OnCreateAutoDevice: func( - ctx context.Context, - id agd.ProfileID, - humanID agd.HumanID, - devType agd.DeviceType, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByDedicatedIP: newOnProfileByDedicatedIP(dnssvctest.DedicatedAddr), - - OnProfileByDeviceID: func( - _ context.Context, - _ agd.DeviceID, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByHumanID: func( - _ context.Context, - _ agd.ProfileID, - _ agd.HumanIDLower, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByLinkedIP: newOnProfileByLinkedIP(dnssvctest.LinkedAddr), - } + profDB := agdtest.NewProfileDB() + profDB.OnProfileByDedicatedIP = newOnProfileByDedicatedIP(dnssvctest.DedicatedAddr) + profDB.OnProfileByLinkedIP = newOnProfileByLinkedIP(dnssvctest.LinkedAddr) df := devicefinder.NewDefault(&devicefinder.Config{ Logger: slogutil.NewDiscardLogger(), @@ -177,40 +151,8 @@ func TestDefault_Find_plainEDNS(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - profDB := &agdtest.ProfileDB{ - OnCreateAutoDevice: func( - ctx context.Context, - id agd.ProfileID, - humanID agd.HumanID, - devType agd.DeviceType, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByDedicatedIP: func( - _ context.Context, - _ netip.Addr, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByDeviceID: newOnProfileByDeviceID(dnssvctest.DeviceID), - - OnProfileByHumanID: func( - _ context.Context, - _ agd.ProfileID, - _ agd.HumanIDLower, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByLinkedIP: func( - _ context.Context, - _ netip.Addr, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - } + profDB := agdtest.NewProfileDB() + profDB.OnProfileByDeviceID = newOnProfileByDeviceID(dnssvctest.DeviceID) df := devicefinder.NewDefault(&devicefinder.Config{ Logger: slogutil.NewDiscardLogger(), @@ -231,44 +173,12 @@ func TestDefault_Find_plainEDNS(t *testing.T) { func TestDefault_Find_deleted(t *testing.T) { t.Parallel() - profDB := &agdtest.ProfileDB{ - OnCreateAutoDevice: func( - ctx context.Context, - id agd.ProfileID, - humanID agd.HumanID, - devType agd.DeviceType, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByDedicatedIP: func( - _ context.Context, - _ netip.Addr, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByDeviceID: func( - _ context.Context, - _ agd.DeviceID, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByHumanID: func( - _ context.Context, - _ agd.ProfileID, - _ agd.HumanIDLower, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByLinkedIP: func( - _ context.Context, - _ netip.Addr, - ) (p *agd.Profile, d *agd.Device, err error) { - return profDeleted, devNormal, nil - }, + profDB := agdtest.NewProfileDB() + profDB.OnProfileByLinkedIP = func( + _ context.Context, + _ netip.Addr, + ) (p *agd.Profile, d *agd.Device, err error) { + return profDeleted, devNormal, nil } df := devicefinder.NewDefault(&devicefinder.Config{ @@ -291,44 +201,21 @@ func TestDefault_Find_byHumanID(t *testing.T) { // device-type and profile data regardless of the case. extIDStr := "OTR-" + strings.ToUpper(dnssvctest.ProfileIDStr) + "-" + dnssvctest.HumanIDStr + "-!!!" - profDB := &agdtest.ProfileDB{ - OnCreateAutoDevice: func( - ctx context.Context, - id agd.ProfileID, - humanID agd.HumanID, - devType agd.DeviceType, - ) (p *agd.Profile, d *agd.Device, err error) { - return profNormal, devAuto, nil - }, - - OnProfileByDedicatedIP: func( - _ context.Context, - _ netip.Addr, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByDeviceID: func( - _ context.Context, - devID agd.DeviceID, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByHumanID: func( - _ context.Context, - _ agd.ProfileID, - _ agd.HumanIDLower, - ) (p *agd.Profile, d *agd.Device, err error) { - return nil, nil, profiledb.ErrDeviceNotFound - }, - - OnProfileByLinkedIP: func( - _ context.Context, - _ netip.Addr, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, + profDB := agdtest.NewProfileDB() + profDB.OnCreateAutoDevice = func( + ctx context.Context, + id agd.ProfileID, + humanID agd.HumanID, + devType agd.DeviceType, + ) (p *agd.Profile, d *agd.Device, err error) { + return profNormal, devAuto, nil + } + profDB.OnProfileByHumanID = func( + _ context.Context, + _ agd.ProfileID, + _ agd.HumanIDLower, + ) (p *agd.Profile, d *agd.Device, err error) { + return nil, nil, profiledb.ErrDeviceNotFound } df := devicefinder.NewDefault(&devicefinder.Config{ diff --git a/internal/dnssvc/internal/devicefinder/devicedata_test.go b/internal/dnssvc/internal/devicefinder/devicedata_test.go index f06c261..18d7997 100644 --- a/internal/dnssvc/internal/devicefinder/devicedata_test.go +++ b/internal/dnssvc/internal/devicefinder/devicedata_test.go @@ -2,7 +2,6 @@ package devicefinder_test import ( "context" - "net/netip" "net/url" "path" "testing" @@ -88,48 +87,16 @@ func TestDefault_Find_DoHAuth(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - profDB := &agdtest.ProfileDB{ - OnCreateAutoDevice: func( - ctx context.Context, - id agd.ProfileID, - humanID agd.HumanID, - devType agd.DeviceType, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, + profDB := agdtest.NewProfileDB() + profDB.OnProfileByDeviceID = func( + _ context.Context, + devID agd.DeviceID, + ) (p *agd.Profile, d *agd.Device, err error) { + if tc.profDBDev != nil { + return profNormal, tc.profDBDev, nil + } - OnProfileByDedicatedIP: func( - _ context.Context, - _ netip.Addr, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByDeviceID: func( - _ context.Context, - devID agd.DeviceID, - ) (p *agd.Profile, d *agd.Device, err error) { - if tc.profDBDev != nil { - return profNormal, tc.profDBDev, nil - } - - return nil, nil, profiledb.ErrDeviceNotFound - }, - - OnProfileByHumanID: func( - _ context.Context, - _ agd.ProfileID, - _ agd.HumanIDLower, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByLinkedIP: func( - _ context.Context, - _ netip.Addr, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, + return nil, nil, profiledb.ErrDeviceNotFound } df := devicefinder.NewDefault(&devicefinder.Config{ @@ -220,44 +187,12 @@ func TestDefault_Find_DoHAuthOnly(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - profDB := &agdtest.ProfileDB{ - OnCreateAutoDevice: func( - ctx context.Context, - id agd.ProfileID, - humanID agd.HumanID, - devType agd.DeviceType, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByDedicatedIP: func( - _ context.Context, - _ netip.Addr, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByDeviceID: func( - _ context.Context, - devID agd.DeviceID, - ) (p *agd.Profile, d *agd.Device, err error) { - return profNormal, tc.profDBDev, nil - }, - - OnProfileByHumanID: func( - _ context.Context, - _ agd.ProfileID, - _ agd.HumanIDLower, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByLinkedIP: func( - _ context.Context, - _ netip.Addr, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, + profDB := agdtest.NewProfileDB() + profDB.OnProfileByDeviceID = func( + _ context.Context, + devID agd.DeviceID, + ) (p *agd.Profile, d *agd.Device, err error) { + return profNormal, tc.profDBDev, nil } df := devicefinder.NewDefault(&devicefinder.Config{ @@ -351,34 +286,9 @@ func TestDefault_Find_DoH(t *testing.T) { name: "human_id_path_match", }} - profDB := &agdtest.ProfileDB{ - OnCreateAutoDevice: func( - ctx context.Context, - id agd.ProfileID, - humanID agd.HumanID, - devType agd.DeviceType, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByDedicatedIP: func( - _ context.Context, - _ netip.Addr, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByDeviceID: newOnProfileByDeviceID(dnssvctest.DeviceID), - - OnProfileByHumanID: newOnProfileByHumanID(dnssvctest.ProfileID, dnssvctest.HumanIDLower), - - OnProfileByLinkedIP: func( - _ context.Context, - _ netip.Addr, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - } + profDB := agdtest.NewProfileDB() + profDB.OnProfileByDeviceID = newOnProfileByDeviceID(dnssvctest.DeviceID) + profDB.OnProfileByHumanID = newOnProfileByHumanID(dnssvctest.ProfileID, dnssvctest.HumanIDLower) for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { @@ -450,34 +360,9 @@ func TestDefault_Find_stdEncrypted(t *testing.T) { deviceDomains: []string{dnssvctest.DomainForDevices}, }} - profDB := &agdtest.ProfileDB{ - OnCreateAutoDevice: func( - ctx context.Context, - id agd.ProfileID, - humanID agd.HumanID, - devType agd.DeviceType, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByDedicatedIP: func( - _ context.Context, - _ netip.Addr, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByDeviceID: newOnProfileByDeviceID(dnssvctest.DeviceID), - - OnProfileByHumanID: newOnProfileByHumanID(dnssvctest.ProfileID, dnssvctest.HumanIDLower), - - OnProfileByLinkedIP: func( - _ context.Context, - _ netip.Addr, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - } + profDB := agdtest.NewProfileDB() + profDB.OnProfileByDeviceID = newOnProfileByDeviceID(dnssvctest.DeviceID) + profDB.OnProfileByHumanID = newOnProfileByHumanID(dnssvctest.ProfileID, dnssvctest.HumanIDLower) srvData := []struct { srv *agd.Server diff --git a/internal/dnssvc/internal/devicefinder/humanid_test.go b/internal/dnssvc/internal/devicefinder/humanid_test.go index 676a6b1..a70c4ad 100644 --- a/internal/dnssvc/internal/devicefinder/humanid_test.go +++ b/internal/dnssvc/internal/devicefinder/humanid_test.go @@ -1,8 +1,6 @@ package devicefinder_test import ( - "context" - "net/netip" "testing" "github.com/AdguardTeam/AdGuardDNS/internal/agd" @@ -49,53 +47,13 @@ func TestDefault_Find_humanID(t *testing.T) { in: "otr-abcd1234-!!!", }} - profDB := &agdtest.ProfileDB{ - OnCreateAutoDevice: func( - ctx context.Context, - id agd.ProfileID, - humanID agd.HumanID, - devType agd.DeviceType, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByDedicatedIP: func( - _ context.Context, - _ netip.Addr, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByDeviceID: func( - _ context.Context, - devID agd.DeviceID, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByHumanID: func( - _ context.Context, - _ agd.ProfileID, - _ agd.HumanIDLower, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - - OnProfileByLinkedIP: func( - _ context.Context, - _ netip.Addr, - ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") - }, - } - for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { t.Parallel() df := devicefinder.NewDefault(&devicefinder.Config{ Logger: slogutil.NewDiscardLogger(), - ProfileDB: profDB, + ProfileDB: agdtest.NewProfileDB(), HumanIDParser: agd.NewHumanIDParser(), Server: srvDoT, DeviceDomains: []string{dnssvctest.DomainForDevices}, diff --git a/internal/dnssvc/internal/dnssvctest/dnssvctest.go b/internal/dnssvc/internal/dnssvctest/dnssvctest.go index a5cdc67..65f0cfb 100644 --- a/internal/dnssvc/internal/dnssvctest/dnssvctest.go +++ b/internal/dnssvc/internal/dnssvctest/dnssvctest.go @@ -55,8 +55,16 @@ const ( DomainRewrittenCNAMEFQDN = DomainRewrittenCNAME + "." ) -// ServerName is the common server name for tests. -const ServerName agd.ServerName = "test_server_dns_tls" +const ( + // FilteringGroupID is the common filtering-group ID for tests. + FilteringGroupID agd.FilteringGroupID = "test_filtering_group" + + // ServerName is the common server name for tests. + ServerName agd.ServerName = "test_server_dns_tls" + + // ServerGroupName is the common server-group name for tests. + ServerGroupName agd.ServerGroupName = "test_server_group" +) const ( // DomainForDevices is the upper-level domain name for requests with device diff --git a/internal/dnssvc/internal/initial/specialdomain.go b/internal/dnssvc/internal/initial/specialdomain.go index 81a8415..d79d102 100644 --- a/internal/dnssvc/internal/initial/specialdomain.go +++ b/internal/dnssvc/internal/initial/specialdomain.go @@ -29,8 +29,8 @@ const ( // Resolvers for querying the resolver with unknown or absent name. DDRDomain = DDRLabel + "." + ResolverARPADomain - // FirefoxCanaryHost is the hostname that Firefox uses to check if it - // should use its own DNS-over-HTTPS settings. + // FirefoxCanaryHost is the hostname that Firefox uses to check if it should + // use its own DNS-over-HTTPS settings. // // See https://support.mozilla.org/en-US/kb/configuring-networks-disable-dns-over-https. FirefoxCanaryHost = "use-application-dns.net" @@ -56,6 +56,11 @@ func (mw *Middleware) reqInfoSpecialHandler( return nil, "" } + // As per RFC-9462 section 6.4, resolvers SHOULD respond to queries of any + // type other than SVCB for _dns.resolver.arpa. with NODATA and queries of + // any type for any domain name under resolver.arpa with NODATA. + // + // TODO(e.burkov): Consider adding SOA records for these NODATA responses. if mw.isDDRRequest(ri) { if _, ok := ri.DeviceResult.(*agd.DeviceResultAuthenticationFailure); ok { return mw.handleDDRNoData, "ddr_doh" @@ -84,8 +89,6 @@ type reqInfoHandlerFunc func( ri *agd.RequestInfo, ) (err error) -// DDR And Resolver ARPA Domain - // isDDRRequest determines if the message is the request for Discovery of // Designated Resolvers as defined by the RFC draft. The request is considered // ARPA if the requested host is a subdomain of resolver.arpa SUDN. @@ -141,8 +144,8 @@ func isDDRDomain(ri *agd.RequestInfo, host string) (ok bool) { return false } -// handleDDR checks if the request is for the Discovery of Designated Resolvers -// and writes a response if needed. +// handleDDR responds to Discovery of Designated Resolvers (DDR) queries with a +// response containing the designated resolvers. func (mw *Middleware) handleDDR( ctx context.Context, rw dnsserver.ResponseWriter, @@ -157,11 +160,11 @@ func (mw *Middleware) handleDDR( return rw.WriteMsg(ctx, req, mw.newRespDDR(req, ri)) } - return rw.WriteMsg(ctx, req, ri.Messages.NewMsgNXDOMAIN(req)) + return rw.WriteMsg(ctx, req, ri.Messages.NewRespRCode(req, dns.RcodeNameError)) } -// handleDDRNoData processes DDR (Discovery of Designated Resolvers) requests -// for devices which need NODATA response and writes the response if needed. +// handleDDRNoData responds to Discovery of Designated Resolvers (DDR) queries +// with a NODATA response. func (mw *Middleware) handleDDRNoData( ctx context.Context, rw dnsserver.ResponseWriter, @@ -173,17 +176,17 @@ func (mw *Middleware) handleDDRNoData( metrics.DNSSvcDDRRequestsTotal.Inc() if ri.ServerGroup.DDR.Enabled { - return rw.WriteMsg(ctx, req, ri.Messages.NewMsgNODATA(req)) + return rw.WriteMsg(ctx, req, ri.Messages.NewRespRCode(req, dns.RcodeSuccess)) } - return rw.WriteMsg(ctx, req, ri.Messages.NewMsgNXDOMAIN(req)) + return rw.WriteMsg(ctx, req, ri.Messages.NewRespRCode(req, dns.RcodeNameError)) } // newRespDDR returns a new Discovery of Designated Resolvers response copying // it from the prebuilt templates in srvGrp and modifying it in accordance with // the request data. req must not be nil. func (mw *Middleware) newRespDDR(req *dns.Msg, ri *agd.RequestInfo) (resp *dns.Msg) { - resp = ri.Messages.NewRespMsg(req) + resp = ri.Messages.NewResp(req) name := req.Question[0].Name ddr := ri.ServerGroup.DDR @@ -210,7 +213,8 @@ func (mw *Middleware) newRespDDR(req *dns.Msg, ri *agd.RequestInfo) (resp *dns.M return resp } -// handleBadResolverARPA writes a NODATA response. +// handleBadResolverARPA responds to badly formed resolver.arpa queries with a +// NODATA response. func (mw *Middleware) handleBadResolverARPA( ctx context.Context, rw dnsserver.ResponseWriter, @@ -219,7 +223,8 @@ func (mw *Middleware) handleBadResolverARPA( ) (err error) { metrics.DNSSvcBadResolverARPA.Inc() - err = rw.WriteMsg(ctx, req, ri.Messages.NewRespMsg(req)) + resp := ri.Messages.NewRespRCode(req, dns.RcodeSuccess) + err = rw.WriteMsg(ctx, req, resp) return errors.Annotate(err, "writing nodata resp for %q: %w", ri.Host) } @@ -257,8 +262,6 @@ func (mw *Middleware) specialDomainHandler( return nil, "" } -// Apple Private Relay - // 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. @@ -280,13 +283,12 @@ func (mw *Middleware) handlePrivateRelay( ) (err error) { metrics.DNSSvcApplePrivateRelayRequestsTotal.Inc() - err = rw.WriteMsg(ctx, req, ri.Messages.NewMsgNXDOMAIN(req)) + resp := ri.Messages.NewRespRCode(req, dns.RcodeNameError) + err = rw.WriteMsg(ctx, req, resp) return errors.Annotate(err, "writing private relay resp: %w") } -// Firefox canary domain - // 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. @@ -298,8 +300,8 @@ func shouldBlockFirefoxCanary(ri *agd.RequestInfo, prof *agd.Profile) (ok bool) return ri.FilteringGroup.BlockFirefoxCanary } -// handleFirefoxCanary checks if the request is for the fully-qualified domain -// name that Firefox uses to check DoH settings and writes a response if needed. +// handleFirefoxCanary responds to Firefox canary domain queries with a REFUSED +// response. func (mw *Middleware) handleFirefoxCanary( ctx context.Context, rw dnsserver.ResponseWriter, @@ -308,7 +310,7 @@ func (mw *Middleware) handleFirefoxCanary( ) (err error) { metrics.DNSSvcFirefoxRequestsTotal.Inc() - resp := ri.Messages.NewMsgREFUSED(req) + resp := ri.Messages.NewRespRCode(req, dns.RcodeRefused) err = rw.WriteMsg(ctx, req, resp) return errors.Annotate(err, "writing firefox canary resp: %w") diff --git a/internal/dnssvc/internal/mainmw/debug_internal_test.go b/internal/dnssvc/internal/mainmw/debug_internal_test.go index a19d528..4a1580b 100644 --- a/internal/dnssvc/internal/mainmw/debug_internal_test.go +++ b/internal/dnssvc/internal/mainmw/debug_internal_test.go @@ -44,7 +44,9 @@ func TestMiddleware_writeDebugResponse(t *testing.T) { msgs, err := dnsmsg.NewConstructor(&dnsmsg.ConstructorConfig{ Cloner: cloner, BlockingMode: &dnsmsg.BlockingModeNullIP{}, + StructuredErrors: agdtest.NewSDEConfig(true), FilteredResponseTTL: agdtest.FilteredResponseTTL, + EDEEnabled: true, }) require.NoError(t, err) diff --git a/internal/dnssvc/internal/mainmw/filter.go b/internal/dnssvc/internal/mainmw/filter.go index 0920e42..72b1a73 100644 --- a/internal/dnssvc/internal/mainmw/filter.go +++ b/internal/dnssvc/internal/mainmw/filter.go @@ -162,7 +162,7 @@ func (mw *Middleware) setFilteredResponse( mw.setFilteredResponseNoReq(ctx, fctx, ri) case *filter.ResultBlocked: var err error - fctx.filteredResponse, err = ri.Messages.NewBlockedRespMsg(fctx.originalRequest) + fctx.filteredResponse, err = ri.Messages.NewBlockedResp(fctx.originalRequest) if err != nil { mw.reportf(ctx, "creating blocked resp for filtered req: %w", err) fctx.filteredResponse = fctx.originalResponse @@ -199,7 +199,7 @@ func (mw *Middleware) setFilteredResponseNoReq( fctx.filteredResponse = fctx.originalResponse case *filter.ResultBlocked: var err error - fctx.filteredResponse, err = ri.Messages.NewBlockedRespMsg(fctx.originalRequest) + fctx.filteredResponse, err = ri.Messages.NewBlockedResp(fctx.originalRequest) if err != nil { mw.reportf(ctx, "creating blocked resp for filtered resp: %w", err) fctx.filteredResponse = fctx.originalResponse diff --git a/internal/dnssvc/internal/mainmw/mainmw.go b/internal/dnssvc/internal/mainmw/mainmw.go index 1fab249..1442636 100644 --- a/internal/dnssvc/internal/mainmw/mainmw.go +++ b/internal/dnssvc/internal/mainmw/mainmw.go @@ -4,6 +4,7 @@ package mainmw import ( "context" + "log/slog" "github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/agdnet" @@ -14,7 +15,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/errcoll" "github.com/AdguardTeam/AdGuardDNS/internal/filter" "github.com/AdguardTeam/AdGuardDNS/internal/geoip" - "github.com/AdguardTeam/AdGuardDNS/internal/optlog" + "github.com/AdguardTeam/AdGuardDNS/internal/optslog" "github.com/AdguardTeam/AdGuardDNS/internal/querylog" "github.com/AdguardTeam/AdGuardDNS/internal/rulestat" "github.com/AdguardTeam/golibs/errors" @@ -24,14 +25,15 @@ import ( // Middleware is the main middleware of AdGuard DNS. type Middleware struct { - messages *dnsmsg.Constructor cloner *dnsmsg.Cloner fltCtxPool *syncutil.Pool[filteringContext] - metrics Metrics + 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 } @@ -39,19 +41,17 @@ type Middleware struct { // Config is the configuration structure for the main middleware. All fields // must be non-nil. type Config struct { - // Metrics is used to collect the statistics. - Metrics Metrics + // Cloner is used to clone messages more efficiently by disposing of parts + // of DNS responses for later reuse. + Cloner *dnsmsg.Cloner + + // Logger is used to log the operation of the middleware. + Logger *slog.Logger // Messages is the message constructor used to create blocked and other // messages for this middleware. Messages *dnsmsg.Constructor - // Cloner is used to clone messages more efficiently by disposing of parts - // of DNS responses for later reuse. - // - // TODO(a.garipov): Use. - Cloner *dnsmsg.Cloner - // BillStat is used to collect billing statistics. BillStat billstat.Recorder @@ -66,6 +66,9 @@ type Config struct { // addresses in requests and responses. GeoIP geoip.Interface + // Metrics is used to collect the statistics. + Metrics Metrics + // QueryLog is used to write the logs into. QueryLog querylog.Interface @@ -77,16 +80,17 @@ type Config struct { // New returns a new main middleware. c must not be nil. func New(c *Config) (mw *Middleware) { return &Middleware{ - metrics: c.Metrics, - messages: c.Messages, - cloner: c.Cloner, + cloner: c.Cloner, fltCtxPool: syncutil.NewPool(func() (v *filteringContext) { return &filteringContext{} }), + logger: c.Logger, + messages: c.Messages, billStat: c.BillStat, errColl: c.ErrColl, fltStrg: c.FilterStorage, geoIP: c.GeoIP, + metrics: c.Metrics, queryLog: c.QueryLog, ruleStat: c.RuleStat, } @@ -106,8 +110,20 @@ func (mw *Middleware) Wrap(next dnsserver.Handler) (wrapped dnsserver.Handler) { defer mw.fltCtxPool.Put(fctx) ri := agd.MustRequestInfoFromContext(ctx) - optlog.Debug2("processing request %q from %s", ri.ID, ri.RemoteIP) - defer optlog.Debug2("finished processing request %q from %s", ri.ID, ri.RemoteIP) + optslog.Debug2( + ctx, + mw.logger, + "processing request", + "req_id", ri.ID, + "remote_ip", ri.RemoteIP, + ) + defer optslog.Debug2( + ctx, + mw.logger, + "finished processing request", + "req_id", ri.ID, + "remote_ip", ri.RemoteIP, + ) flt := mw.fltStrg.FilterFromContext(ctx, ri) mw.filterRequest(ctx, fctx, flt, ri) @@ -184,10 +200,12 @@ func (mw *Middleware) nextParams( ctx = agd.ContextWithRequestInfo(ctx, modReqInfo) - optlog.Debug2( - "mainmw: request for %q rewritten to %q by CNAME rewrite rule", - ri.Host, - modReqInfo.Host, + optslog.Debug2( + ctx, + mw.logger, + "request rewritten by cname rewrite rule", + "orig_host", ri.Host, + "mod_host", modReqInfo.Host, ) return ctx, origRW, modReq diff --git a/internal/dnssvc/internal/mainmw/mainmw_test.go b/internal/dnssvc/internal/mainmw/mainmw_test.go index 9893b12..e904b8b 100644 --- a/internal/dnssvc/internal/mainmw/mainmw_test.go +++ b/internal/dnssvc/internal/mainmw/mainmw_test.go @@ -17,17 +17,13 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/filter" "github.com/AdguardTeam/AdGuardDNS/internal/geoip" "github.com/AdguardTeam/AdGuardDNS/internal/querylog" - "github.com/AdguardTeam/golibs/netutil" + "github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/testutil" "github.com/miekg/dns" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestMain(m *testing.M) { - testutil.DiscardLogOutput(m) -} - // Common constants for tests. const ( testASN geoip.ASN = 12345 @@ -121,24 +117,17 @@ func TestMiddleware_Wrap(t *testing.T) { OnHasListID: func(_ agd.FilterListID) (ok bool) { panic("not implemented") }, } - geoIP := &agdtest.GeoIP{ - OnSubnetByLocation: func( - _ *geoip.Location, - _ netutil.AddrFamily, - ) (n netip.Prefix, err error) { - panic("not implemented") - }, - OnData: func(host string, addr netip.Addr) (l *geoip.Location, err error) { - pt := testutil.PanicT{} - require.Equal(pt, dnssvctest.Domain, host) - if addr.Is4() { - require.Equal(pt, addr, testRespAddr4) - } else if addr.Is6() { - require.Equal(pt, addr, testRespAddr6) - } + geoIP := agdtest.NewGeoIP() + geoIP.OnData = func(host string, addr netip.Addr) (l *geoip.Location, err error) { + pt := testutil.PanicT{} + require.Equal(pt, dnssvctest.Domain, host) + if addr.Is4() { + require.Equal(pt, addr, testRespAddr4) + } else if addr.Is6() { + require.Equal(pt, addr, testRespAddr6) + } - return nil, nil - }, + return nil, nil } ruleStat := &agdtest.RuleStat{ @@ -153,7 +142,9 @@ func TestMiddleware_Wrap(t *testing.T) { msgs, err := dnsmsg.NewConstructor(&dnsmsg.ConstructorConfig{ Cloner: cloner, BlockingMode: &dnsmsg.BlockingModeNullIP{}, + StructuredErrors: agdtest.NewSDEConfig(true), FilteredResponseTTL: agdtest.FilteredResponseTTL, + EDEEnabled: true, }) require.NoError(t, err) @@ -227,13 +218,14 @@ func TestMiddleware_Wrap(t *testing.T) { } c := &mainmw.Config{ - Metrics: mainmw.EmptyMetrics{}, - Messages: msgs, Cloner: cloner, + Logger: slogutil.NewDiscardLogger(), + Messages: msgs, BillStat: tc.billStat, ErrColl: agdtest.NewErrorCollector(), FilterStorage: fltStrg, GeoIP: geoIP, + Metrics: mainmw.EmptyMetrics{}, QueryLog: queryLog, RuleStat: ruleStat, } @@ -411,16 +403,9 @@ func TestMiddleware_Wrap_filtering(t *testing.T) { } ) - geoIP := &agdtest.GeoIP{ - OnSubnetByLocation: func( - _ *geoip.Location, - _ netutil.AddrFamily, - ) (n netip.Prefix, err error) { - panic("not implemented") - }, - OnData: func(host string, addr netip.Addr) (l *geoip.Location, err error) { - return nil, nil - }, + geoIP := agdtest.NewGeoIP() + geoIP.OnData = func(_ string, _ netip.Addr) (l *geoip.Location, err error) { + return nil, nil } var ( @@ -537,7 +522,9 @@ func TestMiddleware_Wrap_filtering(t *testing.T) { msgs, err := dnsmsg.NewConstructor(&dnsmsg.ConstructorConfig{ Cloner: cloner, BlockingMode: &dnsmsg.BlockingModeNullIP{}, + StructuredErrors: agdtest.NewSDEConfig(true), FilteredResponseTTL: agdtest.FilteredResponseTTL, + EDEEnabled: true, }) require.NoError(t, err) @@ -701,13 +688,14 @@ func TestMiddleware_Wrap_filtering(t *testing.T) { } c := &mainmw.Config{ - Metrics: mainmw.EmptyMetrics{}, - Messages: msgs, Cloner: cloner, + Logger: slogutil.NewDiscardLogger(), + Messages: msgs, BillStat: tc.billStat, ErrColl: agdtest.NewErrorCollector(), FilterStorage: fltStrg, GeoIP: geoIP, + Metrics: mainmw.EmptyMetrics{}, QueryLog: queryLog, RuleStat: ruleStat, } diff --git a/internal/dnssvc/internal/mainmw/record.go b/internal/dnssvc/internal/mainmw/record.go index 4fc39a9..9065adc 100644 --- a/internal/dnssvc/internal/mainmw/record.go +++ b/internal/dnssvc/internal/mainmw/record.go @@ -12,7 +12,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver" "github.com/AdguardTeam/AdGuardDNS/internal/geoip" - "github.com/AdguardTeam/AdGuardDNS/internal/optlog" + "github.com/AdguardTeam/AdGuardDNS/internal/optslog" "github.com/AdguardTeam/AdGuardDNS/internal/querylog" "github.com/AdguardTeam/golibs/netutil" "github.com/miekg/dns" @@ -112,8 +112,7 @@ func (mw *Middleware) responseCountry( } ctry = mw.country(ctx, host, respIP) - // TODO(a.garipov): Use optslog.Trace2. - optlog.Debug2("mainmw: got ctry %q for resp ip %v", ctry, respIP) + optslog.Trace2(ctx, mw.logger, "geoip for resp", "ctry", ctry, "resp_ip", respIP) return ctry } diff --git a/internal/dnssvc/internal/mainmw/record_internal_test.go b/internal/dnssvc/internal/mainmw/record_internal_test.go index 4daaee3..7bcadb7 100644 --- a/internal/dnssvc/internal/mainmw/record_internal_test.go +++ b/internal/dnssvc/internal/mainmw/record_internal_test.go @@ -16,7 +16,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/geoip" "github.com/AdguardTeam/AdGuardDNS/internal/querylog" "github.com/AdguardTeam/AdGuardDNS/internal/rulestat" - "github.com/AdguardTeam/golibs/netutil" + "github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/miekg/dns" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -134,20 +134,13 @@ func TestMiddleware_recordQueryInfo_respCtry(t *testing.T) { Country: testCtry, } - geoIP := &agdtest.GeoIP{ - OnSubnetByLocation: func( - _ *geoip.Location, - _ netutil.AddrFamily, - ) (n netip.Prefix, err error) { - panic("not implemented") - }, - OnData: func(_ string, _ netip.Addr) (l *geoip.Location, err error) { - if !tc.wantGeoIP { - t.Error("unexpected call to geoip") - } + geoIP := agdtest.NewGeoIP() + geoIP.OnData = func(_ string, _ netip.Addr) (l *geoip.Location, err error) { + if !tc.wantGeoIP { + t.Error("unexpected call to geoip") + } - return loc, nil - }, + return loc, nil } queryLogCalled := false @@ -164,6 +157,7 @@ func TestMiddleware_recordQueryInfo_respCtry(t *testing.T) { } mw := &Middleware{ + logger: slogutil.NewDiscardLogger(), billStat: billstat.EmptyRecorder{}, geoIP: geoIP, queryLog: queryLog, diff --git a/internal/dnssvc/internal/preservice/preservice.go b/internal/dnssvc/internal/preservice/preservice.go index 4864e5d..120d6d6 100644 --- a/internal/dnssvc/internal/preservice/preservice.go +++ b/internal/dnssvc/internal/preservice/preservice.go @@ -5,15 +5,16 @@ package preservice import ( "context" "fmt" + "log/slog" "github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/dnscheck" "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver" "github.com/AdguardTeam/AdGuardDNS/internal/filter" - "github.com/AdguardTeam/AdGuardDNS/internal/optlog" + "github.com/AdguardTeam/AdGuardDNS/internal/optslog" "github.com/AdguardTeam/golibs/errors" - "github.com/AdguardTeam/golibs/log" + "github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/miekg/dns" ) @@ -22,19 +23,18 @@ import ( // names that may be filtered by safe browsing or parental control filters as // well as handling of the DNS-server check queries. type Middleware struct { - // messages is used to construct TXT responses. - messages *dnsmsg.Constructor - - // hashMatcher is the safe browsing DNS hashMatcher. + logger *slog.Logger + messages *dnsmsg.Constructor hashMatcher filter.HashMatcher - - // checker is used to detect and process DNS-check requests. - checker dnscheck.Interface + checker dnscheck.Interface } // Config is the configurational structure for the preservice middleware. All // fields must be non-nil. type Config struct { + // Logger is used to log the operation of the middleware. + Logger *slog.Logger + // Messages is used to construct TXT responses. Messages *dnsmsg.Constructor @@ -48,6 +48,7 @@ type Config struct { // New returns a new preservice middleware. c must not be nil. func New(c *Config) (mw *Middleware) { return &Middleware{ + logger: c.Logger, messages: c.Messages, hashMatcher: c.HashMatcher, checker: c.Checker, @@ -60,7 +61,7 @@ var _ dnsserver.Middleware = (*Middleware)(nil) // Wrap implements the [dnsserver.Middleware] interface for *Middleware. func (mw *Middleware) Wrap(next dnsserver.Handler) (wrapped dnsserver.Handler) { f := func(ctx context.Context, rw dnsserver.ResponseWriter, req *dns.Msg) (err error) { - defer func() { err = errors.Annotate(err, "preservice mw: %w") }() + defer func() { err = errors.Annotate(err, "presvcmw: %w") }() ri := agd.MustRequestInfoFromContext(ctx) if ri.QType == dns.TypeTXT { @@ -71,13 +72,20 @@ func (mw *Middleware) Wrap(next dnsserver.Handler) (wrapped dnsserver.Handler) { resp, err := mw.checker.Check(ctx, req, ri) if err != nil { return fmt.Errorf("calling dnscheck: %w", err) - } else if resp != nil { - return errors.Annotate(rw.WriteMsg(ctx, req, resp), "writing dnscheck response: %w") } - // Don't wrap the error, because this is the main flow, and there is - // already [errors.Annotate] here. - return next.ServeDNS(ctx, rw, req) + if resp == nil { + // Don't wrap the error, because this is the main flow, and there is + // already [errors.Annotate] here. + return next.ServeDNS(ctx, rw, req) + } + + err = rw.WriteMsg(ctx, req, resp) + if err != nil { + return fmt.Errorf("writing dnscheck response: %w", err) + } + + return nil } return dnsserver.HandlerFunc(f) @@ -92,15 +100,19 @@ func (mw *Middleware) respondWithHashes( req *dns.Msg, ri *agd.RequestInfo, ) (err error) { - optlog.Debug1("preservice mw: safe browsing: got txt req for %q", ri.Host) + optslog.Debug1(ctx, mw.logger, "got txt req", "host", ri.Host) hashes, matched, err := mw.hashMatcher.MatchByPrefix(ctx, ri.Host) if err != nil { // Don't return or collect this error to prevent DDoS of the error // collector by sending bad requests. - log.Error("preservice mw: safe browsing: matching hashes: %s", err) + mw.logger.ErrorContext( + ctx, + "matching hashes", + slogutil.KeyError, err, + ) - resp := mw.messages.NewMsgREFUSED(req) + resp := mw.messages.NewRespRCode(req, dns.RcodeRefused) err = rw.WriteMsg(ctx, req, resp) return errors.Annotate(err, "writing refused response: %w") @@ -110,7 +122,7 @@ func (mw *Middleware) respondWithHashes( return next.ServeDNS(ctx, rw, req) } - resp, err := mw.messages.NewTXTRespMsg(req, hashes...) + resp, err := mw.messages.NewRespTXT(req, hashes...) if err != nil { // Technically should never happen since the only error that could arise // in [dnsmsg.Constructor.NewTXTRespMsg] is the one about request type @@ -118,7 +130,7 @@ func (mw *Middleware) respondWithHashes( return fmt.Errorf("creating safe browsing result: %w", err) } - optlog.Debug1("preservice mw: safe browsing: writing hashes %q", hashes) + optslog.Debug1(ctx, mw.logger, "writing hashes", "hashes", hashes) err = rw.WriteMsg(ctx, req, resp) if err != nil { diff --git a/internal/dnssvc/internal/preservice/preservice_test.go b/internal/dnssvc/internal/preservice/preservice_test.go index 2cffe48..01a7b30 100644 --- a/internal/dnssvc/internal/preservice/preservice_test.go +++ b/internal/dnssvc/internal/preservice/preservice_test.go @@ -17,18 +17,16 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/filter" "github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix" "github.com/AdguardTeam/golibs/errors" + "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" ) -func TestMain(m *testing.M) { - testutil.DiscardLogOutput(m) -} - func TestPreServiceMwHandler_ServeDNS(t *testing.T) { + t.Parallel() + const safeBrowsingHost = "scam.example.net." var ( @@ -124,6 +122,8 @@ func TestPreServiceMwHandler_ServeDNS(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { + t.Parallel() + rw := dnsserver.NewNonWriterResponseWriter(nil, dnssvctest.ClientTCPAddr) tctx := agd.ContextWithRequestInfo(ctx, tc.ri) @@ -152,16 +152,19 @@ func TestPreServiceMwHandler_ServeDNS(t *testing.T) { msgs, err := dnsmsg.NewConstructor(&dnsmsg.ConstructorConfig{ Cloner: cloner, BlockingMode: &dnsmsg.BlockingModeNullIP{}, + StructuredErrors: agdtest.NewSDEConfig(true), FilteredResponseTTL: ttl * time.Second, + EDEEnabled: true, }) require.NoError(t, err) mw := preservice.New(&preservice.Config{ + Logger: slogutil.NewDiscardLogger(), Messages: msgs, HashMatcher: hashMatcher, Checker: dnsCk, }) - handler := dnsservertest.DefaultHandler() + handler := dnsservertest.NewDefaultHandler() h := mw.Wrap(handler) err = h.ServeDNS(tctx, rw, tc.req) diff --git a/internal/dnssvc/internal/preupstream/preupstream.go b/internal/dnssvc/internal/preupstream/preupstream.go index eecdd7d..6c3d4bd 100644 --- a/internal/dnssvc/internal/preupstream/preupstream.go +++ b/internal/dnssvc/internal/preupstream/preupstream.go @@ -1,91 +1,40 @@ -// Package preupstream contains the middleware that prepares records for -// upstream handling and caches them, as well as records anonymous DNS +// Package preupstream contains the middleware that records anonymous DNS // statistics. +// +// TODO(a.garipov): Consider merging with mainmw if not expanded. package preupstream import ( "context" "fmt" - "time" "github.com/AdguardTeam/AdGuardDNS/internal/agd" - "github.com/AdguardTeam/AdGuardDNS/internal/agdcache" "github.com/AdguardTeam/AdGuardDNS/internal/agdnet" "github.com/AdguardTeam/AdGuardDNS/internal/dnsdb" "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver" - "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/cache" - "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/prometheus" "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal" - "github.com/AdguardTeam/AdGuardDNS/internal/ecscache" - "github.com/AdguardTeam/AdGuardDNS/internal/geoip" - "github.com/AdguardTeam/AdGuardDNS/internal/metrics" "github.com/AdguardTeam/golibs/errors" - "github.com/AdguardTeam/golibs/log" "github.com/miekg/dns" ) // Middleware is a middleware that prepares records for caching and upstream // handling as well as records anonymous DNS statistics. type Middleware struct { - cloner *dnsmsg.Cloner - cacheManager agdcache.Manager - db dnsdb.Interface - geoIP geoip.Interface - cacheMinTTL time.Duration - cacheSize int - ecsCacheSize int - useECSCache bool - useCacheTTLOverride bool + db dnsdb.Interface } -// Config is the configurational structure for the preupstream middleware. DB -// must not be nil. +// Config is the configuration structure for the preupstream middleware. type Config struct { - // Cloner is used to clone messages taken from cache. - Cloner *dnsmsg.Cloner - - // CacheManager is the global cache manager. CacheManager must not be nil. - CacheManager agdcache.Manager - - // DB is used to update anonymous statistics about DNS queries. + // DB is used to update anonymous statistics about DNS queries. It must not + // be nil. DB dnsdb.Interface - - // GeoIP is the GeoIP database used to detect geographic data about IP - // addresses in requests and responses. - GeoIP geoip.Interface - - // CacheMinTTL is the minimum supported TTL for cache items. - CacheMinTTL time.Duration - - // CacheSize is the size of the DNS cache for domain names that don't - // support ECS. - CacheSize int - - // ECSCacheSize is the size of the DNS cache for domain names that support - // ECS. - ECSCacheSize int - - // UseECSCache shows if the EDNS Client Subnet (ECS) aware cache should be - // used. - UseECSCache bool - - // UseCacheTTLOverride shows if the TTL overrides logic should be used. - UseCacheTTLOverride bool } // New returns a new preupstream middleware. c must not be nil. -func New(c *Config) (mw *Middleware) { +func New(ctx context.Context, c *Config) (mw *Middleware) { return &Middleware{ - cloner: c.Cloner, - cacheManager: c.CacheManager, - db: c.DB, - geoIP: c.GeoIP, - cacheMinTTL: c.CacheMinTTL, - cacheSize: c.CacheSize, - ecsCacheSize: c.ECSCacheSize, - useECSCache: c.UseECSCache, - useCacheTTLOverride: c.UseCacheTTLOverride, + db: c.DB, } } @@ -94,10 +43,8 @@ var _ dnsserver.Middleware = (*Middleware)(nil) // Wrap implements the [dnsserver.Middleware] interface for *Middleware. func (mw *Middleware) Wrap(next dnsserver.Handler) (wrapped dnsserver.Handler) { - next = mw.wrapCacheMw(next) - f := func(ctx context.Context, rw dnsserver.ResponseWriter, req *dns.Msg) (err error) { - defer func() { err = errors.Annotate(err, "preupstream mw: %w") }() + defer func() { err = errors.Annotate(err, "preupstreammw: %w") }() if rn := agdnet.AndroidMetricDomainReplacement(req.Question[0].Name); rn != "" { // Don't wrap the error, because it's informative enough as is. @@ -127,40 +74,6 @@ func (mw *Middleware) Wrap(next dnsserver.Handler) (wrapped dnsserver.Handler) { return dnsserver.HandlerFunc(f) } -// wrapCacheMw does nothing if cacheSize is zero otherwise returns wrapped -// handler with caching middleware which is ECS-aware or not. -// -// TODO(s.chzhen): Consider separating caching middleware. -func (mw *Middleware) wrapCacheMw(next dnsserver.Handler) (wrapped dnsserver.Handler) { - log.Info("cache: size: %d, ecs: %t", mw.cacheSize, mw.useECSCache) - - if mw.cacheSize == 0 { - return next - } - - var cacheMw dnsserver.Middleware - if mw.useECSCache { - cacheMw = ecscache.NewMiddleware(&ecscache.MiddlewareConfig{ - Cloner: mw.cloner, - CacheManager: mw.cacheManager, - GeoIP: mw.geoIP, - Size: mw.cacheSize, - ECSSize: mw.ecsCacheSize, - MinTTL: mw.cacheMinTTL, - UseTTLOverride: mw.useCacheTTLOverride, - }) - } else { - cacheMw = cache.NewMiddleware(&cache.MiddlewareConfig{ - MetricsListener: prometheus.NewCacheMetricsListener(metrics.Namespace()), - Size: mw.cacheSize, - MinTTL: mw.cacheMinTTL, - UseTTLOverride: mw.useCacheTTLOverride, - }) - } - - return cacheMw.Wrap(next) -} - // serveAndroidMetric makes sure we avoid resolving random Android DoT, DoH // metric domains. replName is the replacement domain name to use to improve // caching of these metric domains. diff --git a/internal/dnssvc/internal/preupstream/preupstream_test.go b/internal/dnssvc/internal/preupstream/preupstream_test.go index 89f842f..0d89388 100644 --- a/internal/dnssvc/internal/preupstream/preupstream_test.go +++ b/internal/dnssvc/internal/preupstream/preupstream_test.go @@ -2,180 +2,36 @@ package preupstream_test import ( "context" - "net" - "net/netip" "testing" "github.com/AdguardTeam/AdGuardDNS/internal/agd" - "github.com/AdguardTeam/AdGuardDNS/internal/agdcache" - "github.com/AdguardTeam/AdGuardDNS/internal/agdtest" "github.com/AdguardTeam/AdGuardDNS/internal/dnsdb" "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest" "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/dnssvctest" "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/preupstream" - "github.com/AdguardTeam/AdGuardDNS/internal/geoip" - "github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/testutil" "github.com/miekg/dns" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestMain(m *testing.M) { - testutil.DiscardLogOutput(m) -} - -const ( - reqHostname = "example.com." - defaultTTL = 3600 -) - -func TestPreUpstreamMwHandler_ServeDNS_withCache(t *testing.T) { - aReq := dnsservertest.NewReq(reqHostname, dns.TypeA, dns.ClassINET) - - resp := dnsservertest.NewResp(dns.RcodeSuccess, aReq, dnsservertest.SectionAnswer{ - dnsservertest.NewA(reqHostname, defaultTTL, dnssvctest.ClientAddr), - }) - ctx := agd.ContextWithRequestInfo(context.Background(), &agd.RequestInfo{ - Host: aReq.Question[0].Name, - }) - - const N = 5 - testCases := []struct { - name string - cacheSize int - wantNumReq int - }{{ - name: "no_cache", - cacheSize: 0, - wantNumReq: N, - }, { - name: "with_cache", - cacheSize: 100, - wantNumReq: 1, - }} - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - numReq := 0 - handler := dnsserver.HandlerFunc(func( - ctx context.Context, - rw dnsserver.ResponseWriter, - req *dns.Msg, - ) error { - numReq++ - - return rw.WriteMsg(ctx, req, resp) - }) - - mw := preupstream.New(&preupstream.Config{ - Cloner: agdtest.NewCloner(), - DB: dnsdb.Empty{}, - CacheSize: tc.cacheSize, - }) - h := mw.Wrap(handler) - - for range N { - req := dnsservertest.NewReq(reqHostname, dns.TypeA, dns.ClassINET) - addr := &net.UDPAddr{IP: dnssvctest.ClientIP, Port: 53} - nrw := dnsserver.NewNonWriterResponseWriter(addr, addr) - - err := h.ServeDNS(ctx, nrw, req) - require.NoError(t, err) - } - - assert.Equal(t, tc.wantNumReq, numReq) - }) - } -} - -func TestPreUpstreamMwHandler_ServeDNS_withECSCache(t *testing.T) { - aReq := dnsservertest.NewReq(reqHostname, dns.TypeA, dns.ClassINET) - subnet := netip.MustParsePrefix("1.2.3.4/24") - - const ctry = geoip.CountryAD - - resp := dnsservertest.NewResp(dns.RcodeSuccess, aReq, dnsservertest.SectionAnswer{ - dnsservertest.NewA(reqHostname, defaultTTL, dnssvctest.ClientAddr), - }) - - numReq := 0 - handler := dnsserver.HandlerFunc( - func(ctx context.Context, rw dnsserver.ResponseWriter, req *dns.Msg) error { - numReq++ - - return rw.WriteMsg(ctx, req, resp) - }, - ) - - geoIP := &agdtest.GeoIP{ - OnSubnetByLocation: func( - _ *geoip.Location, - _ netutil.AddrFamily, - ) (n netip.Prefix, err error) { - return netip.MustParsePrefix("1.2.0.0/16"), nil - }, - OnData: func(_ string, _ netip.Addr) (_ *geoip.Location, _ error) { - panic("not implemented") - }, - } - - mw := preupstream.New(&preupstream.Config{ - Cloner: agdtest.NewCloner(), - CacheManager: agdcache.EmptyManager{}, - DB: dnsdb.Empty{}, - GeoIP: geoIP, - CacheSize: 100, - ECSCacheSize: 100, - UseECSCache: true, - }) - h := mw.Wrap(handler) - - ctx := agd.ContextWithRequestInfo(context.Background(), &agd.RequestInfo{ - Location: &geoip.Location{ - Country: ctry, - }, - ECS: &dnsmsg.ECS{ - Location: &geoip.Location{ - Country: ctry, - }, - Subnet: subnet, - Scope: 0, - }, - Host: aReq.Question[0].Name, - RemoteIP: dnssvctest.ClientAddr, - }) - - const N = 5 - var nrw *dnsserver.NonWriterResponseWriter - for range N { - addr := &net.UDPAddr{IP: dnssvctest.ClientIP, Port: 53} - nrw = dnsserver.NewNonWriterResponseWriter(addr, addr) - req := dnsservertest.NewReq(reqHostname, dns.TypeA, dns.ClassINET) - - err := h.ServeDNS(ctx, nrw, req) - require.NoError(t, err) - } - - assert.Equal(t, 1, numReq) -} - func TestPreUpstreamMwHandler_ServeDNS_androidMetric(t *testing.T) { - mw := preupstream.New(&preupstream.Config{ - Cloner: agdtest.NewCloner(), - DB: dnsdb.Empty{}, + t.Parallel() + + ctx := testutil.ContextWithTimeout(t, dnssvctest.Timeout) + mw := preupstream.New(ctx, &preupstream.Config{ + DB: dnsdb.Empty{}, }) - req := dnsservertest.CreateMessage(reqHostname, dns.TypeA) - resp := new(dns.Msg).SetReply(req) + req := dnsservertest.CreateMessage(dnssvctest.DomainFQDN, dns.TypeA) + defaultResp := new(dns.Msg).SetReply(req) - ctx := context.Background() ctx = agd.ContextWithRequestInfo(ctx, &agd.RequestInfo{}) - ipA := netip.MustParseAddr("1.2.3.4") - ipB := netip.MustParseAddr("1.2.3.5") + ipA := dnssvctest.ClientAddr + ipB := ipA.Next() const ttl = 100 @@ -192,20 +48,20 @@ func TestPreUpstreamMwHandler_ServeDNS_androidMetric(t *testing.T) { wantAns []dns.RR }{{ name: "no_changes", - req: dnsservertest.CreateMessage(reqHostname, dns.TypeA), - resp: resp, - wantName: reqHostname, + req: dnsservertest.CreateMessage(dnssvctest.DomainFQDN, dns.TypeA), + resp: dnsmsg.Clone(defaultResp), + wantName: dnssvctest.DomainFQDN, wantAns: nil, }, { name: "android-tls-metric", req: dnsservertest.CreateMessage("12345678"+tlsDomain, dns.TypeA), - resp: resp, + resp: dnsmsg.Clone(defaultResp), wantName: "00000000" + tlsDomain, wantAns: nil, }, { name: "android-https-metric", req: dnsservertest.CreateMessage("123456"+httpsDomain, dns.TypeA), - resp: resp, + resp: dnsmsg.Clone(defaultResp), wantName: "000000" + httpsDomain, wantAns: nil, }, { @@ -224,6 +80,8 @@ func TestPreUpstreamMwHandler_ServeDNS_androidMetric(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { + t.Parallel() + handler := dnsserver.HandlerFunc(func( ctx context.Context, rw dnsserver.ResponseWriter, diff --git a/internal/dnssvc/internal/ratelimitmw/access_test.go b/internal/dnssvc/internal/ratelimitmw/access_test.go index 5d7c248..51b5656 100644 --- a/internal/dnssvc/internal/ratelimitmw/access_test.go +++ b/internal/dnssvc/internal/ratelimitmw/access_test.go @@ -18,7 +18,6 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/geoip" "github.com/AdguardTeam/golibs/errors" "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" @@ -56,16 +55,9 @@ func TestMiddleware_Wrap_access(t *testing.T) { ) require.NoError(t, errAccess) - geoIP := &agdtest.GeoIP{ - OnSubnetByLocation: func( - _ *geoip.Location, - _ netutil.AddrFamily, - ) (n netip.Prefix, err error) { - panic("not implemented") - }, - OnData: func(_ string, _ netip.Addr) (l *geoip.Location, err error) { - return nil, nil - }, + geoIP := agdtest.NewGeoIP() + geoIP.OnData = func(_ string, _ netip.Addr) (l *geoip.Location, err error) { + return nil, nil } rlMw := ratelimitmw.New(&ratelimitmw.Config{ @@ -77,7 +69,8 @@ func TestMiddleware_Wrap_access(t *testing.T) { // Use a DoT server to prevent ratelimiting. Protocol: agd.ProtoDoT, }, - AccessManager: accessMgr, + StructuredErrors: agdtest.NewSDEConfig(true), + AccessManager: accessMgr, DeviceFinder: &agdtest.DeviceFinder{ OnFind: func(_ context.Context, _ *dns.Msg, _, _ netip.AddrPort) (r agd.DeviceResult) { return nil @@ -86,21 +79,11 @@ func TestMiddleware_Wrap_access(t *testing.T) { ErrColl: agdtest.NewErrorCollector(), GeoIP: geoIP, Metrics: ratelimitmw.EmptyMetrics{}, - Limiter: &agdtest.RateLimit{ - OnIsRateLimited: func( - _ context.Context, - _ *dns.Msg, - _ netip.Addr, - ) (shouldDrop, isAllowlisted bool, err error) { - panic("not implemented") - }, - OnCountResponses: func(_ context.Context, _ *dns.Msg, _ netip.Addr) { - panic("not implemented") - }, - }, + Limiter: agdtest.NewRateLimit(), Protocols: []agd.Protocol{ agd.ProtoDNS, }, + EDEEnabled: true, }) testCases := []struct { diff --git a/internal/dnssvc/internal/ratelimitmw/ratelimitmw.go b/internal/dnssvc/internal/ratelimitmw/ratelimitmw.go index 16efd7f..338dfad 100644 --- a/internal/dnssvc/internal/ratelimitmw/ratelimitmw.go +++ b/internal/dnssvc/internal/ratelimitmw/ratelimitmw.go @@ -34,6 +34,7 @@ type Middleware struct { logger *slog.Logger messages *dnsmsg.Constructor pool *syncutil.Pool[agd.RequestInfo] + sdeConf *dnsmsg.StructuredDNSErrorsConfig accessManager access.Interface deviceFinder agd.DeviceFinder errColl errcoll.Interface @@ -41,6 +42,7 @@ type Middleware struct { limiter ratelimit.Interface metrics Metrics protos []dnsserver.Protocol + edeEnabled bool } // Config is the configuration structure for the access and ratelimiting @@ -61,6 +63,10 @@ type Config struct { // Server is the current server which serves the request. Server *agd.Server + // StructuredErrors is the configuration for the experimental Structured DNS + // Errors feature for the profiles' message constructors. + StructuredErrors *dnsmsg.StructuredDNSErrorsConfig + // AccessManager is the global access manager. AccessManager access.Interface @@ -82,6 +88,10 @@ type Config struct { // Protocols is a list of protocols this middleware applies ratelimiting // logic to. Protocols must not be changed after calling [New]. Protocols []agd.Protocol + + // EDEEnabled enables the addition of the Extended DNS Error (EDE) codes in + // the profiles' message constructors. + EDEEnabled bool } // New returns a new access middleware. c must not be nil. @@ -98,6 +108,7 @@ func New(c *Config) (mw *Middleware) { Proto: c.Server.Protocol, } }), + sdeConf: c.StructuredErrors, accessManager: c.AccessManager, deviceFinder: c.DeviceFinder, errColl: c.ErrColl, @@ -105,6 +116,7 @@ func New(c *Config) (mw *Middleware) { limiter: c.Limiter, metrics: c.Metrics, protos: c.Protocols, + edeEnabled: c.EDEEnabled, } } @@ -172,7 +184,8 @@ func (mw *Middleware) processLocationErr( // We've got a bad ECS option. Log and respond with a FORMERR immediately. optslog.Debug1(ctx, mw.logger, "ecs error", slogutil.KeyError, origErr) - writeErr := rw.WriteMsg(ctx, req, mw.messages.NewMsgFORMERR(req)) + resp := mw.messages.NewRespRCode(req, dns.RcodeFormatError) + writeErr := rw.WriteMsg(ctx, req, resp) writeErr = errors.Annotate(writeErr, "writing formerr resp: %w") return errors.WithDeferred(origErr, writeErr) diff --git a/internal/dnssvc/internal/ratelimitmw/requestinfo.go b/internal/dnssvc/internal/ratelimitmw/requestinfo.go index bf4b330..7e6df09 100644 --- a/internal/dnssvc/internal/ratelimitmw/requestinfo.go +++ b/internal/dnssvc/internal/ratelimitmw/requestinfo.go @@ -56,7 +56,9 @@ func (mw *Middleware) newRequestInfo( messages, err := dnsmsg.NewConstructor(&dnsmsg.ConstructorConfig{ Cloner: cloner, BlockingMode: p.BlockingMode, + StructuredErrors: mw.sdeConf, FilteredResponseTTL: p.FilteredResponseTTL, + EDEEnabled: mw.edeEnabled, }) if err != nil { err = fmt.Errorf("creating constructor for profile %q: %w", p.ID, err) diff --git a/internal/dnssvc/reexport.go b/internal/dnssvc/reexport.go new file mode 100644 index 0000000..7e3ca11 --- /dev/null +++ b/internal/dnssvc/reexport.go @@ -0,0 +1,16 @@ +package dnssvc + +import ( + "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/mainmw" + "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/ratelimitmw" +) + +type ( + // MainMiddlewareMetrics is a re-export of the internal filtering-middleware + // metrics interface. + MainMiddlewareMetrics = mainmw.Metrics + + // RatelimitMiddlewareMetrics is a re-export of the metrics interface of the + // internal access and ratelimiting middleware. + RatelimitMiddlewareMetrics = ratelimitmw.Metrics +) diff --git a/internal/ecscache/cache.go b/internal/ecscache/cache.go index fa3b915..08ed43c 100644 --- a/internal/ecscache/cache.go +++ b/internal/ecscache/cache.go @@ -1,6 +1,7 @@ package ecscache import ( + "context" "encoding/binary" "hash/maphash" "net/netip" @@ -8,7 +9,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/agdcache" "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" - "github.com/AdguardTeam/AdGuardDNS/internal/optlog" + "github.com/AdguardTeam/AdGuardDNS/internal/optslog" "github.com/AdguardTeam/golibs/mathutil" "github.com/miekg/dns" ) @@ -43,9 +44,13 @@ type cacheRequest struct { // there is one. If the host was found in the cache for domain names that // support ECS, isECSDependent is true. cr, cr.req, and cr.subnet must not be // nil. -func (mw *Middleware) get(req *dns.Msg, cr *cacheRequest) (resp *dns.Msg, isECSDependent bool) { +func (mw *Middleware) get( + ctx context.Context, + req *dns.Msg, + cr *cacheRequest, +) (resp *dns.Msg, isECSDependent bool) { key := mw.toCacheKey(cr, false) - item, ok := itemFromCache(mw.cache, key, cr) + item, ok := mw.itemFromCache(ctx, mw.cache, key, cr) if ok { return fromCacheItem(item, mw.cloner, req, cr.reqDO), false } else if cr.isECSDeclined { @@ -54,7 +59,7 @@ func (mw *Middleware) get(req *dns.Msg, cr *cacheRequest) (resp *dns.Msg, isECSD // Try ECS-aware cache. key = mw.toCacheKey(cr, true) - item, ok = itemFromCache(mw.ecsCache, key, cr) + item, ok = mw.itemFromCache(ctx, mw.ecsCache, key, cr) if ok { return fromCacheItem(item, mw.cloner, req, cr.reqDO), true } @@ -65,7 +70,8 @@ func (mw *Middleware) get(req *dns.Msg, cr *cacheRequest) (resp *dns.Msg, isECSD // itemFromCache retrieves a DNS message for the given key. cr.host is used to // detect key collisions. If there is a key collision, it returns nil and // false. -func itemFromCache( +func (mw *Middleware) itemFromCache( + ctx context.Context, cache agdcache.Interface[uint64, *cacheItem], key uint64, cr *cacheRequest, @@ -77,7 +83,7 @@ func itemFromCache( // Check for cache key collisions. if item.host != cr.host { - optlog.Error2("ecs-cache: collision: bad cache item %v for host %q", item, cr.host) + optslog.Warn2(ctx, mw.logger, "cache collision", "item", item, "host", cr.host) return nil, false } @@ -136,7 +142,7 @@ func (mw *Middleware) set(resp *dns.Msg, cr *cacheRequest, respIsECSDependent bo } exp := time.Duration(ttl) * time.Second - if mw.useTTLOverride && resp.Rcode != dns.RcodeServerFailure { + if mw.overrideTTL && resp.Rcode != dns.RcodeServerFailure { exp = max(exp, mw.cacheMinTTL) dnsmsg.SetMinTTL(resp, uint32(exp.Seconds())) } diff --git a/internal/ecscache/cache_internal_test.go b/internal/ecscache/cache_internal_test.go index de0e754..2084b07 100644 --- a/internal/ecscache/cache_internal_test.go +++ b/internal/ecscache/cache_internal_test.go @@ -1,6 +1,7 @@ package ecscache import ( + "context" "net/netip" "testing" @@ -35,9 +36,11 @@ func BenchmarkMiddleware_Get(b *testing.B) { reqDO: true, } + ctx := context.Background() + b.ReportAllocs() b.ResetTimer() for range b.N { - msgSink, _ = mw.get(req, cr) + msgSink, _ = mw.get(ctx, req, cr) } } diff --git a/internal/ecscache/ecsblocklist.go b/internal/ecscache/ecsblocklist.go index b3ff467..7f0e84f 100644 --- a/internal/ecscache/ecsblocklist.go +++ b/internal/ecscache/ecsblocklist.go @@ -10,12 +10,13 @@ var FakeECSFQDNs = container.NewMapSet( "0231034e888a4837a17fd85b1ab159.ba.tenant.api.powerplatform.com.", "0272ac85-5199-4024-a555-397c3d825d95.prmutv.co.", "0b732edd-087f-4c27-b0f6-5ff26d96cdf6.prmutv.co.", - "0bb8856ba8259ec33e3d-a40599a114f3a4c6d0979c3ffe0b2bf5.ssl.cf2.rackcdn.com.", "0cf.io.", "0cf17917-395b-4f25-91cc-db3bdd6044b0.prmutv.co.", + "1024tera.com.", "1024terabox.com.", "103.chtsite.com.", "11-alarm-mop.meshare.com.", + "11222.cn.", "12-alarm-mop.meshare.com.", "126.com.", "126.net.", @@ -26,11 +27,11 @@ var FakeECSFQDNs = container.NewMapSet( "163jiasu.com.", "163yun.com.", "1688.com.", - "173bf105.akstat.io.", + "173bf104.akstat.io.", + "173bf10f.akstat.io.", "18ea70d2d9a945cfb97d818ba71817dc.pacloudflare.com.", "2.401402081.west-gcloud.codm.activision.com.", "2.realtime.services.box.net.", - "201205igp.gameloft.com.", "2acdb9b66bb242618283aadb21ede6c1.pacloudflare.com.", "2e4b93d1-a8ae-4a89-8885-6109135ac0de.prmutv.co.", "2owo1y.n0qq3z.com.", @@ -42,7 +43,12 @@ 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.", @@ -50,9 +56,8 @@ var FakeECSFQDNs = container.NewMapSet( "5nines.com.", "6.401402081.west-gcloud.codm.activision.com.", "6093eccf-6734-4877-ac8b-83d6d0e27b46.prmutv.co.", - "6c3e19e3-d05e-45d1-8f79-fcd6cb2f3a21.prmutv.co.", + "66yahoo.com.", "733868e706dd40d3a4a0588fc39b3df8.pacloudflare.com.", - "79423.analytics.edgekey.net.", "7c7b02d4bc3d48dd81a7c7738d4de1ab.pacloudflare.com.", "82fff5ef370dea21b562bafc3aa751.85.environment.api.powerplatform.com.", "88a66e5c-8fe8-48af-9c6c-3ec3f4983aad.prmutv.co.", @@ -62,152 +67,14 @@ var FakeECSFQDNs = container.NewMapSet( "a-api.anthropic.com.", "a-cdn.anthropic.com.", "a-m-p.xyz.", + "a.ad.gt.cdn.cloudflare.net.", "a.getepic.com.", + "a.klaviyo.com.cdn.cloudflare.net.", "a.nel.cloudflare.com.", "a.nitropay.com.", - "a.poki.com.", - "a010.casalemedia.com.", - "a011.casalemedia.com.", - "a012.casalemedia.com.", - "a013.casalemedia.com.", - "a015.casalemedia.com.", - "a016.casalemedia.com.", - "a017.casalemedia.com.", - "a018.casalemedia.com.", - "a019.casalemedia.com.", - "a020.casalemedia.com.", - "a021.casalemedia.com.", - "a022.casalemedia.com.", - "a023.casalemedia.com.", - "a024.casalemedia.com.", - "a025.casalemedia.com.", - "a026.casalemedia.com.", - "a027.casalemedia.com.", - "a028.casalemedia.com.", - "a029.casalemedia.com.", - "a030.casalemedia.com.", - "a031.casalemedia.com.", - "a032.casalemedia.com.", - "a033.casalemedia.com.", - "a034.casalemedia.com.", - "a035.casalemedia.com.", - "a036.casalemedia.com.", - "a037.casalemedia.com.", - "a038.casalemedia.com.", - "a039.casalemedia.com.", - "a040.casalemedia.com.", - "a041.casalemedia.com.", - "a043.casalemedia.com.", - "a044.casalemedia.com.", - "a046.casalemedia.com.", - "a049.casalemedia.com.", - "a050.casalemedia.com.", - "a054.casalemedia.com.", - "a058.casalemedia.com.", - "a074.casalemedia.com.", - "a075.casalemedia.com.", - "a077.casalemedia.com.", - "a084.casalemedia.com.", - "a088.casalemedia.com.", - "a091.casalemedia.com.", - "a095.casalemedia.com.", - "a096.casalemedia.com.", - "a097.casalemedia.com.", - "a098.casalemedia.com.", - "a099.casalemedia.com.", - "a101.casalemedia.com.", - "a108.casalemedia.com.", - "a119.casalemedia.com.", - "a121.casalemedia.com.", - "a123.casalemedia.com.", - "a127.casalemedia.com.", - "a128.casalemedia.com.", - "a129.casalemedia.com.", - "a131.casalemedia.com.", - "a132.casalemedia.com.", - "a134.casalemedia.com.", - "a136.casalemedia.com.", - "a137.casalemedia.com.", - "a138.casalemedia.com.", - "a139.casalemedia.com.", - "a140.casalemedia.com.", - "a141.casalemedia.com.", - "a143.casalemedia.com.", - "a144.casalemedia.com.", - "a145.casalemedia.com.", - "a146.casalemedia.com.", - "a1463.casalemedia.com.", - "a1469.casalemedia.com.", - "a147.casalemedia.com.", - "a1472.casalemedia.com.", - "a1478.casalemedia.com.", - "a149.casalemedia.com.", - "a1490.casalemedia.com.", - "a1496.casalemedia.com.", - "a1497.casalemedia.com.", - "a150.casalemedia.com.", - "a1506.casalemedia.com.", - "a151.casalemedia.com.", - "a152.casalemedia.com.", - "a1523.casalemedia.com.", - "a1524.casalemedia.com.", - "a1525.casalemedia.com.", - "a153.casalemedia.com.", - "a1530.casalemedia.com.", - "a1541.casalemedia.com.", - "a1542.casalemedia.com.", - "a1550.casalemedia.com.", - "a156.casalemedia.com.", - "a157.casalemedia.com.", - "a1575.casalemedia.com.", - "a1579.casalemedia.com.", - "a158.casalemedia.com.", - "a1585.casalemedia.com.", - "a159.casalemedia.com.", - "a1591.casalemedia.com.", - "a1594.casalemedia.com.", - "a1598.casalemedia.com.", - "a160.casalemedia.com.", - "a1602.casalemedia.com.", - "a1605.casalemedia.com.", - "a161.casalemedia.com.", - "a1617.casalemedia.com.", - "a1623.casalemedia.com.", - "a1629.casalemedia.com.", - "a1631.casalemedia.com.", - "a1635.casalemedia.com.", - "a1637.casalemedia.com.", - "a1641.casalemedia.com.", - "a1651.casalemedia.com.", - "a1656.casalemedia.com.", - "a1658.casalemedia.com.", - "a166.casalemedia.com.", - "a1665.casalemedia.com.", - "a1667.casalemedia.com.", - "a1669.casalemedia.com.", - "a1670.casalemedia.com.", - "a1681.casalemedia.com.", - "a1682.casalemedia.com.", - "a1683.casalemedia.com.", - "a1687.casalemedia.com.", - "a1689.casalemedia.com.", - "a169.casalemedia.com.", - "a1691.casalemedia.com.", - "a1695.casalemedia.com.", - "a1698.casalemedia.com.", - "a1700.casalemedia.com.", - "a172.casalemedia.com.", - "a177.casalemedia.com.", - "a178.casalemedia.com.", - "a179.casalemedia.com.", - "a182.casalemedia.com.", - "a187.casalemedia.com.", - "a189.casalemedia.com.", - "a190.casalemedia.com.", - "a195.casalemedia.com.", - "a200.casalemedia.com.", - "a201.casalemedia.com.", - "a21006071257.cdn.optimizely.com.", + "a.researchgate.net.", + "a.vsstatic.com.", + "a19558781057.cdn.optimizely.com.", "a2321.casalemedia.com.", "a2322.casalemedia.com.", "a2323.casalemedia.com.", @@ -224,19 +91,19 @@ var FakeECSFQDNs = container.NewMapSet( "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.", + "a2353.casalemedia.com.", "a2354.casalemedia.com.", "a2355.casalemedia.com.", "a2356.casalemedia.com.", @@ -264,11 +131,11 @@ var FakeECSFQDNs = container.NewMapSet( "a2378.casalemedia.com.", "a2379.casalemedia.com.", "a2380.casalemedia.com.", - "a2381.casalemedia.com.", "a2382.casalemedia.com.", "a2383.casalemedia.com.", "a2384.casalemedia.com.", "a2385.casalemedia.com.", + "a2386.casalemedia.com.", "a2387.casalemedia.com.", "a2388.casalemedia.com.", "a2389.casalemedia.com.", @@ -277,6 +144,7 @@ var FakeECSFQDNs = container.NewMapSet( "a2392.casalemedia.com.", "a2393.casalemedia.com.", "a2394.casalemedia.com.", + "a2395.casalemedia.com.", "a2396.casalemedia.com.", "a2397.casalemedia.com.", "a2398.casalemedia.com.", @@ -285,15 +153,19 @@ var FakeECSFQDNs = container.NewMapSet( "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.", "a2414.casalemedia.com.", "a2415.casalemedia.com.", "a2416.casalemedia.com.", + "a2417.casalemedia.com.", "a2418.casalemedia.com.", "a2419.casalemedia.com.", "a2420.casalemedia.com.", @@ -302,15 +174,16 @@ var FakeECSFQDNs = container.NewMapSet( "a2423.casalemedia.com.", "a2424.casalemedia.com.", "a2425.casalemedia.com.", + "a2426.casalemedia.com.", "a2427.casalemedia.com.", "a2428.casalemedia.com.", "a2429.casalemedia.com.", + "a2430.casalemedia.com.", "a2431.casalemedia.com.", "a2432.casalemedia.com.", "a2433.casalemedia.com.", "a2434.casalemedia.com.", "a2435.casalemedia.com.", - "a2436.casalemedia.com.", "a2437.casalemedia.com.", "a2438.casalemedia.com.", "a2439.casalemedia.com.", @@ -318,6 +191,7 @@ var FakeECSFQDNs = container.NewMapSet( "a2441.casalemedia.com.", "a2442.casalemedia.com.", "a2443.casalemedia.com.", + "a2444.casalemedia.com.", "a2445.casalemedia.com.", "a2446.casalemedia.com.", "a2447.casalemedia.com.", @@ -326,18 +200,17 @@ var FakeECSFQDNs = container.NewMapSet( "a2450.casalemedia.com.", "a2451.casalemedia.com.", "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.", @@ -355,7 +228,6 @@ var FakeECSFQDNs = container.NewMapSet( "a2481.casalemedia.com.", "a2482.casalemedia.com.", "a2483.casalemedia.com.", - "a2484.casalemedia.com.", "a2485.casalemedia.com.", "a2486.casalemedia.com.", "a2487.casalemedia.com.", @@ -367,7 +239,6 @@ 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.", @@ -377,7 +248,6 @@ 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.", @@ -388,87 +258,300 @@ var FakeECSFQDNs = container.NewMapSet( "a2515.casalemedia.com.", "a2516.casalemedia.com.", "a2517.casalemedia.com.", + "a2518.casalemedia.com.", "a2519.casalemedia.com.", - "a252.casalemedia.com.", "a2520.casalemedia.com.", "a2521.casalemedia.com.", - "a2522.casalemedia.com.", "a2523.casalemedia.com.", "a2524.casalemedia.com.", "a2525.casalemedia.com.", "a2526.casalemedia.com.", "a2527.casalemedia.com.", "a2528.casalemedia.com.", - "a254.casalemedia.com.", - "a255.casalemedia.com.", - "a260.casalemedia.com.", - "a2801.casalemedia.com.", - "a2802.casalemedia.com.", - "a2806.casalemedia.com.", - "a2811.casalemedia.com.", - "a2829.casalemedia.com.", - "a2832.casalemedia.com.", - "a2834.casalemedia.com.", - "a2842.casalemedia.com.", - "a2844.casalemedia.com.", - "a2848.casalemedia.com.", - "a2851.casalemedia.com.", - "a2870.casalemedia.com.", - "a2877.casalemedia.com.", - "a2878.casalemedia.com.", - "a2880.casalemedia.com.", - "a2882.casalemedia.com.", - "a2890.casalemedia.com.", - "a2891.casalemedia.com.", - "a2897.casalemedia.com.", - "a2900.casalemedia.com.", - "a2902.casalemedia.com.", - "a2904.casalemedia.com.", - "a2905.casalemedia.com.", - "a2912.casalemedia.com.", - "a2918.casalemedia.com.", - "a2920.casalemedia.com.", - "a2933.casalemedia.com.", - "a2934.casalemedia.com.", - "a2938.casalemedia.com.", - "a2949.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.", + "a2689.casalemedia.com.", + "a2690.casalemedia.com.", + "a2691.casalemedia.com.", + "a2692.casalemedia.com.", + "a2693.casalemedia.com.", + "a2694.casalemedia.com.", + "a2695.casalemedia.com.", + "a2696.casalemedia.com.", + "a2697.casalemedia.com.", + "a2698.casalemedia.com.", + "a2699.casalemedia.com.", + "a2700.casalemedia.com.", + "a2701.casalemedia.com.", + "a2702.casalemedia.com.", + "a2703.casalemedia.com.", + "a2704.casalemedia.com.", + "a2705.casalemedia.com.", + "a2706.casalemedia.com.", + "a2707.casalemedia.com.", + "a2708.casalemedia.com.", + "a2709.casalemedia.com.", + "a2710.casalemedia.com.", + "a2711.casalemedia.com.", + "a2712.casalemedia.com.", + "a2713.casalemedia.com.", + "a2714.casalemedia.com.", + "a2715.casalemedia.com.", + "a2717.casalemedia.com.", + "a2719.casalemedia.com.", + "a2720.casalemedia.com.", + "a2721.casalemedia.com.", + "a2722.casalemedia.com.", + "a2723.casalemedia.com.", + "a2724.casalemedia.com.", + "a2725.casalemedia.com.", + "a2726.casalemedia.com.", + "a2727.casalemedia.com.", + "a2728.casalemedia.com.", + "a2729.casalemedia.com.", + "a2730.casalemedia.com.", + "a2731.casalemedia.com.", + "a2732.casalemedia.com.", + "a2733.casalemedia.com.", + "a2734.casalemedia.com.", + "a2736.casalemedia.com.", + "a2737.casalemedia.com.", + "a2738.casalemedia.com.", + "a2739.casalemedia.com.", + "a2741.casalemedia.com.", + "a2742.casalemedia.com.", + "a2743.casalemedia.com.", + "a2744.casalemedia.com.", + "a2745.casalemedia.com.", + "a2746.casalemedia.com.", + "a2747.casalemedia.com.", + "a2748.casalemedia.com.", + "a2749.casalemedia.com.", + "a2750.casalemedia.com.", + "a2751.casalemedia.com.", + "a2752.casalemedia.com.", + "a2753.casalemedia.com.", + "a2754.casalemedia.com.", + "a2755.casalemedia.com.", + "a2756.casalemedia.com.", + "a2757.casalemedia.com.", + "a2758.casalemedia.com.", + "a2759.casalemedia.com.", + "a2760.casalemedia.com.", + "a2761.casalemedia.com.", + "a2762.casalemedia.com.", + "a2763.casalemedia.com.", + "a2764.casalemedia.com.", + "a2765.casalemedia.com.", + "a2766.casalemedia.com.", + "a2767.casalemedia.com.", + "a2768.casalemedia.com.", + "a2769.casalemedia.com.", + "a2770.casalemedia.com.", + "a2771.casalemedia.com.", + "a2772.casalemedia.com.", + "a2773.casalemedia.com.", + "a2774.casalemedia.com.", + "a2776.casalemedia.com.", + "a2777.casalemedia.com.", + "a2778.casalemedia.com.", + "a2779.casalemedia.com.", + "a2780.casalemedia.com.", + "a2781.casalemedia.com.", + "a2782.casalemedia.com.", + "a2783.casalemedia.com.", + "a2784.casalemedia.com.", + "a2785.casalemedia.com.", + "a2786.casalemedia.com.", + "a2787.casalemedia.com.", + "a2788.casalemedia.com.", + "a2790.casalemedia.com.", + "a2791.casalemedia.com.", + "a2793.casalemedia.com.", + "a2794.casalemedia.com.", + "a2795.casalemedia.com.", + "a2796.casalemedia.com.", + "a2797.casalemedia.com.", + "a2798.casalemedia.com.", + "a2799.casalemedia.com.", + "a2800.casalemedia.com.", "a2a5c7f9-3fa0-4182-889a-15aa61acf59b.prmutv.co.", "a3.tuyacn.com.", - "a364.casalemedia.com.", - "a367.casalemedia.com.", - "a375.casalemedia.com.", - "a376.casalemedia.com.", - "a377.casalemedia.com.", - "a381.casalemedia.com.", - "a385.casalemedia.com.", - "a390.casalemedia.com.", - "a398.casalemedia.com.", - "a401.casalemedia.com.", - "a407.casalemedia.com.", - "a408.casalemedia.com.", - "a412.casalemedia.com.", - "a415.casalemedia.com.", - "a417.casalemedia.com.", - "a461.casalemedia.com.", - "a462.casalemedia.com.", - "a463.casalemedia.com.", - "a464.casalemedia.com.", - "a465.casalemedia.com.", - "a466.casalemedia.com.", - "a467.casalemedia.com.", - "a468.casalemedia.com.", - "a469.casalemedia.com.", - "a470.casalemedia.com.", - "a471.casalemedia.com.", - "a472.casalemedia.com.", - "a473.casalemedia.com.", - "a474.casalemedia.com.", - "a475.casalemedia.com.", - "a476.casalemedia.com.", - "a477.casalemedia.com.", - "a478.casalemedia.com.", - "a479.casalemedia.com.", - "a480.casalemedia.com.", + "a3551.casalemedia.com.", + "a3552.casalemedia.com.", + "a3553.casalemedia.com.", + "a3554.casalemedia.com.", + "a3555.casalemedia.com.", + "a3556.casalemedia.com.", + "a3557.casalemedia.com.", + "a3558.casalemedia.com.", + "a3559.casalemedia.com.", + "a3560.casalemedia.com.", + "a3562.casalemedia.com.", + "a3563.casalemedia.com.", + "a3564.casalemedia.com.", + "a3565.casalemedia.com.", + "a3566.casalemedia.com.", + "a3567.casalemedia.com.", + "a3568.casalemedia.com.", + "a3569.casalemedia.com.", + "a3570.casalemedia.com.", + "a3571.casalemedia.com.", + "a3572.casalemedia.com.", + "a3573.casalemedia.com.", + "a3574.casalemedia.com.", + "a3575.casalemedia.com.", + "a3578.casalemedia.com.", + "a3579.casalemedia.com.", + "a3580.casalemedia.com.", + "a3581.casalemedia.com.", + "a3582.casalemedia.com.", + "a3583.casalemedia.com.", + "a3584.casalemedia.com.", + "a3585.casalemedia.com.", + "a3586.casalemedia.com.", + "a3587.casalemedia.com.", + "a3589.casalemedia.com.", + "a3590.casalemedia.com.", + "a3591.casalemedia.com.", + "a3592.casalemedia.com.", + "a3593.casalemedia.com.", + "a3594.casalemedia.com.", + "a3595.casalemedia.com.", + "a3596.casalemedia.com.", + "a3597.casalemedia.com.", + "a3598.casalemedia.com.", + "a3599.casalemedia.com.", + "a3600.casalemedia.com.", + "a3601.casalemedia.com.", + "a3602.casalemedia.com.", + "a3603.casalemedia.com.", + "a3604.casalemedia.com.", + "a3605.casalemedia.com.", + "a3606.casalemedia.com.", + "a3607.casalemedia.com.", + "a3608.casalemedia.com.", + "a3609.casalemedia.com.", + "a3610.casalemedia.com.", + "a3611.casalemedia.com.", + "a3612.casalemedia.com.", + "a3613.casalemedia.com.", + "a3614.casalemedia.com.", + "a3615.casalemedia.com.", + "a3616.casalemedia.com.", + "a3617.casalemedia.com.", + "a3618.casalemedia.com.", + "a3619.casalemedia.com.", + "a3620.casalemedia.com.", + "a3621.casalemedia.com.", + "a3622.casalemedia.com.", + "a3623.casalemedia.com.", + "a3624.casalemedia.com.", + "a3625.casalemedia.com.", + "a3626.casalemedia.com.", + "a3627.casalemedia.com.", + "a3628.casalemedia.com.", + "a3629.casalemedia.com.", + "a3630.casalemedia.com.", + "a3631.casalemedia.com.", + "a3632.casalemedia.com.", + "a3633.casalemedia.com.", + "a3634.casalemedia.com.", + "a3635.casalemedia.com.", + "a3636.casalemedia.com.", + "a3637.casalemedia.com.", + "a3638.casalemedia.com.", + "a3639.casalemedia.com.", + "a3640.casalemedia.com.", + "a3641.casalemedia.com.", + "a3642.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.", + "a3656.casalemedia.com.", + "a3657.casalemedia.com.", + "a3658.casalemedia.com.", + "a3659.casalemedia.com.", + "a3660.casalemedia.com.", + "a3661.casalemedia.com.", + "a3662.casalemedia.com.", + "a3663.casalemedia.com.", + "a3664.casalemedia.com.", + "a3666.casalemedia.com.", + "a3667.casalemedia.com.", + "a3668.casalemedia.com.", + "a3669.casalemedia.com.", + "a3670.casalemedia.com.", + "a3671.casalemedia.com.", + "a3672.casalemedia.com.", + "a3673.casalemedia.com.", + "a3675.casalemedia.com.", + "a3676.casalemedia.com.", + "a3677.casalemedia.com.", + "a3678.casalemedia.com.", + "a3679.casalemedia.com.", + "a3680.casalemedia.com.", + "a3681.casalemedia.com.", + "a3683.casalemedia.com.", + "a3684.casalemedia.com.", + "a3685.casalemedia.com.", + "a3686.casalemedia.com.", + "a3687.casalemedia.com.", + "a3688.casalemedia.com.", + "a3689.casalemedia.com.", + "a3690.casalemedia.com.", + "a3691.casalemedia.com.", + "a3692.casalemedia.com.", + "a3693.casalemedia.com.", + "a3694.casalemedia.com.", + "a3695.casalemedia.com.", + "a3696.casalemedia.com.", + "a3698.casalemedia.com.", + "a3699.casalemedia.com.", + "a3700.casalemedia.com.", + "a3702.casalemedia.com.", + "a3703.casalemedia.com.", + "a3704.casalemedia.com.", + "a3705.casalemedia.com.", + "a3706.casalemedia.com.", + "a3707.casalemedia.com.", + "a3708.casalemedia.com.", + "a3709.casalemedia.com.", + "a3710.casalemedia.com.", + "a3711.casalemedia.com.", + "a3712.casalemedia.com.", + "a3713.casalemedia.com.", + "a3714.casalemedia.com.", + "a3715.casalemedia.com.", + "a3716.casalemedia.com.", + "a3717.casalemedia.com.", + "a3718.casalemedia.com.", + "a3719.casalemedia.com.", + "a3720.casalemedia.com.", + "a3721.casalemedia.com.", + "a3722.casalemedia.com.", + "a3723.casalemedia.com.", + "a3724.casalemedia.com.", + "a3725.casalemedia.com.", + "a3726.casalemedia.com.", + "a3727.casalemedia.com.", + "a3728.casalemedia.com.", + "a3729.casalemedia.com.", + "a3730.casalemedia.com.", "a5551.casalemedia.com.", "a5555.casalemedia.com.", "a5556.casalemedia.com.", @@ -488,14 +571,12 @@ var FakeECSFQDNs = container.NewMapSet( "a5570.casalemedia.com.", "a5571.casalemedia.com.", "a5572.casalemedia.com.", - "a5574.casalemedia.com.", + "a5573.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.", @@ -505,13 +586,11 @@ var FakeECSFQDNs = container.NewMapSet( "a5588.casalemedia.com.", "a5589.casalemedia.com.", "a5590.casalemedia.com.", - "a5591.casalemedia.com.", "a5593.casalemedia.com.", "a5594.casalemedia.com.", "a5595.casalemedia.com.", "a5596.casalemedia.com.", "a5597.casalemedia.com.", - "a5598.casalemedia.com.", "a5599.casalemedia.com.", "a55a84b3-9632-4869-b625-3d8ef43ed18d.prmutv.co.", "a5600.casalemedia.com.", @@ -528,7 +607,7 @@ var FakeECSFQDNs = container.NewMapSet( "a5611.casalemedia.com.", "a5612.casalemedia.com.", "a5613.casalemedia.com.", - "a5615.casalemedia.com.", + "a5614.casalemedia.com.", "a5616.casalemedia.com.", "a5617.casalemedia.com.", "a5618.casalemedia.com.", @@ -540,48 +619,19 @@ var FakeECSFQDNs = container.NewMapSet( "a5624.casalemedia.com.", "a5625.casalemedia.com.", "a5626.casalemedia.com.", - "a5627.casalemedia.com.", "a5628.casalemedia.com.", "a5629.casalemedia.com.", "a5630.casalemedia.com.", "a5631.casalemedia.com.", "a5632.casalemedia.com.", "a5633.casalemedia.com.", + "a5634.casalemedia.com.", "a5635.casalemedia.com.", "a5636.casalemedia.com.", "a5637.casalemedia.com.", "a5638.casalemedia.com.", "a5639.casalemedia.com.", "a5640.casalemedia.com.", - "a5641.casalemedia.com.", - "a5642.casalemedia.com.", - "a5643.casalemedia.com.", - "a5644.casalemedia.com.", - "a5645.casalemedia.com.", - "a5646.casalemedia.com.", - "a5647.casalemedia.com.", - "a5648.casalemedia.com.", - "a5649.casalemedia.com.", - "a5650.casalemedia.com.", - "a5651.casalemedia.com.", - "a5652.casalemedia.com.", - "a5653.casalemedia.com.", - "a5654.casalemedia.com.", - "a5655.casalemedia.com.", - "a5656.casalemedia.com.", - "a5657.casalemedia.com.", - "a5658.casalemedia.com.", - "a5659.casalemedia.com.", - "a5660.casalemedia.com.", - "a5661.casalemedia.com.", - "a5662.casalemedia.com.", - "a5663.casalemedia.com.", - "a5664.casalemedia.com.", - "a5665.casalemedia.com.", - "a5666.casalemedia.com.", - "a5667.casalemedia.com.", - "a5668.casalemedia.com.", - "a5669.casalemedia.com.", "a5671.casalemedia.com.", "a5672.casalemedia.com.", "a5673.casalemedia.com.", @@ -591,20 +641,21 @@ var FakeECSFQDNs = container.NewMapSet( "a5677.casalemedia.com.", "a5678.casalemedia.com.", "a5679.casalemedia.com.", - "a568.casalemedia.com.", + "a5680.casalemedia.com.", "a5681.casalemedia.com.", + "a5682.casalemedia.com.", "a5683.casalemedia.com.", "a5684.casalemedia.com.", + "a5685.casalemedia.com.", "a5686.casalemedia.com.", "a5687.casalemedia.com.", + "a5688.casalemedia.com.", "a5689.casalemedia.com.", - "a569.casalemedia.com.", "a5690.casalemedia.com.", "a5691.casalemedia.com.", "a5692.casalemedia.com.", "a5693.casalemedia.com.", "a5694.casalemedia.com.", - "a5695.casalemedia.com.", "a5696.casalemedia.com.", "a5697.casalemedia.com.", "a5698.casalemedia.com.", @@ -616,127 +667,220 @@ var FakeECSFQDNs = container.NewMapSet( "a5704.casalemedia.com.", "a5705.casalemedia.com.", "a5706.casalemedia.com.", + "a5707.casalemedia.com.", "a5708.casalemedia.com.", "a5709.casalemedia.com.", "a5710.casalemedia.com.", - "a576.casalemedia.com.", "a5776.casalemedia.com.", "a5777.casalemedia.com.", "a5778.casalemedia.com.", "a5779.casalemedia.com.", - "a578.casalemedia.com.", "a5780.casalemedia.com.", "a5781.casalemedia.com.", - "a5782.casalemedia.com.", "a5783.casalemedia.com.", "a5784.casalemedia.com.", "a5786.casalemedia.com.", "a5787.casalemedia.com.", "a5788.casalemedia.com.", "a5789.casalemedia.com.", - "a579.casalemedia.com.", "a5790.casalemedia.com.", - "a5791.casalemedia.com.", "a5792.casalemedia.com.", "a5793.casalemedia.com.", "a5794.casalemedia.com.", + "a5795.casalemedia.com.", + "a5796.casalemedia.com.", "a5797.casalemedia.com.", + "a5798.casalemedia.com.", "a5799.casalemedia.com.", - "a580.casalemedia.com.", - "a5800.casalemedia.com.", "a5801.casalemedia.com.", "a5802.casalemedia.com.", - "a5803.casalemedia.com.", "a5804.casalemedia.com.", "a5805.casalemedia.com.", - "a5806.casalemedia.com.", "a5807.casalemedia.com.", + "a5808.casalemedia.com.", "a5809.casalemedia.com.", + "a581.casalemedia.com.", "a5810.casalemedia.com.", - "a5811.casalemedia.com.", "a5812.casalemedia.com.", - "a5814.casalemedia.com.", + "a5813.casalemedia.com.", "a5815.casalemedia.com.", + "a582.casalemedia.com.", + "a583.casalemedia.com.", + "a584.casalemedia.com.", + "a585.casalemedia.com.", + "a586.casalemedia.com.", + "a587.casalemedia.com.", + "a588.casalemedia.com.", + "a589.casalemedia.com.", + "a590.casalemedia.com.", + "a591.casalemedia.com.", + "a592.casalemedia.com.", + "a593.casalemedia.com.", + "a595.casalemedia.com.", + "a597.casalemedia.com.", + "a599.casalemedia.com.", + "a600.casalemedia.com.", + "a601.casalemedia.com.", + "a602.casalemedia.com.", + "a603.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.", + "a617.casalemedia.com.", + "a618.casalemedia.com.", + "a619.casalemedia.com.", + "a620.casalemedia.com.", + "a621.casalemedia.com.", + "a622.casalemedia.com.", + "a623.casalemedia.com.", + "a624.casalemedia.com.", + "a625.casalemedia.com.", + "a626.casalemedia.com.", + "a627.casalemedia.com.", + "a628.casalemedia.com.", + "a629.casalemedia.com.", + "a630.casalemedia.com.", + "a632.casalemedia.com.", + "a633.casalemedia.com.", + "a634.casalemedia.com.", + "a635.casalemedia.com.", + "a636.casalemedia.com.", + "a637.casalemedia.com.", + "a638.casalemedia.com.", + "a639.casalemedia.com.", + "a640.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.", + "a936.casalemedia.com.", + "a937.casalemedia.com.", + "a938.casalemedia.com.", + "a939.casalemedia.com.", + "a940.casalemedia.com.", + "a941.casalemedia.com.", + "a942.casalemedia.com.", + "a943.casalemedia.com.", + "a944.casalemedia.com.", + "a945.casalemedia.com.", + "a946.casalemedia.com.", + "a947.casalemedia.com.", + "a948.casalemedia.com.", + "a950.casalemedia.com.", + "a951.casalemedia.com.", + "a952.casalemedia.com.", + "a953.casalemedia.com.", + "a954.casalemedia.com.", + "a955.casalemedia.com.", + "a956.casalemedia.com.", + "a957.casalemedia.com.", + "a958.casalemedia.com.", + "a959.casalemedia.com.", + "a961.casalemedia.com.", + "a962.casalemedia.com.", + "a963.casalemedia.com.", + "a964.casalemedia.com.", + "a965.casalemedia.com.", + "a966.casalemedia.com.", + "a967.casalemedia.com.", + "a968.casalemedia.com.", "a9695278-4085-40b3-9f02-8d4c38a6ff01.prmutv.co.", + "a970.casalemedia.com.", + "a971.casalemedia.com.", + "a972.casalemedia.com.", + "a973.casalemedia.com.", + "a974.casalemedia.com.", + "a975.casalemedia.com.", + "a977.casalemedia.com.", + "a979.casalemedia.com.", + "a980.casalemedia.com.", "aa.online-metrix.net.", "aa.quantummetric.com.", "ab.qq.com.", "aba2c424-419a-4d03-9aed-2dca8a7139e4.prmutv.co.", - "ably-realtime.com.", - "abm2.listenloop.com.", "abroad.apilocate.amap.com.", "abtest-aws-us-east-01.saas.sensorsdata.com.", - "abtest.omiapp.me.", - "ac.flixcart.com.", "accela.com.", "accentuate.io.", "access.mp.lura.live.", "account.box.com.", - "account.sogou.com.", + "account.msa.msidentity.com.", "accountapi.agoda.com.", - "accounts.ikea.com.", - "accounts.intuit.com.", "accurint.com.", "acdn.tinkoff.ru.", "acgvideo.com.", "achi.faceit.com.", "acme-v02.api.letsencrypt.org.", "acp.haplat.net.", - "acpbk.haplat.net.", "acrobits.cz.", "acsdk.gameyw.easebar.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.", "adash.man.aliyuncs.com.", - "adashx.ut.amap.com.", "adblock.telemetry.getadblock.com.", "adcell.com.", "adder.feeder.co.", "additionfi.com.", "adfarm1.adition.com.", "adiam.tech.", - "adintl.cn.", "adition.com.", "aditude.io.", "adm.wsms.haplat.net.", "admbk.wsms.haplat.net.", "admixer.com.", - "adobelogin-weighted-prod07.prod.ims.adobejanus.com.", "adoric.com.", "adrs.org.cn.", "adsco.re.", "adsolut.in.", "adtarget.market.", "adtarget.me.", + "adtechnacity.com.", "advantage.purpleguys.com.", "advantage2.purpleguys.com.", + "adventisthealthwest.sharepoint.com.", "adview.com.", + "adx-os.bridgeoos.com.", "adx-sg-req.anythinktech.com.", "adxpremium.services.", - "ae.nflximg.net.", "aee.myisolved.com.", "aeries.com.", - "afd-eus2-guestagentsvc-prod.trafficmanager.net.", "afd-lnkd.www.linkedin.com.", "affpa.top.", "afss.zhiqinyun.cn.", - "agent-logos.storage.googleapis.com.", + "afterpay.com.", "agent.marketingcloudfx.com.", "ai-voice.cloudbirds.cn.", - "aic-gfts.lge.com.", "aicdn.com.", "aid.send.microad.jp.", "aidata.io.", + "aiq-in.caranddriver.com.", "airasia.com.", "airtory.com.", "aisee.tv.", "ajcloud.net.", - "akinkanalang876.wixsite.com.", - "akrdinfo.cn.", "akulaku.com.", "alarm.wsms.haplat.net.", "alarmbk.wsms.haplat.net.", @@ -753,7 +897,6 @@ var FakeECSFQDNs = container.NewMapSet( "alive2.cloudbirds.cn.", "alive3.cloudbirds.cn.", "aliyuncs.com.", - "alljoyn.org.", "alpha1-ap-public.val.qq.com.", "alpha1-gp-ping-cq2.val.qq.com.", "alpha1-gp-ping-gz2.val.qq.com.", @@ -762,8 +905,7 @@ var FakeECSFQDNs = container.NewMapSet( "amd.com.", "amdcopen.m.taobao.com.", "amdcopen.m.taobao.com.gds.alibabadns.com.", - "amp-api.videos.apple.com.", - "amp.namequery.com.", + "americanexpress.com.edgekey.net.", "amp.permutive.com.", "amplitudelab.usemotion.com.", "ams-itm-radar-testobject.citrix.com.", @@ -777,32 +919,34 @@ var FakeECSFQDNs = container.NewMapSet( "analytics.languagetoolplus.com.", "analytics.pdf24.org.", "analytics.qumucloud.com.", - "analytics.talbots.com.", "analytics.trovit.com.", + "analyticssystems.net.", "android.crashsight.wetest.net.", "anlian.co.", "announce.torrentsmd.com.", + "answers.yext-pixel.com.", "anthropic.com.", "antstream.com.", "aon.com.", - "ap.sd-rtn.com.", "api-analytics-us3.zepp.com.", "api-asyncgw-gcc-teams.usgovtrafficmanager.net.", + "api-cdn.prod.life360.com.", "api-eu1.hubspot.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-aeuw3b.smoot.apple.com.", - "api-glb-aeuw3c.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.", "api-glb-ause2a.smoot.apple.com.", "api-glb-ause2b.smoot.apple.com.", "api-glb-ause2c.smoot.apple.com.", + "api-glb-ausw2b.smoot.apple.com.", + "api-glb-ausw2c.smoot.apple.com.", "api-mayi.django.t.taobao.com.", "api-mifit-us3.zepp.com.", "api-player.musicstylingonline.com.", @@ -820,13 +964,14 @@ var FakeECSFQDNs = container.NewMapSet( "api.config-security.com.", "api.crobox.com.", "api.crush.163.com.", + "api.crxcavator.io.", "api.dealersocket.com.", "api.deepl.com.", - "api.digitalinsight.com.cdn.cloudflare.net.", - "api.foxnews.com.", "api.glanceapis.com.", "api.gleap.io.", "api.gravitec.media.", + "api.gravitec.net.", + "api.gx.me.", "api.icalendars.app.", "api.inboxsdk.com.", "api.ipgeolocation.io.", @@ -834,20 +979,20 @@ var FakeECSFQDNs = container.NewMapSet( "api.kinogram.best.", "api.lightboxcdn.com.", "api.loyalhealth.com.", - "api.m.taobao.com.", + "api.mangacoin.net.", "api.matetranslate.com.", "api.moyoung.com.", - "api.mrg.agency.", "api.my.jbi.global.", "api.onedrive.com.", "api.permutive.app.", "api.permutive.com.", - "api.pgf-asw0zz.com.", "api.pgf-thek63.com.", "api.playlnk.io.", "api.popin.cc.", "api.pushbullet.com.", "api.queryly.com.", + "api.radiotime.com.", + "api.recs.sky.com.", "api.reverso.net.", "api.ringcentral.biz.", "api.rollbar.com.", @@ -856,43 +1001,42 @@ var FakeECSFQDNs = container.NewMapSet( "api.sobot.com.", "api.streak.com.", "api.swishapps.ai.", - "api.taplytics.com.", - "api.trainerize.com.", + "api.tiles.virtualearth.net.", "api.transitapp.com.", - "api.triptease.io.", "api.tx4.pw.adn.cloud.", "api.ultimaker.com.", "api.ultimate-guitar.com.", "api.unity.com.", "api.us.minga.io.", "api.vieon.vn.", - "api.weglot.com.", + "api.voicemod.net.", "api.workjam.com.", + "api.y41w4.com.", "api000.backblazeb2.com.", "api001.backblazeb2.com.", "api002.backblazeb2.com.", - "api2.chase.com.", "api2.matetranslate.com.", + "apiblink.ru.", "apiisgp.ezvizlife.com.", "apiisgp.hik-connect.com.", - "apim-dev-cn-pcm-ms-01.azure-api.cn.", "apimgmttmgpxfqy6dfjiqfsk6t67i30fgsnfhah4rrjw51coy3.trafficmanager.net.", - "apio.lloydsbank.co.uk.", "apis.live.net.", - "apis.lloydsbank.co.uk.", "apitm.toolmatrix.plus.", - "apituner.ecbsn.com.", "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.", @@ -906,21 +1050,20 @@ var FakeECSFQDNs = container.NewMapSet( "appcafe.starbucks.com.", "appconf.mail.163.com.", "appdump.nie.easebar.com.", - "appgrowth.com.", "applog.matrix.easebar.com.", "appocean.media.", "apponline.research.qq.com.", - "apps-ds.shopifynetwork.com.", - "apps.foxnews.com.edgekey.net.", "apps.wix.com.", "appstoreonrt-stsdk.vivo.com.cn.", "appstoreort-stsdk.vivo.com.cn.", - "arbiter.warframe.com.", + "apro-api.collegeboard.org.", "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.", @@ -935,8 +1078,8 @@ var FakeECSFQDNs = container.NewMapSet( "asia-cota.vivoglobal.com.", "asia-ex-adlog.vivoglobal.com.", "asia-exbrowser.vivoglobal.com.", + "asia-exbrowsercrp.vivoglobal.com.", "asia-exmagazineunlock-proxy.vivoglobal.com.", - "asia-f-up.vivoglobal.com.", "asia-gamecenter.vivoglobal.com.", "asia-gsearch.vivoglobal.com.", "asia-magazineunlock.vivoglobal.com.", @@ -961,7 +1104,6 @@ var FakeECSFQDNs = container.NewMapSet( "asia-vpushonrt-stsdk.vivoglobal.com.", "asia-vpushort-stsdk.vivoglobal.com.", "asia-weather.vivoglobal.com.", - "asia-wifi.vivoglobal.com.", "asia.gikken.co.", "asia.remotepc.com.", "asm-api-golocal-geo-am-teams.trafficmanager.net.", @@ -973,12 +1115,15 @@ var FakeECSFQDNs = container.NewMapSet( "asm-api-prod-geo-eu-skype.trafficmanager.net.", "asset.fwcdn3.com.", "asset.fwpub1.com.", - "assets-prod.servicetitan.com.", + "assets-cms.thescore.com.", + "assets-www.xbox.com.", "assets.api.stairwell.com.", + "assets.mayoclinic.org.", + "assets.thdstatic.com.edgekey.net.", "assistant-dre.op.hicloud.com.", "astemo-am.spectrum.colortokens.com.", + "async-motiondetection-us-1d.oss-us-west-1.aliyuncs.com.", "asyncim.zoom.us.", - "athena.archive.org.", "ati.com.", "atlanta.remotepc.com.", "atlanta3.remotepc.com.", @@ -988,37 +1133,34 @@ var FakeECSFQDNs = container.NewMapSet( "au.footballbros.io.", "auc-collabrtc.officeapps.live.com.", "auckland.remotepc.com.", - "audio-public.canva.com.", + "audio-ssl.itunes.apple.com.", "audio.vivintsky.com.", "audiostatlog.cc.easebar.com.", - "aura-dsp.com.", "australiaeast.api.cognitive.microsoft.com.", "auth-l7.bereal.com.", "auth.bereal.com.", - "auth.ichano.cn.", + "auth.cengage.com.", "auth.jbisumari.org.", - "auth.laureate.net.", - "auth.services.adobe.com.cdn.cloudflare.net.", - "auth.syf.com.", + "auth.tesla.com.", "autohome.com.cn.", - "autolinkmaker.itunes.apple.com.", "autotrack.studyquicks.com.", "avalanche.autotrader.co.uk.", - "avaya.com.", "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.", - "azmatch.adsrvr.org.", "azscus1-client-s.gateway.messenger.live.com.", "azwcus1-client-s.gateway.messenger.live.com.", "azwus1-client-s.gateway.messenger.live.com.", "azwus2-client-s.gateway.messenger.live.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.", @@ -1028,14 +1170,16 @@ var FakeECSFQDNs = container.NewMapSet( "baishan-cloud.net.", "baltimore.remotepc.com.", "bam.nr-data.net.cdn.cloudflare.net.", + "bancomermovil.com.", "bangalore2.remotepc.com.", "bangalore3.remotepc.com.", "bangalore4.remotepc.com.", "bangkok.remotepc.com.", "barstoolsports.com.", - "basf.com.", "bd1cec50-00d1-4ce9-9572-785857419a1e.prmutv.co.", - "beacon-sjc2.rubiconproject.com.", + "bdpnt-my.sharepoint.com.", + "bea.gov.", + "beacon-iad2.rubiconproject.com.", "beacon.qq.com.", "beacons4.gvt2.com.", "beacons5.gvt2.com.", @@ -1043,10 +1187,13 @@ var FakeECSFQDNs = container.NewMapSet( "belgium.remotepc.com.", "belgrad.remotepc.com.", "bend.remotepc.com.", + "benefitfocus.com.", "bgtfs.transitapp.com.", + "bi-tracker-global.rivergame.net.", + "bidmyadz.com.", "bidster.net.", - "bifrost-https-v4.gw.postman.com.cdn.cloudflare.net.", "bifrost.vivaldi.com.", + "bigcommerce.com.cdn.cloudflare.net.", "bigdata.talkie-ai.com.", "bik.gov.tr.", "bl6pap003.storage.live.com.", @@ -1054,15 +1201,14 @@ var FakeECSFQDNs = container.NewMapSet( "black-cat.crypto.com.", "blackboard.com.", "blackbox.dropbox-dns.com.", - "blog.csdn.net.", + "block64.com.", + "blockchain.info.", "bloomerang.co.", "bluffdale.remotepc.com.", "blz04pap002.storage.live.com.", - "blz04pap003.storage.live.com.", "blz04pap005.storage.live.com.", "blz04pap006.storage.live.com.", - "blz04pap007.storage.live.com.", - "bmoolbb-app.quantummetric.com.", + "bmaus.bumble.com.", "bn02pap001.storage.live.com.", "bnz05pap001.storage.live.com.", "bnz06pap002.storage.live.com.", @@ -1079,21 +1225,16 @@ var FakeECSFQDNs = container.NewMapSet( "box.com.", "box.net.", "br.chat.si.riotgames.com.", - "brainx.tech.", "bratislava.remotepc.com.", "brazilsouth.api.cognitive.microsoft.com.", - "brc-collabrtc.officeapps.live.com.", "breitbart.com.", "bridge.tonapi.io.", - "britishairways.com.", "broadstreetads.com.", "broker-ws-prod-cag-sg.vasdgame.com.", - "browsingpublic.trendyol.com.", "bscedge.com.", "bsprings.remotepc.com.", "bssrvc66.com.", "bt.moack.co.kr.", - "bt2.archive.org.", "btrace.qq.com.", "bucharest.remotepc.com.", "bucharest1.remotepc.com.", @@ -1101,21 +1242,18 @@ var FakeECSFQDNs = container.NewMapSet( "bugly.qcloud.com.", "bugreport.huorong.cn.", "bundler.nice-team.net.", - "burnsmcd.sharepoint.com.", "bytecdn.cn.", "bytedance.net.", "c.4dex.io.", "c.fakespot.io.", - "c.footprint.net.", "c.pub.network.", "c1.ttcache.com.", - "c13.groundctl.com.", - "c2-eu.piano.io.", "c2.ttcache.com.", "c2c.wechat.com.", "c3.ttcache.com.", "c4.ttcache.com.", "c7l.cyberhaven.io.", + "c8ee9446-97ed-462f-a5e9-1af66c8e9104.prmutv.co.", "ca-prod.asyncgw.teams.microsoft.com.", "ca.gov.", "ca.rogers.rcs.telephony.goog.", @@ -1127,19 +1265,19 @@ var FakeECSFQDNs = container.NewMapSet( "cachenetworks.com.", "caldav.163.com.", "california.remotepc.com.", - "campaign-archive.com.", - "cams.gratis.", "camsoda.com.", "canada.remotepc.com.", "canadacentral.api.cognitive.microsoft.com.", "canadaeast.api.cognitive.microsoft.com.", "canberra.remotepc.com.", "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.", "cat2.hbwrapper.com.", @@ -1149,71 +1287,62 @@ var FakeECSFQDNs = container.NewMapSet( "cbsipv4.shuzilm.cn.", "cc.easebar.com.", "ccf.ivalua.com.", + "cdml02.contentdm.oclc.org.", "cdn-audio-gcp-media.getepic.com.", - "cdn-client.medium.com.", + "cdn-cname.pendo.io.", "cdn-gcp-media-drm.getepic.com.", "cdn-gcp-media.getepic.com.", "cdn-gcp.getepic.com.", "cdn-us.algoliaradar.com.", "cdn-video-gcp-media.getepic.com.", "cdn.adjust.com.", - "cdn.adpool.bet.", "cdn.adtarget.market.", "cdn.adtarget.me.", - "cdn.adtwister.me.", + "cdn.chargeafter.com.", "cdn.conveythis.com.", "cdn.deepintent.com.", - "cdn.flashtalking.com.edgekey.net.", - "cdn.ftd.agency.", + "cdn.flyaa.aa.com.", "cdn.groupbycloud.com.", "cdn.hw.gcloudcs.com.", + "cdn.instamed.com.", "cdn.instapagemetrics.com.", - "cdn.logr-ingest.com.", + "cdn.jsdelivrs.com.", "cdn.overleaf.com.", "cdn.qq.com.", "cdn.registerdisney.go.com.", "cdn.sierrapacificgroup.com.", - "cdn.sitestatic.net.", "cdn.skcrtxr.com.", "cdn.t-bank-app.ru.", "cdn.tapdb-dev.com.", + "cdn.themoneytizer.fr.", "cdn.us.minga.io.", "cdn.uxfeedback.ru.", "cdn1.wixdns.net.", - "cdnapi.kaltura.com.", "cdnetworks.net.", "cdngslb.com.", "cdntm.hsbc.co.uk.", "cdnwidget.com.", "cds.swishapps.ai.", "cdsi.signal.org.", - "cdt.ca.gov.", - "cdxdns.com.", - "cdxflare.com.", - "cec.hpsmart.com.", "cedexis-test.com.", "cedock.com.", - "center00.deltaork.com.", - "center01.deltaork.com.", - "center02.deltaork.com.", - "center03.deltaork.com.", + "cef.com.br.", + "cekdptonline.kpu.go.id.", "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.perf.linkedin.com.", "cfa.fidelity.com.", + "cform.loyalhealth.com.", + "cgi.twns.qq.com.", "changehealthcare.com.", + "chargeafter.com.", "charlotte.remotepc.com.", "check2.lloydsbank.co.uk.", "chennai.remotepc.com.", - "chess.com.", "chi01pap001.storage.live.com.", "chi01pap002.storage.live.com.", "chicago2.remotepc.com.", @@ -1223,14 +1352,15 @@ var FakeECSFQDNs = container.NewMapSet( "chinaccl-a.haplat.net.", "chinaccl-b.haplat.net.", "chinaccl-c.haplat.net.", - "christianbook.com.", "chrome.com.", "chtsite.com.", "chub1.imp.us.contentkeeper.net.", + "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.", @@ -1238,13 +1368,16 @@ var FakeECSFQDNs = container.NewMapSet( "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.", "click-eu.preclknu.com.", - "click-v4.cldirplarimo.com.", - "click-v4.exclkplat.com.", "click-v4.expdirclk.com.", + "click.admeking.com.", + "click.pclk.name.", "click.preclknu.com.", "clickiocmp.com.", "client-log.box.com.", @@ -1252,36 +1385,40 @@ var FakeECSFQDNs = container.NewMapSet( "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.huawei.ru.", + "cloud.browser.360.cn.", "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.", - "cm.mobydix.com.", - "cmegroup.com.", "cmgate.vip.qq.com.", "cmpassport.com.", "cn-hangzhou.oss-cdn.aliyun-inc.com.", "cn-mib2high-mbbservices.audi-connect.cn.", "cn-mib2plus.mbbservices-1a.audi-connect.cn.", "cn.gcloudcs.com.", + "cname.cordial.com.", "cname.pendo.io.", "cnplug.ttlock.com.", + "cnvid.site.", "cnzz.com.", "co-vcode-od.vivoglobal.com.", - "co.amx.rcs.telephony.goog.", "codacloud.net.", - "code.etracker.com.", "codepush.akulaku.net.", "codis-bak.ngb.haplat.net.", "codis.ngb.haplat.net.", @@ -1290,72 +1427,81 @@ var FakeECSFQDNs = container.NewMapSet( "collect.quickcep.com.", "collect.trendyol.com.", "collector.quillbot.com.", - "collector.seexh.com.", + "collector.therealxh.com.", "collector.wdp.brave.com.", "collector.xhamster.com.", + "collector.xhamster.desi.", "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.", "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.content-settings.com.", + "config.inmobi.com.", "config.office.net.", - "config.onefootball.com.", - "config.trackingplan.com.", "config.y5en.com.", "config2.cmpassport.com.", "configdl.teamviewer.com.", "connect.garenanow.com.", "contacts.zoho.com.", - "content.citizensbankonline.com.", - "content.cxpublic.com.", + "content.assist.chromeriver.com.", + "content.bhrpendo.bamboohr.com.", + "content.data.aleks.com.", + "content.data.mheducation.com.", "content.discover.com.", "content.discovercard.com.", "content.ebanking-services.com.", + "content.fisglobal.com.", "content.gap.com.", "content.help.explorelearning.com.", "content.maxconnector.com.", + "content.pendo.careporthealth.com.", "content.pendo.follettdestiny.com.", - "content.pncmc.com.", + "content.pendo.saashr.com.", "content.productinsights.blackline.com.", - "content22.bancanet.banamex.com.", + "content.readiness.imaginelearning.com.", "content22.bmo.com.", "content22.citicards.com.", "content22.online.citi.com.", "context.reverso.net.", "control-out.mna.qq.com.", + "coolccloud.com.", "coolkit.cc.", "copenhagen.remotepc.com.", "cordial.com.", - "core.gssv-play-prod.xboxlive.com.", "core.iprom.net.", "core.omiapp.me.", "corpmdm.v.aaplimg.com.", "cosmos.azure.com.", "countly.mail.163.com.", "covers.vitalbook.com.", - "cp2.cloudflare.com.", "cpisolutions.com.", "cpm.appocean.media.", "cpm.aserve1.net.", - "cpm.catapultx.com.", "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.", "crashsight.qq.com.", "crashsight.wetest.net.", - "creative.bbrdbr.com.", "creator.pdf24.org.", + "creditmaven.com.", + "crl.americanexpress.com.", "crlocsp.cn.", "croatia.remotepc.com.", "crobox.com.", @@ -1366,7 +1512,6 @@ var FakeECSFQDNs = container.NewMapSet( "cs.nex8.net.", "cs.playdigo.com.", "csdn.net.", - "cspire.com.", "cstse02.ultipro.com.", "cstsew02.ultipro.com.", "cstsn02.ultipro.com.", @@ -1374,8 +1519,6 @@ var FakeECSFQDNs = container.NewMapSet( "cti.roku.com.", "ctmail.com.", "customer.homedepot.com.", - "cvision.media.net.", - "cvpn-blr.ras.qualcomm.com.", "cvs.quantummetric.com.", "cwogzftn.usw.stape.io.", "czechrepublic.remotepc.com.", @@ -1385,7 +1528,9 @@ var FakeECSFQDNs = container.NewMapSet( "d2fb08da-1c03-4c8a-978f-ad8a96b4c31f.prmutv.co.", "d3-pr-cu-tm-secapi.trafficmanager.net.", "d6691a17-6fdb-4d26-85d6-b3dd27f55f08.prmutv.co.", + "d82f7a30-751a-4689-b7e9-19336a89ab46.prmutv.co.", "d837da8d.cloudsrv.minerva-labs.com.", + "da.footballbros.io.", "da.mosspf.com.", "da.toponadss.com.", "daemon.nanoleaf.me.", @@ -1410,7 +1555,6 @@ var FakeECSFQDNs = container.NewMapSet( "data.data.mheducation.com.", "data.guide-app.zoominfo.co.", "data.guides.oncoursesystems.com.", - "data.guides.percipio.com.", "data.hockeystack.com.", "data.investing.com.", "data.ipd.goto.com.", @@ -1422,13 +1566,12 @@ var FakeECSFQDNs = container.NewMapSet( "data.pendo-tracking.seismic.com.", "data.pendo.careporthealth.com.", "data.pendo.gomotive.com.", - "data.pendo.progresslearning.com.", "data.pendo.saashr.com.", "data.pendo.udsrv.com.", "data.pendoanalytics.dayforcehcm.com.", - "data.productanalytics.coconutcalendar.com.", "data.productinsights.blackline.com.", "data.queryly.com.", + "data.readiness.imaginelearning.com.", "data.tracking.billtrust.com.", "data.useranalytics.global.datasite.com.", "data00.adlooxtracking.com.", @@ -1439,12 +1582,11 @@ var FakeECSFQDNs = container.NewMapSet( "dayunlinks.cn.", "dc-o.api.leiniao.com.", "dc.ads.linkedin.com.", + "dc.cftls.t.co.cdn.cloudflare.net.", "dc.di.atlas.samsung.com.", "dc.dqa.samsung.com.", "dc.sigmob.cn.", "dc.wondershare.com.", - "dcs-live-ue1.mp.lura.live.", - "dcs-live-uw1.mp.lura.live.", "dcs-live.mp.lura.live.", "dcs-png.mp.lura.live.", "dcs-vod.mp.lura.live.", @@ -1452,32 +1594,32 @@ var FakeECSFQDNs = container.NewMapSet( "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.", - "dec-collabrtc.officeapps.live.com.", "decagon.ai.", "delivery.upremium.asia.", "dell-prod.actioniq.mr-in.com.", - "dellupdater.dell.com.", "delta.quantummetric.com.", "demeter-int-ecom-collect.trendyol.com.", "demeter-tr-core-collect.trendyol.com.", "denver.remotepc.com.", - "designer-api.hu-manity.co.", + "dev.av380.net.", + "devc.zztfly.com.", "deviceapi.ca1.absolute.com.", "deviceops.hstgps.com.", "dewu.com.", - "dexx.ai.", + "dexerto.media.", "dfaklj.tech.", "dhs.gov.", "dialpad.com.", - "dicontent.creditkarma.com.", + "dicontent.ckapis.com.", "dict.deepl.com.", "dict.ntes53.netease.com.", "dictvip-business.youdao.com.", - "diffuser-cdn.app-us1.com.", "digiapp.vietcombank.com.vn.", "digiboy.ir.", "digitalcardservice.com.", @@ -1486,7 +1628,6 @@ var FakeECSFQDNs = container.NewMapSet( "discovery.ringcentral.biz.", "dispatcher.omiapp.me.", "dispatchosglobal.yuanshen.com.", - "distribution.hulu.com.", "distservp1.pb.com.", "divide.dalyfeds.com.", "dl2.discordapp.net.", @@ -1496,40 +1637,37 @@ var FakeECSFQDNs = container.NewMapSet( "dmv.ca.gov.", "dns-e.ns4v.icu.", "dns-tunnel-check.googlezip.net.", - "dns.cloudflare.com.", "dns.rubyfish.cn.", "dns1.nettica.com.", "dns101.register.com.", + "dns23.llnwi.net.", "doceditor.wrike.com.", "docs.live.net.", "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.", - "douyin.starrydyn.com.", "dowjones-prod.actioniq.mr-in.com.", "download.2.401402081.west-gcloud.codm.activision.com.", - "download.amd.com.", - "download.lenovo.com.edgekey.net.", "downloadcenter.genetec.com.", "dp.barclaysus.com.", "dp.im.weibo.cn.", "dpf.authorize.net.", "dpool.sina.com.cn.", "dr.netease.im.", - "dragate-sg.dc.heytapmobi.com.", "dreame.tech.", "drfdisvc.walmart.com.", + "drop-assets.ea.com.", "drsquatch.com.", - "drummond.mixtubeapp.com.", "ds.gsma.com.", + "dsa-eu.hybrid.ai.", "dsm01pap001.storage.live.com.", - "dsm01pap002.storage.live.com.", "dsm01pap003.storage.live.com.", "dsm01pap004.storage.live.com.", "dsm01pap007.storage.live.com.", @@ -1540,16 +1678,18 @@ var FakeECSFQDNs = container.NewMapSet( "dsp-cookie.adfarm1.adition.com.", "dsp-trk.eskimi.com.", "dsp-trvm.eskimi.com.", - "dss.hybrid.ai.", + "dspcluster.adfarm1.adition.com.", + "dspx.tv.", "dstillery.com.", "dt.netease.im.", "dtscout.com.", + "dualspaceapi.com.", "dubai.remotepc.com.", "dublin.remotepc.com.", "dun.163yun.com.", "dz.cyberhaven.io.", + "dzfread.cn.", "e-189.21cn.com.", - "e-bea.dc2.ovid.com.", "e.189.cn.", "e.cdnwidget.com.", "e.userflow.com.", @@ -1633,6 +1773,7 @@ var FakeECSFQDNs = container.NewMapSet( "e2c79.gcp.gvt2.com.", "e2c8.gcp.gvt2.com.", "e2c80.gcp.gvt2.com.", + "e2c81.gcp.gvt2.com.", "e2c9.gcp.gvt2.com.", "e2cs01.gcp.gvt2.com.", "e2cs02.gcp.gvt2.com.", @@ -1688,11 +1829,10 @@ 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.", - "e65uuo.jiaoshuanggb.com.", - "ea.com.", "ea.footballbros.io.", "eafddirect.msedge.net.", "eagle.haplat.net.", @@ -1701,113 +1841,97 @@ var FakeECSFQDNs = container.NewMapSet( "eastasia.api.cognitive.microsoft.com.", "eastmoney.com.", "eastus.api.cognitive.microsoft.com.", - "eastus2-gas.guestconfiguration.azure.com.", "eastus2.api.cognitive.microsoft.com.", "ecatholic.com.", - "ecdd.walkme.com.", - "ecdn.teacherspayteachers.com.", "ecom.wixapps.net.", - "ecommerce.iap.unity3d.com.", "ecs-gallatin-c2s.trafficmanager.net.", "ed.link.", "edevice.toshiba-solutions.com.", - "edge.personalizer.io.", - "edge.rgs.pmc.pay.riotgames.com.", - "edge.sitecorecloud.io.", - "edge.txryan.com.", + "edge.xero.com.", "edgecdn.ru.", "edgedl.me.gvt1.com.", "edgelocation.ivanticloud.com.", "editor.wix.com.", "editorial.femaledaily.com.", "edna.id.", - "edpms.doh.gov.ph.", "education-certification.youdao.com.", "ee-share.com.", "efercro.com.", "efs.ultipro.com.", "egateway.ultipro.com.", "ei.dyn-rev.app.", - "eitri.api.useinsider.com.", + "ejoyspace.com.", "elemecdn.com.", + "elixarco.com.", + "elonuniversity-my.sharepoint.com.", "emailaptitude.com.", - "emo.v-mate.mobi.", - "emp.bbci.co.uk.edgekey.net.", "employers.indeed.com.", "endpointprotector.com.", "engage.wixapps.net.", "engagementapi.skype.com.", + "engine.phn.doublepimp.com.", "enplug.com.", "ent.box.com.", - "enthusiastgaming.com.", + "entitlements.auth.riotgames.com.", "envoy-ios-prod.getepic.com.", "envysion.com.", "epdg.vowifi.cspire.com.", "epicmobile.ohsu.edu.", - "epoch.cloud.", "eponesh.com.", "eportal.fda.gov.ph.", "epubgame.com.", "errortracking.deepl.com.", "esignlive.com.", - "esm.archive.org.", "essence.com.", - "etail.mysynchrony.com.", "etracker.com.", "etsv2.datalake.gameloft.com.", "eu-aa.online-metrix.net.", "eu-api.asm.skype.com.", "eu-prod.asyncgw.teams.microsoft.com.", "eu-push.api.intl.miui.com.", - "eu.account.riotgames.com.", "eu.global.market.xiaomi.com.", - "eu.inspidspad.com.", - "eu.mvconf.50union.com.", "eu.statusapi.micloud.xiaomi.net.", - "eu.whatfix.com.", "eu1.badoo.com.", "eu1a-excel-collab.officeapps.live.com.", "eu1a-powerpoint-collab.officeapps.live.com.", "eu1a-word-collab.officeapps.live.com.", - "eu2.concursolutions.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.", - "eu4-word-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-edit-geo.wac.trafficmanager.net.", "euler-saas-cn.heytapmobi.com.", "europe-west1-skyuk-uk-pa-tds-prod.cloudfunctions.net.", "europe.remotepc.com.", - "eus2-region.present.officeapps.live.com.", "euw1.chat.si.riotgames.com.", "eve.gameloft.com.", "event.evtm.53.com.", - "event.togothermany.com.", + "events.attentivemobile.com.", "events.glanceapis.com.", "events.swishapps.ai.", - "ew33.ultipro.com.", "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.", "exodus.desync.com.", "exp.host.", "exponential.com.", - "ext.securifyprotect.com.", "extension.faro.speechify.dev.", "extension.savvy.security.", "external-ams2-1.xx.fbcdn.net.", "external-ams4-1.xx.fbcdn.net.", "external-atl3-1.xx.fbcdn.net.", "external-atl3-2.xx.fbcdn.net.", + "external-atl3-3.xx.fbcdn.net.", "external-bos5-1.xx.fbcdn.net.", "external-den2-1.xx.fbcdn.net.", "external-dfw5-1.xx.fbcdn.net.", "external-dfw5-2.xx.fbcdn.net.", - "external-fra3-1.xx.fbcdn.net.", "external-hou1-1.xx.fbcdn.net.", "external-iad3-1.xx.fbcdn.net.", "external-iad3-2.xx.fbcdn.net.", @@ -1828,26 +1952,19 @@ var FakeECSFQDNs = container.NewMapSet( "external-phx1-1.xx.fbcdn.net.", "external-sea1-1.xx.fbcdn.net.", "external-sjc3-1.xx.fbcdn.net.", - "f-p-sandbox.bytedance.net.", "f002.backblazeb2.com.", - "fa000000120.resources.office.net.", - "fa000000131.resources.office.net.", - "fa1f96ab-b693-40e4-82d5-8698592ef9ac.prmutv.co.", - "fa3fca7ce79f4b81a39f216e916397d5.pacloudflare.com.", + "f9tg.com.", "faas.marktplaats.nl.", "factor.reg.163.com.", "factset.com.", "fanatics.ent.box.com.", "fanduel.quantummetric.com.", - "fastenal-my.sharepoint.com.", - "fastformstax.prosystemfx.com.", + "fanyiegg.youdao.com.", + "fcix.net.", "fdccpaadaptor.forddirectservices.com.", "fe.xiaohongshu.com.", - "featureflags.lavasoft.com.", "fed.federate365.com.", - "feed-stub.com.", "feeder.co.", - "feedly.com.", "feelinsonice.l.google.com.", "fef.amsub0302.manage.microsoft.com.", "fef.fxpasu01.manage.microsoft.us.", @@ -1864,52 +1981,57 @@ var FakeECSFQDNs = container.NewMapSet( "field59.com.", "files.jotform.com.", "filesyscrm.com.", + "filter.revrtb.net.", "finalsite.com.", "finalsite.net.", + "fincen.gov.", "fireeye.com.", - "fisherinvestments.com.", + "fishdom-cdn.playrix.com.", "five9.com.", "flashcards.vitalsource.com.", "flashjoin.net.", - "fledge.teads.tv.", "flip.to.", "flixcdn.com.", - "fm.cnbc.com.", + "flypgs.com.", "fm.printaudit.com.", "fn.us.ipqscdn.com.", "fo.iemiq.com.", "foisonad.com.", "forcesafesearch.google.com.", + "forethought.ai.", "form.jotform.com.", "forms-eu1.hscollectedforms.net.", "forms-eu1.hsforms.com.", "forms-eu1.hubspot.com.", + "forms-na1.hubspot.com.", "fortisimperious.com.", "fortworth.remotepc.com.", + "fotpro135alto.com.", "foundation-ipv4.youdao.com.", + "fp-ca-bell.rcs.telephony.goog.", "fp-de-carrier-vodafone.rcs.telephony.goog.", "fp-de-telefonica.rcs.telephony.goog.", "fp-gb-ee.rcs.telephony.goog.", "fp-us-att.rcs.telephony.goog.", "fp-us-carrier-spectrum.rcs.telephony.goog.", + "fp-us-cspire.rcs.telephony.goog.", "fp-us-tmobile.rcs.telephony.goog.", "fp-us-uscc.rcs.telephony.goog.", "fp-us-verizon.rcs.telephony.goog.", "fp-us-xfinity.rcs.telephony.goog.", + "fp.ca.rogers.rcs.telephony.goog.", "fp.de.dt.rcs.telephony.goog.", "fp.ps.easebar.com.", "fp.us.tracfone.rcs.telephony.goog.", "fp4-us-att.rcs.telephony.goog.", + "fp4-us-tmobile.rcs.telephony.goog.", "fp4-us-verizon.rcs.telephony.goog.", "fpdlp.applxweb.com.", "fpt.brainly.com.", "fr-prod.asyncgw.teams.microsoft.com.", - "fra-01.braze.eu.cdn.cloudflare.net.", "fran.frvr.com.", "francecentral.api.cognitive.microsoft.com.", "frankfurt.remotepc.com.", - "frc-collabrtc.officeapps.live.com.", - "freight.api.dat.com.", "fremont.remotepc.com.", "freseniusmedicalcare.com.", "fs.ultiproworkplace.com.", @@ -1917,19 +2039,23 @@ var FakeECSFQDNs = container.NewMapSet( "ftkew02.ultipro.com.", "ftkn01.ultipro.com.", "ftkn02.ultipro.com.", - "functions.classroomscreen.com.", + "ftp.ext.hp.com.edgekey.net.", + "func-fhcc-bacdnlqayn.cn-shenzhen.fcapp.run.", + "futuretechuu.com.", "fxltsbl.com.", + "g10498469755.co.", "g9hc4.cn.", "ga.badambiz.com.", "galaxyappstore.com.", "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-excel-collab.officeapps.live.com.", + "gbc-powerpoint.officeapps.live.com.", + "gbc-word-edit.officeapps.live.com.", "gccmod.ecs.office.com.", "gcdn.co.", "gcloud.qq.com.", @@ -1940,14 +2066,15 @@ var FakeECSFQDNs = container.NewMapSet( "gdc-scouccl1.haplat.net.", "gdc-scouccl2.haplat.net.", "gdid.datalake.gameloft.com.", + "gdp.haplat.net.", "geappl.io.", - "geico-app.quantummetric.com.", + "geappliances.sharepoint.com.", "geico-sync.quantummetric.com.", + "genetherapyhub.com.", "geniusmonkey.com.", - "geo-dc.adobe.com.", "geo-dra.platform.hicloud.com.", - "geo.cnbc.com.", - "geoip.apps.avada.io.", + "geocodeservice.costco.com.", + "geometrygame.org.", "geoplugin.net.", "germanywestcentral.api.cognitive.microsoft.com.", "getadmiral.com.", @@ -1955,22 +2082,24 @@ var FakeECSFQDNs = container.NewMapSet( "getnitropack.com.", "gettopple.com.", "getui.net.", - "gfts.lge.com.", "gifer.com.", "gifshow.com.", + "git.rancher.io.", "gitlab.com.", "gla.gameloft.com.", - "global-eds.prismray.io.", + "gleap.io.", "global-tokenserver-la.headline.uodoo.com.", "global.datasite.com.", - "global.gme.qcloud.com.", + "globalapi.smart2pay.com.", + "globalgme.com.", "globalsigncdn.com.cdn.cloudflare.net.", "globalsun.io.", - "globalvale.sharepoint.com.", "gme.qcloud.com.", "gnc.com.", + "go.blcdog.com.", "goaffpro.com.", "gohighlevel.com.", + "gonines.com.", "good-loop.com.", "google.org.", "googledomains.com.", @@ -1985,9 +2114,9 @@ var FakeECSFQDNs = container.NewMapSet( "group-ib.com.", "grow.me.", "grpc.vivintsky.com.", - "gslb.finzfin.com.", "gslb.xiaohongshu.com.", - "gsp85-cn-ssl.ls.apple.com.", + "gsp85-ssl.ls.apple.com.", + "gtimg.cn.", "gtm.deepl.com.", "gtm.vividseats.com.", "guid.tpns.sgp.tencent.com.", @@ -1996,6 +2125,7 @@ var FakeECSFQDNs = container.NewMapSet( "gyazo.com.", "gz0.googleusercontent.com.", "h-5h8i3ud8.online-metrix.net.", + "h-adp.online-metrix.net.", "h-discover.online-metrix.net.", "h-e04kqxof.online-metrix.net.", "h-ebay.online-metrix.net.", @@ -2012,34 +2142,37 @@ var FakeECSFQDNs = container.NewMapSet( "hanoi.remotepc.com.", "hapsee.cn.", "hapseemate.cn.", - "harmonyoffice-my.sharepoint.com.", "harry.lu.", "hawaii.remotepc.com.", "hbopenbid-apac-v2.pubmnet.com.", "hbwrapper.com.", - "heart.bmj.com.", + "hcaptcha.com.", "hecheck.bitmyanmar.info.", "hello.idocdn.com.", "helloid.com.", - "hermes-us.inspidspad.com.", + "hermes-us.inspi-dsp.com.", "hetangsmart.com.", "heytapdownload.com.", "heytapmobi.com.", "hft-prod.actioniq.mr-in.com.", "highwire.org.", + "hillsboroughcounty-my.sharepoint.com.", "hisearch-dra.dt.dbankcloud.com.", "hismarttv.com.", "hits.getelevar.com.", - "hiya-dist.e8779dac8790f4da93c09f34e73b7c7b.r2.cloudflarestorage.com.", "hk.gcloudcs.com.", "hk.ntp.org.cn.", "hk.wechat.com.", + "holid.io.", "holykjvbible.com.", "home.highwire.org.", "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.", @@ -2047,15 +2180,16 @@ var FakeECSFQDNs = container.NewMapSet( "html5.qq.com.", "htms.heytapmobi.com.", "httpdns.y5en.com.", - "httpdns.yunxindns.com.", "huan.tv.", "huaweicloud.com.", "huaweicloudwaf.com.", + "hub.gibraltarsoftware.com.", "hubble.netease.com.", "hubcloud.com.cn.", "hubspotemail.net.", "huion.cn.", "huorong.cn.", + "hw.118114.net.", "hw.gcloudcs.com.", "hwapps-o.api.leiniao.com.", "hzmklvdieo.com.", @@ -2066,11 +2200,9 @@ var FakeECSFQDNs = container.NewMapSet( "i.one-bid.com.", "i.qchannel03.cn.", "i.voe.sx.", - "i5.walmartimages.com.", - "i5.walmartimages.com.mx.", "i6-vn.weather.oppomobile.com.", "iaas.jdcloud.com.", - "iac-demo.idmgroup.com.", + "iappscontent.courts.state.ny.us.", "ibm.account.box.com.", "ibm.box.com.", "ibsrv.net.", @@ -2081,11 +2213,9 @@ var FakeECSFQDNs = container.NewMapSet( "id-ooredoo.rcs.telephony.goog.", "id-telkom.rcs.telephony.goog.", "id-timer-appstore.vivoglobal.com.", - "id.seexh.com.", - "id.tv.global.mi.com.", + "id.therealxh.com.", "idahofalls.remotepc.com.", "idchicago1.remotepc.com.", - "idcta.api.bbc.com.", "iddallas1.remotepc.com.", "iddenver.remotepc.com.", "iddetroit.remotepc.com.", @@ -2099,28 +2229,27 @@ var FakeECSFQDNs = container.NewMapSet( "igame.gcloudcs.com.", "ijoysoftconnect.com.", "ikki.youdao.com.", - "il1.nayax.net.", + "illuminate.zendesk.com.", "ilog-sea-aliyun.alipayplus.com.", - "im-x.jd.com.", - "im.uniqlo.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.", "img2021.navyfederal.org.", "img9.target.com.", - "imgc.nxjimg.com.", "imghst-de.com.", "imgs.signifyd.com.", - "imodules.com.", - "imoim.net.", - "imolive2.com.", "imotech.site.", "imou-sg-ali-online-paas-iot-private-picture.oss-ap-southeast-1.aliyuncs.com.", "imou-sg3-ali-online-paas-private-picture.oss-ap-southeast-1.aliyuncs.com.", "imoulife.com.", - "impacthero.co.", + "imp.admeking.com.", + "imp.adx.roockmobile.com.", "impactify.media.", "imptrk.siteplug.com.", "in-api.asm.skype.com.", @@ -2130,18 +2259,17 @@ var FakeECSFQDNs = container.NewMapSet( "in.gov.", "in.visitors.live.", "inc-collabrtc.officeapps.live.com.", - "inc-excel.officeapps.live.com.", "indianapolis.remotepc.com.", "inf.miui.com.", - "informa.com.", - "ingov.zendesk.com.", + "ingov.sharepoint.com.", + "init.clmbosean.space.", "inneraudioms.cc.easebar.com.", "innity.com.", "innity.net.", + "ino.qq.com.", "ins-tgrfs7t3.ias.tencent-cloud.net.", - "inside-graph.com.cdn.cloudflare.net.", "inskinad.com.", - "inspidspad.com.", + "inspi-dsp.com.", "inspirebrands-sync.quantummetric.com.", "inspirebrands.quantummetric.com.", "instantmessaging-pa-jms-ap.googleapis.com.", @@ -2150,7 +2278,6 @@ var FakeECSFQDNs = container.NewMapSet( "instantmessaging-pa-jms-us.googleapis.com.", "instatus.com.", "int.dpool.sina.com.cn.", - "internal.engine.intl.mi.com.", "internetdownloadmanager.com.", "intl-im-conn.iq.com.", "intuit.zoom.us.", @@ -2160,16 +2287,18 @@ var FakeECSFQDNs = container.NewMapSet( "iowa.remotepc.com.", "ip-api.com.", "ip.istatmenus.app.", - "ip.remotepc.com.", - "ipinyou.com.", + "ipfs.io.", + "iphonesubmissions.apple.com.", + "ipm.atm.youku.com.", "iprofiles.apple.com.", "iprom.net.", "ipv4.cadc.absolute.com.", + "ipv4.geojs.io.", + "ipv4.sdiptest.com.", "ipv4.tracker.harry.lu.", + "ipv6-4.sdiptest.com.", "iq.com.", "iscorp.com.", - "ispeech.org.", - "issuepcdn.freeterabox.com.", "istanbul.remotepc.com.", "istatmenus.app.", "italynorth.api.cognitive.microsoft.com.", @@ -2179,18 +2308,17 @@ var FakeECSFQDNs = container.NewMapSet( "itemorder.com.", "itm.cloud.com.", "itoon.org.", - "itunes.apple.com.edgekey.net.", "itzmx.com.", "ivalua.com.", + "ivt.np.community.playstation.net.", "ivview.com.", "ivview.net.", "izatcloud.net.", "jabfm.org.", "japanwest.api.cognitive.microsoft.com.", - "jbisumari.org.", "jdcloud.com.", - "jenzabarcloud.com.", "jhahosted.com.", + "jishiyuchat.com.", "johannesburg.remotepc.com.", "jotfor.ms.", "jp-prod.asyncgw.teams.microsoft.com.", @@ -2200,18 +2328,19 @@ 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.", - "jsapi.lightboxcdn.com.", - "jsdelivr.net.cdn.cloudflare.net.", + "jsdelivr.net.", "jsonatm.broker.tplay.qq.com.", "jss.starbucks.com.", "jssprod-starbucks.trafficmanager.net.", @@ -2229,11 +2358,11 @@ 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.", - "kamalaharris.com.", + "karmanow.com.", "kelvin.education.", "kiev.remotepc.com.", - "kiprotect.com.", "kirkland.zoom.us.", "kiwisizing.com.", "klagenfurt.remotepc.com.", @@ -2251,7 +2380,10 @@ var FakeECSFQDNs = container.NewMapSet( "kunlunhuf.com.", "kunlunsl.com.", "kunlunso.com.", + "kunvertads.com.", + "kurogame.xyz.", "kwimgs.com.", + "kzhi.tech.", "la.remotepc.com.", "la1.chat.si.riotgames.com.", "la10.remotepc.com.", @@ -2262,11 +2394,11 @@ var FakeECSFQDNs = container.NewMapSet( "la4.remotepc.com.", "la8.remotepc.com.", "la9.remotepc.com.", + "labtech.corcystems.com.", "labtech.myitpros.com.", "lahuashanbx.com.", "lalapush.com.", "lan.sdk.linkedin.com.", - "landing.adobe.com.", "lansing.remotepc.com.", "lansweeper.com.", "larksuite.com.", @@ -2275,32 +2407,31 @@ var FakeECSFQDNs = container.NewMapSet( "layerxsecurity.com.", "lazpay-fe-kyc-module-file.oss-ap-southeast-1.aliyuncs.com.", "ldap.google.com.", + "ldgslb.com.", "ldmnq.com.", + "ldy.ieypg.com.", "leadmanagerfx.com.", "leihuo.netease.com.", "leiniao.com.", - "lenovomm.com.", "levect.com.", "level10gc.com.", "leveldata.poki.io.", "lexicon.33across.com.", "lianmeng.360.cn.", - "librarydaap.itunes.apple.com.", "libretexts.org.", "license.gonative.io.", "license.litespeedtech.com.", "license.unity3d.com.", "licensing.bitmovin.com.", - "licensing.sbullet.com.", "lichess.org.", "lightwidget.com.", "likr.tw.", - "lima-tpgateway3.factset.com.", "lima.remotepc.com.", "lineicons.com.", "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.", @@ -2309,34 +2440,39 @@ var FakeECSFQDNs = container.NewMapSet( "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.", - "livekilleenisd.sharepoint.com.", + "livedmpsk12ia.sharepoint.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.", "ljubljana.remotepc.com.", "loandepot.zoom.us.", "local.adguard.org.", "local.info.g9hc4.cn.", "log-api.newrelic.com.cdn.cloudflare.net.", - "log-yex.youdao.com.", + "log-auth.zztfly.com.", "log.getadblock.com.", "log.webmaxlogger.net.", "log.zoom.us.", "log1.cmpassport.com.", "log2.cmpassport.com.", - "log22-normal-alisg.tiktokv.com.", - "logger.moviead55.ru.", "logging-service-prod.getepic.com.", "logging.mp.lura.live.", + "loggly.com.", + "login.newrelic.com.", "login.teamviewer.com.", "login.vivaldi.net.", + "logs.impactify.media.", "logs2.sportslocalmedia.com.", "logu.hpplay.cn.", - "logus.xiaoyi.com.", "logx.optimizely.com.", "lol.sw.game.qq.com.", "london.remotepc.com.", @@ -2348,17 +2484,14 @@ var FakeECSFQDNs = container.NewMapSet( "london8.remotepc.com.", "long.tv.", "look.360.cn.", - "love8.ltd.", "lpa.ds.gsma.com.", "lplfinancial.app.box.com.", - "lptkw.s4xx6.com.", "lsagentrelay.lansweeper.com.", "ltfl.librarything.com.", "ludashi.com.", "luxembourg.remotepc.com.", "lycraservice-pa-cam-prod.googleapis.com.", "lyric.alarmnet.com.", - "m.sogo.com.", "m1.ubianet.com.", "m110601-fcdn.mp.lura.live.", "m2.ubianet.com.", @@ -2366,27 +2499,29 @@ var FakeECSFQDNs = container.NewMapSet( "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.", - "magna.com.", "maidenhead.remotepc.com.", "mail.superhuman.com.", "mailinblue.com.", + "main.clmbosean.space.", "maintenanceconnection.com.", - "makerbot.com.", "malware-filter.gitlab.io.", "mam.manage.microsoft.us.", - "mam.netease.com.", + "manage-selfhost.microsoft.com.", "manage.wix.com.", "management.azure.com.", "management.privatelink.azure.com.", "manassas.remotepc.com.", "manchester.remotepc.com.", + "marketingassets.staples.com.", "marmot-cloud.com.", "marseille.remotepc.com.", - "masonitecloud-my.sharepoint.com.", "master1.teamviewer.com.", "master10.teamviewer.com.", "master11.teamviewer.com.", @@ -2406,15 +2541,15 @@ var FakeECSFQDNs = container.NewMapSet( "match.adfarm1.adition.com.", "matetranslate.com.", "matrix.netease.com.", - "maumeredegale.wixsite.com.", "max-l.mediav.com.", "mbboauth-1c.prd.cn.vwg-connect.cn.", "mcallen.remotepc.com.", "mcds.dalyfeds.com.", - "mclarenhealth.sharepoint.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.", @@ -2423,7 +2558,7 @@ var FakeECSFQDNs = container.NewMapSet( "media-arn2-1.cdn.whatsapp.net.", "media-atl3-1.cdn.whatsapp.net.", "media-atl3-2.cdn.whatsapp.net.", - "media-ber1-1.cdn.whatsapp.net.", + "media-atl3-3.cdn.whatsapp.net.", "media-bog2-1.cdn.whatsapp.net.", "media-bog2-2.cdn.whatsapp.net.", "media-bos5-1.cdn.whatsapp.net.", @@ -2438,14 +2573,11 @@ var FakeECSFQDNs = container.NewMapSet( "media-dfw5-1.cdn.whatsapp.net.", "media-dfw5-2.cdn.whatsapp.net.", "media-dus1-1.cdn.whatsapp.net.", - "media-for1-1.cdn.whatsapp.net.", "media-fra3-1.cdn.whatsapp.net.", "media-fra3-2.cdn.whatsapp.net.", "media-fra5-1.cdn.whatsapp.net.", "media-fra5-2.cdn.whatsapp.net.", - "media-gateway.mlb.com.", "media-gig4-1.cdn.whatsapp.net.", - "media-gig4-2.cdn.whatsapp.net.", "media-gru1-1.cdn.whatsapp.net.", "media-gru1-2.cdn.whatsapp.net.", "media-gru2-1.cdn.whatsapp.net.", @@ -2461,7 +2593,6 @@ var FakeECSFQDNs = container.NewMapSet( "media-iad3-1.cdn.whatsapp.net.", "media-iad3-2.cdn.whatsapp.net.", "media-ist1-1.cdn.whatsapp.net.", - "media-ist1-2.cdn.whatsapp.net.", "media-kul2-1.cdn.whatsapp.net.", "media-kul2-2.cdn.whatsapp.net.", "media-kul3-1.cdn.whatsapp.net.", @@ -2490,51 +2621,38 @@ var FakeECSFQDNs = container.NewMapSet( "media-qro1-2.cdn.whatsapp.net.", "media-scl2-1.cdn.whatsapp.net.", "media-sea1-1.cdn.whatsapp.net.", + "media-sin11-1.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-sof1-2.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-xsp2-1.cdn.whatsapp.net.", - "media-yyz1-1.cdn.whatsapp.net.", - "media.defense.gov.", - "media.pa.betrivers.com.", + "media.expedia.com.", "media.ringcentral.com.", - "media.subway.com.", "media.superhuman.com.", - "media.united.com.", + "media.tinkoff.ru.", + "mediafuse.com.", "mediav.com.", - "medicinetechnet.com.", "melbourne.remotepc.com.", - "member.virginpulse.com.", "memphis.remotepc.com.", - "mercadolibre.com.ar.", - "messages.indeed.com.", + "metadata.decagon.ai.", "metric.picodiglobal.com.", "metrics-dre.dt.dbankcloud.cn.", "metrics-dre.dt.dbankcloud.com.", "metrics-dre.dt.hihonorcloud.com.", "metrics5.data.hicloud.com.", "mexicocity.remotepc.com.", - "mf.b37mrtl.ru.", "mgbsdknaeast.matrix.easebar.com.", "mgspabst.prismray.io.", "mgspfacts.prismray.io.", "mgsphdr1.prismray.io.", - "mgspmess.prismray.io.", "mgsppres.prismray.io.", "mgsppros1.prismray.io.", - "mgsppros2.prismray.io.", - "mgsppush.prismray.io.", - "mgspsc.prismray.io.", "mgsptele.prismray.io.", - "mgspturn.prismray.io.", "mgtv.com.", "miami.remotepc.com.", "miami2.remotepc.com.", @@ -2544,18 +2662,17 @@ var FakeECSFQDNs = container.NewMapSet( "microad.jp.", "microvirt.com.", "mid4.linkedin.com.", - "midfield.mlbstatic.com.", "migu.cn.", "milan.remotepc.com.", - "milestoneinternet.com.cdn.cloudflare.net.", - "milwaukeetool.com.", "mimir2.vivaldi.com.", "min-api.cryptocompare.com.", "mini.browser.360.cn.", "minigame.vip.", "mintkeyboard.com.", "mixi.media.", + "mlmannouncements.pearson.com.", "mmods.site.", + "mobile-bank.cdn-tinkoff.ru.", "mobile-collector.newrelic.com.cdn.cloudflare.net.", "mobile-l7.bereal.com.", "mobile-protect-api.securetheorem.com.", @@ -2564,16 +2681,16 @@ var FakeECSFQDNs = container.NewMapSet( "mobiledataplan-pa.googleapis.com.", "mobilemaps-pa-gz.googleapis.com.", "mobilemaps.googleapis.com.", - "mobydix.com.", + "mobilesecuritycore-cdn.norton.com.edgekey.net.", "modelportrait.xiaohongshu.com.", "modesto.remotepc.com.", - "mon0-misc-hl.amemv.com.", "moni-onrt-stsdk.vivo.com.cn.", "monitoring.getelevar.com.", "monitoring.worksighted.com.", "montage-updates.displaynote.com.", "monticello.remotepc.com.", "montreal.remotepc.com.", + "monumetric.com.", "morningstar.zoom.us.", "motiondetection-us-1d.oss-us-west-1.aliyuncs.com.", "motiondetection-us-7d.oss-us-west-1.aliyuncs.com.", @@ -2581,21 +2698,24 @@ var FakeECSFQDNs = container.NewMapSet( "mouser.com.", "mp.360.cn.", "mrisoftware.com.", + "ms.applvn.com.", "ms1app.pb.com.", - "msch.f.360.cn.", "msdl.microsoft.com.", - "mservices.navyfcu.org.", "msf.3g.qq.com.", "msg-img-hk.oss-cn-hongkong.aliyuncs.com.", - "msync-im1-sgp-ga.easemob.com.", + "msg.cmpassport.com.", "mtrace.qq.com.", "mumbai.remotepc.com.", "munich.remotepc.com.", - "muscache.cn.", + "music.163.com.", + "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.", "mxp-pusa01.app.blackbaud.net.", @@ -2604,30 +2724,29 @@ var FakeECSFQDNs = container.NewMapSet( "my.dealersocket.com.", "my.getadmiral.com.", "my.jbi.global.", + "my.microsoftpersonalcontent.com.", "my.nalpeiron.com.", "myim3banner.kloc.co.", "myisolved.com.", - "myou.cvte.com.", "myporn.club.", "myqcloud.com.", "mystery-game-tile.poki.io.", "myvscloud.com.", "myworkdaycdn.com.cn.", - "n13.ultipro.com.", - "n21.ultipro.com.", - "n21c.ultipro.com.", "n33.ultipro.com.", "n35.ultipro.com.", - "na113.epm.cyberark.com.", - "na121.epm.cyberark.com.", + "na120.epm.cyberark.com.", "na2.chat.si.riotgames.com.", "nab.com.au.", "najva.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.", @@ -2655,19 +2774,17 @@ var FakeECSFQDNs = container.NewMapSet( "newyork3.remotepc.com.", "nex.163.com.", "nexstar.amp.permutive.com.", + "nextinsure.com.", "nexx360.io.", "ng1.angus.mrisoftware.com.", "ngb.haplat.net.", "nice-team.net.", "nie.netease.com.", - "niemanlab.org.", - "nieuwsblad.be.", "nist.gov.", "nitroapps.co.", "nitropay.com.", "noc.computerhelpnj.com.", "node.setupad.com.", - "nodejs.org.", "nokia.com.", "nordcurrent.com.", "norma-external-collect.meizu.com.", @@ -2676,8 +2793,10 @@ var FakeECSFQDNs = container.NewMapSet( "norwayeast.api.cognitive.microsoft.com.", "notes-analytics-events.apple.com.", "notes.services.box.com.", + "notification889.com.", + "notify.music.163.com.", "novaicare.com.", - "novel.itoon.org.", + "npmjs.com.", "nps.gov.", "ns-cloud-a1.googledomains.com.", "ns-cloud-a2.googledomains.com.", @@ -2703,6 +2822,7 @@ 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.", @@ -2724,7 +2844,6 @@ var FakeECSFQDNs = container.NewMapSet( "ntes53.netease.com.", "ntp.aliyun.com.", "ntp.arlo.com.", - "ntp.arlo.com.cdn.cloudflare.net.", "ntp.org.cn.", "ntp1.aliyun.com.", "ntp1.huan.tv.", @@ -2736,48 +2855,52 @@ var FakeECSFQDNs = container.NewMapSet( "nuremberg.remotepc.com.", "nv.gov.", "nvu-prd.mqtt.ivanticloud.com.", + "nvz-prd-apim.ivanticloud.com.", "nwr.mmcdn.com.", "nwr.static.mmcdn.com.", "nws-platform.zoom.us.", "nwsalert.onelouder.com.", "nycourts.gov.", "nycrt.marphezis.com.", - "oauth.gov.online.office365.us.", - "oauth.ring.com.cdn.cloudflare.net.", + "o.quizlet.com.", + "oauth.ws.sonos.com.", "obihai.telephony.goog.", "obs.ap-southeast-3.myhuaweicloud.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.", - "ocmant.com.", "ocps-xfer.kronos.net.", - "ocsp.comodoca.com.", - "ocsp.entrust.net.", "ocsp.identrust.com.", - "octaneai.com.", + "octossp.com.", "odrs.fda.gov.ph.", "odw7bf.dood.video.", "oec22-normal-alisg.tokopediax.com.", + "oec22-normal-useast2a.tiktokv.com.", "office.microsoft.com.", - "officepreviewredir.microsoft.com.", + "officeathand.att.com.", "ogma-l7.bereal.com.", "ogma.bereal.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.", - "on-hweudc-conf-o.api.leiniao.com.", + "one.newrelic.com.", "onekey1.cmpassport.com.", "oneplus.net.", "onethingpcs.com.", "onezapp.com.", + "online-stopwatch.com.", "onlinewebfonts.com.", "op.mykonf.com.", "opamarketplace.com.", @@ -2786,16 +2909,17 @@ var FakeECSFQDNs = container.NewMapSet( "opencmp.net.", "opendsp.ru.", "openrice.com.", - "operationchicken.com.", "opex-service-cn.allawntech.com.", "oppo.com.", + "oppomobile.com.", "optimize.ulinq.asia.", "optimize.urekamedia.com.", "optimizely.com.", + "orangehire.com.au.", "oregon.remotepc.com.", - "origin.com.", "origin.fe-image-cache-ttp.useast8.byteglb.com.", "orlando.remotepc.com.", + "ort.stsdk.vivo.com.cn.", "os7lm.6kvses.com.", "osaka.remotepc.com.", "oss-ap-southeast-1.aliyuncs.com.", @@ -2817,11 +2941,16 @@ var FakeECSFQDNs = container.NewMapSet( "overseasccl-major-b.haplat.net.", "overseasccl-major-c.haplat.net.", "ovh.maxhost.io.", + "ovidsp.ovid.com.", "p.adlooxtracking.com.", + "p.teads.tv.", "p0-pu-private-useast8.tiktokv.us.", "p107609.cedexis-test.com.", "p109522.cedexis-test.com.", - "p16-buy.itunes.apple.com.", + "p11-buy.itunes.apple.com.", + "p12-buy.itunes.apple.com.", + "p13.zdusercontent.com.", + "p2-buy.itunes.apple.com.", "p20304.cedexis-test.com.", "p20305.cedexis-test.com.", "p20306.cedexis-test.com.", @@ -2831,6 +2960,7 @@ var FakeECSFQDNs = container.NewMapSet( "p20311.cedexis-test.com.", "p20314.cedexis-test.com.", "p20315.cedexis-test.com.", + "p26-buy.itunes.apple.com.", "p2p-cal-2.anker-in.com.", "p2p-cal-3.anker-in.com.", "p2p-cal.anker-in.com.", @@ -2840,8 +2970,8 @@ var FakeECSFQDNs = container.NewMapSet( "p2p2.cloudbirds.cn.", "p2p3.cloudbirds.cn.", "p2pm-ali.reolink.com.", + "p3-buy.itunes.apple.com.", "p30605.cedexis-test.com.", - "p33-buy.itunes.apple.com.", "p33231.cedexis-test.com.", "p33236.cedexis-test.com.", "p33242.cedexis-test.com.", @@ -2859,11 +2989,10 @@ var FakeECSFQDNs = container.NewMapSet( "p35883.cedexis-test.com.", "p38635.cedexis-test.com.", "p39604.cedexis-test.com.", + "p40-buy.itunes.apple.com.", "p40255.cedexis-test.com.", - "p40256.cedexis-test.com.", "p40259.cedexis-test.com.", "p40264.cedexis-test.com.", - "p40265.cedexis-test.com.", "p40266.cedexis-test.com.", "p40267.cedexis-test.com.", "p40480.cedexis-test.com.", @@ -2877,11 +3006,11 @@ var FakeECSFQDNs = container.NewMapSet( "p48434.cedexis-test.com.", "p48435.cedexis-test.com.", "p48436.cedexis-test.com.", - "p49-buy.itunes.apple.com.", "p4p.arenabg.com.", - "p50-buy.itunes.apple.com.", "p52066.cedexis-test.com.", "p56745.cedexis-test.com.", + "p70-buy.itunes.apple.com.", + "p71-buy.itunes.apple.com.", "p76593.cedexis-test.com.", "p86075.cedexis-test.com.", "p86077.cedexis-test.com.", @@ -2898,11 +3027,13 @@ var FakeECSFQDNs = container.NewMapSet( "partnerboost.com.", "pasadena.remotepc.com.", "passportalmsp.com.", + "pat-issuer.cloudflare.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.", @@ -2910,20 +3041,23 @@ var FakeECSFQDNs = container.NewMapSet( "pd.cdnwidget.com.", "pdf24.org.", "pdfforge.org.", + "peloton.netlifyglobalcdn.com.", + "penngaming-my.sharepoint.com.", "peopleadmin.com.", "perf-eu1.hsforms.com.", - "perfops2.byte-test.com.", "perfsight.qq.com.", "perfsight.wetest.net.", + "perkspot-api.perkspot.com.", "permutive.arstechnica.com.", "permutive.businessinsider.com.", - "permutive.com.", + "permutive.newyorker.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.", @@ -2932,15 +3066,15 @@ var FakeECSFQDNs = container.NewMapSet( "phx02pap006.storage.live.com.", "phx02pap008.storage.live.com.", "pi2850.ci.managedwhitelisting.com.", - "piano.io.", "pic.rutubelist.ru.", - "pics.ebaystatic.com.", + "picodiglobal.com.", + "piicmgvmss.polaris.com.", "pikabu.ru.", + "pin.apiblink.ru.", "ping.getadblock.com.", "pingler.com.", "pingma.qq.com.", "pingmesh.bigo.sg.", - "piojm.tech.", "pitk.unioneeu.com.", "pittsburgh.remotepc.com.", "pix.cdnwidget.com.", @@ -2952,38 +3086,44 @@ var FakeECSFQDNs = container.NewMapSet( "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.", "plrm.zone.", "pm.geniusmonkey.com.", + "podcastswaves.com.", "poizon.com.", "polandcentral.api.cognitive.microsoft.com.", "polling.zoom.us.", - "polyfill.archive.org.", + "polymarket.com.", + "poopstream.co.", "pop-convert.com.", "popt.in.", - "portal.discover.com.", "portal.us.ubianet.com.", + "portals.mobi.", "portland.remotepc.com.", "posthog.com.", + "potatovpn.com.", "pov.spectrum.net.", "powerpoint-collab.officeapps.live.com.", - "powerschool.com.", + "pp.ringcentral.biz.", "ppgames.net.", "ppos.com.", - "pptservicescast.gcc.osi.office365.us.", - "pragmaticplay.net.", "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.", - "prepareplanes.com.", "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.", @@ -2992,24 +3132,20 @@ 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.", - "prod.do.dsp.mp.microsoft.com.edgekey.net.", "production.kabutoservices.com.", - "profile.indeed.com.", - "profiler-collector.dalyfeds.com.", + "prolearning.nwea.org.", "proquest.com.", "protonvpn.com.", "provaltech.com.", "proxy-safebrowsing.googleapis.com.", "proxy.mob.maps.yandex.net.", "ps.namequery.com.", - "psnobj.prod.dl.playstation.net.", - "psyche.co.", "pub.affilimateapis.com.", - "pub.doubleverify.com.cdn.cloudflare.net.", "pub.network.", "public-api.uxfeedback.ru.", "publicfaas.vasdgame.com.", @@ -3017,7 +3153,9 @@ var FakeECSFQDNs = container.NewMapSet( "puffer.6.401402081.west-gcloud.codm.activision.com.", "pull-flv-f58-tt03.fcdn.eu.tiktokcdn.com.", "punch.p2p.qq.com.", + "push-ads-cn.heytapmobi.com.", "push-row.zui.com.", + "push.bitdefender.net.", "push.omiapp.me.", "pushcrew.com.", "pushimg.com.", @@ -3032,12 +3170,14 @@ var FakeECSFQDNs = container.NewMapSet( "qagpublic.qg1.apps.qualys.co.uk.", "qagpublic.qg1.apps.qualys.com.", "qagpublic.qg1.apps.qualys.eu.", + "qagpublic.qg1.apps.qualys.in.", "qagpublic.qg2.apps.qualys.com.", "qagpublic.qg2.apps.qualys.eu.", "qagpublic.qg3.apps.qualys.com.", "qagpublic.qg4.apps.qualys.com.", "qatarcentral.api.cognitive.microsoft.com.", "qc-static.coccoc.com.", + "qcloud.com.", "qfp.intuit.com.", "qiezibenpao.com.", "qinglong.me.", @@ -3045,14 +3185,18 @@ var FakeECSFQDNs = container.NewMapSet( "qiniup.com.", "qiyukf.com.", "qookkagames.com.", + "qortex.ai.", + "qpic.cn.", "qualcomm.cn.", "qualcomm.com.", "qualys.ca.", "qualys.com.", "qualys.eu.", - "quantamagazine.org.", + "qualys.in.", + "quantil.com.", "quantummetric.com.", "quebeccity.remotepc.com.", + "questionai.com.", "quicinc.com.", "quickcep.com.", "qxwz.com.", @@ -3061,7 +3205,6 @@ var FakeECSFQDNs = container.NewMapSet( "r.intake-lr.com.", "r.logr-ingest.com.", "r.logrocket.io.", - "r.lr-hv-in.com.", "r.lr-in-prod.com.", "r.lr-in.com.", "r.lr-ingest.com.", @@ -3069,63 +3212,69 @@ var FakeECSFQDNs = container.NewMapSet( "r.lr-intake.com.", "r.lrkt-in.com.", "r.superhuman.com.", - "r1---sn-2imeyn7k.c.2mdn.net.", - "r1---sn-a5meknzk.c.2mdn.net.", - "r1---sn-a5mlrnls.c.2mdn.net.", - "r1---sn-a5mlrnlz.c.2mdn.net.", - "r1---sn-ab5l6ndr.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-ab5sznz6.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-p5qs7n6d.c.2mdn.net.", + "r1---sn-p5qlsnrl.c.2mdn.net.", + "r1---sn-p5qlsnrr.c.2mdn.net.", "r1---sn-p5qs7nsk.c.2mdn.net.", - "r1---sn-q4fl6n66.c.2mdn.net.", + "r1---sn-p5qs7nzr.c.2mdn.net.", "r1---sn-q4fl6n6d.c.2mdn.net.", "r1---sn-q4fl6nd7.c.2mdn.net.", - "r1---sn-q4fl6ndl.c.2mdn.net.", - "r1---sn-q4fl6ns7.c.2mdn.net.", - "r1---sn-q4fl6nsd.c.2mdn.net.", "r1---sn-q4fl6nsk.c.2mdn.net.", - "r1---sn-q4fl6nsr.c.2mdn.net.", - "r1---sn-q4fl6nz7.c.2mdn.net.", - "r1---sn-q4flrnee.c.2mdn.net.", - "r1---sn-q4flrnsk.c.2mdn.net.", + "r1---sn-q4flrne7.c.2mdn.net.", + "r1---sn-q4flrner.c.2mdn.net.", + "r1---sn-q4flrnez.c.2mdn.net.", + "r1---sn-q4flrnlz.c.2mdn.net.", "r1---sn-q4flrnsl.c.2mdn.net.", - "r1---sn-q4fzen7l.c.2mdn.net.", + "r1---sn-q4fzene7.c.2mdn.net.", + "r1---sn-vgqskn66.c.2mdn.net.", "r1---sn-vgqskn67.c.2mdn.net.", "r1---sn-vgqskn6d.c.2mdn.net.", - "r1---sn-vgqskne6.c.2mdn.net.", + "r1---sn-vgqskn6s.c.2mdn.net.", "r1---sn-vgqsknes.c.2mdn.net.", - "r1---sn-vgqsknez.c.2mdn.net.", "r1---sn-vgqsknld.c.2mdn.net.", "r1---sn-vgqsknse.c.2mdn.net.", - "r1---sn-vgqsknze.c.2mdn.net.", - "r1---sn-vgqsknzk.c.2mdn.net.", + "r1---sn-vgqsknzl.c.2mdn.net.", + "r1---sn-vgqsknzs.c.2mdn.net.", "r1---sn-vgqsknzy.c.2mdn.net.", - "r1---sn-vgqsknzz.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-vgqsrnsy.c.2mdn.net.", + "r1---sn-vgqsrnsd.c.2mdn.net.", + "r1---sn-vgqsrnz7.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-a5mekndz.c.2mdn.net.", - "r2---sn-ab5l6ndr.c.2mdn.net.", + "r2---sn-a5mekn6d.c.2mdn.net.", "r2---sn-ab5l6nk6.c.2mdn.net.", "r2---sn-ab5l6nkd.c.2mdn.net.", "r2---sn-ab5l6nr6.c.2mdn.net.", @@ -3134,216 +3283,238 @@ var FakeECSFQDNs = container.NewMapSet( "r2---sn-ab5l6nrl.c.2mdn.net.", "r2---sn-ab5l6nrr.c.2mdn.net.", "r2---sn-ab5l6nrs.c.2mdn.net.", + "r2---sn-ab5l6nrz.c.2mdn.net.", "r2---sn-ab5sznz6.c.2mdn.net.", "r2---sn-ab5sznzd.c.2mdn.net.", + "r2---sn-ab5sznze.c.2mdn.net.", "r2---sn-ab5sznzk.c.2mdn.net.", "r2---sn-ab5sznzl.c.2mdn.net.", "r2---sn-ab5sznzr.c.2mdn.net.", "r2---sn-ab5sznzs.c.2mdn.net.", "r2---sn-ab5sznzy.c.2mdn.net.", "r2---sn-ab5sznzz.c.2mdn.net.", - "r2---sn-nx57ynsk.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-p5qlsn6l.c.2mdn.net.", - "r2---sn-p5qs7n6d.c.2mdn.net.", - "r2---sn-p5qs7nsk.c.2mdn.net.", + "r2---sn-p5qlsn7s.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-q4fl6nz6.c.2mdn.net.", - "r2---sn-q4flrn7r.c.2mdn.net.", - "r2---sn-q4flrne7.c.2mdn.net.", - "r2---sn-q4flrnl7.c.2mdn.net.", - "r2---sn-q4flrnsk.c.2mdn.net.", + "r2---sn-q4fl6nsk.c.2mdn.net.", + "r2---sn-q4fl6nsr.c.2mdn.net.", + "r2---sn-q4flrnlz.c.2mdn.net.", "r2---sn-q4flrnsl.c.2mdn.net.", - "r2---sn-q4flrnss.c.2mdn.net.", "r2---sn-q4fzen7l.c.2mdn.net.", - "r2---sn-q4fzen7y.c.2mdn.net.", + "r2---sn-q4fzene7.c.2mdn.net.", "r2---sn-q4fzenee.c.2mdn.net.", - "r2---sn-vgqskn66.c.2mdn.net.", - "r2---sn-vgqskn67.c.2mdn.net.", + "r2---sn-vgqskn6s.c.2mdn.net.", + "r2---sn-vgqskn6z.c.2mdn.net.", "r2---sn-vgqsknld.c.2mdn.net.", - "r2---sn-vgqsknly.c.2mdn.net.", "r2---sn-vgqsknlz.c.2mdn.net.", - "r2---sn-vgqsknse.c.2mdn.net.", "r2---sn-vgqsknsk.c.2mdn.net.", "r2---sn-vgqsknze.c.2mdn.net.", - "r2---sn-vgqsknzs.c.2mdn.net.", - "r2---sn-vgqsknzy.c.2mdn.net.", + "r2---sn-vgqsknzl.c.2mdn.net.", "r2---sn-vgqsknzz.c.2mdn.net.", - "r2---sn-vgqsrn66.c.2mdn.net.", "r2---sn-vgqsrn67.c.2mdn.net.", - "r2---sn-vgqsrned.c.2mdn.net.", + "r2---sn-vgqsrn6e.c.2mdn.net.", "r2---sn-vgqsrnld.c.2mdn.net.", - "r2---sn-vgqsrnlk.c.2mdn.net.", - "r2---sn-vgqsrnsd.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-vgqsrnzz.c.2mdn.net.", - "r3---sn-2imern76.c.2mdn.net.", - "r3---sn-2imeyn7k.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-a5mlrnl6.c.2mdn.net.", + "r3---sn-a5meknzl.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-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-ab5sznld.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-npoe7ndl.c.2mdn.net.", - "r3---sn-nx57ynsk.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-p5qlsn7s.c.2mdn.net.", "r3---sn-p5qlsndk.c.2mdn.net.", - "r3---sn-p5qs7nsk.c.2mdn.net.", - "r3---sn-p5qs7nsr.c.2mdn.net.", - "r3---sn-q4fl6n6s.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-q4fl6nsl.c.2mdn.net.", - "r3---sn-q4fl6nsr.c.2mdn.net.", - "r3---sn-q4fl6nzy.c.2mdn.net.", + "r3---sn-q4fl6nss.c.2mdn.net.", + "r3---sn-q4flrne6.c.2mdn.net.", + "r3---sn-q4flrnsk.c.2mdn.net.", "r3---sn-q4flrnsl.c.2mdn.net.", - "r3---sn-q4flrnss.c.2mdn.net.", - "r3---sn-q4fzen7l.c.2mdn.net.", - "r3---sn-q4fzene7.c.2mdn.net.", - "r3---sn-vgqskn67.c.2mdn.net.", + "r3---sn-q4fzen7s.c.2mdn.net.", + "r3---sn-vgqskn66.c.2mdn.net.", "r3---sn-vgqskn6d.c.2mdn.net.", - "r3---sn-vgqskn6z.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-vgqskns7.c.2mdn.net.", - "r3---sn-vgqsknse.c.2mdn.net.", - "r3---sn-vgqsknsk.c.2mdn.net.", + "r3---sn-vgqsknzd.c.2mdn.net.", "r3---sn-vgqsknzs.c.2mdn.net.", "r3---sn-vgqsknzy.c.2mdn.net.", - "r3---sn-vgqsrn66.c.2mdn.net.", + "r3---sn-vgqsrn6e.c.2mdn.net.", "r3---sn-vgqsrn6l.c.2mdn.net.", - "r3---sn-vgqsrnld.c.2mdn.net.", - "r3---sn-vgqsrnll.c.2mdn.net.", + "r3---sn-vgqsrnes.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-vgqsrnzd.c.2mdn.net.", "r3---sn-vgqsrnzs.c.2mdn.net.", + "r3---sn-vgqsrnzy.c.2mdn.net.", "r3---sn-vgqsrnzz.c.2mdn.net.", - "r4---sn-a5mekn6k.c.2mdn.net.", - "r4---sn-a5meknsd.c.2mdn.net.", - "r4---sn-a5meknzr.c.2mdn.net.", - "r4---sn-a5mlrnlz.c.2mdn.net.", - "r4---sn-a5msenle.c.2mdn.net.", "r4---sn-ab5l6ndr.c.2mdn.net.", "r4---sn-ab5l6nk6.c.2mdn.net.", - "r4---sn-ab5l6nkd.c.2mdn.net.", - "r4---sn-ab5l6nr6.c.2mdn.net.", "r4---sn-ab5l6nrd.c.2mdn.net.", + "r4---sn-ab5l6nrk.c.2mdn.net.", "r4---sn-ab5l6nrl.c.2mdn.net.", + "r4---sn-ab5l6nrr.c.2mdn.net.", "r4---sn-ab5l6nrs.c.2mdn.net.", "r4---sn-ab5l6nrz.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-hp57knds.c.2mdn.net.", - "r4---sn-nx57ynsk.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-p5qlsn7s.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-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-vgqskn66.c.2mdn.net.", - "r4---sn-vgqskn6z.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-vgqskns7.c.2mdn.net.", "r4---sn-vgqsknz6.c.2mdn.net.", "r4---sn-vgqsknzd.c.2mdn.net.", - "r4---sn-vgqsknzr.c.2mdn.net.", + "r4---sn-vgqsknzk.c.2mdn.net.", "r4---sn-vgqsknzs.c.2mdn.net.", "r4---sn-vgqsknzy.c.2mdn.net.", - "r4---sn-vgqsrn67.c.2mdn.net.", - "r4---sn-vgqsrnek.c.2mdn.net.", - "r4---sn-vgqsrnl6.c.2mdn.net.", - "r4---sn-vgqsrnlk.c.2mdn.net.", - "r4---sn-vgqsrnsd.c.2mdn.net.", - "r4---sn-vgqsrnz7.c.2mdn.net.", - "r4---sn-vgqsrnzd.c.2mdn.net.", + "r4---sn-vgqsrn6l.c.2mdn.net.", + "r4---sn-vgqsrnez.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-vgqsrnzk.c.2mdn.net.", "r4---sn-vgqsrnzz.c.2mdn.net.", - "r5---sn-a5m7lnld.c.2mdn.net.", - "r5---sn-a5meknde.c.2mdn.net.", - "r5---sn-a5meknds.c.2mdn.net.", - "r5---sn-a5msen7z.c.2mdn.net.", - "r5---sn-ab5l6ndr.c.2mdn.net.", - "r5---sn-ab5l6ndy.c.2mdn.net.", "r5---sn-ab5l6nk6.c.2mdn.net.", "r5---sn-ab5l6nkd.c.2mdn.net.", "r5---sn-ab5l6nr6.c.2mdn.net.", + "r5---sn-ab5l6nrd.c.2mdn.net.", + "r5---sn-ab5l6nrk.c.2mdn.net.", "r5---sn-ab5l6nrl.c.2mdn.net.", "r5---sn-ab5l6nrr.c.2mdn.net.", "r5---sn-ab5l6nrs.c.2mdn.net.", "r5---sn-ab5l6nrz.c.2mdn.net.", - "r5---sn-ab5sznly.c.2mdn.net.", "r5---sn-ab5sznz6.c.2mdn.net.", "r5---sn-ab5sznzd.c.2mdn.net.", + "r5---sn-ab5sznze.c.2mdn.net.", "r5---sn-ab5sznzk.c.2mdn.net.", "r5---sn-ab5sznzl.c.2mdn.net.", "r5---sn-ab5sznzr.c.2mdn.net.", - "r5---sn-ab5sznzs.c.2mdn.net.", "r5---sn-ab5sznzy.c.2mdn.net.", - "r5---sn-ab5sznzz.c.2mdn.net.", "r5---sn-ajab55-55.c.2mdn.net.", - "r5---sn-nx57ynsk.c.2mdn.net.", + "r5---sn-jxopj-nh4e.googlevideo.com.", + "r5---sn-nx57ynsl.c.2mdn.net.", + "r5---sn-p5qddn76.c.2mdn.net.", + "r5---sn-p5qddn7k.c.2mdn.net.", + "r5---sn-p5qlsn7d.c.2mdn.net.", "r5---sn-p5qlsn7s.c.2mdn.net.", - "r5---sn-p5qs7nsk.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-p5qs7nzy.c.2mdn.net.", - "r5---sn-q4fl6n6z.c.2mdn.net.", - "r5---sn-q4fl6ndl.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-q4flrnld.c.2mdn.net.", + "r5---sn-q4flrnlz.c.2mdn.net.", + "r5---sn-q4flrnsd.c.2mdn.net.", + "r5---sn-q4flrnsl.c.2mdn.net.", "r5---sn-q4fzen7l.c.2mdn.net.", - "r5---sn-q4fzen7y.c.2mdn.net.", + "r5---sn-qjp5q5-55.c.2mdn.net.", "r5---sn-vgqskn67.c.2mdn.net.", - "r5---sn-vgqskn6s.c.2mdn.net.", + "r5---sn-vgqskn6d.c.2mdn.net.", "r5---sn-vgqskn6z.c.2mdn.net.", - "r5---sn-vgqsknly.c.2mdn.net.", - "r5---sn-vgqsknlz.c.2mdn.net.", + "r5---sn-vgqsknlr.c.2mdn.net.", + "r5---sn-vgqsknz6.c.2mdn.net.", + "r5---sn-vgqsknze.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-vgqsrn6e.c.2mdn.net.", + "r5---sn-vgqsrn6l.c.2mdn.net.", "r5---sn-vgqsrne6.c.2mdn.net.", + "r5---sn-vgqsrnek.c.2mdn.net.", + "r5---sn-vgqsrnez.c.2mdn.net.", "r5---sn-vgqsrnl6.c.2mdn.net.", "r5---sn-vgqsrnlk.c.2mdn.net.", - "r5---sn-vgqsrnlz.c.2mdn.net.", - "r5---sn-vgqsrnsr.c.2mdn.net.", - "r5---sn-vgqsrnsy.c.2mdn.net.", - "r5---sn-vgqsrnzz.c.2mdn.net.", + "r5---sn-vgqsrnz7.c.2mdn.net.", + "r6---sn-jxopj-nh4e.googlevideo.com.", "radar.cedexis.com.", "radar.com.", "radyushin.com.", "railway.app.", "raleigh.remotepc.com.", - "rapi.av380.net.", + "randomhouse.app.box.com.", "rba-screen.healthsafe-id.com.", "rba.onehealthcareid.com.", "rbdata.boostymark.com.", + "rbhs7ex3.onequince.com.", "rbm-us.storage.googleapis.com.", "rcs-acs-att-us.jibe.google.com.", "rcs-acs-mcc311.jibe.google.com.", @@ -3358,20 +3529,22 @@ var FakeECSFQDNs = container.NewMapSet( "recombee.com.", "recruiting.ultipro.com.", "recruiting2.ultipro.com.", + "referralrock.com.", "reflector.makerbot.com.", - "region1.app-measurement.com.", + "refpaucqkl.top.", "regions.com.", "registration.prna01.cmdagent.trafficmanager.net.", "related.queryly.com.", + "relay.shhnowisnottheti.me.", "remote-config.gslb.sgw.shopeemobile.com.", "remote.control4.com.", "repo.zabbix.com.", + "requality.android.shouji.sogou.com.", "request-global.czilladx.com.", - "researchgate.net.", "resideo.com.", - "resizer.otstatic.com.", "resource.digitalinsight.com.", - "resources.finalsite.net.", + "resources.office.net.edgekey.net.", + "rest.iad-01.braze.com.", "restproxy-analytics.ascendlearning.com.", "restrict.youtube.com.", "restrictmoderate.youtube.com.", @@ -3386,16 +3559,13 @@ var FakeECSFQDNs = container.NewMapSet( "rl.progressive.com.", "rl.quantummetric.com.", "rlm.haokan.mobi.", - "rmkcdn.successfactors.com.", "rmm.aunalytics.com.", - "rmm.creativeplanning.com.", "rmm2.jmark.com.", "rms-dra.platform.dbankcloud.com.", "rn-resource-app.xiaohongshu.com.", "roborock.com.", "rockylinux.org.", - "rogueone.aristotleinsight.com.", - "romsp-unifyconfig.vivo.com.cn.", + "roockmobile.com.", "router.teamviewer.com.", "roxy.azurefd.net.", "rpt.cedexis.com.", @@ -3409,7 +3579,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-2imeyn7k.googlevideo.com.", "rr1---sn-2napbiu-p5ie.googlevideo.com.", "rr1---sn-2napbiu-p5ie.gvt1.com.", - "rr1---sn-2oaig5-55.googlevideo.com.", "rr1---sn-2pmxapm0n-gpje.googlevideo.com.", "rr1---sn-2pmxapm0n-gpjl.googlevideo.com.", "rr1---sn-30a7rne6.googlevideo.com.", @@ -3432,6 +3601,7 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-4g5e6nsz.googlevideo.com.", "rr1---sn-4g5e6nz7.googlevideo.com.", "rr1---sn-4g5e6nze.googlevideo.com.", + "rr1---sn-4g5e6nzl.googlevideo.com.", "rr1---sn-4g5e6nzs.googlevideo.com.", "rr1---sn-4g5e6nzz.googlevideo.com.", "rr1---sn-4g5edn6k.googlevideo.com.", @@ -3444,6 +3614,7 @@ 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.", @@ -3469,52 +3640,53 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-4g5lznez.googlevideo.com.", "rr1---sn-4g5lznl6.googlevideo.com.", "rr1---sn-4g5lznl7.googlevideo.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-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.", "rr1---sn-5hne6ns6.googlevideo.com.", + "rr1---sn-5hne6nsd.googlevideo.com.", "rr1---sn-5hne6nsk.googlevideo.com.", "rr1---sn-5hne6nsr.googlevideo.com.", "rr1---sn-5hne6nsy.googlevideo.com.", + "rr1---sn-5hne6nsy.gvt1.com.", "rr1---sn-5hne6nsz.googlevideo.com.", "rr1---sn-5hne6nz6.googlevideo.com.", + "rr1---sn-5hne6nzd.googlevideo.com.", + "rr1---sn-5hne6nzd.gvt1.com.", "rr1---sn-5hne6nzk.googlevideo.com.", - "rr1---sn-5hne6nzs.googlevideo.com.", "rr1---sn-5hne6nzy.googlevideo.com.", "rr1---sn-5hnednss.googlevideo.com.", "rr1---sn-5hnednsz.googlevideo.com.", - "rr1---sn-5hnednsz.gvt1.com.", "rr1---sn-5hnekn76.googlevideo.com.", "rr1---sn-5hnekn7d.googlevideo.com.", "rr1---sn-5hnekn7l.googlevideo.com.", "rr1---sn-5hnekn7s.googlevideo.com.", "rr1---sn-5hnekn7z.googlevideo.com.", - "rr1---sn-5hnekn7z.gvt1.com.", "rr1---sn-5hneknee.googlevideo.com.", - "rr1---sn-5hneknee.gvt1.com.", "rr1---sn-5hneknek.googlevideo.com.", "rr1---sn-5hneknes.googlevideo.com.", - "rr1---sn-5hneknes.gvt1.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.", + "rr1---sn-5uaeznel.googlevideo.com.", "rr1---sn-5uaeznes.googlevideo.com.", "rr1---sn-5uaeznez.googlevideo.com.", "rr1---sn-5uaeznl6.googlevideo.com.", - "rr1---sn-5uaeznld.googlevideo.com.", "rr1---sn-5uaeznlz.googlevideo.com.", - "rr1---sn-5uaeznrz.googlevideo.com.", "rr1---sn-5uaezns7.googlevideo.com.", "rr1---sn-5uaeznse.googlevideo.com.", "rr1---sn-5uaeznsl.googlevideo.com.", "rr1---sn-5uaeznss.googlevideo.com.", - "rr1---sn-5uaeznys.googlevideo.com.", - "rr1---sn-5uaeznyz.googlevideo.com.", + "rr1---sn-5uaezny6.googlevideo.com.", + "rr1---sn-5uaeznze.googlevideo.com.", "rr1---sn-5ualdnle.googlevideo.com.", "rr1---sn-5ualdnll.googlevideo.com.", "rr1---sn-5ualdnlr.googlevideo.com.", @@ -3531,38 +3703,40 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-5ualdnsz.googlevideo.com.", "rr1---sn-5ualdnz7.googlevideo.com.", "rr1---sn-5ualdnze.googlevideo.com.", - "rr1---sn-8qj-i5okl.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.", "rr1---sn-8xgp1vo-ab5e.googlevideo.com.", + "rr1---sn-8xgp1vo-ab5l.googlevideo.com.", "rr1---sn-8xgp1vo-ab5s.googlevideo.com.", "rr1---sn-8xgp1vo-ab5z.googlevideo.com.", "rr1---sn-8xgp1vo-p5ie.googlevideo.com.", "rr1---sn-8xgp1vo-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-9gv76n7s.googlevideo.com.", "rr1---sn-9gv76n7z.googlevideo.com.", "rr1---sn-9gv7ene6.googlevideo.com.", "rr1---sn-9gv7ened.googlevideo.com.", + "rr1---sn-9gv7zn76.googlevideo.com.", "rr1---sn-9gv7zn7e.googlevideo.com.", "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-a5mekn6r.googlevideo.com.", - "rr1---sn-a5mekn6r.gvt1.com.", "rr1---sn-a5mekn6s.googlevideo.com.", - "rr1---sn-a5mekn6s.gvt1.com.", "rr1---sn-a5mekn6z.googlevideo.com.", "rr1---sn-a5meknd6.googlevideo.com.", "rr1---sn-a5meknd6.gvt1.com.", @@ -3571,49 +3745,45 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-a5mekndl.googlevideo.com.", "rr1---sn-a5mekndl.gvt1.com.", "rr1---sn-a5meknds.googlevideo.com.", - "rr1---sn-a5meknds.gvt1.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-a5mlrnek.gvt1.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-a5msenle.gvt1.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-ab5l6nr6.gvt1.com.", "rr1---sn-ab5l6nrd.googlevideo.com.", "rr1---sn-ab5l6nrk.googlevideo.com.", - "rr1---sn-ab5l6nrk.gvt1.com.", "rr1---sn-ab5l6nrl.googlevideo.com.", - "rr1---sn-ab5l6nrl.gvt1.com.", "rr1---sn-ab5l6nrr.googlevideo.com.", "rr1---sn-ab5l6nrs.googlevideo.com.", "rr1---sn-ab5l6nrz.googlevideo.com.", @@ -3621,7 +3791,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-ab5sznly.googlevideo.com.", "rr1---sn-ab5sznly.gvt1.com.", "rr1---sn-ab5sznz6.googlevideo.com.", - "rr1---sn-ab5sznz6.gvt1.com.", "rr1---sn-ab5sznzd.googlevideo.com.", "rr1---sn-ab5sznze.googlevideo.com.", "rr1---sn-ab5sznzk.googlevideo.com.", @@ -3631,22 +3800,25 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-ab5sznzs.googlevideo.com.", "rr1---sn-ab5sznzy.googlevideo.com.", "rr1---sn-ab5sznzz.googlevideo.com.", - "rr1---sn-ab5sznzz.gvt1.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.", "rr1---sn-aigl6nzr.googlevideo.com.", "rr1---sn-aigl6nzs.googlevideo.com.", "rr1---sn-aigzrn76.googlevideo.com.", @@ -3664,11 +3836,9 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-aigzrnsz.googlevideo.com.", "rr1---sn-aigzrnz7.googlevideo.com.", "rr1---sn-aigzrnze.googlevideo.com.", + "rr1---sn-aj5ua5-5c.googlevideo.com.", "rr1---sn-ajab55-55.googlevideo.com.", "rr1---sn-avbpj-cq5e.googlevideo.com.", - "rr1---sn-bg5oqxjvh-50nz.googlevideo.com.", - "rr1---sn-bg5oqxjvh-jg2s.googlevideo.com.", - "rr1---sn-bg5oqxjvh-xa2s.googlevideo.com.", "rr1---sn-cvb7lne7.googlevideo.com.", "rr1---sn-cvb7lnee.googlevideo.com.", "rr1---sn-cvb7lnls.googlevideo.com.", @@ -3678,44 +3848,38 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-fpnjoxu-hnol.googlevideo.com.", "rr1---sn-gpuuxg-hxhl.googlevideo.com.", "rr1---sn-gpuuxg-hxhs.googlevideo.com.", - "rr1---sn-gpuuxg-hxhs.gvt1.com.", "rr1---sn-gpuuxg-hxhz.googlevideo.com.", "rr1---sn-gpuuxg-hxhz.gvt1.com.", - "rr1---sn-hgn7rn7y.googlevideo.com.", - "rr1---sn-hgn7ynek.googlevideo.com.", + "rr1---sn-hjoj-jaul.googlevideo.com.", + "rr1---sn-hjoj-poul.googlevideo.com.", "rr1---sn-hoa7kn76.googlevideo.com.", "rr1---sn-hoa7kn7z.googlevideo.com.", "rr1---sn-hoa7rn76.googlevideo.com.", "rr1---sn-hoa7rn7z.googlevideo.com.", "rr1---sn-hp57kn6r.googlevideo.com.", "rr1---sn-hp57kn6y.googlevideo.com.", - "rr1---sn-hp57knd6.googlevideo.com.", "rr1---sn-hp57kndd.googlevideo.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-hp57kndz.googlevideo.com.", - "rr1---sn-hp57kndz.gvt1.com.", "rr1---sn-hp57yn7r.googlevideo.com.", "rr1---sn-hp57yn7y.googlevideo.com.", - "rr1---sn-hp57yn7y.gvt1.com.", "rr1---sn-hp57yne7.googlevideo.com.", "rr1---sn-hp57ynee.googlevideo.com.", "rr1---sn-hp57ynl6.googlevideo.com.", - "rr1---sn-hp57ynl6.gvt1.com.", "rr1---sn-hp57ynlr.googlevideo.com.", - "rr1---sn-hp57ynlr.gvt1.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-hxgpu-qufs.googlevideo.com.", "rr1---sn-i3b7kn6s.googlevideo.com.", "rr1---sn-i3b7knld.googlevideo.com.", "rr1---sn-i3b7knlk.googlevideo.com.", @@ -3724,6 +3888,7 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-i3b7knse.googlevideo.com.", "rr1---sn-i3b7knsl.googlevideo.com.", "rr1---sn-i3b7knzl.googlevideo.com.", + "rr1---sn-i3b7knzs.googlevideo.com.", "rr1---sn-i3belne6.googlevideo.com.", "rr1---sn-i3belney.googlevideo.com.", "rr1---sn-i3belnl6.googlevideo.com.", @@ -3732,19 +3897,23 @@ 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-i5goxu-i3bl.googlevideo.com.", "rr1---sn-jn2pgx4pcxg-w5os.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-qufl.googlevideo.com.", + "rr1---sn-jvhj5nu-qufs.googlevideo.com.", "rr1---sn-jvooxqouf3-cqaz.googlevideo.com.", "rr1---sn-jxopj-n5oe.googlevideo.com.", "rr1---sn-n2uxaxjvh-j5xl.googlevideo.com.", + "rr1---sn-n2uxaxjvh-j5xl.gvt1.com.", "rr1---sn-n2uxaxjvh-j5xs.googlevideo.com.", + "rr1---sn-n2uxaxjvh-j5xs.gvt1.com.", "rr1---sn-n4v7snee.googlevideo.com.", "rr1---sn-n4v7sney.googlevideo.com.", "rr1---sn-n4v7snl7.googlevideo.com.", @@ -3757,7 +3926,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-nh5gujvh-h4xe.googlevideo.com.", "rr1---sn-nh5gujvh-h4xe.gvt1.com.", "rr1---sn-npoe7ndl.googlevideo.com.", - "rr1---sn-npoe7ndl.gvt1.com.", "rr1---sn-npoe7nds.googlevideo.com.", "rr1---sn-npoe7ne6.googlevideo.com.", "rr1---sn-npoe7ne7.googlevideo.com.", @@ -3770,10 +3938,12 @@ 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-npoe7ns7.gvt1.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.", "rr1---sn-npoe7nss.googlevideo.com.", "rr1---sn-npoe7nsy.googlevideo.com.", @@ -3790,57 +3960,55 @@ var FakeECSFQDNs = container.NewMapSet( "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.", "rr1---sn-npoldn7e.googlevideo.com.", - "rr1---sn-npoldn7e.gvt1.com.", "rr1---sn-npoldn7l.googlevideo.com.", "rr1---sn-npoldn7s.googlevideo.com.", "rr1---sn-npoldn7s.gvt1.com.", "rr1---sn-npoldn7y.googlevideo.com.", "rr1---sn-npoldn7z.googlevideo.com.", "rr1---sn-npoldne7.googlevideo.com.", - "rr1---sn-ntq7ynle.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-nx57ynsl.gvt1.com.", "rr1---sn-nx57ynss.googlevideo.com.", - "rr1---sn-nx57ynss.gvt1.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-nx5s7n7y.gvt1.com.", + "rr1---sn-nx5s7nee.googlevideo.com.", "rr1---sn-nx5s7nel.googlevideo.com.", - "rr1---sn-nx5s7nel.gvt1.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-o097znzd.gvt1.com.", "rr1---sn-o097znze.googlevideo.com.", "rr1---sn-o097znzk.googlevideo.com.", "rr1---sn-o097znzr.googlevideo.com.", - "rr1---sn-o097znzr.gvt1.com.", "rr1---sn-oj5hn5-55.googlevideo.com.", "rr1---sn-oxgpj-5ace.googlevideo.com.", "rr1---sn-p5qddn76.googlevideo.com.", "rr1---sn-p5qddn7d.googlevideo.com.", "rr1---sn-p5qddn7k.googlevideo.com.", - "rr1---sn-p5qddn7k.gvt1.com.", "rr1---sn-p5qddn7r.googlevideo.com.", "rr1---sn-p5qddn7z.googlevideo.com.", "rr1---sn-p5qlsn6l.googlevideo.com.", @@ -3851,7 +4019,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-p5qlsnd6.googlevideo.com.", "rr1---sn-p5qlsndk.googlevideo.com.", "rr1---sn-p5qlsndr.googlevideo.com.", - "rr1---sn-p5qlsndr.gvt1.com.", "rr1---sn-p5qlsndz.googlevideo.com.", "rr1---sn-p5qlsnrl.googlevideo.com.", "rr1---sn-p5qlsnrr.googlevideo.com.", @@ -3860,20 +4027,16 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-p5qs7nsk.googlevideo.com.", "rr1---sn-p5qs7nsk.gvt1.com.", "rr1---sn-p5qs7nsr.googlevideo.com.", - "rr1---sn-p5qs7nzk.googlevideo.com.", "rr1---sn-p5qs7nzr.googlevideo.com.", - "rr1---sn-p5qs7nzr.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-q4fl6n6r.gvt1.com.", "rr1---sn-q4fl6n6s.googlevideo.com.", "rr1---sn-q4fl6n6s.gvt1.com.", "rr1---sn-q4fl6n6y.googlevideo.com.", @@ -3883,63 +4046,57 @@ var FakeECSFQDNs = container.NewMapSet( "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-q4fl6nss.googlevideo.com.", "rr1---sn-q4fl6nsy.googlevideo.com.", - "rr1---sn-q4fl6nsy.gvt1.com.", - "rr1---sn-q4fl6nz6.googlevideo.com.", - "rr1---sn-q4fl6nz6.gvt1.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-q4flrn7r.gvt1.com.", "rr1---sn-q4flrn7y.googlevideo.com.", - "rr1---sn-q4flrn7y.gvt1.com.", "rr1---sn-q4flrne6.googlevideo.com.", - "rr1---sn-q4flrne6.gvt1.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-q4flrner.gvt1.com.", - "rr1---sn-q4flrnes.googlevideo.com.", - "rr1---sn-q4flrnes.gvt1.com.", "rr1---sn-q4flrney.googlevideo.com.", "rr1---sn-q4flrnez.googlevideo.com.", "rr1---sn-q4flrnl6.googlevideo.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-q4fzen7r.gvt1.com.", "rr1---sn-q4fzen7s.googlevideo.com.", "rr1---sn-q4fzen7s.gvt1.com.", "rr1---sn-q4fzen7y.googlevideo.com.", @@ -3947,84 +4104,73 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-q4fzene7.googlevideo.com.", "rr1---sn-q4fzene7.gvt1.com.", "rr1---sn-q4fzenee.googlevideo.com.", - "rr1---sn-q4fzenee.gvt1.com.", + "rr1---sn-qjp5q5-55.googlevideo.com.", "rr1---sn-qxo7rn7k.googlevideo.com.", "rr1---sn-qxo7rn7r.googlevideo.com.", "rr1---sn-qxo7rn7y.googlevideo.com.", "rr1---sn-qxoedn7k.googlevideo.com.", "rr1---sn-qxoedne7.googlevideo.com.", "rr1---sn-qxoednee.googlevideo.com.", - "rr1---sn-t0a7lnee.googlevideo.com.", "rr1---sn-u1hp55-5c.googlevideo.com.", + "rr1---sn-uqnuxaxjvh-hnoe.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-vgqskn66.gvt1.com.", "rr1---sn-vgqskn67.googlevideo.com.", "rr1---sn-vgqskn67.gvt1.com.", "rr1---sn-vgqskn6d.googlevideo.com.", "rr1---sn-vgqskn6s.googlevideo.com.", "rr1---sn-vgqskn6z.googlevideo.com.", - "rr1---sn-vgqskn6z.gvt1.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-vgqsknez.gvt1.com.", "rr1---sn-vgqsknld.googlevideo.com.", "rr1---sn-vgqsknlk.googlevideo.com.", - "rr1---sn-vgqsknlk.gvt1.com.", "rr1---sn-vgqsknll.googlevideo.com.", - "rr1---sn-vgqsknll.gvt1.com.", "rr1---sn-vgqsknlr.googlevideo.com.", "rr1---sn-vgqsknls.googlevideo.com.", "rr1---sn-vgqsknly.googlevideo.com.", "rr1---sn-vgqskns7.googlevideo.com.", - "rr1---sn-vgqskns7.gvt1.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-vgqsknz7.gvt1.com.", "rr1---sn-vgqsknzd.googlevideo.com.", + "rr1---sn-vgqsknzd.gvt1.com.", "rr1---sn-vgqsknze.googlevideo.com.", - "rr1---sn-vgqsknze.gvt1.com.", "rr1---sn-vgqsknzk.googlevideo.com.", "rr1---sn-vgqsknzl.googlevideo.com.", - "rr1---sn-vgqsknzl.gvt1.com.", "rr1---sn-vgqsknzr.googlevideo.com.", "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-vgqsrn66.gvt1.com.", "rr1---sn-vgqsrn67.googlevideo.com.", - "rr1---sn-vgqsrn67.gvt1.com.", "rr1---sn-vgqsrn6e.googlevideo.com.", "rr1---sn-vgqsrn6l.googlevideo.com.", - "rr1---sn-vgqsrn6l.gvt1.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-vgqsrnlz.gvt1.com.", "rr1---sn-vgqsrns6.googlevideo.com.", "rr1---sn-vgqsrnsd.googlevideo.com.", "rr1---sn-vgqsrnsr.googlevideo.com.", - "rr1---sn-vgqsrnsr.gvt1.com.", "rr1---sn-vgqsrnsy.googlevideo.com.", "rr1---sn-vgqsrnz6.googlevideo.com.", "rr1---sn-vgqsrnz7.googlevideo.com.", @@ -4032,33 +4178,19 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-vgqsrnzd.googlevideo.com.", "rr1---sn-vgqsrnzk.googlevideo.com.", "rr1---sn-vgqsrnzr.googlevideo.com.", - "rr1---sn-vgqsrnzr.gvt1.com.", "rr1---sn-vgqsrnzs.googlevideo.com.", "rr1---sn-vgqsrnzy.googlevideo.com.", "rr1---sn-vgqsrnzz.googlevideo.com.", - "rr1---sn-vgqsrnzz.gvt1.com.", "rr1---sn-vnix5o-28ql.googlevideo.com.", "rr1---sn-voxoxu-v3jl.googlevideo.com.", "rr1---sn-voxoxu-v3js.googlevideo.com.", - "rr1---sn-xo5-co5l.googlevideo.com.", - "rr1.sn-5hneknee.googlevideo.com.", - "rr1.sn-hgn7rn7y.googlevideo.com.", - "rr1.sn-hp57knds.googlevideo.com.", - "rr1.sn-hp57kndz.googlevideo.com.", - "rr1.sn-nx57ynsk.googlevideo.com.", - "rr1.sn-p5qs7nsk.googlevideo.com.", - "rr1.sn-q4fl6n6z.googlevideo.com.", + "rr1.sn-q4fl6nd7.googlevideo.com.", + "rr1.sn-q4fl6ns7.googlevideo.com.", "rr1.sn-q4fl6nsd.googlevideo.com.", - "rr1.sn-q4fl6nsy.googlevideo.com.", "rr1.sn-q4flrnee.googlevideo.com.", - "rr1.sn-q4flrnes.googlevideo.com.", "rr1.sn-q4flrnlz.googlevideo.com.", - "rr1.sn-q4fzen7e.googlevideo.com.", - "rr1.sn-q4fzen7l.googlevideo.com.", - "rr1.sn-q4fzen7r.googlevideo.com.", - "rr1.sn-q4fzen7y.googlevideo.com.", - "rr1.sn-t0a7lnee.googlevideo.com.", - "rr1.sn-vgqsrnzz.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.", @@ -4066,12 +4198,9 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-2imeyn7k.googlevideo.com.", "rr2---sn-2napbiu-p5ie.googlevideo.com.", "rr2---sn-2napbiu-p5ie.gvt1.com.", - "rr2---sn-2oaig5-55.googlevideo.com.", "rr2---sn-2pmxapm0n-gpje.googlevideo.com.", "rr2---sn-2pmxapm0n-gpjl.googlevideo.com.", "rr2---sn-30a7rne6.googlevideo.com.", - "rr2---sn-30a7rned.googlevideo.com.", - "rr2---sn-30a7rnek.googlevideo.com.", "rr2---sn-30a7rner.googlevideo.com.", "rr2---sn-30a7ynek.googlevideo.com.", "rr2---sn-30a7yner.googlevideo.com.", @@ -4084,12 +4213,13 @@ var FakeECSFQDNs = container.NewMapSet( "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.", "rr2---sn-4g5e6nze.googlevideo.com.", "rr2---sn-4g5e6nzl.googlevideo.com.", "rr2---sn-4g5e6nzs.googlevideo.com.", + "rr2---sn-4g5e6nzz.googlevideo.com.", "rr2---sn-4g5edn6k.googlevideo.com.", "rr2---sn-4g5edn6r.googlevideo.com.", "rr2---sn-4g5edn6y.googlevideo.com.", @@ -4103,7 +4233,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-4g5edndy.googlevideo.com.", "rr2---sn-4g5edndz.googlevideo.com.", "rr2---sn-4g5ednkl.googlevideo.com.", - "rr2---sn-4g5ednkl.gvt1.com.", "rr2---sn-4g5ednld.googlevideo.com.", "rr2---sn-4g5ednly.googlevideo.com.", "rr2---sn-4g5edns6.googlevideo.com.", @@ -4125,14 +4254,19 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-4g5lzney.googlevideo.com.", "rr2---sn-4g5lznez.googlevideo.com.", "rr2---sn-4g5lznl6.googlevideo.com.", + "rr2---sn-4g5lznl7.googlevideo.com.", "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-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.", @@ -4140,41 +4274,36 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-5hne6nsy.googlevideo.com.", "rr2---sn-5hne6nsz.googlevideo.com.", "rr2---sn-5hne6nz6.googlevideo.com.", + "rr2---sn-5hne6nz6.gvt1.com.", "rr2---sn-5hne6nzd.googlevideo.com.", - "rr2---sn-5hne6nzd.gvt1.com.", "rr2---sn-5hne6nzk.googlevideo.com.", "rr2---sn-5hne6nzs.googlevideo.com.", "rr2---sn-5hne6nzy.googlevideo.com.", - "rr2---sn-5hne6nzy.gvt1.com.", "rr2---sn-5hnednss.googlevideo.com.", - "rr2---sn-5hnednss.gvt1.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-5hnekn7l.gvt1.com.", "rr2---sn-5hnekn7s.googlevideo.com.", "rr2---sn-5hnekn7z.googlevideo.com.", "rr2---sn-5hneknee.googlevideo.com.", "rr2---sn-5hneknek.googlevideo.com.", - "rr2---sn-5hneknek.gvt1.com.", "rr2---sn-5hneknes.googlevideo.com.", - "rr2---sn-5hneknes.gvt1.com.", - "rr2---sn-5jn5a5n35-5ojs.googlevideo.com.", - "rr2---sn-5pgnugx5h-hn2z.googlevideo.com.", "rr2---sn-5uaezndd.googlevideo.com.", "rr2---sn-5uaezne6.googlevideo.com.", + "rr2---sn-5uaezned.googlevideo.com.", + "rr2---sn-5uaeznel.googlevideo.com.", "rr2---sn-5uaeznes.googlevideo.com.", "rr2---sn-5uaeznez.googlevideo.com.", "rr2---sn-5uaeznl6.googlevideo.com.", - "rr2---sn-5uaeznrz.googlevideo.com.", + "rr2---sn-5uaeznld.googlevideo.com.", + "rr2---sn-5uaeznlz.googlevideo.com.", "rr2---sn-5uaezns7.googlevideo.com.", "rr2---sn-5uaeznse.googlevideo.com.", "rr2---sn-5uaeznsl.googlevideo.com.", "rr2---sn-5uaeznss.googlevideo.com.", "rr2---sn-5uaezny6.googlevideo.com.", - "rr2---sn-5uaeznys.googlevideo.com.", "rr2---sn-5uaeznyz.googlevideo.com.", "rr2---sn-5uaeznze.googlevideo.com.", "rr2---sn-5ualdnle.googlevideo.com.", @@ -4193,9 +4322,10 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-5ualdnsz.googlevideo.com.", "rr2---sn-5ualdnz7.googlevideo.com.", "rr2---sn-5ualdnze.googlevideo.com.", - "rr2---sn-8qj-i5okl.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.", "rr2---sn-8xgp1vo-a5ml.googlevideo.com.", "rr2---sn-8xgp1vo-ab56.googlevideo.com.", "rr2---sn-8xgp1vo-ab5d.googlevideo.com.", @@ -4205,30 +4335,34 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-8xgp1vo-ab5z.googlevideo.com.", "rr2---sn-8xgp1vo-p5ie.googlevideo.com.", "rr2---sn-8xgp1vo-vgqe.googlevideo.com.", + "rr2---sn-8xgp1vo-xfge.googlevideo.com.", "rr2---sn-8xgp1vo-xfgl.googlevideo.com.", "rr2---sn-8xgp1vo-xfgs.googlevideo.com.", "rr2---sn-9gv76n7e.googlevideo.com.", - "rr2---sn-9gv76n7l.googlevideo.com.", "rr2---sn-9gv76n7s.googlevideo.com.", "rr2---sn-9gv76n7z.googlevideo.com.", "rr2---sn-9gv7ene6.googlevideo.com.", "rr2---sn-9gv7ened.googlevideo.com.", + "rr2---sn-9gv7zn76.googlevideo.com.", "rr2---sn-9gv7zn7e.googlevideo.com.", + "rr2---sn-9gv7zn7y.googlevideo.com.", "rr2---sn-a5m7lnl6.googlevideo.com.", + "rr2---sn-a5m7lnl6.gvt1.com.", "rr2---sn-a5m7lnld.googlevideo.com.", + "rr2---sn-a5m7lnld.gvt1.com.", "rr2---sn-a5mekn6d.googlevideo.com.", "rr2---sn-a5mekn6d.gvt1.com.", "rr2---sn-a5mekn6k.googlevideo.com.", + "rr2---sn-a5mekn6k.gvt1.com.", "rr2---sn-a5mekn6l.googlevideo.com.", - "rr2---sn-a5mekn6l.gvt1.com.", "rr2---sn-a5mekn6r.googlevideo.com.", "rr2---sn-a5mekn6r.gvt1.com.", "rr2---sn-a5mekn6s.googlevideo.com.", - "rr2---sn-a5mekn6s.gvt1.com.", "rr2---sn-a5mekn6z.googlevideo.com.", "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.", @@ -4240,20 +4374,21 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-a5meknzk.googlevideo.com.", "rr2---sn-a5meknzk.gvt1.com.", "rr2---sn-a5meknzl.googlevideo.com.", - "rr2---sn-a5meknzl.gvt1.com.", "rr2---sn-a5meknzr.googlevideo.com.", + "rr2---sn-a5meknzr.gvt1.com.", "rr2---sn-a5mlrnl6.googlevideo.com.", + "rr2---sn-a5mlrnl6.gvt1.com.", "rr2---sn-a5mlrnll.googlevideo.com.", - "rr2---sn-a5mlrnll.gvt1.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-a5msener.gvt1.com.", "rr2---sn-a5msenes.googlevideo.com.", "rr2---sn-a5msenes.gvt1.com.", "rr2---sn-a5msenl7.googlevideo.com.", @@ -4261,7 +4396,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-a5msenle.googlevideo.com.", "rr2---sn-a5msenle.gvt1.com.", "rr2---sn-a5msenll.googlevideo.com.", - "rr2---sn-a5msenll.gvt1.com.", "rr2---sn-ab5l6ndr.googlevideo.com.", "rr2---sn-ab5l6ndy.googlevideo.com.", "rr2---sn-ab5l6nk6.googlevideo.com.", @@ -4270,20 +4404,15 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-ab5l6nr6.gvt1.com.", "rr2---sn-ab5l6nrd.googlevideo.com.", "rr2---sn-ab5l6nrk.googlevideo.com.", - "rr2---sn-ab5l6nrk.gvt1.com.", "rr2---sn-ab5l6nrl.googlevideo.com.", "rr2---sn-ab5l6nrr.googlevideo.com.", "rr2---sn-ab5l6nrr.gvt1.com.", "rr2---sn-ab5l6nrs.googlevideo.com.", - "rr2---sn-ab5l6nrs.gvt1.com.", "rr2---sn-ab5l6nrz.googlevideo.com.", - "rr2---sn-ab5l6nrz.gvt1.com.", "rr2---sn-ab5sznld.googlevideo.com.", "rr2---sn-ab5sznly.googlevideo.com.", "rr2---sn-ab5sznz6.googlevideo.com.", - "rr2---sn-ab5sznz6.gvt1.com.", "rr2---sn-ab5sznzd.googlevideo.com.", - "rr2---sn-ab5sznzd.gvt1.com.", "rr2---sn-ab5sznze.googlevideo.com.", "rr2---sn-ab5sznzk.googlevideo.com.", "rr2---sn-ab5sznzl.googlevideo.com.", @@ -4292,27 +4421,22 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-ab5sznzs.googlevideo.com.", "rr2---sn-ab5sznzy.googlevideo.com.", "rr2---sn-ab5sznzz.googlevideo.com.", - "rr2---sn-ab5sznzz.gvt1.com.", "rr2---sn-aigl6n6s.googlevideo.com.", - "rr2---sn-aigl6n6s.gvt1.com.", "rr2---sn-aigl6ned.googlevideo.com.", "rr2---sn-aigl6nek.googlevideo.com.", "rr2---sn-aigl6ner.googlevideo.com.", - "rr2---sn-aigl6ner.gvt1.com.", "rr2---sn-aigl6ney.googlevideo.com.", "rr2---sn-aigl6nl7.googlevideo.com.", "rr2---sn-aigl6ns6.googlevideo.com.", - "rr2---sn-aigl6nsd.googlevideo.com.", - "rr2---sn-aigl6nsd.gvt1.com.", "rr2---sn-aigl6nsk.googlevideo.com.", "rr2---sn-aigl6nsr.googlevideo.com.", + "rr2---sn-aigl6nsr.gvt1.com.", "rr2---sn-aigl6nz7.googlevideo.com.", "rr2---sn-aigl6nze.googlevideo.com.", "rr2---sn-aigl6nzk.googlevideo.com.", "rr2---sn-aigl6nzl.googlevideo.com.", "rr2---sn-aigl6nzr.googlevideo.com.", "rr2---sn-aigl6nzs.googlevideo.com.", - "rr2---sn-aigl6nzs.gvt1.com.", "rr2---sn-aigzrn76.googlevideo.com.", "rr2---sn-aigzrn7d.googlevideo.com.", "rr2---sn-aigzrn7e.googlevideo.com.", @@ -4329,10 +4453,8 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-aigzrnz7.googlevideo.com.", "rr2---sn-aigzrnze.googlevideo.com.", "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-cvb7lne7.googlevideo.com.", "rr2---sn-cvb7lnee.googlevideo.com.", "rr2---sn-cvb7lnls.googlevideo.com.", @@ -4343,18 +4465,21 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-fpnjoxu-hnol.googlevideo.com.", "rr2---sn-gpuuxg-hxhl.googlevideo.com.", "rr2---sn-gpuuxg-hxhs.googlevideo.com.", - "rr2---sn-gpuuxg-hxhs.gvt1.com.", "rr2---sn-gpuuxg-hxhz.googlevideo.com.", - "rr2---sn-gpuuxg-hxhz.gvt1.com.", + "rr2---sn-hgn7rn7r.googlevideo.com.", + "rr2---sn-hjoj-gq0l.googlevideo.com.", + "rr2---sn-hjoj-jaul.googlevideo.com.", + "rr2---sn-hjoj-poul.googlevideo.com.", "rr2---sn-hoa7kn76.googlevideo.com.", "rr2---sn-hoa7kn7z.googlevideo.com.", "rr2---sn-hoa7rn76.googlevideo.com.", "rr2---sn-hoa7rn7z.googlevideo.com.", "rr2---sn-hp57kn6r.googlevideo.com.", - "rr2---sn-hp57kn6r.gvt1.com.", "rr2---sn-hp57kn6y.googlevideo.com.", "rr2---sn-hp57knd6.googlevideo.com.", "rr2---sn-hp57kndd.googlevideo.com.", + "rr2---sn-hp57kndk.googlevideo.com.", + "rr2---sn-hp57kndk.gvt1.com.", "rr2---sn-hp57kndr.googlevideo.com.", "rr2---sn-hp57knds.googlevideo.com.", "rr2---sn-hp57knds.gvt1.com.", @@ -4362,18 +4487,17 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-hp57kndz.googlevideo.com.", "rr2---sn-hp57yn7r.googlevideo.com.", "rr2---sn-hp57yn7y.googlevideo.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-hp57yns7.gvt1.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-hxgpu-qufs.googlevideo.com.", "rr2---sn-i3b7kn6s.googlevideo.com.", "rr2---sn-i3b7knld.googlevideo.com.", @@ -4381,7 +4505,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-i3b7kns6.googlevideo.com.", "rr2---sn-i3b7knsd.googlevideo.com.", "rr2---sn-i3b7knse.googlevideo.com.", - "rr2---sn-i3b7knsl.googlevideo.com.", "rr2---sn-i3b7knzl.googlevideo.com.", "rr2---sn-i3b7knzs.googlevideo.com.", "rr2---sn-i3belne6.googlevideo.com.", @@ -4392,31 +4515,34 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-i3belnls.googlevideo.com.", "rr2---sn-i3belnlz.googlevideo.com.", "rr2---sn-i3bssn7e.googlevideo.com.", - "rr2---sn-i5f5ppuxa-ioal.googlevideo.com.", "rr2---sn-i5f5ppuxa-ioas.googlevideo.com.", "rr2---sn-jn2pgx4pcxg-w5os.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-n2uxaxjvh-j5xl.googlevideo.com.", + "rr2---sn-n2uxaxjvh-j5xl.gvt1.com.", "rr2---sn-n2uxaxjvh-j5xs.googlevideo.com.", + "rr2---sn-n2uxaxjvh-j5xs.gvt1.com.", "rr2---sn-n4v7snee.googlevideo.com.", "rr2---sn-n4v7sney.googlevideo.com.", "rr2---sn-n4v7snl7.googlevideo.com.", "rr2---sn-n4v7snll.googlevideo.com.", "rr2---sn-n4v7snlr.googlevideo.com.", "rr2---sn-n4v7snls.googlevideo.com.", - "rr2---sn-n4v7snls.gvt1.com.", "rr2---sn-n4v7snly.googlevideo.com.", "rr2---sn-n4v7sns7.googlevideo.com.", "rr2---sn-n4v7snse.googlevideo.com.", - "rr2---sn-n4v7snse.gvt1.com.", "rr2---sn-nh5gujvh-h4xe.googlevideo.com.", "rr2---sn-nh5gujvh-h4xe.gvt1.com.", "rr2---sn-npoe7ndl.googlevideo.com.", @@ -4433,10 +4559,9 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-npoe7nlz.googlevideo.com.", "rr2---sn-npoe7ns6.googlevideo.com.", "rr2---sn-npoe7ns7.googlevideo.com.", - "rr2---sn-npoe7ns7.gvt1.com.", "rr2---sn-npoe7nsd.googlevideo.com.", - "rr2---sn-npoe7nsd.gvt1.com.", "rr2---sn-npoe7nsk.googlevideo.com.", + "rr2---sn-npoe7nsl.googlevideo.com.", "rr2---sn-npoe7nsr.googlevideo.com.", "rr2---sn-npoe7nss.googlevideo.com.", "rr2---sn-npoe7nsy.googlevideo.com.", @@ -4448,7 +4573,6 @@ 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.", @@ -4456,7 +4580,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-npoeens7.googlevideo.com.", "rr2---sn-npoldn76.googlevideo.com.", "rr2---sn-npoldn7d.googlevideo.com.", - "rr2---sn-npoldn7d.gvt1.com.", "rr2---sn-npoldn7e.googlevideo.com.", "rr2---sn-npoldn7l.googlevideo.com.", "rr2---sn-npoldn7s.googlevideo.com.", @@ -4464,29 +4587,26 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-npoldn7y.googlevideo.com.", "rr2---sn-npoldn7z.googlevideo.com.", "rr2---sn-npoldne7.googlevideo.com.", - "rr2---sn-ntq7yney.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-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-nx57ynsl.gvt1.com.", "rr2---sn-nx57ynss.googlevideo.com.", "rr2---sn-nx57ynss.gvt1.com.", "rr2---sn-nx57ynsz.googlevideo.com.", - "rr2---sn-nx57ynsz.gvt1.com.", "rr2---sn-nx5s7n76.googlevideo.com.", "rr2---sn-nx5s7n7d.googlevideo.com.", - "rr2---sn-nx5s7n7d.gvt1.com.", + "rr2---sn-nx5s7n7s.googlevideo.com.", "rr2---sn-nx5s7n7y.googlevideo.com.", - "rr2---sn-nx5s7n7y.gvt1.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.", @@ -4500,26 +4620,22 @@ var FakeECSFQDNs = container.NewMapSet( "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-p5qddn7z.gvt1.com.", "rr2---sn-p5qlsn6l.googlevideo.com.", - "rr2---sn-p5qlsn6l.gvt1.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.", "rr2---sn-p5qlsnrr.googlevideo.com.", - "rr2---sn-p5qlsnrr.gvt1.com.", "rr2---sn-p5qlsny6.googlevideo.com.", "rr2---sn-p5qs7n6d.googlevideo.com.", "rr2---sn-p5qs7nsk.googlevideo.com.", @@ -4530,7 +4646,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-paapovpnjxou0gt-nual.googlevideo.com.", "rr2---sn-paapovpnjxou0gt-nuas.googlevideo.com.", "rr2---sn-pjnpu-5hfe.googlevideo.com.", - "rr2---sn-pjx-nwv6.googlevideo.com.", "rr2---sn-pobpb-poql.googlevideo.com.", "rr2---sn-q4fl6n66.googlevideo.com.", "rr2---sn-q4fl6n66.gvt1.com.", @@ -4541,7 +4656,6 @@ var FakeECSFQDNs = container.NewMapSet( "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.", @@ -4555,7 +4669,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-q4fl6nlz.googlevideo.com.", "rr2---sn-q4fl6ns6.googlevideo.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.", @@ -4563,40 +4676,37 @@ var FakeECSFQDNs = container.NewMapSet( "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-q4fl6nss.gvt1.com.", "rr2---sn-q4fl6nsy.googlevideo.com.", "rr2---sn-q4fl6nz6.googlevideo.com.", + "rr2---sn-q4fl6nz6.gvt1.com.", "rr2---sn-q4fl6nz7.googlevideo.com.", - "rr2---sn-q4fl6nz7.gvt1.com.", "rr2---sn-q4fl6nzy.googlevideo.com.", - "rr2---sn-q4fl6nzy.gvt1.com.", "rr2---sn-q4flrn7k.googlevideo.com.", - "rr2---sn-q4flrn7k.gvt1.com.", "rr2---sn-q4flrn7r.googlevideo.com.", - "rr2---sn-q4flrn7r.gvt1.com.", "rr2---sn-q4flrn7y.googlevideo.com.", "rr2---sn-q4flrne6.googlevideo.com.", - "rr2---sn-q4flrne6.gvt1.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-q4flrney.gvt1.com.", "rr2---sn-q4flrnez.googlevideo.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-q4flrnle.gvt1.com.", + "rr2---sn-q4flrnlz.googlevideo.com.", "rr2---sn-q4flrnsd.googlevideo.com.", "rr2---sn-q4flrnsd.gvt1.com.", "rr2---sn-q4flrnsk.googlevideo.com.", @@ -4611,9 +4721,8 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-q4fzen7r.googlevideo.com.", "rr2---sn-q4fzen7r.gvt1.com.", "rr2---sn-q4fzen7s.googlevideo.com.", - "rr2---sn-q4fzen7y.googlevideo.com.", + "rr2---sn-q4fzen7s.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.", @@ -4624,59 +4733,46 @@ 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-v53a5oqnji-4oul.googlevideo.com.", "rr2---sn-v5goxu-jhi6.googlevideo.com.", "rr2---sn-v5goxu-jhil.googlevideo.com.", "rr2---sn-v5goxu-jhiz.googlevideo.com.", - "rr2---sn-vgqskn66.googlevideo.com.", - "rr2---sn-vgqskn66.gvt1.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-vgqskned.gvt1.com.", "rr2---sn-vgqsknek.googlevideo.com.", - "rr2---sn-vgqsknek.gvt1.com.", "rr2---sn-vgqsknes.googlevideo.com.", "rr2---sn-vgqsknez.googlevideo.com.", - "rr2---sn-vgqsknez.gvt1.com.", "rr2---sn-vgqsknld.googlevideo.com.", "rr2---sn-vgqsknlk.googlevideo.com.", - "rr2---sn-vgqsknlk.gvt1.com.", "rr2---sn-vgqsknll.googlevideo.com.", - "rr2---sn-vgqsknll.gvt1.com.", "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-vgqsknse.googlevideo.com.", "rr2---sn-vgqsknsk.googlevideo.com.", - "rr2---sn-vgqsknsk.gvt1.com.", "rr2---sn-vgqsknz6.googlevideo.com.", - "rr2---sn-vgqsknz6.gvt1.com.", "rr2---sn-vgqsknz7.googlevideo.com.", - "rr2---sn-vgqsknz7.gvt1.com.", "rr2---sn-vgqsknzd.googlevideo.com.", "rr2---sn-vgqsknze.googlevideo.com.", "rr2---sn-vgqsknzk.googlevideo.com.", "rr2---sn-vgqsknzl.googlevideo.com.", - "rr2---sn-vgqsknzl.gvt1.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-vgqsknzz.gvt1.com.", "rr2---sn-vgqsrn66.googlevideo.com.", - "rr2---sn-vgqsrn66.gvt1.com.", "rr2---sn-vgqsrn67.googlevideo.com.", - "rr2---sn-vgqsrn67.gvt1.com.", "rr2---sn-vgqsrn6e.googlevideo.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.", @@ -4687,17 +4783,13 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-vgqsrnl6.googlevideo.com.", "rr2---sn-vgqsrnld.googlevideo.com.", "rr2---sn-vgqsrnlk.googlevideo.com.", - "rr2---sn-vgqsrnlk.gvt1.com.", "rr2---sn-vgqsrnll.googlevideo.com.", "rr2---sn-vgqsrnls.googlevideo.com.", - "rr2---sn-vgqsrnls.gvt1.com.", "rr2---sn-vgqsrnlz.googlevideo.com.", - "rr2---sn-vgqsrnlz.gvt1.com.", "rr2---sn-vgqsrns6.googlevideo.com.", "rr2---sn-vgqsrnsd.googlevideo.com.", "rr2---sn-vgqsrnsr.googlevideo.com.", "rr2---sn-vgqsrnsy.googlevideo.com.", - "rr2---sn-vgqsrnsy.gvt1.com.", "rr2---sn-vgqsrnz6.googlevideo.com.", "rr2---sn-vgqsrnz7.googlevideo.com.", "rr2---sn-vgqsrnz7.gvt1.com.", @@ -4711,20 +4803,10 @@ 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-5hneknek.googlevideo.com.", - "rr2.sn-aigl6ney.googlevideo.com.", "rr2.sn-hp57knds.googlevideo.com.", - "rr2.sn-ntq7yney.googlevideo.com.", - "rr2.sn-nx57ynsk.googlevideo.com.", - "rr2.sn-p5qs7nsk.googlevideo.com.", - "rr2.sn-q4fl6nlz.googlevideo.com.", + "rr2.sn-ntq7yned.googlevideo.com.", "rr2.sn-q4fl6nsk.googlevideo.com.", - "rr2.sn-q4flrnee.googlevideo.com.", - "rr2.sn-q4flrnel.googlevideo.com.", - "rr2.sn-q4flrnl7.googlevideo.com.", - "rr2.sn-q4fzen7l.googlevideo.com.", - "rr2.sn-vgqsrnzz.googlevideo.com.", + "rr2.sn-q4fl6nsr.googlevideo.com.", "rr3---sn-0nnpbo5a-bggl.googlevideo.com.", "rr3---sn-2imern76.googlevideo.com.", "rr3---sn-2imern7d.googlevideo.com.", @@ -4732,7 +4814,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-2napbiu-p5ie.googlevideo.com.", "rr3---sn-2napbiu-p5ie.gvt1.com.", "rr3---sn-30a7rne6.googlevideo.com.", - "rr3---sn-30a7rned.googlevideo.com.", "rr3---sn-30a7rnek.googlevideo.com.", "rr3---sn-30a7rner.googlevideo.com.", "rr3---sn-30a7ynek.googlevideo.com.", @@ -4760,7 +4841,6 @@ 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.", @@ -4776,13 +4856,12 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-4g5ednsk.googlevideo.com.", "rr3---sn-4g5ednsl.googlevideo.com.", "rr3---sn-4g5ednsr.googlevideo.com.", - "rr3---sn-4g5ednsy.googlevideo.com.", + "rr3---sn-4g5ednss.googlevideo.com.", "rr3---sn-4g5ednsz.googlevideo.com.", "rr3---sn-4g5ednz7.googlevideo.com.", "rr3---sn-4g5lzne6.googlevideo.com.", "rr3---sn-4g5lzned.googlevideo.com.", "rr3---sn-4g5lznek.googlevideo.com.", - "rr3---sn-4g5lznek.gvt1.com.", "rr3---sn-4g5lzner.googlevideo.com.", "rr3---sn-4g5lznes.googlevideo.com.", "rr3---sn-4g5lzney.googlevideo.com.", @@ -4792,50 +4871,46 @@ 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.", "rr3---sn-5hne6nsd.googlevideo.com.", - "rr3---sn-5hne6nsd.gvt1.com.", "rr3---sn-5hne6nsk.googlevideo.com.", "rr3---sn-5hne6nsr.googlevideo.com.", "rr3---sn-5hne6nsy.googlevideo.com.", "rr3---sn-5hne6nsz.googlevideo.com.", "rr3---sn-5hne6nz6.googlevideo.com.", - "rr3---sn-5hne6nz6.gvt1.com.", "rr3---sn-5hne6nzd.googlevideo.com.", "rr3---sn-5hne6nzk.googlevideo.com.", "rr3---sn-5hne6nzs.googlevideo.com.", "rr3---sn-5hne6nzy.googlevideo.com.", - "rr3---sn-5hne6nzy.gvt1.com.", "rr3---sn-5hnednss.googlevideo.com.", "rr3---sn-5hnednsz.googlevideo.com.", - "rr3---sn-5hnednsz.gvt1.com.", "rr3---sn-5hnekn76.googlevideo.com.", "rr3---sn-5hnekn7d.googlevideo.com.", "rr3---sn-5hnekn7l.googlevideo.com.", "rr3---sn-5hnekn7s.googlevideo.com.", - "rr3---sn-5hnekn7s.gvt1.com.", "rr3---sn-5hnekn7z.googlevideo.com.", "rr3---sn-5hneknee.googlevideo.com.", "rr3---sn-5hneknek.googlevideo.com.", "rr3---sn-5hneknes.googlevideo.com.", - "rr3---sn-5hneknes.gvt1.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-5uaeznlz.googlevideo.com.", - "rr3---sn-5uaeznrz.googlevideo.com.", "rr3---sn-5uaezns7.googlevideo.com.", "rr3---sn-5uaeznse.googlevideo.com.", "rr3---sn-5uaeznsl.googlevideo.com.", "rr3---sn-5uaeznss.googlevideo.com.", "rr3---sn-5uaezny6.googlevideo.com.", - "rr3---sn-5uaeznys.googlevideo.com.", "rr3---sn-5uaeznyz.googlevideo.com.", "rr3---sn-5uaeznze.googlevideo.com.", "rr3---sn-5ualdnle.googlevideo.com.", @@ -4855,6 +4930,8 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-5ualdnz7.googlevideo.com.", "rr3---sn-5ualdnze.googlevideo.com.", "rr3---sn-8qj-nbo66.googlevideo.com.", + "rr3---sn-8xgp1vo-2iae7.googlevideo.com.", + "rr3---sn-8xgp1vo-a5me.googlevideo.com.", "rr3---sn-8xgp1vo-ab56.googlevideo.com.", "rr3---sn-8xgp1vo-ab5d.googlevideo.com.", "rr3---sn-8xgp1vo-ab5e.googlevideo.com.", @@ -4871,93 +4948,85 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-9gv76n7z.googlevideo.com.", "rr3---sn-9gv7ene6.googlevideo.com.", "rr3---sn-9gv7ened.googlevideo.com.", + "rr3---sn-9gv7zn76.googlevideo.com.", "rr3---sn-9gv7zn7e.googlevideo.com.", "rr3---sn-9gv7zn7r.googlevideo.com.", "rr3---sn-9gv7zn7y.googlevideo.com.", "rr3---sn-a5m7lnl6.googlevideo.com.", "rr3---sn-a5m7lnld.googlevideo.com.", - "rr3---sn-a5m7lnld.gvt1.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-a5mekn6l.gvt1.com.", "rr3---sn-a5mekn6r.googlevideo.com.", - "rr3---sn-a5mekn6r.gvt1.com.", "rr3---sn-a5mekn6s.googlevideo.com.", "rr3---sn-a5mekn6s.gvt1.com.", "rr3---sn-a5mekn6z.googlevideo.com.", - "rr3---sn-a5mekn6z.gvt1.com.", "rr3---sn-a5meknd6.googlevideo.com.", "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.", "rr3---sn-a5meknsd.googlevideo.com.", - "rr3---sn-a5meknsd.gvt1.com.", "rr3---sn-a5meknsy.googlevideo.com.", "rr3---sn-a5meknzk.googlevideo.com.", "rr3---sn-a5meknzl.googlevideo.com.", "rr3---sn-a5meknzr.googlevideo.com.", "rr3---sn-a5meknzs.googlevideo.com.", "rr3---sn-a5mlrnek.googlevideo.com.", - "rr3---sn-a5mlrnek.gvt1.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-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-a5msener.googlevideo.com.", - "rr3---sn-a5msener.gvt1.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-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-ab5l6nrr.gvt1.com.", "rr3---sn-ab5l6nrs.googlevideo.com.", - "rr3---sn-ab5l6nrs.gvt1.com.", "rr3---sn-ab5l6nrz.googlevideo.com.", - "rr3---sn-ab5l6nrz.gvt1.com.", "rr3---sn-ab5sznld.googlevideo.com.", "rr3---sn-ab5sznly.googlevideo.com.", "rr3---sn-ab5sznz6.googlevideo.com.", - "rr3---sn-ab5sznz6.gvt1.com.", "rr3---sn-ab5sznzd.googlevideo.com.", - "rr3---sn-ab5sznzd.gvt1.com.", "rr3---sn-ab5sznze.googlevideo.com.", - "rr3---sn-ab5sznze.gvt1.com.", "rr3---sn-ab5sznzk.googlevideo.com.", "rr3---sn-ab5sznzl.googlevideo.com.", "rr3---sn-ab5sznzr.googlevideo.com.", - "rr3---sn-ab5sznzr.gvt1.com.", "rr3---sn-ab5sznzs.googlevideo.com.", - "rr3---sn-ab5sznzs.gvt1.com.", "rr3---sn-ab5sznzy.googlevideo.com.", + "rr3---sn-ab5sznzz.googlevideo.com.", "rr3---sn-aigl6n6s.googlevideo.com.", "rr3---sn-aigl6ned.googlevideo.com.", "rr3---sn-aigl6nek.googlevideo.com.", "rr3---sn-aigl6ner.googlevideo.com.", + "rr3---sn-aigl6ney.googlevideo.com.", "rr3---sn-aigl6nl7.googlevideo.com.", "rr3---sn-aigl6ns6.googlevideo.com.", "rr3---sn-aigl6nsd.googlevideo.com.", @@ -4967,7 +5036,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-aigl6nze.googlevideo.com.", "rr3---sn-aigl6nze.gvt1.com.", "rr3---sn-aigl6nzk.googlevideo.com.", - "rr3---sn-aigl6nzl.googlevideo.com.", "rr3---sn-aigl6nzr.googlevideo.com.", "rr3---sn-aigl6nzs.googlevideo.com.", "rr3---sn-aigzrn76.googlevideo.com.", @@ -4985,15 +5053,13 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-aigzrnsz.googlevideo.com.", "rr3---sn-aigzrnz7.googlevideo.com.", "rr3---sn-aigzrnze.googlevideo.com.", - "rr3---sn-aj5ua5-5c.googlevideo.com.", - "rr3---sn-ajab55-55.googlevideo.com.", - "rr3---sn-bg5oqxjvh-50nz.googlevideo.com.", "rr3---sn-cvb7lne7.googlevideo.com.", - "rr3---sn-cvb7lnls.googlevideo.com.", "rr3---sn-cvb7lnlz.googlevideo.com.", "rr3---sn-cvb7sn7r.googlevideo.com.", "rr3---sn-gpuuxg-hxhl.googlevideo.com.", - "rr3---sn-gpuuxg-hxhl.gvt1.com.", + "rr3---sn-hjoj-gq0l.googlevideo.com.", + "rr3---sn-hjoj-jaul.googlevideo.com.", + "rr3---sn-hjoj-poul.googlevideo.com.", "rr3---sn-hoa7kn76.googlevideo.com.", "rr3---sn-hoa7kn7z.googlevideo.com.", "rr3---sn-hoa7rn76.googlevideo.com.", @@ -5001,28 +5067,28 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-hp57kn6r.googlevideo.com.", "rr3---sn-hp57kn6r.gvt1.com.", "rr3---sn-hp57kn6y.googlevideo.com.", + "rr3---sn-hp57kn6y.gvt1.com.", "rr3---sn-hp57knd6.googlevideo.com.", "rr3---sn-hp57knd6.gvt1.com.", "rr3---sn-hp57kndd.googlevideo.com.", - "rr3---sn-hp57kndd.gvt1.com.", + "rr3---sn-hp57kndk.googlevideo.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-hp57yn7r.googlevideo.com.", "rr3---sn-hp57yn7y.googlevideo.com.", - "rr3---sn-hp57yn7y.gvt1.com.", "rr3---sn-hp57yne7.googlevideo.com.", - "rr3---sn-hp57yne7.gvt1.com.", "rr3---sn-hp57ynee.googlevideo.com.", + "rr3---sn-hp57ynl6.googlevideo.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-hp57ynsl.googlevideo.com.", - "rr3---sn-hp57ynsl.gvt1.com.", "rr3---sn-hp57ynss.googlevideo.com.", "rr3---sn-i3b7kn6s.googlevideo.com.", "rr3---sn-i3b7knld.googlevideo.com.", @@ -5034,18 +5100,23 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-i3b7knzl.googlevideo.com.", "rr3---sn-i3b7knzs.googlevideo.com.", "rr3---sn-i3belne6.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-i5f5ppuxa-ioal.googlevideo.com.", "rr3---sn-jn2pgx4pcxg-w5os.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.", + "rr3---sn-jvhj5nu-qufz.googlevideo.com.", "rr3---sn-jxopj-n5oe.googlevideo.com.", "rr3---sn-jxopj-nh4e.googlevideo.com.", "rr3---sn-jxopj-nh4e.gvt1.com.", @@ -5056,14 +5127,15 @@ 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-n4v7snse.gvt1.com.", "rr3---sn-npoe7ndl.googlevideo.com.", "rr3---sn-npoe7nds.googlevideo.com.", "rr3---sn-npoe7ne6.googlevideo.com.", "rr3---sn-npoe7ne7.googlevideo.com.", "rr3---sn-npoe7ned.googlevideo.com.", + "rr3---sn-npoe7nek.googlevideo.com.", "rr3---sn-npoe7ner.googlevideo.com.", "rr3---sn-npoe7nes.googlevideo.com.", "rr3---sn-npoe7ney.googlevideo.com.", @@ -5072,9 +5144,9 @@ 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.", "rr3---sn-npoe7nsr.googlevideo.com.", "rr3---sn-npoe7nss.googlevideo.com.", "rr3---sn-npoe7nsy.googlevideo.com.", @@ -5097,29 +5169,27 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-npoldn7e.googlevideo.com.", "rr3---sn-npoldn7l.googlevideo.com.", "rr3---sn-npoldn7s.googlevideo.com.", - "rr3---sn-npoldn7s.gvt1.com.", "rr3---sn-npoldn7y.googlevideo.com.", "rr3---sn-npoldn7z.googlevideo.com.", "rr3---sn-npoldne7.googlevideo.com.", - "rr3---sn-ntqe6nes.googlevideo.com.", "rr3---sn-nx57ynlk.googlevideo.com.", - "rr3---sn-nx57ynlk.gvt1.com.", "rr3---sn-nx57ynsd.googlevideo.com.", + "rr3---sn-nx57ynsd.gvt1.com.", "rr3---sn-nx57ynsk.googlevideo.com.", - "rr3---sn-nx57ynsk.gvt1.com.", "rr3---sn-nx57ynsl.googlevideo.com.", "rr3---sn-nx57ynss.googlevideo.com.", "rr3---sn-nx57ynss.gvt1.com.", + "rr3---sn-nx57ynsz.googlevideo.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-o097znse.googlevideo.com.", "rr3---sn-o097znsk.googlevideo.com.", - "rr3---sn-o097znsk.gvt1.com.", "rr3---sn-o097znsl.googlevideo.com.", "rr3---sn-o097znsr.googlevideo.com.", "rr3---sn-o097znss.googlevideo.com.", @@ -5128,12 +5198,13 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-o097znzd.googlevideo.com.", "rr3---sn-o097znze.googlevideo.com.", "rr3---sn-o097znzk.googlevideo.com.", + "rr3---sn-o097znzr.googlevideo.com.", "rr3---sn-oj5hn5-55.googlevideo.com.", - "rr3---sn-oj5hn5-55.gvt1.com.", "rr3---sn-p5qddn76.googlevideo.com.", "rr3---sn-p5qddn7d.googlevideo.com.", "rr3---sn-p5qddn7k.googlevideo.com.", "rr3---sn-p5qddn7r.googlevideo.com.", + "rr3---sn-p5qddn7z.googlevideo.com.", "rr3---sn-p5qlsn6l.googlevideo.com.", "rr3---sn-p5qlsn76.googlevideo.com.", "rr3---sn-p5qlsn7d.googlevideo.com.", @@ -5148,8 +5219,8 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-p5qlsny6.googlevideo.com.", "rr3---sn-p5qs7n6d.googlevideo.com.", "rr3---sn-p5qs7nsk.googlevideo.com.", - "rr3---sn-p5qs7nsk.gvt1.com.", "rr3---sn-p5qs7nsr.googlevideo.com.", + "rr3---sn-p5qs7nsr.gvt1.com.", "rr3---sn-p5qs7nzk.googlevideo.com.", "rr3---sn-p5qs7nzr.googlevideo.com.", "rr3---sn-p5qs7nzy.googlevideo.com.", @@ -5165,13 +5236,8 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-q4fl6n6z.googlevideo.com.", "rr3---sn-q4fl6n6z.gvt1.com.", "rr3---sn-q4fl6nd7.googlevideo.com.", - "rr3---sn-q4fl6nd7.gvt1.com.", - "rr3---sn-q4fl6nde.googlevideo.com.", - "rr3---sn-q4fl6nde.gvt1.com.", "rr3---sn-q4fl6ndl.googlevideo.com.", - "rr3---sn-q4fl6ndl.gvt1.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.", @@ -5183,63 +5249,50 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-q4fl6nsk.googlevideo.com.", "rr3---sn-q4fl6nsk.gvt1.com.", "rr3---sn-q4fl6nsl.googlevideo.com.", - "rr3---sn-q4fl6nsl.gvt1.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-q4fl6nzy.googlevideo.com.", - "rr3---sn-q4fl6nzy.gvt1.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-q4flrn7y.gvt1.com.", "rr3---sn-q4flrne6.googlevideo.com.", - "rr3---sn-q4flrne6.gvt1.com.", "rr3---sn-q4flrne7.googlevideo.com.", "rr3---sn-q4flrnee.googlevideo.com.", - "rr3---sn-q4flrnee.gvt1.com.", "rr3---sn-q4flrnek.googlevideo.com.", + "rr3---sn-q4flrnek.gvt1.com.", "rr3---sn-q4flrnel.googlevideo.com.", "rr3---sn-q4flrner.googlevideo.com.", - "rr3---sn-q4flrner.gvt1.com.", "rr3---sn-q4flrnes.googlevideo.com.", - "rr3---sn-q4flrnes.gvt1.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-q4flrnss.googlevideo.com.", - "rr3---sn-q4flrnss.gvt1.com.", "rr3---sn-q4fzen7e.googlevideo.com.", - "rr3---sn-q4fzen7e.gvt1.com.", "rr3---sn-q4fzen7l.googlevideo.com.", "rr3---sn-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-q4fzenee.googlevideo.com.", - "rr3---sn-q4fzenee.gvt1.com.", "rr3---sn-qjp5q5-55.googlevideo.com.", "rr3---sn-qxo7rn7k.googlevideo.com.", "rr3---sn-qxo7rn7r.googlevideo.com.", @@ -5250,78 +5303,56 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-u1hp55-5c.googlevideo.com.", "rr3---sn-v5goxu-jhil.googlevideo.com.", "rr3---sn-vgqskn66.googlevideo.com.", - "rr3---sn-vgqskn66.gvt1.com.", "rr3---sn-vgqskn67.googlevideo.com.", - "rr3---sn-vgqskn67.gvt1.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-vgqskne6.gvt1.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.", - "rr3---sn-vgqsknez.gvt1.com.", "rr3---sn-vgqsknld.googlevideo.com.", - "rr3---sn-vgqsknld.gvt1.com.", "rr3---sn-vgqsknlk.googlevideo.com.", - "rr3---sn-vgqsknlk.gvt1.com.", "rr3---sn-vgqsknll.googlevideo.com.", - "rr3---sn-vgqsknll.gvt1.com.", "rr3---sn-vgqsknlr.googlevideo.com.", "rr3---sn-vgqsknly.googlevideo.com.", "rr3---sn-vgqsknlz.googlevideo.com.", - "rr3---sn-vgqsknlz.gvt1.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-vgqsknz6.gvt1.com.", "rr3---sn-vgqsknz7.googlevideo.com.", - "rr3---sn-vgqsknz7.gvt1.com.", "rr3---sn-vgqsknzd.googlevideo.com.", "rr3---sn-vgqsknze.googlevideo.com.", - "rr3---sn-vgqsknze.gvt1.com.", "rr3---sn-vgqsknzk.googlevideo.com.", "rr3---sn-vgqsknzl.googlevideo.com.", - "rr3---sn-vgqsknzl.gvt1.com.", "rr3---sn-vgqsknzr.googlevideo.com.", "rr3---sn-vgqsknzr.gvt1.com.", "rr3---sn-vgqsknzs.googlevideo.com.", - "rr3---sn-vgqsknzs.gvt1.com.", "rr3---sn-vgqsknzy.googlevideo.com.", - "rr3---sn-vgqsknzy.gvt1.com.", "rr3---sn-vgqsknzz.googlevideo.com.", "rr3---sn-vgqsrn66.googlevideo.com.", - "rr3---sn-vgqsrn66.gvt1.com.", "rr3---sn-vgqsrn67.googlevideo.com.", - "rr3---sn-vgqsrn67.gvt1.com.", "rr3---sn-vgqsrn6e.googlevideo.com.", - "rr3---sn-vgqsrn6e.gvt1.com.", "rr3---sn-vgqsrn6l.googlevideo.com.", - "rr3---sn-vgqsrn6l.gvt1.com.", "rr3---sn-vgqsrn6z.googlevideo.com.", "rr3---sn-vgqsrn6z.gvt1.com.", "rr3---sn-vgqsrne6.googlevideo.com.", - "rr3---sn-vgqsrne6.gvt1.com.", "rr3---sn-vgqsrned.googlevideo.com.", "rr3---sn-vgqsrnek.googlevideo.com.", "rr3---sn-vgqsrnes.googlevideo.com.", - "rr3---sn-vgqsrnes.gvt1.com.", "rr3---sn-vgqsrnez.googlevideo.com.", "rr3---sn-vgqsrnl6.googlevideo.com.", - "rr3---sn-vgqsrnl6.gvt1.com.", "rr3---sn-vgqsrnld.googlevideo.com.", - "rr3---sn-vgqsrnld.gvt1.com.", + "rr3---sn-vgqsrnlk.googlevideo.com.", "rr3---sn-vgqsrnll.googlevideo.com.", "rr3---sn-vgqsrnls.googlevideo.com.", "rr3---sn-vgqsrnlz.googlevideo.com.", - "rr3---sn-vgqsrnlz.gvt1.com.", "rr3---sn-vgqsrns6.googlevideo.com.", "rr3---sn-vgqsrnsd.googlevideo.com.", "rr3---sn-vgqsrnsr.googlevideo.com.", @@ -5329,31 +5360,25 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-vgqsrnsy.googlevideo.com.", "rr3---sn-vgqsrnz6.googlevideo.com.", "rr3---sn-vgqsrnz7.googlevideo.com.", - "rr3---sn-vgqsrnz7.gvt1.com.", "rr3---sn-vgqsrnzd.googlevideo.com.", + "rr3---sn-vgqsrnzd.gvt1.com.", "rr3---sn-vgqsrnzk.googlevideo.com.", "rr3---sn-vgqsrnzr.googlevideo.com.", - "rr3---sn-vgqsrnzr.gvt1.com.", "rr3---sn-vgqsrnzs.googlevideo.com.", "rr3---sn-vgqsrnzy.googlevideo.com.", "rr3---sn-vgqsrnzz.googlevideo.com.", - "rr3---sn-vgqsrnzz.gvt1.com.", "rr3.sn-5hnednss.googlevideo.com.", - "rr3.sn-5hnednsz.googlevideo.com.", - "rr3.sn-hp57knds.googlevideo.com.", - "rr3.sn-p5qs7nsk.googlevideo.com.", - "rr3.sn-q4fl6n66.googlevideo.com.", - "rr3.sn-q4fl6nds.googlevideo.com.", - "rr3.sn-q4fl6ndz.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-vgqsknly.googlevideo.com.", - "rr3.sn-vgqsknzd.googlevideo.com.", - "rr3.sn-vgqsrnzz.googlevideo.com.", + "rr3.sn-q4fzenee.googlevideo.com.", "rr4---sn-0nnpbo5a-bggl.googlevideo.com.", "rr4---sn-2imern76.googlevideo.com.", "rr4---sn-2imern7d.googlevideo.com.", - "rr4---sn-2oaig5-55.googlevideo.com.", - "rr4---sn-30a7rne6.googlevideo.com.", + "rr4---sn-2imeyn7k.googlevideo.com.", + "rr4---sn-30a7rned.googlevideo.com.", "rr4---sn-30a7rnek.googlevideo.com.", "rr4---sn-30a7rner.googlevideo.com.", "rr4---sn-30a7ynek.googlevideo.com.", @@ -5397,6 +5422,7 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-4g5ednss.googlevideo.com.", "rr4---sn-4g5ednsy.googlevideo.com.", "rr4---sn-4g5ednsz.googlevideo.com.", + "rr4---sn-4g5ednz7.googlevideo.com.", "rr4---sn-4g5lzne6.googlevideo.com.", "rr4---sn-4g5lzned.googlevideo.com.", "rr4---sn-4g5lznek.googlevideo.com.", @@ -5406,8 +5432,13 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-4g5lznez.googlevideo.com.", "rr4---sn-4g5lznl6.googlevideo.com.", "rr4---sn-4g5lznl7.googlevideo.com.", + "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.", @@ -5415,18 +5446,15 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-5hne6nsk.googlevideo.com.", "rr4---sn-5hne6nsr.googlevideo.com.", "rr4---sn-5hne6nsy.googlevideo.com.", - "rr4---sn-5hne6nsy.gvt1.com.", "rr4---sn-5hne6nsz.googlevideo.com.", "rr4---sn-5hne6nz6.googlevideo.com.", + "rr4---sn-5hne6nz6.gvt1.com.", "rr4---sn-5hne6nzd.googlevideo.com.", "rr4---sn-5hne6nzk.googlevideo.com.", "rr4---sn-5hne6nzs.googlevideo.com.", - "rr4---sn-5hne6nzs.gvt1.com.", "rr4---sn-5hne6nzy.googlevideo.com.", - "rr4---sn-5hne6nzy.gvt1.com.", "rr4---sn-5hnednss.googlevideo.com.", "rr4---sn-5hnednsz.googlevideo.com.", - "rr4---sn-5hnednsz.gvt1.com.", "rr4---sn-5hnekn76.googlevideo.com.", "rr4---sn-5hnekn7d.googlevideo.com.", "rr4---sn-5hnekn7l.googlevideo.com.", @@ -5434,23 +5462,21 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-5hnekn7z.googlevideo.com.", "rr4---sn-5hneknee.googlevideo.com.", "rr4---sn-5hneknek.googlevideo.com.", - "rr4---sn-5hneknek.gvt1.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.", + "rr4---sn-5uaeznel.googlevideo.com.", "rr4---sn-5uaeznes.googlevideo.com.", "rr4---sn-5uaeznez.googlevideo.com.", "rr4---sn-5uaeznl6.googlevideo.com.", "rr4---sn-5uaeznld.googlevideo.com.", - "rr4---sn-5uaeznrz.googlevideo.com.", + "rr4---sn-5uaeznlz.googlevideo.com.", "rr4---sn-5uaezns7.googlevideo.com.", "rr4---sn-5uaeznse.googlevideo.com.", "rr4---sn-5uaeznsl.googlevideo.com.", "rr4---sn-5uaeznss.googlevideo.com.", "rr4---sn-5uaezny6.googlevideo.com.", - "rr4---sn-5uaeznys.googlevideo.com.", "rr4---sn-5uaeznyz.googlevideo.com.", "rr4---sn-5uaeznze.googlevideo.com.", "rr4---sn-5ualdnle.googlevideo.com.", @@ -5469,50 +5495,46 @@ 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-nbo66.googlevideo.com.", + "rr4---sn-8xgp1vo-2iae7.googlevideo.com.", "rr4---sn-8xgp1vo-ab56.googlevideo.com.", "rr4---sn-8xgp1vo-ab5d.googlevideo.com.", "rr4---sn-8xgp1vo-ab5e.googlevideo.com.", "rr4---sn-8xgp1vo-ab5s.googlevideo.com.", "rr4---sn-8xgp1vo-ab5z.googlevideo.com.", "rr4---sn-8xgp1vo-p5ie.googlevideo.com.", - "rr4---sn-8xgp1vo-vgqe.googlevideo.com.", "rr4---sn-8xgp1vo-xfge.googlevideo.com.", "rr4---sn-8xgp1vo-xfgl.googlevideo.com.", "rr4---sn-9gv76n7e.googlevideo.com.", - "rr4---sn-9gv76n7l.googlevideo.com.", "rr4---sn-9gv76n7s.googlevideo.com.", "rr4---sn-9gv76n7z.googlevideo.com.", "rr4---sn-9gv7ene6.googlevideo.com.", "rr4---sn-9gv7ened.googlevideo.com.", + "rr4---sn-9gv7zn76.googlevideo.com.", "rr4---sn-9gv7zn7e.googlevideo.com.", "rr4---sn-9gv7zn7r.googlevideo.com.", + "rr4---sn-9gv7zn7y.googlevideo.com.", "rr4---sn-a5m7lnl6.googlevideo.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-a5mekn6k.gvt1.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-a5mekn6z.googlevideo.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-a5mekndz.gvt1.com.", "rr4---sn-a5meknsd.googlevideo.com.", "rr4---sn-a5meknsy.googlevideo.com.", - "rr4---sn-a5meknzk.googlevideo.com.", - "rr4---sn-a5meknzk.gvt1.com.", "rr4---sn-a5meknzl.googlevideo.com.", - "rr4---sn-a5meknzr.googlevideo.com.", "rr4---sn-a5meknzs.googlevideo.com.", "rr4---sn-a5mlrnek.googlevideo.com.", "rr4---sn-a5mlrnl6.googlevideo.com.", @@ -5520,9 +5542,9 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-a5mlrnll.gvt1.com.", "rr4---sn-a5mlrnls.googlevideo.com.", "rr4---sn-a5mlrnlz.googlevideo.com.", - "rr4---sn-a5mlrnlz.gvt1.com.", "rr4---sn-a5msen76.googlevideo.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.", @@ -5537,10 +5559,8 @@ var FakeECSFQDNs = container.NewMapSet( "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.", @@ -5553,19 +5573,14 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-ab5sznly.googlevideo.com.", "rr4---sn-ab5sznz6.googlevideo.com.", "rr4---sn-ab5sznzd.googlevideo.com.", - "rr4---sn-ab5sznzd.gvt1.com.", "rr4---sn-ab5sznze.googlevideo.com.", - "rr4---sn-ab5sznze.gvt1.com.", "rr4---sn-ab5sznzk.googlevideo.com.", - "rr4---sn-ab5sznzk.gvt1.com.", + "rr4---sn-ab5sznzl.googlevideo.com.", "rr4---sn-ab5sznzr.googlevideo.com.", - "rr4---sn-ab5sznzr.gvt1.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-aigl6n6s.gvt1.com.", "rr4---sn-aigl6nek.googlevideo.com.", "rr4---sn-aigl6ner.googlevideo.com.", "rr4---sn-aigl6ney.googlevideo.com.", @@ -5574,6 +5589,7 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-aigl6nsd.googlevideo.com.", "rr4---sn-aigl6nsk.googlevideo.com.", "rr4---sn-aigl6nsr.googlevideo.com.", + "rr4---sn-aigl6nz7.googlevideo.com.", "rr4---sn-aigl6nze.googlevideo.com.", "rr4---sn-aigl6nzk.googlevideo.com.", "rr4---sn-aigl6nzl.googlevideo.com.", @@ -5594,15 +5610,13 @@ 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-ajab55-55.gvt1.com.", - "rr4---sn-bg5oqxjvh-50nz.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.", + "rr4---sn-hjoj-gq0l.googlevideo.com.", "rr4---sn-hoa7kn76.googlevideo.com.", "rr4---sn-hoa7kn7z.googlevideo.com.", "rr4---sn-hoa7rn76.googlevideo.com.", @@ -5611,28 +5625,27 @@ var FakeECSFQDNs = container.NewMapSet( "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-hp57kndd.gvt1.com.", - "rr4---sn-hp57kndk.googlevideo.com.", "rr4---sn-hp57kndr.googlevideo.com.", + "rr4---sn-hp57kndr.gvt1.com.", "rr4---sn-hp57knds.googlevideo.com.", "rr4---sn-hp57knds.gvt1.com.", "rr4---sn-hp57kndy.googlevideo.com.", + "rr4---sn-hp57kndy.gvt1.com.", "rr4---sn-hp57kndz.googlevideo.com.", "rr4---sn-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-hp57ynl6.gvt1.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-i3b7kn6s.googlevideo.com.", "rr4---sn-i3b7knld.googlevideo.com.", "rr4---sn-i3b7knlk.googlevideo.com.", @@ -5643,7 +5656,6 @@ 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.", @@ -5652,10 +5664,14 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-i3bssn7e.googlevideo.com.", "rr4---sn-jn2pgx4pcxg-w5os.googlevideo.com.", "rr4---sn-jvhj5nu-2iae.googlevideo.com.", + "rr4---sn-jvhj5nu-2ial.googlevideo.com.", + "rr4---sn-jvhj5nu-2ias.googlevideo.com.", "rr4---sn-jvhj5nu-nh4e.googlevideo.com.", "rr4---sn-jvhj5nu-nh4l.googlevideo.com.", "rr4---sn-jvhj5nu-nh4s.googlevideo.com.", "rr4---sn-jvhj5nu-nh4z.googlevideo.com.", + "rr4---sn-jvhj5nu-qufe.googlevideo.com.", + "rr4---sn-jvhj5nu-qufz.googlevideo.com.", "rr4---sn-jxopj-n5oe.googlevideo.com.", "rr4---sn-jxopj-nh4e.googlevideo.com.", "rr4---sn-jxopj-nh4e.gvt1.com.", @@ -5669,8 +5685,8 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-n4v7sns7.googlevideo.com.", "rr4---sn-n4v7snse.googlevideo.com.", "rr4---sn-npoe7ndl.googlevideo.com.", + "rr4---sn-npoe7ndl.gvt1.com.", "rr4---sn-npoe7nds.googlevideo.com.", - "rr4---sn-npoe7ne6.googlevideo.com.", "rr4---sn-npoe7ne7.googlevideo.com.", "rr4---sn-npoe7ned.googlevideo.com.", "rr4---sn-npoe7nek.googlevideo.com.", @@ -5681,13 +5697,15 @@ 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.", "rr4---sn-npoe7nsr.googlevideo.com.", "rr4---sn-npoe7nss.googlevideo.com.", "rr4---sn-npoe7nsy.googlevideo.com.", + "rr4---sn-npoe7nz7.googlevideo.com.", "rr4---sn-npoeene6.googlevideo.com.", "rr4---sn-npoeened.googlevideo.com.", "rr4---sn-npoeenee.googlevideo.com.", @@ -5695,28 +5713,27 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-npoeener.googlevideo.com.", "rr4---sn-npoeeney.googlevideo.com.", "rr4---sn-npoeenez.googlevideo.com.", + "rr4---sn-npoeenl7.googlevideo.com.", "rr4---sn-npoeenle.googlevideo.com.", "rr4---sn-npoeenlk.googlevideo.com.", "rr4---sn-npoeenll.googlevideo.com.", "rr4---sn-npoeenly.googlevideo.com.", "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.", "rr4---sn-npoldn7l.googlevideo.com.", "rr4---sn-npoldn7s.googlevideo.com.", - "rr4---sn-npoldn7s.gvt1.com.", "rr4---sn-npoldn7y.googlevideo.com.", "rr4---sn-npoldn7z.googlevideo.com.", "rr4---sn-npoldne7.googlevideo.com.", - "rr4---sn-ntq7yner.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-nx57ynse.gvt1.com.", "rr4---sn-nx57ynsk.googlevideo.com.", - "rr4---sn-nx57ynsk.gvt1.com.", "rr4---sn-nx57ynsl.googlevideo.com.", "rr4---sn-nx57ynss.googlevideo.com.", "rr4---sn-nx57ynsz.googlevideo.com.", @@ -5726,7 +5743,6 @@ 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.", @@ -5734,7 +5750,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-o097znsl.googlevideo.com.", "rr4---sn-o097znsr.googlevideo.com.", "rr4---sn-o097znss.googlevideo.com.", - "rr4---sn-o097znss.gvt1.com.", "rr4---sn-o097znsz.googlevideo.com.", "rr4---sn-o097znz7.googlevideo.com.", "rr4---sn-o097znzd.googlevideo.com.", @@ -5745,13 +5760,9 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-p5qddn76.googlevideo.com.", "rr4---sn-p5qddn7d.googlevideo.com.", "rr4---sn-p5qddn7k.googlevideo.com.", - "rr4---sn-p5qddn7k.gvt1.com.", "rr4---sn-p5qddn7r.googlevideo.com.", - "rr4---sn-p5qddn7r.gvt1.com.", - "rr4---sn-p5qddn7z.googlevideo.com.", "rr4---sn-p5qlsn6l.googlevideo.com.", "rr4---sn-p5qlsn76.googlevideo.com.", - "rr4---sn-p5qlsn76.gvt1.com.", "rr4---sn-p5qlsn7d.googlevideo.com.", "rr4---sn-p5qlsn7l.googlevideo.com.", "rr4---sn-p5qlsn7s.googlevideo.com.", @@ -5759,33 +5770,26 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-p5qlsndk.googlevideo.com.", "rr4---sn-p5qlsndz.googlevideo.com.", "rr4---sn-p5qlsnrl.googlevideo.com.", - "rr4---sn-p5qlsnrl.gvt1.com.", "rr4---sn-p5qlsnrr.googlevideo.com.", "rr4---sn-p5qlsny6.googlevideo.com.", "rr4---sn-p5qs7n6d.googlevideo.com.", "rr4---sn-p5qs7nsk.googlevideo.com.", - "rr4---sn-p5qs7nsk.gvt1.com.", "rr4---sn-p5qs7nsr.googlevideo.com.", "rr4---sn-p5qs7nzk.googlevideo.com.", "rr4---sn-p5qs7nzr.googlevideo.com.", "rr4---sn-p5qs7nzy.googlevideo.com.", "rr4---sn-q4fl6n66.googlevideo.com.", + "rr4---sn-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-q4fl6n6z.gvt1.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.", @@ -5794,66 +5798,54 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-q4fl6ns6.googlevideo.com.", "rr4---sn-q4fl6ns6.gvt1.com.", "rr4---sn-q4fl6ns7.googlevideo.com.", - "rr4---sn-q4fl6ns7.gvt1.com.", "rr4---sn-q4fl6nsd.googlevideo.com.", "rr4---sn-q4fl6nsd.gvt1.com.", "rr4---sn-q4fl6nsk.googlevideo.com.", "rr4---sn-q4fl6nsl.googlevideo.com.", - "rr4---sn-q4fl6nsl.gvt1.com.", "rr4---sn-q4fl6nsr.googlevideo.com.", - "rr4---sn-q4fl6nsr.gvt1.com.", "rr4---sn-q4fl6nss.googlevideo.com.", "rr4---sn-q4fl6nsy.googlevideo.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-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-q4flrner.googlevideo.com.", - "rr4---sn-q4flrner.gvt1.com.", "rr4---sn-q4flrnes.googlevideo.com.", "rr4---sn-q4flrney.googlevideo.com.", + "rr4---sn-q4flrney.gvt1.com.", "rr4---sn-q4flrnez.googlevideo.com.", "rr4---sn-q4flrnl6.googlevideo.com.", - "rr4---sn-q4flrnl6.gvt1.com.", "rr4---sn-q4flrnl7.googlevideo.com.", - "rr4---sn-q4flrnl7.gvt1.com.", "rr4---sn-q4flrnld.googlevideo.com.", - "rr4---sn-q4flrnld.gvt1.com.", "rr4---sn-q4flrnle.googlevideo.com.", "rr4---sn-q4flrnlz.googlevideo.com.", - "rr4---sn-q4flrnlz.gvt1.com.", "rr4---sn-q4flrnsd.googlevideo.com.", - "rr4---sn-q4flrnsd.gvt1.com.", "rr4---sn-q4flrnsk.googlevideo.com.", - "rr4---sn-q4flrnsk.gvt1.com.", "rr4---sn-q4flrnsl.googlevideo.com.", - "rr4---sn-q4flrnsl.gvt1.com.", "rr4---sn-q4flrnss.googlevideo.com.", + "rr4---sn-q4flrnss.gvt1.com.", "rr4---sn-q4fzen7e.googlevideo.com.", "rr4---sn-q4fzen7e.gvt1.com.", "rr4---sn-q4fzen7l.googlevideo.com.", "rr4---sn-q4fzen7l.gvt1.com.", "rr4---sn-q4fzen7r.googlevideo.com.", - "rr4---sn-q4fzen7r.gvt1.com.", "rr4---sn-q4fzen7s.googlevideo.com.", - "rr4---sn-q4fzen7s.gvt1.com.", "rr4---sn-q4fzen7y.googlevideo.com.", - "rr4---sn-q4fzen7y.gvt1.com.", "rr4---sn-q4fzene7.googlevideo.com.", "rr4---sn-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.", @@ -5866,51 +5858,42 @@ 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.", "rr4---sn-vgqskn6z.googlevideo.com.", "rr4---sn-vgqskne6.googlevideo.com.", - "rr4---sn-vgqskne6.gvt1.com.", + "rr4---sn-vgqskned.googlevideo.com.", "rr4---sn-vgqsknek.googlevideo.com.", + "rr4---sn-vgqsknes.googlevideo.com.", + "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-vgqsknll.gvt1.com.", "rr4---sn-vgqsknlr.googlevideo.com.", - "rr4---sn-vgqsknlr.gvt1.com.", "rr4---sn-vgqsknls.googlevideo.com.", - "rr4---sn-vgqsknls.gvt1.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-vgqsknse.gvt1.com.", "rr4---sn-vgqsknsk.googlevideo.com.", - "rr4---sn-vgqsknsk.gvt1.com.", "rr4---sn-vgqsknz6.googlevideo.com.", - "rr4---sn-vgqsknz6.gvt1.com.", "rr4---sn-vgqsknz7.googlevideo.com.", - "rr4---sn-vgqsknz7.gvt1.com.", "rr4---sn-vgqsknzd.googlevideo.com.", "rr4---sn-vgqsknze.googlevideo.com.", - "rr4---sn-vgqsknze.gvt1.com.", "rr4---sn-vgqsknzk.googlevideo.com.", - "rr4---sn-vgqsknzk.gvt1.com.", "rr4---sn-vgqsknzl.googlevideo.com.", - "rr4---sn-vgqsknzl.gvt1.com.", "rr4---sn-vgqsknzr.googlevideo.com.", - "rr4---sn-vgqsknzr.gvt1.com.", "rr4---sn-vgqsknzs.googlevideo.com.", "rr4---sn-vgqsknzy.googlevideo.com.", - "rr4---sn-vgqsknzy.gvt1.com.", "rr4---sn-vgqsknzz.googlevideo.com.", "rr4---sn-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-vgqsrn6l.gvt1.com.", "rr4---sn-vgqsrn6z.googlevideo.com.", "rr4---sn-vgqsrn6z.gvt1.com.", "rr4---sn-vgqsrne6.googlevideo.com.", @@ -5918,23 +5901,17 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-vgqsrnek.googlevideo.com.", "rr4---sn-vgqsrnes.googlevideo.com.", "rr4---sn-vgqsrnez.googlevideo.com.", - "rr4---sn-vgqsrnez.gvt1.com.", - "rr4---sn-vgqsrnl6.googlevideo.com.", - "rr4---sn-vgqsrnl6.gvt1.com.", + "rr4---sn-vgqsrnld.googlevideo.com.", "rr4---sn-vgqsrnlk.googlevideo.com.", "rr4---sn-vgqsrnll.googlevideo.com.", "rr4---sn-vgqsrnls.googlevideo.com.", "rr4---sn-vgqsrnlz.googlevideo.com.", - "rr4---sn-vgqsrnlz.gvt1.com.", "rr4---sn-vgqsrns6.googlevideo.com.", - "rr4---sn-vgqsrns6.gvt1.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-vgqsrnz7.gvt1.com.", "rr4---sn-vgqsrnzd.googlevideo.com.", "rr4---sn-vgqsrnzk.googlevideo.com.", "rr4---sn-vgqsrnzr.googlevideo.com.", @@ -5942,22 +5919,20 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-vgqsrnzy.googlevideo.com.", "rr4---sn-vgqsrnzy.gvt1.com.", "rr4---sn-vgqsrnzz.googlevideo.com.", - "rr4---sn-vgqsrnzz.gvt1.com.", - "rr4.sn-hp57knds.googlevideo.com.", - "rr4.sn-ntq7yner.googlevideo.com.", - "rr4.sn-nx57ynsk.googlevideo.com.", - "rr4.sn-p5qs7nsk.googlevideo.com.", - "rr4.sn-q4fl6nd7.googlevideo.com.", - "rr4.sn-q4fl6nss.googlevideo.com.", - "rr4.sn-q4fzen7e.googlevideo.com.", - "rr4.sn-q4fzen7l.googlevideo.com.", - "rr4.sn-q4fzen7r.googlevideo.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.", "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.", @@ -5971,9 +5946,9 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-4g5e6nsk.googlevideo.com.", "rr5---sn-4g5e6nsr.googlevideo.com.", "rr5---sn-4g5e6nss.googlevideo.com.", + "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.", @@ -5985,19 +5960,23 @@ 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.", "rr5---sn-4g5ednly.googlevideo.com.", "rr5---sn-4g5edns6.googlevideo.com.", + "rr5---sn-4g5edns7.googlevideo.com.", "rr5---sn-4g5ednsd.googlevideo.com.", "rr5---sn-4g5ednse.googlevideo.com.", "rr5---sn-4g5ednsk.googlevideo.com.", "rr5---sn-4g5ednsl.googlevideo.com.", "rr5---sn-4g5ednsr.googlevideo.com.", + "rr5---sn-4g5ednss.googlevideo.com.", "rr5---sn-4g5ednsy.googlevideo.com.", "rr5---sn-4g5ednsz.googlevideo.com.", "rr5---sn-4g5ednz7.googlevideo.com.", @@ -6010,52 +5989,49 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-4g5lznez.googlevideo.com.", "rr5---sn-4g5lznl6.googlevideo.com.", "rr5---sn-4g5lznl7.googlevideo.com.", + "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-5hne6n6l.gvt1.com.", "rr5---sn-5hne6ns6.googlevideo.com.", "rr5---sn-5hne6nsk.googlevideo.com.", "rr5---sn-5hne6nsr.googlevideo.com.", "rr5---sn-5hne6nsy.googlevideo.com.", "rr5---sn-5hne6nsz.googlevideo.com.", "rr5---sn-5hne6nz6.googlevideo.com.", - "rr5---sn-5hne6nz6.gvt1.com.", "rr5---sn-5hne6nzd.googlevideo.com.", "rr5---sn-5hne6nzk.googlevideo.com.", "rr5---sn-5hne6nzs.googlevideo.com.", "rr5---sn-5hne6nzy.googlevideo.com.", - "rr5---sn-5hne6nzy.gvt1.com.", "rr5---sn-5hnednss.googlevideo.com.", "rr5---sn-5hnednsz.googlevideo.com.", - "rr5---sn-5hnednsz.gvt1.com.", "rr5---sn-5hnekn76.googlevideo.com.", "rr5---sn-5hnekn7d.googlevideo.com.", "rr5---sn-5hnekn7l.googlevideo.com.", - "rr5---sn-5hnekn7l.gvt1.com.", "rr5---sn-5hnekn7s.googlevideo.com.", "rr5---sn-5hnekn7z.googlevideo.com.", "rr5---sn-5hneknee.googlevideo.com.", - "rr5---sn-5hneknee.gvt1.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-5uaeznes.googlevideo.com.", "rr5---sn-5uaeznez.googlevideo.com.", + "rr5---sn-5uaeznl6.googlevideo.com.", "rr5---sn-5uaeznld.googlevideo.com.", "rr5---sn-5uaeznlz.googlevideo.com.", - "rr5---sn-5uaeznrz.googlevideo.com.", "rr5---sn-5uaezns7.googlevideo.com.", "rr5---sn-5uaeznse.googlevideo.com.", "rr5---sn-5uaeznsl.googlevideo.com.", "rr5---sn-5uaeznss.googlevideo.com.", "rr5---sn-5uaezny6.googlevideo.com.", - "rr5---sn-5uaeznys.googlevideo.com.", "rr5---sn-5uaeznyz.googlevideo.com.", "rr5---sn-5uaeznze.googlevideo.com.", "rr5---sn-5ualdnle.googlevideo.com.", @@ -6074,7 +6050,9 @@ 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.", "rr5---sn-8xgp1vo-ab56.googlevideo.com.", "rr5---sn-8xgp1vo-ab5d.googlevideo.com.", @@ -6086,7 +6064,6 @@ var FakeECSFQDNs = container.NewMapSet( "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.", @@ -6099,67 +6076,54 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-a5mekn6d.googlevideo.com.", "rr5---sn-a5mekn6d.gvt1.com.", "rr5---sn-a5mekn6k.googlevideo.com.", - "rr5---sn-a5mekn6k.gvt1.com.", "rr5---sn-a5mekn6l.googlevideo.com.", - "rr5---sn-a5mekn6l.gvt1.com.", "rr5---sn-a5mekn6r.googlevideo.com.", - "rr5---sn-a5mekn6r.gvt1.com.", "rr5---sn-a5mekn6s.googlevideo.com.", - "rr5---sn-a5mekn6s.gvt1.com.", "rr5---sn-a5mekn6z.googlevideo.com.", - "rr5---sn-a5mekn6z.gvt1.com.", "rr5---sn-a5meknd6.googlevideo.com.", - "rr5---sn-a5meknd6.gvt1.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-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-a5meknzr.gvt1.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-a5mlrnlz.googlevideo.com.", "rr5---sn-a5mlrnlz.gvt1.com.", "rr5---sn-a5msen76.googlevideo.com.", - "rr5---sn-a5msen76.gvt1.com.", "rr5---sn-a5msen7l.googlevideo.com.", "rr5---sn-a5msen7s.googlevideo.com.", "rr5---sn-a5msen7z.googlevideo.com.", "rr5---sn-a5msenek.googlevideo.com.", - "rr5---sn-a5msenek.gvt1.com.", "rr5---sn-a5msener.googlevideo.com.", + "rr5---sn-a5msener.gvt1.com.", "rr5---sn-a5msenes.googlevideo.com.", - "rr5---sn-a5msenes.gvt1.com.", "rr5---sn-a5msenl7.googlevideo.com.", - "rr5---sn-a5msenl7.gvt1.com.", "rr5---sn-a5msenle.googlevideo.com.", - "rr5---sn-a5msenle.gvt1.com.", "rr5---sn-a5msenll.googlevideo.com.", "rr5---sn-a5msenll.gvt1.com.", "rr5---sn-ab5l6ndr.googlevideo.com.", "rr5---sn-ab5l6ndy.googlevideo.com.", - "rr5---sn-ab5l6ndy.gvt1.com.", "rr5---sn-ab5l6nk6.googlevideo.com.", - "rr5---sn-ab5l6nk6.gvt1.com.", "rr5---sn-ab5l6nkd.googlevideo.com.", - "rr5---sn-ab5l6nkd.gvt1.com.", "rr5---sn-ab5l6nr6.googlevideo.com.", "rr5---sn-ab5l6nr6.gvt1.com.", "rr5---sn-ab5l6nrd.googlevideo.com.", - "rr5---sn-ab5l6nrd.gvt1.com.", "rr5---sn-ab5l6nrk.googlevideo.com.", + "rr5---sn-ab5l6nrk.gvt1.com.", "rr5---sn-ab5l6nrl.googlevideo.com.", - "rr5---sn-ab5l6nrl.gvt1.com.", "rr5---sn-ab5l6nrr.googlevideo.com.", "rr5---sn-ab5l6nrs.googlevideo.com.", "rr5---sn-ab5l6nrs.gvt1.com.", @@ -6168,29 +6132,25 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-ab5sznld.googlevideo.com.", "rr5---sn-ab5sznly.googlevideo.com.", "rr5---sn-ab5sznz6.googlevideo.com.", - "rr5---sn-ab5sznz6.gvt1.com.", "rr5---sn-ab5sznzd.googlevideo.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-ab5sznzl.gvt1.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-ab5sznzz.gvt1.com.", "rr5---sn-aigl6n6s.googlevideo.com.", "rr5---sn-aigl6ned.googlevideo.com.", "rr5---sn-aigl6nek.googlevideo.com.", "rr5---sn-aigl6ner.googlevideo.com.", - "rr5---sn-aigl6ney.googlevideo.com.", + "rr5---sn-aigl6ner.gvt1.com.", "rr5---sn-aigl6nl7.googlevideo.com.", - "rr5---sn-aigl6ns6.googlevideo.com.", "rr5---sn-aigl6nsd.googlevideo.com.", - "rr5---sn-aigl6nsd.gvt1.com.", "rr5---sn-aigl6nsk.googlevideo.com.", "rr5---sn-aigl6nsr.googlevideo.com.", "rr5---sn-aigl6nz7.googlevideo.com.", @@ -6216,7 +6176,6 @@ 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-cvb7lne7.googlevideo.com.", "rr5---sn-cvb7lnee.googlevideo.com.", "rr5---sn-cvb7lnls.googlevideo.com.", @@ -6228,37 +6187,31 @@ var FakeECSFQDNs = container.NewMapSet( "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-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-hp57kndz.gvt1.com.", "rr5---sn-hp57yn7r.googlevideo.com.", + "rr5---sn-hp57yn7y.googlevideo.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-hp57ynly.gvt1.com.", - "rr5---sn-hp57yns7.googlevideo.com.", "rr5---sn-hp57ynse.googlevideo.com.", + "rr5---sn-hp57ynse.gvt1.com.", "rr5---sn-hp57ynsl.googlevideo.com.", - "rr5---sn-hp57ynsl.gvt1.com.", "rr5---sn-hp57ynss.googlevideo.com.", "rr5---sn-i3b7kn6s.googlevideo.com.", "rr5---sn-i3b7knld.googlevideo.com.", "rr5---sn-i3b7knlk.googlevideo.com.", "rr5---sn-i3b7kns6.googlevideo.com.", - "rr5---sn-i3b7knsd.googlevideo.com.", "rr5---sn-i3b7knse.googlevideo.com.", "rr5---sn-i3b7knsl.googlevideo.com.", "rr5---sn-i3b7knzl.googlevideo.com.", @@ -6272,9 +6225,11 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-i3belnlz.googlevideo.com.", "rr5---sn-i3bssn7e.googlevideo.com.", "rr5---sn-jn2pgx4pcxg-w5os.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-qufs.googlevideo.com.", "rr5---sn-jxopj-n5oe.googlevideo.com.", "rr5---sn-jxopj-nh4e.googlevideo.com.", "rr5---sn-jxopj-nh4e.gvt1.com.", @@ -6286,7 +6241,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-n4v7snls.googlevideo.com.", "rr5---sn-n4v7snly.googlevideo.com.", "rr5---sn-n4v7sns7.googlevideo.com.", - "rr5---sn-n4v7sns7.gvt1.com.", "rr5---sn-n4v7snse.googlevideo.com.", "rr5---sn-npoe7ndl.googlevideo.com.", "rr5---sn-npoe7nds.googlevideo.com.", @@ -6294,20 +6248,21 @@ 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.", "rr5---sn-npoe7nl6.googlevideo.com.", "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-npoe7nsd.gvt1.com.", "rr5---sn-npoe7nsk.googlevideo.com.", + "rr5---sn-npoe7nsl.googlevideo.com.", "rr5---sn-npoe7nsr.googlevideo.com.", "rr5---sn-npoe7nss.googlevideo.com.", - "rr5---sn-npoe7nss.gvt1.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.", @@ -6316,50 +6271,46 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-npoeener.googlevideo.com.", "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-npoldn7e.googlevideo.com.", "rr5---sn-npoldn7l.googlevideo.com.", + "rr5---sn-npoldn7l.gvt1.com.", "rr5---sn-npoldn7s.googlevideo.com.", - "rr5---sn-npoldn7s.gvt1.com.", "rr5---sn-npoldn7y.googlevideo.com.", "rr5---sn-npoldn7z.googlevideo.com.", "rr5---sn-npoldne7.googlevideo.com.", - "rr5---sn-ntqe6nel.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-nx57ynlk.gvt1.com.", "rr5---sn-nx57ynsd.googlevideo.com.", - "rr5---sn-nx57ynsd.gvt1.com.", "rr5---sn-nx57ynse.googlevideo.com.", - "rr5---sn-nx57ynse.gvt1.com.", "rr5---sn-nx57ynsk.googlevideo.com.", "rr5---sn-nx57ynsk.gvt1.com.", "rr5---sn-nx57ynsl.googlevideo.com.", "rr5---sn-nx57ynss.googlevideo.com.", - "rr5---sn-nx57ynss.gvt1.com.", "rr5---sn-nx57ynsz.googlevideo.com.", "rr5---sn-nx5s7n76.googlevideo.com.", - "rr5---sn-nx5s7n76.gvt1.com.", "rr5---sn-nx5s7n7d.googlevideo.com.", "rr5---sn-nx5s7n7y.googlevideo.com.", - "rr5---sn-nx5s7n7y.gvt1.com.", "rr5---sn-nx5s7n7z.googlevideo.com.", - "rr5---sn-nx5s7n7z.gvt1.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.", "rr5---sn-o097znsl.googlevideo.com.", "rr5---sn-o097znsr.googlevideo.com.", - "rr5---sn-o097znsr.gvt1.com.", "rr5---sn-o097znss.googlevideo.com.", "rr5---sn-o097znsz.googlevideo.com.", "rr5---sn-o097znz7.googlevideo.com.", @@ -6376,37 +6327,32 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-p5qlsn6l.googlevideo.com.", "rr5---sn-p5qlsn76.googlevideo.com.", "rr5---sn-p5qlsn7d.googlevideo.com.", - "rr5---sn-p5qlsn7d.gvt1.com.", "rr5---sn-p5qlsn7l.googlevideo.com.", "rr5---sn-p5qlsn7s.googlevideo.com.", "rr5---sn-p5qlsnd6.googlevideo.com.", "rr5---sn-p5qlsndk.googlevideo.com.", "rr5---sn-p5qlsndr.googlevideo.com.", - "rr5---sn-p5qlsndr.gvt1.com.", "rr5---sn-p5qlsndz.googlevideo.com.", "rr5---sn-p5qlsnrl.googlevideo.com.", "rr5---sn-p5qlsnrr.googlevideo.com.", "rr5---sn-p5qlsny6.googlevideo.com.", "rr5---sn-p5qs7n6d.googlevideo.com.", - "rr5---sn-p5qs7n6d.gvt1.com.", "rr5---sn-p5qs7nsk.googlevideo.com.", - "rr5---sn-p5qs7nsk.gvt1.com.", "rr5---sn-p5qs7nsr.googlevideo.com.", "rr5---sn-p5qs7nzk.googlevideo.com.", "rr5---sn-p5qs7nzr.googlevideo.com.", "rr5---sn-p5qs7nzy.googlevideo.com.", "rr5---sn-q4fl6n66.googlevideo.com.", "rr5---sn-q4fl6n6d.googlevideo.com.", + "rr5---sn-q4fl6n6d.gvt1.com.", "rr5---sn-q4fl6n6r.googlevideo.com.", - "rr5---sn-q4fl6n6r.gvt1.com.", "rr5---sn-q4fl6n6s.googlevideo.com.", - "rr5---sn-q4fl6n6s.gvt1.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.", @@ -6414,52 +6360,39 @@ var FakeECSFQDNs = container.NewMapSet( "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-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-q4fl6nsr.googlevideo.com.", - "rr5---sn-q4fl6nsr.gvt1.com.", "rr5---sn-q4fl6nss.googlevideo.com.", - "rr5---sn-q4fl6nss.gvt1.com.", "rr5---sn-q4fl6nsy.googlevideo.com.", - "rr5---sn-q4fl6nsy.gvt1.com.", "rr5---sn-q4fl6nz6.googlevideo.com.", "rr5---sn-q4fl6nz6.gvt1.com.", "rr5---sn-q4fl6nz7.googlevideo.com.", - "rr5---sn-q4fl6nz7.gvt1.com.", "rr5---sn-q4fl6nzy.googlevideo.com.", - "rr5---sn-q4fl6nzy.gvt1.com.", "rr5---sn-q4flrn7k.googlevideo.com.", "rr5---sn-q4flrn7k.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-q4flrnee.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-q4flrnes.gvt1.com.", "rr5---sn-q4flrney.googlevideo.com.", "rr5---sn-q4flrnez.googlevideo.com.", - "rr5---sn-q4flrnl7.googlevideo.com.", + "rr5---sn-q4flrnl6.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.", @@ -6472,10 +6405,10 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-q4fzen7s.googlevideo.com.", "rr5---sn-q4fzen7s.gvt1.com.", "rr5---sn-q4fzen7y.googlevideo.com.", + "rr5---sn-q4fzen7y.gvt1.com.", "rr5---sn-q4fzene7.googlevideo.com.", "rr5---sn-q4fzene7.gvt1.com.", "rr5---sn-q4fzenee.googlevideo.com.", - "rr5---sn-q4fzenee.gvt1.com.", "rr5---sn-qjp5q5-55.googlevideo.com.", "rr5---sn-qxo7rn7k.googlevideo.com.", "rr5---sn-qxo7rn7r.googlevideo.com.", @@ -6485,100 +6418,83 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-qxoednee.googlevideo.com.", "rr5---sn-u1hp55-5c.googlevideo.com.", "rr5---sn-vgqskn66.googlevideo.com.", - "rr5---sn-vgqskn66.gvt1.com.", "rr5---sn-vgqskn67.googlevideo.com.", "rr5---sn-vgqskn6d.googlevideo.com.", - "rr5---sn-vgqskn6d.gvt1.com.", "rr5---sn-vgqskn6s.googlevideo.com.", "rr5---sn-vgqskn6z.googlevideo.com.", "rr5---sn-vgqskne6.googlevideo.com.", "rr5---sn-vgqskned.googlevideo.com.", "rr5---sn-vgqsknek.googlevideo.com.", - "rr5---sn-vgqsknek.gvt1.com.", "rr5---sn-vgqsknes.googlevideo.com.", "rr5---sn-vgqsknez.googlevideo.com.", - "rr5---sn-vgqsknez.gvt1.com.", "rr5---sn-vgqsknld.googlevideo.com.", "rr5---sn-vgqsknlk.googlevideo.com.", "rr5---sn-vgqsknlk.gvt1.com.", "rr5---sn-vgqsknll.googlevideo.com.", - "rr5---sn-vgqsknll.gvt1.com.", "rr5---sn-vgqsknlr.googlevideo.com.", - "rr5---sn-vgqsknlr.gvt1.com.", "rr5---sn-vgqsknls.googlevideo.com.", "rr5---sn-vgqsknly.googlevideo.com.", "rr5---sn-vgqsknlz.googlevideo.com.", + "rr5---sn-vgqskns7.googlevideo.com.", "rr5---sn-vgqsknse.googlevideo.com.", "rr5---sn-vgqsknsk.googlevideo.com.", "rr5---sn-vgqsknz6.googlevideo.com.", - "rr5---sn-vgqsknz6.gvt1.com.", "rr5---sn-vgqsknz7.googlevideo.com.", - "rr5---sn-vgqsknz7.gvt1.com.", "rr5---sn-vgqsknzd.googlevideo.com.", "rr5---sn-vgqsknze.googlevideo.com.", "rr5---sn-vgqsknzk.googlevideo.com.", "rr5---sn-vgqsknzl.googlevideo.com.", - "rr5---sn-vgqsknzl.gvt1.com.", "rr5---sn-vgqsknzr.googlevideo.com.", + "rr5---sn-vgqsknzr.gvt1.com.", "rr5---sn-vgqsknzs.googlevideo.com.", - "rr5---sn-vgqsknzs.gvt1.com.", "rr5---sn-vgqsknzy.googlevideo.com.", "rr5---sn-vgqsknzz.googlevideo.com.", + "rr5---sn-vgqsknzz.gvt1.com.", "rr5---sn-vgqsrn66.googlevideo.com.", - "rr5---sn-vgqsrn66.gvt1.com.", "rr5---sn-vgqsrn67.googlevideo.com.", "rr5---sn-vgqsrn6e.googlevideo.com.", + "rr5---sn-vgqsrn6e.gvt1.com.", "rr5---sn-vgqsrn6l.googlevideo.com.", - "rr5---sn-vgqsrn6l.gvt1.com.", "rr5---sn-vgqsrn6z.googlevideo.com.", - "rr5---sn-vgqsrn6z.gvt1.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-vgqsrnez.gvt1.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-vgqsrnlk.gvt1.com.", "rr5---sn-vgqsrnll.googlevideo.com.", + "rr5---sn-vgqsrnls.googlevideo.com.", "rr5---sn-vgqsrnlz.googlevideo.com.", - "rr5---sn-vgqsrnlz.gvt1.com.", "rr5---sn-vgqsrns6.googlevideo.com.", "rr5---sn-vgqsrnsd.googlevideo.com.", - "rr5---sn-vgqsrnsd.gvt1.com.", "rr5---sn-vgqsrnsr.googlevideo.com.", - "rr5---sn-vgqsrnsr.gvt1.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-vgqsrnz7.gvt1.com.", "rr5---sn-vgqsrnzd.googlevideo.com.", "rr5---sn-vgqsrnzk.googlevideo.com.", - "rr5---sn-vgqsrnzk.gvt1.com.", "rr5---sn-vgqsrnzr.googlevideo.com.", "rr5---sn-vgqsrnzs.googlevideo.com.", - "rr5---sn-vgqsrnzs.gvt1.com.", "rr5---sn-vgqsrnzy.googlevideo.com.", - "rr5---sn-vgqsrnzy.gvt1.com.", "rr5---sn-vgqsrnzz.googlevideo.com.", - "rr5---sn-vgqsrnzz.gvt1.com.", - "rr5.sn-a5mekn6k.googlevideo.com.", + "rr5.sn-5hneknes.googlevideo.com.", "rr5.sn-hp57knds.googlevideo.com.", - "rr5.sn-ntqe6nel.googlevideo.com.", - "rr5.sn-nx57ynsk.googlevideo.com.", - "rr5.sn-p5qs7nsk.googlevideo.com.", - "rr5.sn-q4fl6nss.googlevideo.com.", - "rr5.sn-q4flrn7y.googlevideo.com.", - "rr5.sn-q4fzen7l.googlevideo.com.", - "rr5.sn-vgqsrnzz.googlevideo.com.", - "rr6---sn-5pgnugx5h-hn2z.googlevideo.com.", + "rr5.sn-ntq7yns7.googlevideo.com.", + "rr5.sn-ntqe6n7r.googlevideo.com.", + "rr5.sn-ntqe6nee.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.", "rr6---sn-8qj-nbo66.googlevideo.com.", "rr6---sn-8xgp1vo-a5me.googlevideo.com.", "rr6---sn-8xgp1vo-ab56.googlevideo.com.", @@ -6592,17 +6508,23 @@ 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-i5f5ppuxa-ioal.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-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.", @@ -6610,14 +6532,18 @@ var FakeECSFQDNs = container.NewMapSet( "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-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.", @@ -6632,60 +6558,61 @@ var FakeECSFQDNs = container.NewMapSet( "rs2.qq.com.", "rsmjournals.com.", "rsx.afterpay.com.", + "rt.applvn.com.", "rt.teramind.co.", + "rta.www.wrike.com.", "rtb-apac.rtbserve.io.", "rtb-eu.rtbserve.io.", + "rtb-useast-v4.qortex.ai.", "rtb-useast.creativedot.net.", - "rtb-useast.openrtb.in.", "rtb-useast.rtbserve.io.", "rtb-uswest-v4.qortex.ai.", - "rtb-uswest.rtbserve.io.", "rtb2-eu.xaprio.net.", "rtb2-useast.xaprio.net.", - "rtb2-uswest.xaprio.net.", - "rtbasia.com.", "rtbsuperhub.com.", "rtbwave.com.", "rttf.citrix.com.", "ru.global.market.xiaomi.com.", - "ru.hlth.io.mi.com.", - "ru3419.ci.managedwhitelisting.com.", "rubyfish.cn.", "rudder.immivision.net.", "ruijienetworks.com.", "rule34video.com.", - "rum-collectors.us2.sumologic.com.", + "rum.perfops.cdb.cdn.orange.com.", + "rum.perfops.mdb.cdn.orange.com.", "rumble.com.", "rumt-sg.com.", + "rutgersconnect-my.sharepoint.com.", "rutubelist.ru.", "s-cdn.anthropic.com.", "s-cs.send.microad.jp.", - "s-pinimg-com-edgekey-net.pinimg.com.", - "s-rtb-pb.send.microad.jp.", + "s-light.tiket.photos.", "s.deepl.com.", "s.q.easebar.com.", "s.seedtag.com.", - "s1-word-view-15.cdn.office.net.", "s2-a.time.mci1.us.rozint.net.", "s2-b.time.mci1.us.rozint.net.", - "s3.global-e.com.", + "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.", "saltlakecity.remotepc.com.", - "sams-checkin.mobile.walmart.com.", "samsclub.quantummetric.com.", "sanantonio.remotepc.com.", + "sandbox.wdesk.com.", "sandboxclient.retinavue.net.", "sandboxregister.retinavue.net.", "sandiego.remotepc.com.", "sandiegodc.remotepc.com.", + "sanguan.online.", "sanjose.remotepc.com.", - "santandernet.sharepoint.com.", "santiago.remotepc.com.", + "sanxia.studyquicks.com.", "saopaulo.remotepc.com.", "saopaulo1.remotepc.com.", "sat02pap001.storage.live.com.", @@ -6695,7 +6622,6 @@ var FakeECSFQDNs = container.NewMapSet( "sc.zoom.us.", "scheduler.eagle.haplat.net.", "schneidercorp.com.", - "science.blitz.gg.", "sciener.cn.", "scm.haplat.net.", "scontent-ams2-1.cdninstagram.com.", @@ -6703,12 +6629,13 @@ var FakeECSFQDNs = container.NewMapSet( "scontent-ams4-1.cdninstagram.com.", "scontent-ams4-1.xx.fbcdn.net.", "scontent-arn2-1.cdninstagram.com.", + "scontent-arn2-1.xx.fbcdn.net.", "scontent-atl3-1.cdninstagram.com.", "scontent-atl3-1.xx.fbcdn.net.", "scontent-atl3-2.cdninstagram.com.", "scontent-atl3-2.xx.fbcdn.net.", - "scontent-ber1-1.cdninstagram.com.", - "scontent-ber1-1.xx.fbcdn.net.", + "scontent-atl3-3.cdninstagram.com.", + "scontent-atl3-3.xx.fbcdn.net.", "scontent-bkk1-1.xx.fbcdn.net.", "scontent-bkk1-2.xx.fbcdn.net.", "scontent-bog2-1.cdninstagram.com.", @@ -6739,7 +6666,6 @@ var FakeECSFQDNs = container.NewMapSet( "scontent-dfw5-2.xx.fbcdn.net.", "scontent-dus1-1.cdninstagram.com.", "scontent-dus1-1.xx.fbcdn.net.", - "scontent-for1-1.cdninstagram.com.", "scontent-fra3-1.cdninstagram.com.", "scontent-fra3-1.xx.fbcdn.net.", "scontent-fra3-2.cdninstagram.com.", @@ -6748,6 +6674,7 @@ 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.", @@ -6759,6 +6686,7 @@ var FakeECSFQDNs = container.NewMapSet( "scontent-ham3-1.cdninstagram.com.", "scontent-ham3-1.xx.fbcdn.net.", "scontent-hel3-1.cdninstagram.com.", + "scontent-hel3-1.xx.fbcdn.net.", "scontent-hkg1-1.cdninstagram.com.", "scontent-hkg1-1.xx.fbcdn.net.", "scontent-hkg1-2.cdninstagram.com.", @@ -6789,7 +6717,6 @@ 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.", @@ -6816,6 +6743,8 @@ var FakeECSFQDNs = container.NewMapSet( "scontent-qro1-2.xx.fbcdn.net.", "scontent-sea1-1.cdninstagram.com.", "scontent-sea1-1.xx.fbcdn.net.", + "scontent-sin11-1.cdninstagram.com.", + "scontent-sin11-1.xx.fbcdn.net.", "scontent-sin6-1.cdninstagram.com.", "scontent-sin6-1.xx.fbcdn.net.", "scontent-sin6-2.cdninstagram.com.", @@ -6839,26 +6768,22 @@ var FakeECSFQDNs = container.NewMapSet( "scontent-xsp1-2.xx.fbcdn.net.", "scontent-xsp1-3.cdninstagram.com.", "scontent-xsp1-3.xx.fbcdn.net.", - "scontent-xsp2-1.cdninstagram.com.", - "scontent-xsp2-1.xx.fbcdn.net.", "scontent-yyz1-1.cdninstagram.com.", "scontent-yyz1-1.xx.fbcdn.net.", "scouccl1.haplat.net.", "scouccl2.haplat.net.", "scraper2.onlineradiobox.com.", - "sdds3.sophosupd.com.", "sdgframeonyc01.frameo.net.", "sdgframeonyc02.frameo.net.", - "sdk-api-us.maticooads.com.", - "sdk-assets.teads.tv.", "sdk.beizi.biz.", - "sdk.iad-04.braze.com.cdn.cloudflare.net.", - "sdk.iad-08.braze.com.cdn.cloudflare.net.", + "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.", "sealsubscriptions.com.", @@ -6868,37 +6793,40 @@ var FakeECSFQDNs = container.NewMapSet( "searchserverapi.com.", "seattle.remotepc.com.", "secaucus.remotepc.com.", + "secure-oldnavy.gap.com.", + "secure-signals.permutive.app.", "secure.accurint.com.", "secure.syndetics.com.", - "secure2.cdn.fastclick.net.edgekey.net.", "securelink.med.usc.edu.", "securetheorem.com.", "securityapi.d3-pr-tm.com.", - "securybrowsenow.com.", "seedtag.com.", - "seexh.com.", "send.microad.jp.", + "sendspace.com.", "sensorsdata.cn.", "sensorsdata.com.", + "sensorsgateway.com.", "sentry-pro.bssrvc66.com.", "sentry-webapp.quillbot.com.", "sentry.appodeal.com.", - "sentry.archive.org.", - "sentry.blum.codes.", "sentry.quillbot.com.", "sentry.radyushin.com.", + "sentry.voicemod.net.", "sentry.wrike.com.", "seoul.remotepc.com.", "served-by.pixfuture.com.", - "servedby.flashtalking.com.", - "service.weather.microsoft.com.", + "service-light.gamesafe.qq.com.", + "service.toonblast.net.", "service2.ultipro.com.", "servicebus1021.myconnectsecure.com.", "servicebus1022.myconnectsecure.com.", "servicebus1023.myconnectsecure.com.", "servicebus1024.myconnectsecure.com.", - "services.intralinks.com.", + "servicebus1041.myconnectsecure.com.", + "services.gradle.org.", "services.lego.com.", + "services.listrak.com.cdn.cloudflare.net.", + "services2.risevision.com.", "servicetitan.com.", "servx.opamarketplace.com.", "servx.playstream.media.", @@ -6915,17 +6843,20 @@ var FakeECSFQDNs = container.NewMapSet( "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.", "shortpixel.ai.", + "show-sb.com.", "shp.ee.", "shrinetheme.com.", "shuzilm.cn.", - "si2.schwabinstitutional.com.", "sigma-performance-h73na.proxima.nie.easebar.com.", "signin.ultipro.com.", + "signup.live.com.", "sina.com.", "sinaimg.cn.", "sip-backup.phonepower.com.", @@ -6944,8 +6875,6 @@ var FakeECSFQDNs = container.NewMapSet( "sip123-1121.ringcentral.com.", "sip123-1131.ringcentral.com.", "sip123-1141.ringcentral.com.", - "sip123-1211.ringcentral.com.", - "sip131-1111.ringcentral.com.", "sip131-1121.ringcentral.com.", "sip131-1131.ringcentral.com.", "sip131-1141.ringcentral.com.", @@ -6953,14 +6882,13 @@ var FakeECSFQDNs = container.NewMapSet( "sip132-1131.ringcentral.com.", "sip132-1141.ringcentral.com.", "sip421-121.ringcentral.biz.", - "sipis.acrobits.cz.", "site-config.com.", "sjc.zoom.us.", "sjc04pap001.storage.live.com.", "sjc04pap002.storage.live.com.", - "skims.com.", "skyapi.onedrive.live.com.", "skyapi.policies.live.net.", + "skydrivesync.policies.live.net.", "skyts.net.", "skyward-gprod.iscorp.com.", "skyward-lisdprod.iscorp.com.", @@ -6970,24 +6898,26 @@ var FakeECSFQDNs = container.NewMapSet( "sm1.selectmedia.asia.", "smartcloudcon.com.", "smarthome.ctdevice.ott4china.com.", - "smarttag.rubiconproject.com.", "smoot-searchv2-aapse1c.v.aaplimg.com.", + "smoot-searchv2-aeun1a.v.aaplimg.com.", + "smoot-searchv2-aeun1b.v.aaplimg.com.", "smoot-searchv2-aeus2a.v.aaplimg.com.", + "smoot-searchv2-aeus2b.v.aaplimg.com.", "smoot-searchv2-aeuw1b.v.aaplimg.com.", - "smoot-searchv2-aeuw3b.v.aaplimg.com.", - "smoot-searchv2-aeuw3c.v.aaplimg.com.", "smoot-searchv2-ause1a.v.aaplimg.com.", "smoot-searchv2-ause1b.v.aaplimg.com.", "smoot-searchv2-ause1c.v.aaplimg.com.", "smoot-searchv2-ause2a.v.aaplimg.com.", "smoot-searchv2-ause2b.v.aaplimg.com.", "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.", "snokido.com.", "snz04pap001.storage.live.com.", - "snz04pap002.storage.live.com.", "so1506.ci.managedwhitelisting.com.", "sobot.com.", "socialchain.app.", @@ -6997,13 +6927,13 @@ var FakeECSFQDNs = container.NewMapSet( "sohu.com.", "sohucs.com.", "solid.preyproject.com.", - "solidworks.com.", "sonar-akl1-1.xx.fbcdn.net.", "sonar-ams2-1.xx.fbcdn.net.", "sonar-ams4-1.xx.fbcdn.net.", "sonar-arn2-1.xx.fbcdn.net.", "sonar-atl3-1.xx.fbcdn.net.", "sonar-atl3-2.xx.fbcdn.net.", + "sonar-atl3-3.xx.fbcdn.net.", "sonar-bcn1-1.xx.fbcdn.net.", "sonar-ber1-1.xx.fbcdn.net.", "sonar-bkk1-1.xx.fbcdn.net.", @@ -7023,10 +6953,6 @@ var FakeECSFQDNs = container.NewMapSet( "sonar-cgk2-1.xx.fbcdn.net.", "sonar-cph2-1.xx.fbcdn.net.", "sonar-cpt1-1.xx.fbcdn.net.", - "sonar-del1-1.xx.fbcdn.net.", - "sonar-del1-2.xx.fbcdn.net.", - "sonar-del2-1.xx.fbcdn.net.", - "sonar-del2-2.xx.fbcdn.net.", "sonar-den2-1.xx.fbcdn.net.", "sonar-dfw5-1.xx.fbcdn.net.", "sonar-dfw5-2.xx.fbcdn.net.", @@ -7037,8 +6963,9 @@ var FakeECSFQDNs = container.NewMapSet( "sonar-fco2-1.xx.fbcdn.net.", "sonar-fml1-1.xx.fbcdn.net.", "sonar-fml20-1.xx.fbcdn.net.", - "sonar-fmx1-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.", "sonar-fra3-2.xx.fbcdn.net.", "sonar-fra5-1.xx.fbcdn.net.", @@ -7090,6 +7017,7 @@ var FakeECSFQDNs = container.NewMapSet( "sonar-man2-1.xx.fbcdn.net.", "sonar-mba1-1.xx.fbcdn.net.", "sonar-mct1-1.xx.fbcdn.net.", + "sonar-mct1-2.xx.fbcdn.net.", "sonar-mia3-1.xx.fbcdn.net.", "sonar-mia3-2.xx.fbcdn.net.", "sonar-mnl1-1.xx.fbcdn.net.", @@ -7116,6 +7044,7 @@ var FakeECSFQDNs = container.NewMapSet( "sonar-qro1-2.xx.fbcdn.net.", "sonar-scl2-1.xx.fbcdn.net.", "sonar-sea1-1.xx.fbcdn.net.", + "sonar-sin11-1.xx.fbcdn.net.", "sonar-sin6-1.xx.fbcdn.net.", "sonar-sin6-2.xx.fbcdn.net.", "sonar-sin6-3.xx.fbcdn.net.", @@ -7134,38 +7063,41 @@ var FakeECSFQDNs = container.NewMapSet( "sonar-xsp1-1.xx.fbcdn.net.", "sonar-xsp1-2.xx.fbcdn.net.", "sonar-xsp1-3.xx.fbcdn.net.", - "sonar-xsp2-1.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.", - "sp.itunes.apple.com.edgekey.net.", "spadsync.com.", "sparteo.com.", + "speedtest.sv5.macarne.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.services.box.net.", - "ssp.hbrd.io.", "ssp.hybrid.ai.", "sstatic.net.", "ssxd.mediav.com.", "st-sysupgrade.vivo.com.cn.", "stable.dl2.discordapp.net.", "staffbase.com.", + "stags.bluekai.com.", + "stalker.to.", + "standwithcrypto.org.", "stardustgod.com.", "starrydyn.com.", "startssl.com.", - "stash.webstaurantstore.com.", - "stat.360safe.com.", "stat.pdfforge.org.", "statad.ru.", "static-atl3-1.xx.fbcdn.net.", @@ -7181,7 +7113,6 @@ var FakeECSFQDNs = container.NewMapSet( "static-lax3-2.xx.fbcdn.net.", "static-lga3-1.xx.fbcdn.net.", "static-lga3-2.xx.fbcdn.net.", - "static-lhr6-1.xx.fbcdn.net.", "static-lhr6-2.xx.fbcdn.net.", "static-lhr8-1.xx.fbcdn.net.", "static-mia3-1.xx.fbcdn.net.", @@ -7189,39 +7120,35 @@ var FakeECSFQDNs = container.NewMapSet( "static-msp1-1.xx.fbcdn.net.", "static-ord5-1.xx.fbcdn.net.", "static-ord5-2.xx.fbcdn.net.", - "static-redesign.cnbcfm.com.", "static-sea1-1.xx.fbcdn.net.", "static-sjc3-1.xx.fbcdn.net.", + "static.atgsvcs.com.", "static.avito.ru.", "static.getliner.com.", - "static.kwcdn.com.", - "static.zdassets.com.", - "static3.mixi.media.", + "static.mytonwallet.io.", + "static.sharepointonline.com.", + "static1.teacherspayteachers.com.", + "static5.mixi.media.", "static6.mixi.media.", - "static7.mixi.media.", "static8.mixi.media.", - "stats.adinplay.com.", + "staticview.msn.com.edgekey.net.", "stats.aeries.com.", "stats.rip.", "stats.transitapp.com.", "statsig.anthropic.com.", - "statsjs.klevu.com.", - "stemchristie.rome2rio.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.", "store.qq.com.", - "streamhub.tech.", "streetviewpixels-pa.googleapis.com.", + "stripchat.ooo.", "stse02.ultipro.com.", "stsew02.ultipro.com.", "stsn02.ultipro.com.", "student.atitesting.com.", "studyquicks.com.", - "stun.2talk.com.", "stun.acrobits.cz.", "stun.cdnbye.com.", "stun.cloudflare.com.", @@ -7232,15 +7159,15 @@ var FakeECSFQDNs = container.NewMapSet( "stun2.ringcentral.com.", "stun3.l.google.com.", "stun4.l.google.com.", - "stylitics.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.dkbinnovative.com.", + "support.oracle.com.", "support.powerschool.com.", - "support.ricoh.com.", "sv8.cyberhaven.io.", "svlive.serraview.com.", "swag.maxhost.io.", @@ -7254,26 +7181,25 @@ var FakeECSFQDNs = container.NewMapSet( "sync.oraki.io.", "sync.videowalldirect.com.", "syndetics.com.", - "sysdk.cl2009.com.", "systemreportservices.genetec.com.", "t-odx.op-mobile.opera.com.", "t.adcell.com.", "t.marketingcloudfx.com.", "t.mookie1.com.", "t.poki.io.", - "t3.nhentai.net.", "t3.xiaohongshu.com.", "tag.winister.app.", + "tags.bluekai.com.edgekey.net.", "tags.natwest.com.", - "tags.tiqcdn.com.edgekey.net.", "taipei.remotepc.com.", + "talon-service-prod.ecosec.on.epicgames.com.", "tampa.remotepc.com.", "tapecontent.net.", "tapsell.ir.", + "target.digitalaudience.io.", "tasks.office.com.", "tasteofhome.com.", "tatracker-us.rivergame.net.", - "tbunet.com.", "tccprod01.honeywell.com.", "tccprod01.resideo.com.", "tccprod02.honeywell.com.", @@ -7281,34 +7207,38 @@ var FakeECSFQDNs = container.NewMapSet( "tccprod03.honeywell.com.", "tccprod03.resideo.com.", "tcdnlive.com.", - "tcdnos.com.", "tclclouds.com.", "tdcservices.tandemdiabetes.com.", "tdm.qq.com.", "teamviewer.com.", + "techcommunity.microsoft.com.", "teddymobile.cn.", "telecom.shuzilm.cn.", "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.", "tencentmusic.com.", "tenda.com.cn.", + "tenpay.com.", "terabox.app.", + "terabox.com.", "terabox1024.com.", "terms3.hicloud.com.", - "test3.dantri.com.vn.", "tgp.qq.com.", "tgpa.qq.com.", "thanhnien.vn.", "theoks.net.", - "thingiverse.com.", + "therealxh.com.", + "thetracker.org.", "thinkific.com.", "thm.visa.com.", "thm12.visa.com.", - "thor.sslauth.sonos.com.", + "thumb-v0.xhpingcdn.com.", "time-a-b.nist.gov.", "time-a-g.nist.gov.", "time-a.nist.gov.", @@ -7325,18 +7255,22 @@ var FakeECSFQDNs = container.NewMapSet( "time.ecansol.net.", "time.lmtlabs.com.", "time.nest.com.", + "time.tritan.gg.", "time.walb.tech.", "time1.aliyun.com.", "time1.google.com.", "time2.aliyun.com.", "time2.google.com.", + "time2.tritan.gg.", "time3.aliyun.com.", "time3.google.com.", "time4.google.com.", "timi-esports.qq.com.", - "tk-sg.anythinktech.com.", + "tinnhiemmang.vn.", + "titank12.com.", + "tk.bridgeoos.com.", "tk.mosspf.com.", - "tkda.mosspf.com.", + "tk.mossru.com.", "tkda.mossru.com.", "tkx.mp.lura.live.", "tlivecdn.com.", @@ -7358,11 +7292,12 @@ 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.gz2.tencent.com.", "tpns.sgp.tencent.com.", "tpns.sh.tencent.com.", "tpns.tencent.com.", @@ -7373,10 +7308,18 @@ var FakeECSFQDNs = container.NewMapSet( "tra-ac-ae2.best82.com.", "tra-ac-id.apktorrents.com.", "tra-ac-id.best82.com.", + "tra-ac-id2.apktorrents.com.", "tra-ac-id2.best82.com.", "tra-ac-ind.apktorrents.com.", "tra-ac-ind.best82.com.", "tra-ac-mas.apktorrents.com.", + "tra-ac-mas.best82.com.", + "tra-ard-id.apktorrents.com.", + "tra-ard-id.best82.com.", + "tra-hd-id.apktorrents.com.", + "tra-hd-id.best82.com.", + "tra-ht-id.apktorrents.com.", + "tra-ht-id.best82.com.", "tra-hz-de.hyper-torrent.com.", "tra-hz-fl.hyper-torrent.com.", "tra-lwb-sg.best61.com.", @@ -7395,10 +7338,11 @@ var FakeECSFQDNs = container.NewMapSet( "tra-ved-ru.best82.com.", "trace.qq.com.", "track-eu1.hubspot.com.", - "track.opentable.com.", + "track.easeus.com.", "track.sendlane.com.", "trackedlink.net.", "tracker-udp.gbitt.info.", + "tracker.auctor.tv.", "tracker.best61.com.", "tracker.ccp.ovh.", "tracker.files.fm.", @@ -7409,18 +7353,19 @@ var FakeECSFQDNs = container.NewMapSet( "tracker.nwps.ws.", "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.", "transaccional.saludtotal.com.co.", "traversal.syncromsp.com.", "treas.gov.", "treasury.gov.", "tribalfusion.com.", - "trivago.com.", "trovit.com.", - "ts.trafget.com.", + "trustlist.adobe.com.", "ts1.qq.com.", "ts2.qq.com.", "tse1.explicit.bing.net.", @@ -7431,31 +7376,28 @@ var FakeECSFQDNs = container.NewMapSet( "tubecup.net.", "tunnel.googlezip.net.", "tuoitre.vn.", - "tuya.com.", - "tvx.adgrx.com.", + "turn.cloudflare.com.", "tw.ntp.org.cn.", + "twcgov-my.sharepoint.com.", "twcloudgz.ucbj.net.", "twcloudgz1.ucbj.net.", - "txcmmov.a.etoote.com.", - "txqcmmov.a.etoote.com.", "tydevice.com.", "u.4dex.io.", "uaenorth.api.cognitive.microsoft.com.", "uapi.mp.360.cn.", + "ubeethiesemo.com.", "uber.zoom.us.", "uc.cn.", "ucweb.com.", "udemycdn.com.", - "udns.weixin.qq.com.", "ue.lenovomm.cn.", "uhabo.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.", @@ -7467,28 +7409,25 @@ var FakeECSFQDNs = container.NewMapSet( "umami.is.", "umeng.com.", "unicom.shuzilm.cn.", - "unification.useinsider.com.", + "uniconsent.com.", "unified-inbox-1-gw.ultipro.com.", "unified-inbox-2-gw.ultipro.com.", - "union.ucweb.com.", "unipay.qq.com.", "unisoc.com.", "united.quantummetric.com.", "unity.cn.", "uodoo.com.", "up.railway.app.", - "update-solarwinds.2d585.cdn.bitdefender.net.", + "update.ee-share.com.", "update.huorong.cn.", "update.kingsoftstore.com.", "update.vivaldi.com.", "update.yealink.com.", "updatechannel.sharegate.com.", + "updater.prntscr.com.", + "updater.skillbrains.com.", "updaterservices.genetec.com.", "updatesnl.macrium.com.", - "upgrade.long.tv.", - "upload3.systemmonitor.co.uk.", - "upload3.systemmonitor.us.cdn.cloudflare.net.", - "upload4.systemmonitor.us.cdn.cloudflare.net.", "upravel.com.", "upremium.asia.", "urekamedia.com.", @@ -7499,28 +7438,28 @@ var FakeECSFQDNs = container.NewMapSet( "us-05.ws-api.ringcentral.com.", "us-06.ws-api.ringcentral.com.", "us-api.asm.skype.com.", - "us-atl-anx-r009.router.teamviewer.com.", + "us-atl-anx-r001.router.teamviewer.com.", + "us-atl-anx-r002.router.teamviewer.com.", + "us-atl-anx-r005.router.teamviewer.com.", "us-central1-adaptive-growth.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.", "us-central1-bps-oi-production.cloudfunctions.net.", "us-central1-clubroom-prod.cloudfunctions.net.", + "us-central1-darden-main.cloudfunctions.net.", "us-central1-digitalproducts-gabbo.cloudfunctions.net.", + "us-central1-ds-specials-dev.cloudfunctions.net.", "us-central1-fsgenergy-shared.cloudfunctions.net.", "us-central1-gaggle-staging.cloudfunctions.net.", "us-central1-justbuild-cdb86.cloudfunctions.net.", - "us-central1-kitsune-271319.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-marketplace-production-east4.cloudfunctions.net.", "us-central1-mikmak-microservices.cloudfunctions.net.", "us-central1-muslim-pro-app.cloudfunctions.net.", "us-central1-noteit-4dca3.cloudfunctions.net.", - "us-central1-omg-na-prd-brandometer-gcp-ca.cloudfunctions.net.", - "us-central1-propane-fusion-170222.cloudfunctions.net.", "us-central1-royal-match-prod-cce6d.cloudfunctions.net.", "us-central1-shopify-instrumentat-ff788286.cloudfunctions.net.", "us-central1-speechifymobile.cloudfunctions.net.", @@ -7528,52 +7467,54 @@ var FakeECSFQDNs = container.NewMapSet( "us-central1-teach-monster.cloudfunctions.net.", "us-central1-webgltest-17af1.cloudfunctions.net.", "us-central1-wise-arch-107501.cloudfunctions.net.", - "us-cmh-gcp-r002.router.teamviewer.com.", + "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-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-las-gcp-r003.router.teamviewer.com.", - "us-las-gcp-r004.router.teamviewer.com.", - "us-lax-anx-r003.router.teamviewer.com.", - "us-lax-anx-r010.router.teamviewer.com.", + "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-r013.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-mia-anx-r006.router.teamviewer.com.", + "us-mia-anx-r004.router.teamviewer.com.", "us-mia-anx-r009.router.teamviewer.com.", - "us-mia-anx-r011.router.teamviewer.com.", - "us-mia-anx-r012.router.teamviewer.com.", "us-mia-anx-r013.router.teamviewer.com.", - "us-njc-anx-r010.router.teamviewer.com.", - "us-njc-anx-r011.router.teamviewer.com.", - "us-ntl.np.community.playstation.net.", - "us-oma-gcp-r002.router.teamviewer.com.", - "us-pdx-gcp-r004.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-prod.asyncgw.teams.microsoft.com.", - "us-sea-anx-r002.router.teamviewer.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-spectrum.rcs.telephony.goog.", - "us-was-anx-r004.router.teamviewer.com.", - "us-was-anx-r006.router.teamviewer.com.", - "us-was-anx-r008.router.teamviewer.com.", - "us-was-anx-r010.router.teamviewer.com.", "us-was-anx-r011.router.teamviewer.com.", - "us-was-anx-r012.router.teamviewer.com.", - "us-was-anx-r015.router.teamviewer.com.", - "us-was-anx-r017.router.teamviewer.com.", - "us-was-anx-r019.router.teamviewer.com.", + "us-was-anx-r014.router.teamviewer.com.", "us.att.rcs.telephony.goog.", + "us.evidation.com.", + "us.hdtvcloud.com.", "us.hlth.io.mi.com.", + "us.questionai.com.", "us.tmobile.rcs.telephony.goog.", "us.tracfone.rcs.telephony.goog.", "us.ubianet.com.", "us.uscc.rcs.telephony.goog.", "us.xfinity.rcs.telephony.goog.", + "us01docs.zoom.us.", "us02log.zoom.us.", "us02polling.zoom.us.", "us02web.zoom.us.", @@ -7581,6 +7522,7 @@ var FakeECSFQDNs = container.NewMapSet( "us04asyncim.zoom.us.", "us04nws-platform.zoom.us.", "us04nws.zoom.us.", + "us04st3.zoom.us.", "us04web.zoom.us.", "us04www3.zoom.us.", "us05nws-platform.zoom.us.", @@ -7591,11 +7533,11 @@ var FakeECSFQDNs = container.NewMapSet( "us06polling.zoom.us.", "us06web.zoom.us.", "us06www3.zoom.us.", - "us1111.alicdn.com.edgekey.net.", - "us2.rtbsystem.org.", "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.", @@ -7628,23 +7570,30 @@ var FakeECSFQDNs = container.NewMapSet( "usc-powerpoint-collab.officeapps.live.com.", "usc-word-collab.officeapps.live.com.", "usc.edu.", + "usc.pods.officeapps.live.com.", "uscis.gov.", + "usdoj.gov.", + "use3-assets.a-mo.net.", + "use4.s.seedtag.com.", "useast.quantumdex.io.", - "userexperience.thehut.net.", + "usefulcontentsites.com.", "usgs.gov.", "ussav.cynet.com.", "usserver.serverapi.org.", "usslb.cynet.com.", "usw.stape.io.", "uswest-beacon.deepintent.com.", + "ut.hzshudian.com.", "uu.qq.com.", "uuidksinc.net.", + "uwcu.cyberhaven.io.", + "uworld.com.", "uxfeedback.ru.", "v.vivintsky.com.", "v39-as.tiktokcdn.com.", "v39-ca.tiktokcdn.com.", "v39-id-telin.tiktokcdn.com.", - "v39-mx.tiktokcdn.com.", + "v39-id.gts.byteoversea.net.", "v39-row.gts.byteoversea.net.", "v39-row.tiktokcdn.com.", "v39-us.gts.byteoversea.net.", @@ -7655,21 +7604,22 @@ var FakeECSFQDNs = container.NewMapSet( "v6-gdvod.kwaicdn.com.", "v8.analytics.pinsightmedia.com.", "v8engine.pinsightmedia.com.", - "vadesecure.com.", "vador.com.", + "valleychildrensorg-my.sharepoint.com.", "vbw.vivoglobal.com.", + "vcsa.vmware.com.cdn.cloudflare.net.", "venafi.com.", - "veoneer.com.", - "verafin.okta.com.", "verification.fda.gov.ph.", "verticals.wix.com.", - "vexgateway.fastly.carvana.io.", "vfa.hpplay.cn.", + "vgorigin.hakunaymatata.com.", + "vi.vipr.ebaydesc.com.", "vibe.co.", "vibeaconstr.onezapp.com.", "vicoo.tech.", "video-atl3-1.xx.fbcdn.net.", "video-atl3-2.xx.fbcdn.net.", + "video-atl3-3.xx.fbcdn.net.", "video-bos5-1.xx.fbcdn.net.", "video-den2-1.xx.fbcdn.net.", "video-dfw5-1.xx.fbcdn.net.", @@ -7693,11 +7643,10 @@ var FakeECSFQDNs = container.NewMapSet( "video-phx1-1.xx.fbcdn.net.", "video-sea1-1.xx.fbcdn.net.", "video-sjc3-1.xx.fbcdn.net.", - "video.foxnews.com.", - "videocontent-dra.himovie.dbankcloud.cn.", "videocontent-dra.himovie.dbankcloud.com.", "vidmate.net.", "vieon.vn.", + "view.admeking.com.", "vik-ca.moonactive.net.", "vik-game.moonactive.net.", "viously.com.", @@ -7708,6 +7657,9 @@ var FakeECSFQDNs = container.NewMapSet( "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.", "vod3.ngb.haplat.net.", @@ -7717,6 +7669,7 @@ var FakeECSFQDNs = container.NewMapSet( "voe.sx.", "voice.gcloudcs.com.", "voice.telephony.goog.", + "vote.donaldjtrump.com.", "voya.com.", "vpn1.ocso.com.", "vx9dle.n0qq3z.com.", @@ -7725,105 +7678,108 @@ var FakeECSFQDNs = container.NewMapSet( "w25.cf.2ksports.com.", "w3.mp.lura.live.", "wahapanih.xyz.", + "wap.cmpassport.com.", "warsaw.remotepc.com.", - "wavemaxpro.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.", - "weatherwidget.io.", - "web-api.ikea.com.", - "web-static.archive.org.", + "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.", - "webservices.global-e.com.", "websocket.app.pdq.com.", "wechatos.net.", - "weerrhoop.cc.", "wegame.com.cn.", "weibocdn.com.", "welcome.ultipro.com.", "wemuslim.com.", + "wesingapp.com.", "westcentralus.api.cognitive.microsoft.com.", "westeurope.api.cognitive.microsoft.com.", "westpalm.remotepc.com.", "westus.api.cognitive.microsoft.com.", "westus2.api.cognitive.microsoft.com.", "westus3.api.cognitive.microsoft.com.", - "wewjyw.qb6ges.com.", - "whiteboard-session.nearpod.com.", "whitingturner-my.sharepoint.com.", "whitingturner.sharepoint.com.", - "whm.usa.experian.com.", - "whova.com.", - "widget.cloudinary.com.", - "widgets.media.weather.com.", "wifispot.io.", - "wildcard.s-msn.com.edgekey.net.", - "win-rtb2-useast.xaprio.net.", + "wild.ww.np.dl.playstation.net.edgekey.net.", + "windows.policies.live.net.", "winscp.net.", "wkhpe.com.", "wmt.dev.", "word-collab.officeapps.live.com.", "wordreference.com.", "workdaycdn.com.cn.", - "worksighted.com.", "worldnic.com.", "worldtimeserver.com.", "wosign.com.", + "wpsw.listrak.com.", "wr.moyoung.com.", "wr.pvp.net.", "ws.gleap.io.", "ws.mybib.com.", + "ws.thales.monumetric.com.", "ws.tildacdn.com.", - "ws.toasttab.com.", "wsdcc.haplat.net.", - "wsms.haplat.net.", "wsoversea.com.", - "wsrv.nl.", + "wss-web.freshchat.com.", + "www.481528.com.", "www.aigconnect.aig.", "www.americanexpress.com.edgekey.net.", - "www.animefox.sbs.", "www.automizely-analytics.com.", - "www.bol.com.", + "www.belkin.com.", "www.breitbart.com.", - "www.carmax.com.", + "www.brilliantearth.com.", + "www.census.gov.", "www.chrome.com.", "www.claudeusercontent.com.", "www.cmpassport.com.", - "www.cnbc.com.edgekey.net.", - "www.costco.com.", - "www.credible.com.", "www.ctmail.com.", - "www.drugs.com.", - "www.foodnetwork.com.", - "www.gap.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.in.gov.", "www.internetdownloadmanager.com.", "www.jimmyjohns.com.", - "www.jpost.com.", "www.maintenanceconnection.com.", - "www.marksandspencer.com.", + "www.mcmaster.com.", "www.nativecos.com.", - "www.niemanlab.org.", + "www.originplatform.com.", "www.overleaf.com.", "www.pingler.com.", + "www.potterybarn.com.", "www.printfriendly.com.", + "www.questionai.com.", "www.regions.com.", - "www.servicenow.com.", + "www.rockchip.com.", + "www.se.com.", "www.sevenrooms.com.", - "www.snokido.com.", - "www.sogou.com.", - "www.starbucks.com.", + "www.southwest.com.", + "www.startssl.com.", "www.stickermule.com.", + "www.terabox.com.", + "www.trivago.com.", "www.users.storage.live.com.", "www.vipads.live.", "www.virustotal.com.", @@ -7832,36 +7788,39 @@ var FakeECSFQDNs = container.NewMapSet( "www.wifispot.io.", "www.wix.com.", "www.worldtimeserver.com.", - "www.writable.com.", - "www.zappos.com.edgekey.net.", - "www.zdf.de.", + "www.zenaps.com.", "www.zoom.com.", "www.zoom.us.", "www1.remotepc.com.", - "www10.wellsfargomedia.com.", - "www11.wellsfargomedia.com.", + "www14.wellsfargomedia.com.", "www2.deepl.com.", "www3.zoom.us.", + "wx.huion.cn.", "wxqcloud.qq.com.", "wxqcloud.qq.com.cn.", - "wyze-re-rule-svc.wyzecam.com.", "wyze.com.", "x-flow.app.", - "x.netease.com.", - "x1.c.lencr.org.", + "xedo.me.", "xhpingcdn.com.", "xiaoyi.com.", + "xinqiucc.com.", "xintaicz.cn.", "xmcsrv.net.", + "xml-eu-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.", "xmt.paze.com.", - "xp.apple.com.", "xp001.itsupport247.net.", "xp002.itsupport247.net.", "xp003.itsupport247.net.", @@ -7878,22 +7837,24 @@ var FakeECSFQDNs = container.NewMapSet( "xp019.itsupport247.net.", "yalla.live.", "yealink.com.", + "yh.yardiaspire.com.", + "ymmobi.com.", "yomedia.vn.", "yomeno.xyz.", "yosmart.com.", - "yp.cdnstream1.com.", "yunxindns.com.", "yunxinfw.com.", + "z-long.cn.", "z.cdn.adpool.bet.", "z.cdn.adtarget.market.", + "z.cdn.xbeat.space.", + "zdx.uber.com.", "zemanta.com.", - "zenno.services.", "zjcdn.com.yangyi19.com.", - "zmedia.vn.", "zog.link.", "zoom.us.", - "zorvian.com.", "zui.com.", "zurich.remotepc.com.", "zybooks.com.", + "zztfly.com.", ) diff --git a/internal/ecscache/ecsblocklist_generate.go b/internal/ecscache/ecsblocklist_generate.go index abc27cb..5571240 100644 --- a/internal/ecscache/ecsblocklist_generate.go +++ b/internal/ecscache/ecsblocklist_generate.go @@ -4,7 +4,9 @@ package main import ( "bytes" + "context" "io" + "log/slog" "net/http" "os" "slices" @@ -12,41 +14,42 @@ import ( "time" "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp" + "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/osutil" ) func main() { + ctx := context.Background() + logger := slogutil.New(nil) + defer slogutil.RecoverAndExit(ctx, logger, osutil.ExitCodeFailure) + c := &http.Client{ Timeout: 10 * time.Second, } - req, err := http.NewRequest(http.MethodGet, fakeECSBlocklistURL, nil) - check(err) + req := errors.Must(http.NewRequest(http.MethodGet, fakeECSBlocklistURL, nil)) req.Header.Add(httphdr.UserAgent, agdhttp.UserAgent()) - resp, err := c.Do(req) - check(err) - defer log.OnCloserError(resp.Body, log.ERROR) + resp := errors.Must(c.Do(req)) + defer slogutil.CloseAndLog(ctx, logger, resp.Body, slog.LevelError) - out, err := os.OpenFile("./ecsblocklist.go", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o664) - check(err) - defer log.OnCloserError(out, log.ERROR) + out := errors.Must(os.OpenFile("./ecsblocklist.go", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o664)) + defer slogutil.CloseAndLog(ctx, logger, out, slog.LevelError) - contents, err := io.ReadAll(resp.Body) - check(err) + contents := errors.Must(io.ReadAll(resp.Body)) lines := bytes.Split(contents, []byte("\n")) lines = lines[:len(lines)-1] slices.SortStableFunc(lines, bytes.Compare) - tmpl, err := template.New("main").Parse(tmplStr) - check(err) + tmpl := template.Must(template.New("main").Parse(tmplStr)) - err = tmpl.Execute(out, lines) - check(err) + err := tmpl.Execute(out, lines) + errors.Check(err) } // fakeECSBlocklistURL is the default URL from where to get ECS fake domains. @@ -67,10 +70,3 @@ var FakeECSFQDNs = container.NewMapSet( {{- end }} ) ` - -// check is a simple error checker. -func check(err error) { - if err != nil { - panic(err) - } -} diff --git a/internal/ecscache/ecscache.go b/internal/ecscache/ecscache.go index 542fc57..d426db4 100644 --- a/internal/ecscache/ecscache.go +++ b/internal/ecscache/ecscache.go @@ -5,6 +5,7 @@ package ecscache import ( "context" "fmt" + "log/slog" "time" "github.com/AdguardTeam/AdGuardDNS/internal/agd" @@ -13,9 +14,8 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver" "github.com/AdguardTeam/AdGuardDNS/internal/geoip" "github.com/AdguardTeam/AdGuardDNS/internal/metrics" - "github.com/AdguardTeam/AdGuardDNS/internal/optlog" + "github.com/AdguardTeam/AdGuardDNS/internal/optslog" "github.com/AdguardTeam/golibs/errors" - "github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/syncutil" "github.com/miekg/dns" @@ -33,6 +33,12 @@ type Middleware struct { // cloner is the memory-efficient cloner of DNS messages. cloner *dnsmsg.Cloner + // cacheReqPool is a pool of cache requests. + cacheReqPool *syncutil.Pool[cacheRequest] + + // logger is used to log the operation of the middleware. + logger *slog.Logger + // cache is the LRU cache for results indicating no support for ECS. cache agdcache.Interface[uint64, *cacheItem] @@ -42,22 +48,23 @@ type Middleware struct { // geoIP is used to get subnets for countries. geoIP geoip.Interface - // cacheReqPool is a pool of cache requests. - cacheReqPool *syncutil.Pool[cacheRequest] - // cacheMinTTL is the minimum supported TTL for cache items. cacheMinTTL time.Duration - // useTTLOverride shows if the TTL overrides logic should be used. - useTTLOverride bool + // overrideTTL shows if the TTL overrides logic should be used. + overrideTTL bool } -// MiddlewareConfig is the configuration structure for NewMiddleware. +// MiddlewareConfig is the configuration structure for [NewMiddleware]. type MiddlewareConfig struct { - // Cloner is used to clone messages taken from cache. + // Cloner is used to clone messages taken from cache. It must not be nil. Cloner *dnsmsg.Cloner - // CacheManager is the global cache manager. CacheManager must not be nil. + // Logger is used to log the operation of the middleware. It must not be + // nil. + Logger *slog.Logger + + // CacheManager is the global cache manager. It must not be nil. CacheManager agdcache.Manager // GeoIP is the GeoIP database used to get subnets for countries. It must @@ -67,16 +74,16 @@ type MiddlewareConfig struct { // MinTTL is the minimum supported TTL for cache items. MinTTL time.Duration - // Size is the number of entities to hold in the cache for hosts that don't - // support ECS. It must be greater than zero. - Size int + // NoECSCount is the number of entities to hold in the cache for hosts that + // don't support ECS, in entries. It must be greater than zero. + NoECSCount int - // ECSSize is the number of entities to hold in the cache for hosts that - // support ECS. It must be greater than zero. - ECSSize int + // ECSCount is the number of entities to hold in the cache for hosts that + // support ECS, in entries. It must be greater than zero. + ECSCount int - // UseTTLOverride shows if the TTL overrides logic should be used. - UseTTLOverride bool + // OverrideTTL shows if the TTL overrides logic should be used. + OverrideTTL bool } // NewMiddleware initializes a new ECS-aware LRU caching middleware. It also @@ -84,25 +91,26 @@ type MiddlewareConfig struct { // manager. c must not be nil. func NewMiddleware(c *MiddlewareConfig) (m *Middleware) { cache := agdcache.NewLRU[uint64, *cacheItem](&agdcache.LRUConfig{ - Size: c.Size, + Size: c.NoECSCount, }) ecsCache := agdcache.NewLRU[uint64, *cacheItem](&agdcache.LRUConfig{ - Size: c.ECSSize, + Size: c.ECSCount, }) c.CacheManager.Add(cacheIDNoECS, cache) c.CacheManager.Add(cacheIDWithECS, ecsCache) return &Middleware{ - cloner: c.Cloner, - cache: cache, - ecsCache: ecsCache, - geoIP: c.GeoIP, + cloner: c.Cloner, + logger: c.Logger, cacheReqPool: syncutil.NewPool(func() (req *cacheRequest) { return &cacheRequest{} }), - cacheMinTTL: c.MinTTL, - useTTLOverride: c.UseTTLOverride, + cache: cache, + ecsCache: ecsCache, + geoIP: c.GeoIP, + cacheMinTTL: c.MinTTL, + overrideTTL: c.OverrideTTL, } } @@ -224,8 +232,7 @@ func (mw *Middleware) writeUpstreamResponse( return fmt.Errorf("getting ecs from resp: %w", err) } - // TODO(a.garipov): Use optslog.Trace2. - optlog.Debug2("ecscache: upstream: %s/%d", subnet, scope) + optslog.Trace2(ctx, mw.logger, "upstream data", "subnet", subnet, "scope", scope) reqDO := cr.reqDO rmHopToHopData(resp, ri.QType, reqDO) @@ -304,7 +311,7 @@ func (mh *mwHandler) ServeDNS( // Cache key calculation shouldn't consider the subnet of the cache // request in this case, but the actual DNS request generated on cache // miss will use this data. - log.Debug("ecscache: explicitly declined ecs") + mw.logger.DebugContext(ctx, "explicitly declined ecs") cr.subnet = netutil.ZeroPrefix(ecsFam) } else { @@ -319,21 +326,28 @@ func (mh *mwHandler) ServeDNS( ) } - optlog.Debug3("ecscache: got ctry %s, asn %d, subnet %s", loc.Country, loc.ASN, cr.subnet) + optslog.Debug3( + ctx, + mw.logger, + "request data", + "ctry", loc.Country, + "asn", loc.ASN, + "subnet", cr.subnet, + ) } // Try getting a cached result using the subnet of the location or zero one // when explicitly requested by user. If there is one, write, increment the // metrics, and return. See also [writeCachedResponse]. - resp, respIsECS := mw.get(req, cr) + resp, respIsECS := mw.get(ctx, req, cr) if resp != nil { - optlog.Debug1("ecscache: using cached response (ecs-aware: %t)", respIsECS) + optslog.Debug1(ctx, mw.logger, "using cached response", "ecs_aware", respIsECS) // Don't wrap the error, because it's informative enough as is. return writeCachedResponse(ctx, rw, req, resp, ri.ECS, ecsFam, respIsECS) } - log.Debug("ecscache: no cached response") + mw.logger.DebugContext(ctx, "no cached response") // Perform an upstream request with the ECS data for the location or zero // one on circumstances described above. If successful, write, increment diff --git a/internal/ecscache/ecscache_test.go b/internal/ecscache/ecscache_test.go index bcffa1f..ef1e221 100644 --- a/internal/ecscache/ecscache_test.go +++ b/internal/ecscache/ecscache_test.go @@ -16,6 +16,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest" "github.com/AdguardTeam/AdGuardDNS/internal/ecscache" "github.com/AdguardTeam/AdGuardDNS/internal/geoip" + "github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/testutil" "github.com/miekg/dns" @@ -23,10 +24,6 @@ import ( "github.com/stretchr/testify/require" ) -func TestMain(m *testing.M) { - testutil.DiscardLogOutput(m) -} - // Common test domain names. const ( reqHostname = "example.com." @@ -668,31 +665,29 @@ func newWithCache( pt := testutil.PanicT{} - // TODO(a.garipov): Actually test ASNs once we have the data. - geoIP := &agdtest.GeoIP{ - OnSubnetByLocation: func( - l *geoip.Location, - _ netutil.AddrFamily, - ) (n netip.Prefix, err error) { - require.Equal(pt, wantCtry, l.Country) + geoIP := agdtest.NewGeoIP() - return geoIPNet, nil - }, - OnData: func(_ string, _ netip.Addr) (_ *geoip.Location, _ error) { - panic("not implemented") - }, + // TODO(a.garipov): Actually test ASNs once we have the data. + geoIP.OnSubnetByLocation = func( + l *geoip.Location, + _ netutil.AddrFamily, + ) (n netip.Prefix, err error) { + require.Equal(pt, wantCtry, l.Country) + + return geoIPNet, nil } return dnsserver.WithMiddlewares( h, ecscache.NewMiddleware(&ecscache.MiddlewareConfig{ - Cloner: agdtest.NewCloner(), - CacheManager: agdcache.EmptyManager{}, - GeoIP: geoIP, - Size: 100, - ECSSize: 100, - MinTTL: minTTL, - UseTTLOverride: useTTLOverride, + Cloner: agdtest.NewCloner(), + Logger: slogutil.NewDiscardLogger(), + CacheManager: agdcache.EmptyManager{}, + GeoIP: geoIP, + NoECSCount: 100, + ECSCount: 100, + MinTTL: minTTL, + OverrideTTL: useTTLOverride, }), ) } diff --git a/internal/filter/filter_test.go b/internal/filter/filter_test.go index da2c85e..063d5ba 100644 --- a/internal/filter/filter_test.go +++ b/internal/filter/filter_test.go @@ -98,7 +98,6 @@ const testDataFiltersTmpl = `{ "filters": [ { "filterKey": %q, - "filterId": 42, "downloadUrl": "http://example.com" } ] diff --git a/internal/filter/hashprefix/filter.go b/internal/filter/hashprefix/filter.go index bc1cc05..a5baca7 100644 --- a/internal/filter/hashprefix/filter.go +++ b/internal/filter/hashprefix/filter.go @@ -310,18 +310,23 @@ 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.NewBlockedRespMsg(req) + return ri.Messages.NewBlockedResp(req) } ip := f.repIP switch { case ip.Is4() && fam == netutil.AddrFamilyIPv4: - return ri.Messages.NewIPRespMsg(req, ip) + return ri.Messages.NewBlockedRespIP(req, ip) case ip.Is6() && fam == netutil.AddrFamilyIPv6: - return ri.Messages.NewIPRespMsg(req, ip) + return ri.Messages.NewBlockedRespIP(req, ip) default: - return ri.Messages.NewMsgNODATA(req), nil + // 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) + + return resp, nil } } diff --git a/internal/filter/hashprefix/filter_test.go b/internal/filter/hashprefix/filter_test.go index 8567c0c..3322495 100644 --- a/internal/filter/hashprefix/filter_test.go +++ b/internal/filter/hashprefix/filter_test.go @@ -234,7 +234,7 @@ func newModRespResult( ) (r *internal.ResultModifiedResponse) { tb.Helper() - resp, err := messages.NewIPRespMsg(req, replIP) + resp, err := messages.NewRespIP(req, replIP) require.NoError(tb, err) return &internal.ResultModifiedResponse{ @@ -370,7 +370,9 @@ func TestFilter_FilterRequest_staleCache(t *testing.T) { msgs, err := dnsmsg.NewConstructor(&dnsmsg.ConstructorConfig{ Cloner: cloner, BlockingMode: &dnsmsg.BlockingModeNullIP{}, + StructuredErrors: agdtest.NewSDEConfig(true), FilteredResponseTTL: agdtest.FilteredResponseTTL, + EDEEnabled: true, }) require.NoError(t, err) diff --git a/internal/filter/index.go b/internal/filter/index.go index 74c66a8..f962d6a 100644 --- a/internal/filter/index.go +++ b/internal/filter/index.go @@ -10,50 +10,97 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp" "github.com/AdguardTeam/AdGuardDNS/internal/errcoll" + "github.com/AdguardTeam/golibs/errors" ) -// filterIndexResp is the struct for the JSON response from a filter index API. -type filterIndexResp struct { - Filters []*filterIndexRespFilter `json:"filters"` -} - -// filterIndexRespFilter is the struct for a filter from the JSON response from -// a filter index API. +// indexResp is the struct for the JSON response from a filter index API. // -// TODO(a.garipov): Remove ID once the index switches the format completely. -type filterIndexRespFilter struct { - DownloadURL string `json:"downloadUrl"` - FilterID any `json:"filterId"` - Key string `json:"filterKey"` +// TODO(a.garipov): Consider validating uniqueness of the keys. +type indexResp struct { + Filters []*indexRespFilter `json:"filters"` } -// filterIndexFilterData is the data of a single item in the filtering-rule -// index response. -type filterIndexFilterData struct { +// indexRespFilter is the struct for a filter from the JSON response from a +// filter index API. +// +// NOTE: Keep these strings instead of unmarshalers to make sure that objects +// with invalid data do not prevent valid objects from being used. +type indexRespFilter struct { + // DownloadURL contains the URL to use for downloading this filter. + DownloadURL string `json:"downloadUrl"` + + // Key contains the ID of the filter as a string. + Key string `json:"filterKey"` +} + +// compare is the comparison function for filters in the index. f and other may +// be nil; nil filters are sorted after non-nil ones. +func (f *indexRespFilter) compare(other *indexRespFilter) (res int) { + if f == nil { + if other == nil { + return 0 + } + + return 1 + } else if other == nil { + return -1 + } + + return cmp.Compare(f.Key, other.Key) +} + +// validate returns an error if f is invalid. +func (f *indexRespFilter) validate() (err error) { + if f == nil { + return errors.ErrNoValue + } + + var errs []error + + // TODO(a.garipov): Use urlutil.URL or add IsValidURLString to golibs. + if f.DownloadURL == "" { + errs = append(errs, fmt.Errorf("downloadUrl: %w", errors.ErrEmptyValue)) + } + + if f.Key == "" { + errs = append(errs, fmt.Errorf("filterKey: %w", errors.ErrEmptyValue)) + } + + return errors.Join(errs...) +} + +// indexData is the data of a single item in the filtering-rule index response. +type indexData struct { url *url.URL id agd.FilterListID } -// toInternal converts the filters from the index to []*filterIndexFilterData. -func (r *filterIndexResp) toInternal( +// toInternal converts the filters from the index to []*indexData. All errors +// are logged and collected. logger and errColl must not be nil. +func (r *indexResp) toInternal( ctx context.Context, logger *slog.Logger, errColl errcoll.Interface, -) (fls []*filterIndexFilterData) { - fls = make([]*filterIndexFilterData, 0, len(r.Filters)) - for _, rf := range r.Filters { - rfFltID, _ := rf.FilterID.(string) - rfID := cmp.Or(rf.Key, rfFltID) - id, err := agd.NewFilterListID(rfID) +) (fls []*indexData) { + fls = make([]*indexData, 0, len(r.Filters)) + for i, rf := range r.Filters { + err := rf.validate() if err != nil { - err = fmt.Errorf("validating id/key: %w", err) + err = fmt.Errorf("validating filter at index %d: %w", i, err) errcoll.Collect(ctx, errColl, logger, "index response", err) continue } - var u *url.URL - u, err = agdhttp.ParseHTTPURL(rf.DownloadURL) + 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) errcoll.Collect(ctx, errColl, logger, "index response", err) @@ -61,7 +108,7 @@ func (r *filterIndexResp) toInternal( continue } - fls = append(fls, &filterIndexFilterData{ + fls = append(fls, &indexData{ url: u, id: id, }) diff --git a/internal/filter/index_internal_test.go b/internal/filter/index_internal_test.go new file mode 100644 index 0000000..d40f9b5 --- /dev/null +++ b/internal/filter/index_internal_test.go @@ -0,0 +1,37 @@ +package filter + +import ( + "slices" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIndexRespFilter_compare(t *testing.T) { + var ( + fltA = &indexRespFilter{ + Key: "a", + } + fltB = &indexRespFilter{ + Key: "b", + } + ) + + want := []*indexRespFilter{ + fltA, + fltB, + nil, + nil, + } + + got := []*indexRespFilter{ + fltB, + nil, + fltA, + nil, + } + + slices.SortStableFunc(got, (*indexRespFilter).compare) + + assert.Equal(t, want, got) +} diff --git a/internal/filter/internal/composite/composite_internal_test.go b/internal/filter/internal/composite/composite_internal_test.go index 416ed9a..f8be5c8 100644 --- a/internal/filter/internal/composite/composite_internal_test.go +++ b/internal/filter/internal/composite/composite_internal_test.go @@ -32,9 +32,13 @@ func BenchmarkFilter_FilterReqWithRuleLists(b *testing.B) { }) msgs, err := dnsmsg.NewConstructor(&dnsmsg.ConstructorConfig{ - Cloner: dnsmsg.NewCloner(dnsmsg.EmptyClonerStat{}), - BlockingMode: &dnsmsg.BlockingModeNullIP{}, + Cloner: dnsmsg.NewCloner(dnsmsg.EmptyClonerStat{}), + BlockingMode: &dnsmsg.BlockingModeNullIP{}, + StructuredErrors: &dnsmsg.StructuredDNSErrorsConfig{ + Enabled: false, + }, FilteredResponseTTL: filtertest.Staleness, + EDEEnabled: false, }) require.NoError(b, err) diff --git a/internal/filter/internal/refreshable.go b/internal/filter/internal/refreshable.go index 2626a1a..18864c4 100644 --- a/internal/filter/internal/refreshable.go +++ b/internal/filter/internal/refreshable.go @@ -40,8 +40,8 @@ type RefreshableConfig struct { // Logger is used to log errors during refreshes. Logger *slog.Logger - // URL is the URL used to refresh the filter. URL should not be nil. The - // scheme of the URL should be one of: "file", "http", or "https". + // 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 *url.URL // ID is the filter list ID for this filter. @@ -65,7 +65,8 @@ type RefreshableConfig struct { func NewRefreshable(c *RefreshableConfig) (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; s != agdhttp.SchemeFile && !agdhttp.CheckHTTPURLScheme(s) { + } else if s := c.URL.Scheme; !strings.EqualFold(s, urlutil.SchemeFile) && + !urlutil.IsValidHTTPURLScheme(s) { return nil, fmt.Errorf("internal.NewRefreshable: bad url scheme %q", s) } @@ -90,7 +91,7 @@ func NewRefreshable(c *RefreshableConfig) (f *Refreshable, err error) { func (f *Refreshable) Refresh(ctx context.Context, acceptStale bool) (text string, err error) { defer func() { err = errors.Annotate(err, "%s: %w", f.id) }() - if f.url.Scheme == agdhttp.SchemeFile { + if strings.EqualFold(f.url.Scheme, urlutil.SchemeFile) { text, err = f.refreshFromFileOnly(ctx) } else { text, err = f.useCachedOrRefreshFromURL(ctx, acceptStale) @@ -130,13 +131,12 @@ func (f *Refreshable) useCachedOrRefreshFromURL( } if text == "" { - f.logger.InfoContext(ctx, "refreshing from url", "url", &urlutil.URL{ - URL: *f.url, - }) + ru := urlutil.RedactUserinfo(f.url) + f.logger.InfoContext(ctx, "refreshing from url", "url", ru) text, err = f.refreshFromURL(ctx, now) if err != nil { - return "", fmt.Errorf("refreshing from url %q: %w", f.url.Redacted(), err) + return "", fmt.Errorf("refreshing from url %q: %w", ru, err) } } else { f.logger.InfoContext(ctx, "using cached data from file", "path", f.cachePath) @@ -213,9 +213,7 @@ func (f *Refreshable) refreshFromURL( "code", resp.StatusCode, "content-length", resp.ContentLength, "server", resp.Header.Get(httphdr.Server), - "url", &urlutil.URL{ - URL: *f.url, - }, + "url", urlutil.RedactUserinfo(f.url), ) err = agdhttp.CheckStatus(resp, http.StatusOK) diff --git a/internal/filter/internal/refreshable_test.go b/internal/filter/internal/refreshable_test.go index a55cf65..34e4b5e 100644 --- a/internal/filter/internal/refreshable_test.go +++ b/internal/filter/internal/refreshable_test.go @@ -9,10 +9,10 @@ import ( "testing" "time" - "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp" "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal" "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/filtertest" "github.com/AdguardTeam/golibs/logutil/slogutil" + "github.com/AdguardTeam/golibs/netutil/urlutil" "github.com/AdguardTeam/golibs/testutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -221,7 +221,7 @@ func TestRefreshable_Refresh_fileURL(t *testing.T) { c := &internal.RefreshableConfig{ Logger: slogutil.NewDiscardLogger(), URL: &url.URL{ - Scheme: agdhttp.SchemeFile, + Scheme: urlutil.SchemeFile, Path: fltFile.Name(), }, ID: refrID, diff --git a/internal/filter/internal/rulelist/dnsrewrite.go b/internal/filter/internal/rulelist/dnsrewrite.go index 6f16063..e0a8267 100644 --- a/internal/filter/internal/rulelist/dnsrewrite.go +++ b/internal/filter/internal/rulelist/dnsrewrite.go @@ -46,8 +46,10 @@ func ProcessDNSRewrites( } if dnsRewriteResult.RCode != dns.RcodeSuccess { - resp := messages.NewRespMsg(req) - resp.Rcode = dnsRewriteResult.RCode + // #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)) return &internal.ResultModifiedResponse{ Msg: resp, @@ -115,9 +117,9 @@ func processDNSRewriteRules(dnsr []*rules.NetworkRule) (res *dnsRewriteResult) { return dnsrr } -// 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. +// 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, @@ -127,7 +129,8 @@ func filterDNSRewrite( return nil, errors.Error("no dns rewrite rule responses") } - resp = messages.NewRespMsg(req) + // TODO(e.burkov): Use another constructor method for this. + resp = messages.NewBlockedRespRCode(req, dns.RcodeSuccess) rr := req.Question[0].Qtype values := dnsrr.Response[rr] diff --git a/internal/filter/internal/rulelist/refreshable.go b/internal/filter/internal/rulelist/refreshable.go index 3cb9f34..cbd8b37 100644 --- a/internal/filter/internal/rulelist/refreshable.go +++ b/internal/filter/internal/rulelist/refreshable.go @@ -5,12 +5,13 @@ import ( "fmt" "log/slog" "net/netip" + "strings" "sync" "github.com/AdguardTeam/AdGuardDNS/internal/agd" - "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp" "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal" + "github.com/AdguardTeam/golibs/netutil/urlutil" "github.com/AdguardTeam/urlfilter" "github.com/AdguardTeam/urlfilter/filterlist" ) @@ -42,7 +43,7 @@ func NewRefreshable(c *internal.RefreshableConfig, cache ResultCache) (f *Refres mu: &sync.RWMutex{}, } - if c.URL.Scheme == agdhttp.SchemeFile { + if strings.EqualFold(c.URL.Scheme, urlutil.SchemeFile) { return nil, fmt.Errorf("unsupported url %q", c.URL) } diff --git a/internal/filter/storage.go b/internal/filter/storage.go index 7811ea4..597bf2a 100644 --- a/internal/filter/storage.go +++ b/internal/filter/storage.go @@ -8,6 +8,7 @@ import ( "net/url" "path" "path/filepath" + "slices" "strings" "sync" "time" @@ -684,7 +685,7 @@ func (s *DefaultStorage) refresh(ctx context.Context, acceptStale bool) (err err func (s *DefaultStorage) addRuleList( ctx context.Context, ruleLists filteringRuleLists, - fl *filterIndexFilterData, + fl *indexData, acceptStale bool, ) { if _, ok := ruleLists[fl.id]; ok { @@ -741,7 +742,7 @@ func (s *DefaultStorage) addRuleList( func (s *DefaultStorage) reportRuleListError( ctx context.Context, ruleLists filteringRuleLists, - fl *filterIndexFilterData, + fl *indexData, err error, ) { errcoll.Collect(ctx, s.errColl, s.logger, "rule-list error", err) @@ -756,22 +757,24 @@ func (s *DefaultStorage) reportRuleListError( } // loadIndex fetches, decodes, and returns the filter list index data of the -// storage. +// storage. resp.Filters are sorted. func (s *DefaultStorage) loadIndex( ctx context.Context, acceptStale bool, -) (resp *filterIndexResp, err error) { +) (resp *indexResp, err error) { text, err := s.refr.Refresh(ctx, acceptStale) if err != nil { return nil, fmt.Errorf("loading index: %w", err) } - resp = &filterIndexResp{} + 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 } diff --git a/internal/geoip/asntops.go b/internal/geoip/asntops.go index 3fd1355..596ec23 100644 --- a/internal/geoip/asntops.go +++ b/internal/geoip/asntops.go @@ -6,10 +6,12 @@ import "github.com/AdguardTeam/golibs/container" // DefaultTopASNs contains all specially handled ASNs. var DefaultTopASNs = container.NewMapSet[ASN]( + 3, 137, 174, 209, 224, + 376, 559, 577, 701, @@ -21,6 +23,8 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 812, 852, 855, + 906, + 932, 967, 984, 1103, @@ -36,13 +40,12 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 1653, 1659, 1680, - 1741, - 1756, 1759, 1764, 1835, 1836, 1853, + 1886, 1897, 1930, 1955, @@ -52,22 +55,23 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 2110, 2116, 2119, + 2199, 2200, + 2470, 2497, 2514, 2516, 2518, 2519, 2527, - 2547, 2586, 2588, 2602, 2607, 2609, 2611, + 2614, 2740, - 2843, 2847, 2850, 2852, @@ -85,10 +89,10 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 3243, 3249, 3255, - 3258, 3269, 3292, 3301, + 3302, 3303, 3308, 3320, @@ -98,6 +102,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 3352, 3356, 3363, + 3399, 3462, 3549, 3559, @@ -107,16 +112,15 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 3741, 3758, 3786, - 3790, 3816, 3855, 4007, 4134, 4181, 4230, + 4515, 4538, 4609, - 4616, 4638, 4651, 4657, @@ -140,22 +144,26 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 4812, 4817, 4818, + 4826, 4837, 4847, 5089, + 5377, 5378, 5384, + 5385, 5390, 5391, + 5394, 5408, 5410, 5413, 5416, 5432, 5466, + 5479, 5483, 5518, - 5532, 5578, 5603, 5607, @@ -177,6 +185,9 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 6327, 6400, 6407, + 6412, + 6429, + 6453, 6535, 6568, 6639, @@ -191,6 +202,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 6739, 6752, 6758, + 6769, 6772, 6799, 6802, @@ -215,8 +227,10 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 7315, 7418, 7438, + 7470, 7482, 7497, + 7511, 7522, 7524, 7545, @@ -229,11 +243,13 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 7794, 7862, 7922, + 7979, 7992, 8014, 8048, 8053, 8075, + 8094, 8151, 8167, 8193, @@ -241,7 +257,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 8220, 8251, 8257, - 8285, 8290, 8301, 8339, @@ -266,11 +281,11 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 8468, 8473, 8517, - 8540, 8542, 8544, 8551, 8560, + 8562, 8585, 8612, 8632, @@ -278,7 +293,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 8680, 8681, 8697, - 8699, 8708, 8717, 8728, @@ -295,7 +309,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 8849, 8866, 8881, - 8896, 8926, 8948, 8953, @@ -324,9 +337,8 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 9155, 9158, 9198, - 9199, + 9208, 9231, - 9241, 9245, 9246, 9249, @@ -334,7 +346,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 9269, 9299, 9304, - 9312, 9316, 9318, 9329, @@ -345,6 +356,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 9365, 9381, 9416, + 9431, 9443, 9471, 9484, @@ -358,7 +370,10 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 9617, 9644, 9674, + 9676, + 9694, 9751, + 9770, 9790, 9808, 9824, @@ -367,29 +382,28 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 9873, 9902, 9908, - 9919, 9922, 9924, 9930, 9931, 9934, + 9976, 9981, 9988, 10010, 10013, 10030, 10036, + 10054, 10066, 10075, 10094, 10099, 10101, - 10103, 10118, 10131, 10139, 10143, - 10214, 10219, 10222, 10226, @@ -399,7 +413,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 10396, 10429, 10474, - 10617, 10620, 10796, 10834, @@ -420,7 +433,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 11492, 11556, 11562, - 11580, 11594, 11664, 11776, @@ -429,11 +441,11 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 11830, 11845, 11888, - 11992, 12046, 12066, 12083, 12091, + 12150, 12252, 12271, 12297, @@ -442,6 +454,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 12322, 12334, 12338, + 12350, 12353, 12361, 12365, @@ -458,14 +471,13 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 12455, 12479, 12491, - 12508, + 12496, 12552, + 12556, 12570, 12576, 12578, - 12597, 12605, - 12620, 12668, 12684, 12709, @@ -477,6 +489,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 12764, 12810, 12829, + 12835, 12849, 12874, 12876, @@ -484,6 +497,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 12929, 12946, 12958, + 12963, 12969, 12975, 12978, @@ -501,35 +515,31 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 13124, 13127, 13156, - 13170, 13188, 13189, 13194, - 13208, - 13213, 13280, 13285, 13306, 13335, 13489, + 13682, 13771, 13999, 14061, 14080, 14117, - 14232, 14259, 14434, 14522, - 14534, 14537, 14593, - 14618, 14638, 14709, 14754, 14813, 14868, + 14956, 14979, 14988, 15146, @@ -544,6 +554,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 15397, 15399, 15404, + 15405, 15419, 15433, 15435, @@ -559,7 +570,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 15557, 15600, 15614, - 15623, 15659, 15704, 15706, @@ -579,7 +589,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 15958, 15962, 15964, - 15965, 15994, 16010, 16019, @@ -596,6 +605,8 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 16223, 16229, 16232, + 16234, + 16245, 16246, 16276, 16322, @@ -623,7 +634,9 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 17411, 17421, 17451, + 17456, 17458, + 17465, 17470, 17480, 17488, @@ -643,12 +656,10 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 17676, 17698, 17709, - 17716, - 17726, 17809, 17816, 17828, - 17849, + 17839, 17853, 17858, 17882, @@ -658,7 +669,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 17993, 18001, 18004, - 18021, + 18013, 18024, 18049, 18053, @@ -670,7 +681,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 18199, 18200, 18209, - 18371, 18390, 18399, 18403, @@ -678,6 +688,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 18419, 18429, 18734, + 18747, 18809, 18822, 18840, @@ -693,6 +704,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 19626, 19711, 19863, + 19901, 19978, 20001, 20011, @@ -707,22 +719,21 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 20473, 20485, 20545, - 20582, + 20626, 20634, 20661, - 20676, 20719, 20723, 20771, 20776, 20804, 20845, - 20846, 20860, 20875, 20880, 20910, 20911, + 20928, 20940, 20963, 20978, @@ -731,7 +742,9 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 21021, 21040, 21050, + 21056, 21107, + 21127, 21183, 21211, 21230, @@ -745,6 +758,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 21351, 21412, 21430, + 21449, 21450, 21491, 21497, @@ -762,11 +776,13 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 22047, 22069, 22085, + 22313, 22423, 22581, 22652, 22690, 22724, + 22735, 22773, 22869, 22884, @@ -776,9 +792,9 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 23114, 23201, 23243, - 23383, 23487, 23520, + 23563, 23655, 23657, 23673, @@ -798,6 +814,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 23956, 23969, 24016, + 24033, 24086, 24088, 24157, @@ -805,7 +822,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 24163, 24164, 24165, - 24183, 24186, 24203, 24309, @@ -815,7 +831,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 24400, 24432, 24439, - 24441, 24444, 24445, 24492, @@ -827,12 +842,9 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 24589, 24608, 24631, - 24634, - 24645, 24651, 24691, 24722, - 24751, 24757, 24800, 24812, @@ -846,7 +858,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 24940, 24955, 25019, - 25094, + 25031, 25106, 25117, 25133, @@ -856,35 +868,41 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 25159, 25184, 25190, + 25206, 25229, 25248, 25250, 25255, 25273, + 25274, 25310, 25369, 25374, 25375, 25400, 25406, + 25424, 25429, 25441, 25447, 25454, - 25467, 25471, 25472, 25491, + 25509, 25512, 25513, + 25521, 25528, 25543, 25607, 25620, 25668, + 25695, 26130, 26210, - 26317, + 26383, + 26523, 26599, 26611, 26615, @@ -919,6 +937,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 27796, 27800, 27813, + 27828, 27831, 27833, 27837, @@ -940,7 +959,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 27932, 27947, 27951, - 27955, 27983, 27984, 27988, @@ -948,20 +966,20 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 28006, 28009, 28015, - 28022, 28024, 28032, + 28033, 28036, 28048, - 28049, + 28067, 28075, + 28080, 28094, 28103, 28104, 28118, 28126, 28146, - 28182, 28186, 28198, 28201, @@ -978,7 +996,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 28509, 28512, 28530, - 28531, 28532, 28534, 28536, @@ -991,6 +1008,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 28598, 28649, 28668, + 28682, 28683, 28685, 28717, @@ -1000,6 +1018,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 28787, 28812, 28840, + 28849, 28851, 28854, 28878, @@ -1007,9 +1026,12 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 28885, 28919, 28952, + 28964, 28972, 29027, 29030, + 29031, + 29039, 29049, 29061, 29070, @@ -1017,8 +1039,8 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 29091, 29119, 29124, + 29148, 29170, - 29182, 29208, 29238, 29244, @@ -1027,28 +1049,23 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 29286, 29310, 29314, - 29348, 29355, 29357, - 29384, 29405, 29447, 29465, 29485, 29492, - 29497, 29518, 29544, - 29545, 29555, 29571, 29580, 29582, 29584, - 29600, 29614, - 29657, 29687, + 29694, 29695, 29975, 30036, @@ -1063,7 +1080,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 30844, 30848, 30873, - 30896, 30982, 30983, 30985, @@ -1084,6 +1100,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 31143, 31148, 31163, + 31169, 31200, 31204, 31205, @@ -1094,24 +1111,24 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 31246, 31250, 31252, + 31257, 31272, 31287, - 31390, 31404, 31423, 31435, 31452, 31499, - 31510, 31543, 31549, 31615, + 31642, 31655, 31679, 31689, 31721, 31725, - 31863, + 31726, 31898, 31960, 32020, @@ -1137,23 +1154,22 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 33885, 33915, 33922, - 33943, - 33947, + 33924, 33983, 34001, 34058, 34087, + 34120, 34170, 34187, 34224, 34244, 34245, + 34263, 34295, 34296, - 34306, 34318, 34362, - 34376, 34447, 34458, 34471, @@ -1164,12 +1180,14 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 34547, 34557, 34569, + 34577, 34594, + 34606, + 34636, 34661, 34666, 34700, 34702, - 34705, 34718, 34724, 34743, @@ -1177,40 +1195,40 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 34779, 34797, 34803, - 34820, 34857, 34876, - 34916, - 34927, 34977, 34984, - 34989, + 35007, 35046, 35047, 35091, 35104, 35132, 35141, - 35158, 35179, 35191, 35197, 35223, + 35224, 35228, 35244, + 35277, 35297, 35311, - 35320, 35328, 35346, 35362, + 35370, + 35394, 35432, 35444, 35457, 35518, - 35546, 35549, + 35566, 35567, + 35568, 35612, 35613, 35699, @@ -1221,22 +1239,20 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 35790, 35805, 35807, + 35816, 35819, - 35892, 35900, 35911, 36290, 36408, 36511, 36549, - 36599, 36864, 36865, 36866, - 36868, 36873, 36874, - 36881, + 36877, 36884, 36890, 36892, @@ -1249,9 +1265,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 36912, 36913, 36914, - 36916, 36920, - 36923, 36924, 36925, 36926, @@ -1261,10 +1275,8 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 36947, 36955, 36958, - 36959, 36962, 36963, - 36965, 36968, 36972, 36974, @@ -1282,6 +1294,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 37014, 37020, 37027, + 37028, 37030, 37035, 37037, @@ -1295,6 +1308,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 37073, 37075, 37076, + 37081, 37084, 37090, 37094, @@ -1306,13 +1320,14 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 37119, 37123, 37124, + 37126, 37129, 37133, 37136, 37141, - 37143, 37148, 37154, + 37163, 37164, 37168, 37173, @@ -1334,7 +1349,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 37228, 37229, 37233, - 37254, 37257, 37273, 37282, @@ -1345,7 +1359,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 37305, 37309, 37313, - 37315, 37323, 37329, 37332, @@ -1357,6 +1370,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 37343, 37349, 37350, + 37353, 37358, 37371, 37376, @@ -1364,9 +1378,9 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 37395, 37406, 37410, - 37414, 37424, 37425, + 37429, 37430, 37440, 37447, @@ -1379,11 +1393,11 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 37463, 37473, 37475, - 37480, 37487, 37492, 37503, 37508, + 37515, 37517, 37518, 37524, @@ -1398,9 +1412,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 37552, 37559, 37563, - 37568, - 37569, - 37571, 37575, 37577, 37580, @@ -1408,6 +1419,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 37584, 37594, 37604, + 37608, 37611, 37612, 37614, @@ -1418,25 +1430,24 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 37645, 37649, 37654, - 37662, 37665, 37671, - 37675, - 37677, 37678, 37680, 37682, 37693, - 37697, 37705, 37721, + 37722, + 37723, + 37731, 37963, 38009, 38067, 38077, + 38082, 38107, 38136, - 38172, 38176, 38195, 38198, @@ -1447,6 +1458,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 38247, 38264, 38266, + 38286, 38322, 38442, 38466, @@ -1457,6 +1469,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 38600, 38623, 38742, + 38794, 38800, 38805, 38819, @@ -1464,41 +1477,36 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 38851, 38875, 38901, - 38987, + 38932, 38999, 39007, 39010, - 39013, 39032, 39067, 39074, - 39120, - 39122, 39184, 39216, 39232, - 39246, - 39251, 39273, - 39279, 39280, 39344, + 39351, 39374, 39397, 39402, 39440, - 39455, 39501, + 39544, + 39571, + 39574, 39603, 39608, 39611, - 39615, 39642, 39686, 39699, - 39766, + 39823, 39824, - 39826, 39891, 39906, 39927, @@ -1506,55 +1514,58 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 40786, 40788, 40945, - 40980, + 40994, + 41007, 41068, 41096, 41124, 41164, 41202, - 41228, 41230, + 41280, 41329, 41330, - 41371, + 41354, 41378, + 41436, 41454, 41496, 41549, 41557, 41563, 41564, - 41627, 41676, + 41697, 41704, + 41712, + 41714, 41733, 41738, 41745, 41750, 41798, 41833, - 41871, 41872, - 41878, + 41881, 41897, - 41922, 41956, 41966, 41997, + 41998, 42003, 42013, 42082, 42083, 42109, + 42162, 42183, - 42189, + 42232, 42298, 42306, 42313, 42334, 42337, 42437, - 42455, 42473, 42532, 42541, @@ -1562,9 +1573,9 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 42571, 42580, 42581, + 42586, 42610, 42652, - 42689, 42708, 42713, 42772, @@ -1573,17 +1584,14 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 42837, 42841, 42863, - 42905, + 42864, 42908, 42912, 42925, 42961, 42991, 43019, - 43030, 43060, - 43061, - 43070, 43139, 43197, 43205, @@ -1592,41 +1600,40 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 43256, 43350, 43375, + 43406, 43451, 43452, 43513, 43529, 43533, + 43568, 43612, 43627, - 43641, 43700, 43733, 43754, 43766, 43769, 43824, - 43883, - 43905, 43925, 43939, 43940, - 43972, 44021, 44027, 44034, 44086, 44087, 44090, + 44124, 44134, 44143, 44213, 44217, - 44218, 44234, 44244, - 44306, - 44324, + 44272, + 44309, + 44313, 44327, 44377, 44384, @@ -1641,22 +1648,20 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 44631, 44702, 44708, - 44725, 44735, + 44814, 44869, - 44925, 45090, 45102, 45143, 45177, 45178, - 45179, 45193, + 45230, 45245, 45271, 45305, 45345, - 45349, 45355, 45356, 45361, @@ -1673,19 +1678,19 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 45637, 45650, 45669, + 45671, 45700, 45754, 45758, 45763, 45766, 45773, + 45780, 45879, 45891, 45899, - 45903, 45905, 45916, - 45918, 45925, 45935, 45960, @@ -1693,57 +1698,58 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 46408, 46650, 46868, - 47110, 47114, + 47132, 47139, 47159, 47169, + 47171, 47204, 47217, 47232, + 47234, 47237, 47253, - 47292, 47331, 47377, 47394, - 47474, 47485, 47524, - 47583, + 47556, 47588, 47589, 47790, - 47794, 47798, + 47881, 47883, 47887, - 47890, 47898, - 47943, 47956, 47962, 48014, 48092, + 48101, + 48133, + 48147, 48161, - 48181, 48190, 48206, 48252, 48260, - 48271, 48288, + 48359, + 48418, 48431, 48437, 48480, 48492, 48503, 48506, - 48551, + 48602, 48629, 48675, - 48685, 48695, + 48715, 48728, 48789, 48830, @@ -1757,50 +1763,52 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 49056, 49100, 49101, - 49110, - 49113, 49115, - 49117, 49129, 49223, 49242, 49273, 49285, - 49472, + 49290, 49561, 49628, 49724, 49770, 49800, 49808, - 49840, - 49847, - 49870, + 49824, + 49826, 49889, 49902, 49911, + 49914, 49981, + 49985, 50010, 50053, 50181, + 50195, 50223, + 50242, 50251, 50261, 50266, 50274, + 50294, + 50304, 50334, 50349, + 50411, 50463, 50467, 50482, 50500, - 50506, - 50563, 50581, - 50593, 50613, 50616, + 50624, 50635, + 50648, 50670, 50685, 50749, @@ -1809,7 +1817,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 50810, 50821, 50825, - 50920, 50925, 50953, 50959, @@ -1818,25 +1825,22 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 50979, 51018, 51020, - 51056, - 51069, + 51026, 51104, 51110, - 51142, 51167, 51175, 51184, 51207, - 51247, 51265, 51336, - 51342, 51346, 51375, + 51395, + 51399, 51407, 51430, - 51495, - 51500, + 51469, 51504, 51582, 51645, @@ -1847,10 +1851,11 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 51809, 51825, 51847, - 51852, + 51878, 51896, 51964, - 52075, + 52080, + 52116, 52228, 52233, 52238, @@ -1860,8 +1865,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 52260, 52262, 52263, - 52286, - 52300, 52312, 52323, 52341, @@ -1869,9 +1872,9 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 52362, 52363, 52373, - 52381, 52398, 52405, + 52409, 52412, 52433, 52436, @@ -1881,19 +1884,18 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 52468, 52471, 52613, - 52783, 52974, 53006, - 53338, 53667, 53764, 54198, 54614, + 54801, 54994, 55033, - 55081, 55329, 55330, + 55387, 55391, 55392, 55424, @@ -1901,7 +1903,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 55430, 55501, 55577, - 55636, 55685, 55699, 55720, @@ -1911,7 +1912,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 55836, 55850, 55853, - 55900, + 55872, 55915, 55933, 55943, @@ -1939,21 +1940,22 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 56329, 56349, 56369, + 56400, 56410, - 56456, 56478, - 56561, + 56491, 56568, + 56572, 56653, 56655, 56656, 56665, 56696, 56704, + 56803, 56902, - 56926, + 56933, 56971, - 56983, 57000, 57013, 57016, @@ -1961,24 +1963,23 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 57070, 57112, 57134, + 57184, 57218, - 57248, - 57256, + 57223, 57269, + 57279, 57293, 57344, 57370, 57374, 57388, 57389, - 57391, 57443, + 57491, 57513, 57564, 57566, - 57606, 57630, - 57695, 57728, 57743, 57760, @@ -1986,21 +1987,17 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 57764, 57778, 57794, - 57826, 57852, 57869, + 57873, 57888, - 58044, - 58056, 58061, 58065, - 58118, 58130, 58224, - 58254, 58299, - 58309, 58322, + 58328, 58453, 58460, 58461, @@ -2019,56 +2016,60 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 58717, 58731, 58821, + 58893, 58895, 58945, 58952, + 58955, + 59108, 59125, - 59127, - 59253, 59257, 59317, 59318, 59355, 59362, - 59381, - 59463, + 59441, 59497, + 59577, 59588, 59625, 59668, 59702, 59729, - 59860, + 59847, 59861, 59890, 59989, + 60016, 60068, - 60111, - 60171, 60258, + 60277, 60294, + 60296, + 60304, 60352, 60367, 60372, 60377, + 60398, 60415, 60471, - 60533, 60587, 60636, 60656, 60690, 60725, - 60754, 60757, 60781, 60806, - 60976, + 60849, + 60895, 60999, - 61010, + 61071, 61079, 61112, 61135, + 61138, 61143, 61174, 61189, @@ -2077,9 +2078,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 61275, 61287, 61307, - 61317, - 61424, - 61437, 61449, 61461, 61466, @@ -2091,12 +2089,14 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 62059, 62161, 62179, + 62183, 62211, - 62214, + 62212, 62240, 62250, 62282, 62336, + 62337, 62419, 62563, 62627, @@ -2111,16 +2111,13 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 63962, 63969, 63991, - 64037, 64043, 64050, 64073, 64098, 64105, - 64126, 64134, 64443, - 64453, 64466, 131090, 131111, @@ -2128,7 +2125,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 131207, 131267, 131284, - 131322, 131325, 131429, 131445, @@ -2139,12 +2135,12 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 131596, 131602, 131627, + 131706, 131769, 132021, 132045, 132061, 132080, - 132104, 132148, 132165, 132167, @@ -2155,27 +2151,25 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 132222, 132255, 132298, + 132347, 132429, 132447, - 132449, 132462, 132468, 132471, - 132525, - 132608, 132618, + 132686, 132831, 132857, 133012, 133076, - 133192, 133199, 133200, - 133206, 133334, 133384, 133385, - 133480, + 133401, + 133433, 133481, 133524, 133533, @@ -2189,15 +2183,17 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 133875, 133894, 133897, + 133957, 133982, 134090, 134113, 134134, 134204, - 134220, 134356, 134420, + 134467, 134489, + 134525, 134562, 134651, 134674, @@ -2210,14 +2206,14 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 134762, 134768, 134783, - 134788, + 134810, 134840, - 134968, - 134972, 134995, 135043, + 135069, 135126, 135298, + 135333, 135341, 135371, 135375, @@ -2237,19 +2233,23 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 136030, 136039, 136093, + 136119, 136141, 136167, + 136168, 136195, 136210, - 136229, + 136224, 136238, 136255, - 136258, + 136379, 136380, 136384, 136400, 136442, 136454, + 136474, + 136477, 136479, 136480, 136515, @@ -2259,37 +2259,43 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 136780, 136787, 136873, - 136907, 136919, - 136920, + 136958, 136969, + 136986, 136994, 137047, 137080, 137187, - 137226, + 137259, 137409, 137412, 137424, 137449, 137453, 137503, + 137511, 137526, + 137549, 137561, 137693, 137697, 137872, + 137891, 137895, + 137905, 137959, 137967, 137989, 138089, + 138109, 138164, 138167, 138168, 138179, 138197, 138322, + 138345, 138346, 138368, 138384, @@ -2302,28 +2308,25 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 138655, 138754, 138886, + 138902, 138915, - 138934, + 138958, 138964, 138965, - 138997, 139003, 139009, 139029, 139043, - 139049, 139224, 139238, + 139285, + 139381, 139609, - 139628, - 139651, - 139741, + 139659, 139759, 139820, 139831, - 139837, 139841, - 139879, 139898, 139922, 139952, @@ -2333,19 +2336,15 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 140045, 140061, 140072, - 140091, - 140096, 140220, 140292, 140401, - 140457, 140499, 140504, + 140659, 140707, - 140867, 140900, 140989, - 141015, 141024, 141031, 141039, @@ -2353,7 +2352,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 141145, 141177, 141216, - 141224, 141342, 141421, 141607, @@ -2363,40 +2361,45 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 141767, 141778, 141995, + 142032, 142065, 142295, - 142386, + 142352, 142539, 142647, 147049, 147302, + 147314, 149034, 149173, 149359, 149360, 149419, 149456, - 149487, + 149521, 149660, 149707, 149771, 150153, + 150331, 150371, - 150407, + 150381, + 150452, 150683, 150692, - 150721, 150748, 150750, 150774, - 150797, + 150812, + 151066, 151080, 151341, 151396, + 151482, 151983, - 152140, 152317, - 152596, + 152337, + 152466, 152605, 152677, 196640, @@ -2414,88 +2417,81 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 197423, 197540, 197556, + 197623, + 197648, 197706, - 197716, 197830, 197862, 197882, 197897, - 198002, 198004, 198023, - 198068, - 198247, 198256, 198265, 198279, 198288, 198440, + 198441, 198471, - 198482, - 198499, + 198504, + 198537, 198589, 198605, 198668, - 198820, 198890, 198961, 198966, 199081, - 199128, 199140, 199155, 199276, - 199326, - 199374, 199469, 199490, 199493, 199524, - 199620, 199636, + 199698, 199707, 199731, 199739, - 199768, 199785, 199811, + 199995, 200019, 200134, 200154, + 200200, 200313, - 200355, 200446, 200590, 200612, - 200640, - 200651, - 200665, - 200698, + 200628, 200724, 200736, 200740, + 200742, 200845, 200865, 200899, + 200923, + 200964, 201019, 201073, 201089, + 201096, + 201107, 201120, + 201150, 201167, - 201187, 201241, 201249, - 201322, - 201363, 201411, - 201502, 201505, + 201540, 201577, 201589, - 201601, + 201596, 201603, - 201701, - 201746, 201749, 201767, 201776, @@ -2508,6 +2504,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 202087, 202098, 202103, + 202204, 202254, 202293, 202422, @@ -2515,21 +2512,19 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 202441, 202468, 202498, - 202558, 202561, 202613, + 202616, 202618, 202632, 202635, 202651, 202662, 202710, - 202759, + 202722, 202870, 202921, - 202931, 202940, - 202960, 202987, 203020, 203136, @@ -2537,18 +2532,18 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 203214, 203217, 203257, - 203409, 203424, - 203448, + 203446, 203451, - 203561, 203622, 203675, 203680, - 203811, + 203715, + 203827, 203877, 203912, 203916, + 203917, 203936, 203953, 203964, @@ -2558,32 +2553,35 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 204020, 204106, 204108, + 204149, 204151, 204165, 204170, 204274, 204279, + 204281, 204317, - 204342, 204403, - 204410, + 204429, 204457, - 204565, + 204566, + 204592, 204595, + 204650, 204716, - 204802, - 204816, + 204894, 204918, 204957, 204986, - 205015, 205110, + 205168, 205244, 205254, + 205275, + 205278, 205293, - 205367, 205368, - 205371, + 205516, 205547, 205638, 205645, @@ -2596,116 +2594,129 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 206026, 206065, 206067, + 206092, 206119, - 206170, 206206, 206238, 206260, 206262, 206283, - 206358, + 206351, 206375, - 206391, 206406, + 206446, 206471, 206485, 206519, 206557, 206610, 206666, + 206774, 206783, 206804, 206892, - 206920, + 206912, 206928, 206977, 207044, 207097, 207137, - 207159, + 207154, 207192, + 207246, 207251, 207348, 207369, 207375, 207474, + 207502, + 207541, 207569, 207589, + 207617, 207713, 207782, + 207790, 207810, 207876, - 207980, 207990, 207991, + 208149, 208286, 208320, 208324, 208339, - 208448, 208570, 208592, 208671, 208730, 208734, - 208859, 208864, 208905, + 208909, 208972, 208997, 209012, 209046, 209049, 209193, + 209196, 209240, 209262, + 209273, 209277, - 209302, 209360, 209424, 209442, 209491, + 209531, 209835, 209839, 209854, 209948, + 210001, 210003, 210016, 210021, 210022, 210125, 210147, + 210150, 210218, + 210273, 210278, 210315, 210402, + 210509, 210616, 210625, 210644, 210693, 210740, - 210797, + 210808, 210964, 211028, 211057, + 211147, + 211196, 211210, 211211, 211235, + 211250, 211309, - 211322, 211356, - 211385, 211450, 211468, - 211504, - 211555, 211559, - 211689, + 211774, 211908, + 211913, 211995, + 212046, 212183, 212238, 212330, + 212444, 212449, 212531, 212572, @@ -2715,36 +2726,36 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 212645, 212655, 212661, - 212766, 212898, + 212910, 212986, 212999, - 213139, 213155, 213295, 213320, 213398, 213402, 214739, - 214798, + 214869, + 214990, 215052, 215284, 215287, + 215304, 215346, 215355, 215416, - 215421, 215423, 215501, 215540, 215733, 215746, - 215886, 215910, + 215921, 216071, 216086, 216139, - 216200, + 216183, 216312, 216325, 216374, @@ -2757,8 +2768,10 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 262186, 262191, 262197, + 262199, 262202, 262210, + 262220, 262223, 262234, 262239, @@ -2769,6 +2782,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 262287, 262354, 262378, + 262459, 262468, 262481, 262494, @@ -2778,22 +2792,24 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 262773, 262916, 262932, + 263026, 263073, - 263170, + 263175, 263210, - 263218, + 263216, 263222, 263224, 263238, 263242, 263245, + 263248, 263292, 263327, - 263684, 263686, 263689, 263694, 263698, + 263699, 263703, 263717, 263725, @@ -2801,7 +2817,6 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 263750, 263751, 263759, - 263761, 263762, 263763, 263765, @@ -2809,6 +2824,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 263785, 263791, 263792, + 263793, 263805, 263824, 263980, @@ -2816,6 +2832,7 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 264605, 264609, 264628, + 264631, 264635, 264637, 264640, @@ -2823,47 +2840,50 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 264645, 264646, 264663, - 264676, 264685, 264694, 264696, 264731, + 264732, 264733, 264738, 264744, 264750, 264756, + 264758, 264770, 264778, 264779, 264780, 264783, - 264796, 264814, 264821, 264825, 264837, 264838, 264847, - 264984, 265509, 265540, 265561, 265594, 265606, - 265608, + 265629, 265631, 265632, 265636, - 265641, + 265662, + 265663, 265675, 265684, - 265688, 265691, + 265705, + 265706, 265711, 265721, 265727, + 265728, 265735, + 265749, 265757, 265767, 265780, @@ -2872,37 +2892,36 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 265816, 265818, 265822, + 265826, + 265840, 265855, 265867, 266445, 266668, 266673, - 266677, - 266686, - 266698, - 266704, - 266716, - 266725, + 266694, 266734, + 266742, 266755, 266757, + 266762, 266792, 266802, + 266809, 266812, 266814, 266815, + 266831, 266841, 266853, 266858, 266860, - 266876, 266880, 266893, 266894, 266904, 267684, 267685, - 267692, 267699, 267705, 267708, @@ -2910,20 +2929,25 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 267749, 267761, 267765, - 267790, 267795, + 267796, 267797, 267803, 267809, + 267815, 267828, + 267837, 267845, 267846, 267869, 267882, + 267883, + 267896, 267904, - 267920, 268323, + 268976, 269036, + 269194, 269729, 269730, 269733, @@ -2932,9 +2956,9 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 269749, 269750, 269769, - 269780, 269782, 269783, + 269794, 269797, 269804, 269806, @@ -2945,35 +2969,35 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 269832, 269838, 269840, + 269843, 269846, 269853, 269862, - 269878, 269894, - 269898, 269901, 269908, 269918, 269919, 269921, - 269926, 269927, 269931, 269934, - 269936, 269940, 269946, + 269953, 269955, 269960, 269964, 269965, + 269973, 269976, - 269981, + 269983, 269984, + 269986, 269989, 270007, + 270023, 270026, - 270029, 270035, 270036, 270049, @@ -2981,26 +3005,26 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 270058, 270068, 270071, - 270073, 270096, - 270098, 270108, 270158, 270161, + 270186, 271773, 271781, - 271785, 271791, 271795, - 271806, + 271804, 271808, + 271812, 271814, - 271816, 271819, - 271822, 271835, + 271837, 271868, 271874, + 271880, + 271898, 271899, 271907, 271909, @@ -3010,92 +3034,84 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 271932, 271933, 271935, + 271936, 271942, - 271945, 271951, - 271962, 271965, 271971, - 271982, + 271978, + 271996, 272011, - 272019, + 272018, 272026, 272027, 272037, + 272057, 272059, - 272062, - 272065, 272083, - 272095, 272099, 272102, 272106, + 272109, 272110, 272112, 272120, 272122, - 272123, 272134, 272809, 272818, 272836, 272851, 272882, + 272886, 272906, 272914, 272916, - 272921, + 272953, 272955, 272978, + 272980, 272991, - 273000, + 273019, 273054, - 273063, 273067, - 273078, 273093, 273113, + 273123, 273133, 273155, 273171, 273172, - 273231, - 273237, - 273309, - 273843, - 273844, + 273189, 327687, 327693, 327697, 327700, 327707, + 327708, 327712, - 327714, 327716, 327724, 327728, + 327733, 327738, - 327747, 327750, 327756, 327760, 327765, - 327768, 327769, - 327770, + 327771, 327776, 327782, 327786, 327794, 327795, - 327798, 327799, 327802, 327804, - 327814, 327819, + 327820, 327828, - 327829, 327830, 327862, 327863, @@ -3112,22 +3128,23 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 327941, 327972, 327975, - 327987, - 327990, 327991, 327992, - 328001, + 327996, 328061, 328068, 328073, + 328075, 328079, 328088, 328111, - 328118, 328136, 328140, + 328146, + 328154, 328169, 328181, + 328182, 328191, 328196, 328198, @@ -3137,17 +3154,15 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 328228, 328250, 328253, - 328258, - 328282, + 328269, 328284, 328286, 328297, - 328304, - 328310, - 328316, + 328317, 328319, 328331, 328341, + 328344, 328358, 328411, 328436, @@ -3156,25 +3171,26 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 328469, 328471, 328473, + 328475, 328479, 328480, 328488, 328490, 328494, + 328509, 328510, 328514, - 328517, 328535, 328539, 328546, 328549, + 328567, 328570, + 328576, 328581, 328586, 328590, 328594, - 328600, - 328604, 328605, 328610, 328611, @@ -3189,27 +3205,20 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 328708, 328717, 328727, + 328733, 328734, - 328753, + 328743, 328755, - 328770, - 328777, 328817, - 328824, - 328835, 328844, 328849, - 328850, 328856, - 328857, 328858, 328880, 328895, 328899, 328923, - 328939, 328943, - 328950, 328954, 328959, 328961, @@ -3223,45 +3232,45 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 328997, 329006, 329014, + 329020, + 329021, 329027, - 329028, 329029, 329044, - 329048, + 329074, 329078, 329082, 329101, - 329103, - 329126, 329129, 329135, - 329155, 329167, 329169, 329170, + 329174, 329179, 329183, 329192, 329205, 329211, 329219, + 329220, 329227, 329253, 329254, - 329255, 329261, 329274, + 329286, 329301, - 329373, 329387, 329390, - 329411, + 329413, 329415, 329422, 329437, 329472, 393275, 393629, + 393894, 394311, 394381, 394684, @@ -3277,10 +3286,8 @@ var DefaultTopASNs = container.NewMapSet[ASN]( 398228, 398721, 398901, - 399444, 399498, 399724, - 400099, 400619, ) @@ -3291,8 +3298,8 @@ var DefaultCountryTopASNs = map[Country]ASN{ CountryAF: 55330, CountryAG: 11594, CountryAI: 396304, - CountryAL: 50616, - CountryAM: 44395, + CountryAL: 50973, + CountryAM: 43733, CountryAO: 37119, CountryAQ: 199707, CountryAR: 7303, @@ -3310,7 +3317,7 @@ var DefaultCountryTopASNs = map[Country]ASN{ CountryBG: 8866, CountryBH: 5416, CountryBI: 327799, - CountryBJ: 328228, + CountryBJ: 37424, CountryBM: 32020, CountryBN: 10094, CountryBO: 6568, @@ -3323,31 +3330,32 @@ var DefaultCountryTopASNs = map[Country]ASN{ CountryBZ: 10269, CountryCA: 812, CountryCD: 37020, - CountryCF: 328079, + CountryCF: 37460, CountryCG: 36924, CountryCH: 6730, CountryCI: 29571, CountryCK: 10131, CountryCL: 27651, - CountryCM: 30992, + CountryCM: 36912, CountryCN: 4134, CountryCO: 27831, CountryCR: 52263, CountryCU: 27725, CountryCV: 37517, - CountryCW: 11081, + CountryCW: 52233, CountryCY: 6866, CountryCZ: 5610, CountryDE: 3320, CountryDJ: 30990, - CountryDK: 212238, + CountryDK: 13335, CountryDM: 40945, CountryDO: 6400, CountryDZ: 36947, CountryEC: 27947, CountryEE: 3249, CountryEG: 8452, - CountryER: 212238, + CountryEH: 6713, + CountryER: 24757, CountryES: 3352, CountryET: 24757, CountryFI: 51765, @@ -3362,11 +3370,11 @@ var DefaultCountryTopASNs = map[Country]ASN{ CountryGF: 16028, CountryGG: 8680, CountryGH: 30986, - CountryGI: 8301, + CountryGI: 202087, CountryGL: 8818, CountryGM: 37552, CountryGN: 37461, - CountryGP: 3215, + CountryGP: 16028, CountryGQ: 37173, CountryGR: 6799, CountryGT: 14754, @@ -3376,17 +3384,17 @@ var DefaultCountryTopASNs = map[Country]ASN{ CountryHK: 4760, CountryHN: 14754, CountryHR: 5391, - CountryHT: 52260, + CountryHT: 27653, CountryHU: 5483, CountryID: 7713, CountryIE: 15502, - CountryIL: 1680, + CountryIL: 12400, CountryIM: 13122, CountryIN: 55836, CountryIO: 17458, CountryIQ: 203214, CountryIR: 197207, - CountryIS: 44735, + CountryIS: 56704, CountryIT: 1267, CountryJE: 8680, CountryJM: 30689, @@ -3395,20 +3403,20 @@ var DefaultCountryTopASNs = map[Country]ASN{ CountryKE: 33771, CountryKG: 47237, CountryKH: 38623, - CountryKI: 134783, - CountryKM: 328061, + CountryKI: 135409, + CountryKM: 36939, CountryKN: 11139, CountryKR: 4766, CountryKW: 29357, CountryKY: 6639, CountryKZ: 206026, CountryLA: 9873, - CountryLB: 42003, + CountryLB: 38999, CountryLC: 15344, CountryLI: 136787, CountryLK: 18001, CountryLR: 37410, - CountryLS: 37057, + CountryLS: 33567, CountryLT: 8764, CountryLU: 6661, CountryLV: 24921, @@ -3417,7 +3425,7 @@ var DefaultCountryTopASNs = map[Country]ASN{ CountryMC: 6758, CountryMD: 8926, CountryME: 43940, - CountryMF: 14593, + CountryMF: 33392, CountryMG: 37054, CountryMH: 24439, CountryMK: 6821, @@ -3426,9 +3434,9 @@ var DefaultCountryTopASNs = map[Country]ASN{ CountryMN: 17882, CountryMO: 4609, CountryMP: 7131, - CountryMQ: 3215, + CountryMQ: 20776, CountryMR: 29544, - CountryMS: 396304, + CountryMS: 11139, CountryMT: 12709, CountryMU: 23889, CountryMV: 7642, @@ -3436,15 +3444,16 @@ var DefaultCountryTopASNs = map[Country]ASN{ CountryMX: 8151, CountryMY: 4788, CountryMZ: 37342, - CountryNA: 37009, - CountryNC: 18200, - CountryNE: 37531, + CountryNA: 36996, + CountryNC: 17480, + CountryNE: 37233, CountryNG: 29465, CountryNI: 14754, CountryNL: 1136, CountryNO: 9009, CountryNP: 17501, CountryNR: 140504, + CountryNU: 198605, CountryNZ: 9790, CountryOM: 28885, CountryPA: 11556, @@ -3455,12 +3464,12 @@ var DefaultCountryTopASNs = map[Country]ASN{ CountryPK: 45669, CountryPL: 5617, CountryPM: 3695, - CountryPR: 14638, + CountryPR: 21928, CountryPS: 12975, CountryPT: 12353, CountryPW: 17893, CountryPY: 23201, - CountryQA: 42298, + CountryQA: 8781, CountryRE: 199140, CountryRO: 8708, CountryRS: 8400, @@ -3468,19 +3477,19 @@ var DefaultCountryTopASNs = map[Country]ASN{ CountryRW: 36890, CountrySA: 39891, CountrySB: 45891, - CountrySC: 131464, + CountrySC: 36958, CountrySD: 15706, CountrySE: 13335, CountrySG: 4773, - CountrySH: 198605, - CountrySI: 5603, + CountrySH: 37645, + CountrySI: 3212, CountrySK: 6855, CountrySL: 37164, CountrySM: 15433, CountrySN: 8346, CountrySO: 37371, CountrySR: 27775, - CountrySS: 328755, + CountrySS: 37594, CountryST: 328191, CountrySV: 14754, CountrySX: 27781, @@ -3498,19 +3507,19 @@ var DefaultCountryTopASNs = map[Country]ASN{ CountryTR: 47331, CountryTT: 27800, CountryTW: 3462, - CountryTZ: 36908, + CountryTZ: 37035, CountryUA: 15895, CountryUG: 37075, - CountryUS: 21928, + CountryUS: 7922, CountryUY: 6057, CountryUZ: 8193, CountryVA: 8978, CountryVC: 46408, CountryVE: 8048, - CountryVG: 11139, + CountryVG: 396357, CountryVI: 14434, CountryVN: 7552, - CountryVU: 9249, + CountryVU: 132429, CountryWF: 45879, CountryWS: 38800, CountryXK: 21246, @@ -3518,5 +3527,5 @@ var DefaultCountryTopASNs = map[Country]ASN{ CountryYT: 3215, CountryZA: 37457, CountryZM: 37287, - CountryZW: 56696, + CountryZW: 37204, } diff --git a/internal/geoip/asntops_generate.go b/internal/geoip/asntops_generate.go index 6b08537..d1a70bc 100644 --- a/internal/geoip/asntops_generate.go +++ b/internal/geoip/asntops_generate.go @@ -13,6 +13,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp" "github.com/AdguardTeam/AdGuardDNS/internal/geoip" + "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/httphdr" "github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/osutil" @@ -27,22 +28,22 @@ func main() { Timeout: 10 * time.Second, } - req, err := http.NewRequestWithContext(ctx, http.MethodGet, countriesASNURL, nil) - check(err) + req := errors.Must(http.NewRequestWithContext(ctx, http.MethodGet, countriesASNURL, nil)) req.Header.Add(httphdr.UserAgent, agdhttp.UserAgent()) - resp, err := c.Do(req) - check(err) + resp := errors.Must(c.Do(req)) defer slogutil.CloseAndLog(ctx, logger, resp.Body, slog.LevelError) - out, err := os.OpenFile("./asntops.go", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o664) - check(err) + err := agdhttp.CheckStatus(resp, http.StatusOK) + errors.Check(err) + + out := errors.Must(os.OpenFile("./asntops.go", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o664)) defer slogutil.CloseAndLog(ctx, logger, out, slog.LevelError) defaultCountryTopASNs := map[geoip.Country][]geoip.ASN{} err = json.NewDecoder(resp.Body).Decode(&defaultCountryTopASNs) - check(err) + errors.Check(err) // Don't use a *container.MapSet here, because the map is iterated over in // the template. @@ -65,11 +66,10 @@ func main() { DefaultCountryTopASNs: defaultCountryTopASNs, } - tmpl, err := template.New("main").Parse(tmplStr) - check(err) + tmpl := template.Must(template.New("main").Parse(tmplStr)) err = tmpl.Execute(out, tmplData) - check(err) + errors.Check(err) } // countriesASNURL is the default URL to get the per-country top ASN statistics @@ -101,10 +101,3 @@ var DefaultCountryTopASNs = map[Country]ASN{ {{- end }} } ` - -// check is a simple error checker. -func check(err error) { - if err != nil { - panic(err) - } -} diff --git a/internal/geoip/country.go b/internal/geoip/country.go index a095cf3..21a198c 100644 --- a/internal/geoip/country.go +++ b/internal/geoip/country.go @@ -9,8 +9,6 @@ import ( "github.com/AdguardTeam/golibs/errors" ) -// Country Codes - // Country represents an ISO 3166-1 alpha-2 country code. type Country string diff --git a/internal/geoip/country_generate.go b/internal/geoip/country_generate.go index 97c2bf9..3601699 100644 --- a/internal/geoip/country_generate.go +++ b/internal/geoip/country_generate.go @@ -14,6 +14,7 @@ import ( "time" "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp" + "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/httphdr" "github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/osutil" @@ -28,22 +29,18 @@ func main() { Timeout: 10 * time.Second, } - req, err := http.NewRequest(http.MethodGet, csvURL, nil) - check(err) + req := errors.Must(http.NewRequest(http.MethodGet, csvURL, nil)) req.Header.Add(httphdr.UserAgent, agdhttp.UserAgent()) - resp, err := c.Do(req) - check(err) + resp := errors.Must(c.Do(req)) defer slogutil.CloseAndLog(ctx, logger, resp.Body, slog.LevelError) - out, err := os.OpenFile("./country.go", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o664) - check(err) + out := errors.Must(os.OpenFile("./country.go", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o664)) defer slogutil.CloseAndLog(ctx, logger, out, slog.LevelError) r := csv.NewReader(resp.Body) - rows, err := r.ReadAll() - check(err) + rows := errors.Must(r.ReadAll()) // Skip the first row, as it is a header. rows = rows[1:] @@ -54,11 +51,10 @@ func main() { return strings.Compare(a[1], b[1]) }) - tmpl, err := template.New("main").Parse(tmplStr) - check(err) + tmpl := template.Must(template.New("main").Parse(tmplStr)) - err = tmpl.Execute(out, rows) - check(err) + err := tmpl.Execute(out, rows) + errors.Check(err) } // csvURL is the default URL of the information about country codes. @@ -76,8 +72,6 @@ import ( "github.com/AdguardTeam/golibs/errors" ) -// Country Codes - // Country represents an ISO 3166-1 alpha-2 country code. type Country string @@ -165,10 +159,3 @@ func isUserAssigned(s string) (ok bool) { } } ` - -// check is a simple error checker. -func check(err error) { - if err != nil { - panic(err) - } -} diff --git a/internal/metrics/consul.go b/internal/metrics/consul.go index d7f45d7..57ef5e4 100644 --- a/internal/metrics/consul.go +++ b/internal/metrics/consul.go @@ -1,33 +1,109 @@ package metrics import ( + "context" + "fmt" + + "github.com/AdguardTeam/golibs/container" + "github.com/AdguardTeam/golibs/errors" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" ) -var ( - // ConsulAllowlistSize is a gauge with the number of records in the - // ratelimit allowlist loaded from Consul. - ConsulAllowlistSize = promauto.NewGauge(prometheus.GaugeOpts{ - Subsystem: subsystemConsul, - Namespace: namespace, - Name: "allowlist_size", - Help: "Size of the ratelimit allowlist loaded from Consul.", - }) - // ConsulAllowlistUpdateStatus is a gauge with the status of the last - // ratelimit allowlist update. 1 means success. - ConsulAllowlistUpdateStatus = promauto.NewGauge(prometheus.GaugeOpts{ - Subsystem: subsystemConsul, - Namespace: namespace, - Name: "allowlist_update_status", - Help: "Status of the last ratelimit allowlist update. 1 means success.", - }) - // ConsulAllowlistUpdateTime is a gauge with the timestamp of the last - // ratelimit allowlist update. - ConsulAllowlistUpdateTime = promauto.NewGauge(prometheus.GaugeOpts{ - Subsystem: subsystemConsul, - Namespace: namespace, - Name: "allowlist_update_timestamp", - Help: "Timestamp of the last ratelimit allowlist update.", - }) -) +// Allowlist is the Prometheus-based implementation of the [consul.Metrics] +// interface. +type Allowlist struct { + // size is a gauge with the number of loaded records in the ratelimit + // allowlist. + size prometheus.Gauge + + // updateStatus is a gauge with the status of the last ratelimit allowlist + // update. 1 means success. + updateStatus prometheus.Gauge + + // updateTime is a gauge with the timestamp of the last ratelimit allowlist + // update. + updateTime prometheus.Gauge +} + +// NewAllowlist registers the Consul allowlist metrics in reg and returns a +// properly initialized [Allowlist]. +func NewAllowlist( + namespace string, + reg prometheus.Registerer, + typ string, +) (m *Allowlist, err error) { + switch typ { + case subsystemBackend, subsystemConsul: + // Go on. + default: + return nil, fmt.Errorf("subsystem: %w: %q", errors.ErrBadEnumValue, typ) + } + + const ( + size = "allowlist_size" + updateStatus = "allowlist_update_status" + updateTime = "allowlist_update_timestamp" + ) + + labels := prometheus.Labels{"type": typ} + + m = &Allowlist{ + size: prometheus.NewGauge(prometheus.GaugeOpts{ + Subsystem: subsystemRateLimit, + Namespace: namespace, + Name: size, + Help: "Size of the loaded ratelimit allowlist.", + ConstLabels: labels, + }), + updateStatus: prometheus.NewGauge(prometheus.GaugeOpts{ + Subsystem: subsystemRateLimit, + Namespace: namespace, + Name: updateStatus, + Help: "Status of the last ratelimit allowlist update. 1 means success.", + ConstLabels: labels, + }), + updateTime: prometheus.NewGauge(prometheus.GaugeOpts{ + Subsystem: subsystemRateLimit, + Namespace: namespace, + Name: updateTime, + Help: "Timestamp of the last ratelimit allowlist update.", + ConstLabels: labels, + }), + } + + var errs []error + collectors := container.KeyValues[string, prometheus.Collector]{{ + Key: size, + Value: m.size, + }, { + Key: updateStatus, + Value: m.updateStatus, + }, { + Key: updateTime, + Value: m.updateTime, + }} + + 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 +} + +// SetSize implements the [consul.Metrics] interface for *Allowlist. +func (m *Allowlist) SetSize(_ context.Context, n int) { + m.size.Set(float64(n)) +} + +// SetStatus implements the [consul.Metrics] interface for *Allowlist. +func (m *Allowlist) SetStatus(_ context.Context, err error) { + m.updateTime.SetToCurrentTime() + SetStatusGauge(m.updateStatus, err) +} diff --git a/internal/metrics/metrics_test.go b/internal/metrics/metrics_test.go index 18f79eb..0a50d9c 100644 --- a/internal/metrics/metrics_test.go +++ b/internal/metrics/metrics_test.go @@ -3,11 +3,13 @@ package metrics_test import ( "github.com/AdguardTeam/AdGuardDNS/internal/backendpb" "github.com/AdguardTeam/AdGuardDNS/internal/billstat" + "github.com/AdguardTeam/AdGuardDNS/internal/consul" "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "github.com/AdguardTeam/AdGuardDNS/internal/dnssvc" "github.com/AdguardTeam/AdGuardDNS/internal/metrics" "github.com/AdguardTeam/AdGuardDNS/internal/profiledb" "github.com/AdguardTeam/AdGuardDNS/internal/remotekv/rediskv" + "github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig" ) // type check @@ -17,6 +19,7 @@ import ( var ( _ backendpb.Metrics = (*metrics.BackendPB)(nil) _ billstat.Metrics = (*metrics.Billstat)(nil) + _ consul.Metrics = (*metrics.Allowlist)(nil) _ dnsmsg.ClonerStat = metrics.ClonerStat{} _ dnssvc.MainMiddlewareMetrics = (*metrics.DefaultMainMiddleware)(nil) _ dnssvc.MainMiddlewareMetrics = metrics.MainMiddleware(nil) @@ -24,4 +27,5 @@ var ( _ dnssvc.RatelimitMiddlewareMetrics = metrics.RatelimitMiddleware(nil) _ profiledb.Metrics = (*metrics.ProfileDB)(nil) _ rediskv.Metrics = (*metrics.RedisKV)(nil) + _ tlsconfig.Metrics = (*metrics.TLSConfig)(nil) ) diff --git a/internal/metrics/tls.go b/internal/metrics/tls.go index 3001dd7..e38d2b8 100644 --- a/internal/metrics/tls.go +++ b/internal/metrics/tls.go @@ -1,112 +1,148 @@ package metrics import ( + "context" "crypto/tls" "fmt" "slices" "strings" + "time" + "github.com/AdguardTeam/golibs/container" + "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/netutil" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" ) -var ( - // TLSCertificateInfo is a gauge with the authentication algorithm of - // the certificate. - TLSCertificateInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ - Name: "cert_info", - Namespace: namespace, - Subsystem: subsystemTLS, - Help: "Authentication algorithm and other information about the certificate.", - }, []string{"auth_algo", "subject"}) +// TLSConfig is the Prometheus-based implementation of the [tlsconfig.Metrics] +// interface. +type TLSConfig struct { + // certificateInfo is a gauge with the authentication algorithm of the + // certificate. + certificateInfo *prometheus.GaugeVec - // TLSCertificateNotAfter is a gauge with the time when the certificate + // certificateNotAfter is a gauge with the time when the certificate // expires. - TLSCertificateNotAfter = promauto.NewGaugeVec(prometheus.GaugeOpts{ - Name: "cert_not_after", - Namespace: namespace, - Subsystem: subsystemTLS, - Help: "Time when the certificate expires.", - }, []string{"subject"}) + certificateNotAfter *prometheus.GaugeVec - // TLSSessionTicketsRotateStatus is a gauge with the status of the last - // tickets rotation. - TLSSessionTicketsRotateStatus = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "session_tickets_rotate_status", - Namespace: namespace, - Subsystem: subsystemTLS, - Help: "Status of the last tickets rotation.", - }) - // TLSSessionTicketsRotateTime is a gauge with the time when the TLS session + // sessionTicketsRotateStatus is a gauge with the status of the last tickets + // rotation. + sessionTicketsRotateStatus prometheus.Gauge + + // sessionTicketsRotateTime is a gauge with the time when the TLS session // tickets were rotated. - TLSSessionTicketsRotateTime = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "session_tickets_rotate_time", - Namespace: namespace, - Subsystem: subsystemTLS, - Help: "Time when the TLS session tickets were rotated.", - }) - // TLSHandshakeAttemptsTotal is a counter with the total number of attempts - // to establish a TLS connection. "supported_protos" is a comma-separated - // list of the protocols supported by the client. - TLSHandshakeAttemptsTotal = promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "handshake_attempts_total", - Namespace: namespace, - Subsystem: subsystemTLS, - Help: "Total count of TLS handshakes.", - }, []string{ - "proto", - "supported_protos", - "tls_version", - }) - // TLSHandshakeTotal is a counter with the total count of TLS handshakes. - TLSHandshakeTotal = promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "handshake_total", - Namespace: namespace, - Subsystem: subsystemTLS, - Help: "Total count of TLS handshakes.", - }, []string{ - "proto", - "tls_version", - "did_resume", - "cipher_suite", - "negotiated_proto", - "server_name", - }) -) + sessionTicketsRotateTime prometheus.Gauge -// TLSMetricsAfterHandshake is a function that needs to be passed to -// *tls.Config VerifyConnection. -func TLSMetricsAfterHandshake( - proto string, - srvName string, - devDomains []string, - srvCerts []tls.Certificate, -) (f func(tls.ConnectionState) error) { - return func(state tls.ConnectionState) error { - sLabel := serverNameToLabel(state.ServerName, srvName, devDomains, srvCerts) + // handshakeAttemptsTotal is a counter with the total number of attempts to + // establish a TLS connection. "supported_protos" is a comma-separated list + // of the protocols supported by the client. + handshakeAttemptsTotal *prometheus.CounterVec - // Stick to using WithLabelValues instead of With in order to avoid - // extra allocations on prometheus.Labels. The labels order is VERY - // important here. - TLSHandshakeTotal.WithLabelValues( - proto, - tlsVersionToString(state.Version), - BoolString(state.DidResume), - tls.CipherSuiteName(state.CipherSuite), - // Don't validate the negotiated protocol since it's expected to - // contain only ASCII after negotiation itself. - state.NegotiatedProtocol, - sLabel, - ).Inc() - - return nil - } + // handshakeTotal is a counter with the total count of TLS handshakes. + handshakeTotal *prometheus.CounterVec } -// TLSMetricsBeforeHandshake is a function that needs to be passed to -// *tls.Config GetConfigForClient. -func TLSMetricsBeforeHandshake(proto string) (f func(*tls.ClientHelloInfo) (*tls.Config, error)) { +// 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 ( + certInfo = "cert_info" + certNotAfter = "cert_not_after" + sessTicketsRotateStatus = "session_tickets_rotate_status" + sessTicketsRotateTime = "session_tickets_rotate_time" + handshakeAttemptsTotal = "handshake_attempts_total" + handshakeTotal = "handshake_total" + ) + + m = &TLSConfig{ + certificateInfo: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: certInfo, + Namespace: namespace, + Subsystem: subsystemTLS, + Help: "Authentication algorithm and other information about the certificate.", + }, []string{"auth_algo", "subject"}), + certificateNotAfter: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: certNotAfter, + Namespace: namespace, + Subsystem: subsystemTLS, + Help: "Time when the certificate expires.", + }, []string{"subject"}), + sessionTicketsRotateStatus: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: sessTicketsRotateStatus, + Namespace: namespace, + Subsystem: subsystemTLS, + Help: "Status of the last tickets rotation.", + }), + sessionTicketsRotateTime: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: sessTicketsRotateTime, + Namespace: namespace, + Subsystem: subsystemTLS, + Help: "Time when the TLS session tickets were rotated.", + }), + handshakeAttemptsTotal: prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: handshakeAttemptsTotal, + Namespace: namespace, + Subsystem: subsystemTLS, + Help: "Total count of TLS handshakes.", + }, []string{ + "proto", + "supported_protos", + "tls_version", + }), + handshakeTotal: prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: handshakeTotal, + Namespace: namespace, + Subsystem: subsystemTLS, + Help: "Total count of TLS handshakes.", + }, []string{ + "proto", + "tls_version", + "did_resume", + "cipher_suite", + "negotiated_proto", + "server_name", + }), + } + + var errs []error + collectors := container.KeyValues[string, prometheus.Collector]{{ + Key: certInfo, + Value: m.certificateInfo, + }, { + Key: certNotAfter, + Value: m.certificateNotAfter, + }, { + Key: sessTicketsRotateStatus, + Value: m.sessionTicketsRotateStatus, + }, { + Key: sessTicketsRotateTime, + Value: m.sessionTicketsRotateTime, + }, { + Key: handshakeAttemptsTotal, + Value: m.handshakeAttemptsTotal, + }, { + Key: handshakeTotal, + Value: m.handshakeTotal, + }} + + 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 +} + +// BeforeHandshake implements the [tlsconfig.Metrics] interface for *TLSConfig. +func (m *TLSConfig) BeforeHandshake( + proto string, +) (f func(*tls.ClientHelloInfo) (*tls.Config, error)) { return func(info *tls.ClientHelloInfo) (*tls.Config, error) { var maxVersion uint16 if len(info.SupportedVersions) > 0 { @@ -121,7 +157,7 @@ func TLSMetricsBeforeHandshake(proto string) (f func(*tls.ClientHelloInfo) (*tls // Stick to using WithLabelValues instead of With in order to avoid // extra allocations on prometheus.Labels. The labels order is VERY // important here. - TLSHandshakeAttemptsTotal.WithLabelValues( + m.handshakeAttemptsTotal.WithLabelValues( proto, strings.Join(supProtos, ","), tlsVersionToString(maxVersion), @@ -131,6 +167,60 @@ func TLSMetricsBeforeHandshake(proto string) (f func(*tls.ClientHelloInfo) (*tls } } +// AfterHandshake implements the [tlsconfig.Metrics] interface for *TLSConfig. +func (m *TLSConfig) AfterHandshake( + proto string, + srvName string, + devDomains []string, + srvCerts []tls.Certificate, +) (f func(tls.ConnectionState) error) { + return func(state tls.ConnectionState) error { + sLabel := serverNameToLabel(state.ServerName, srvName, devDomains, srvCerts) + + // Stick to using WithLabelValues instead of With in order to avoid + // extra allocations on prometheus.Labels. The labels order is VERY + // important here. + m.handshakeTotal.WithLabelValues( + proto, + tlsVersionToString(state.Version), + BoolString(state.DidResume), + tls.CipherSuiteName(state.CipherSuite), + // Don't validate the negotiated protocol since it's expected to + // contain only ASCII after negotiation itself. + state.NegotiatedProtocol, + sLabel, + ).Inc() + + return nil + } +} + +// SetCertificateInfo implements the [tlsconfig.Metrics] interface for +// *TLSConfig. +func (m *TLSConfig) SetCertificateInfo(_ context.Context, algo, subj string, notAfter time.Time) { + m.certificateInfo.With(prometheus.Labels{ + "auth_algo": algo, + "subject": subj, + }).Set(1) + + m.certificateNotAfter.With(prometheus.Labels{ + "subject": subj, + }).Set(float64(notAfter.Unix())) +} + +// SetSessionTicketRotationStatus implements the [tlsconfig.Metrics] interface +// for *TLSConfig. +func (m *TLSConfig) SetSessionTicketRotationStatus(_ context.Context, enabled bool) { + if !enabled { + m.sessionTicketsRotateStatus.Set(0) + + return + } + + m.sessionTicketsRotateStatus.Set(1) + m.sessionTicketsRotateTime.SetToCurrentTime() +} + // tlsVersionToString converts TLS version to string. func tlsVersionToString(ver uint16) (tlsVersion string) { switch ver { diff --git a/internal/metrics/tls_test.go b/internal/metrics/tls_test.go index 6eb7d1f..6f4c042 100644 --- a/internal/metrics/tls_test.go +++ b/internal/metrics/tls_test.go @@ -12,7 +12,12 @@ import ( "github.com/stretchr/testify/require" ) -func TestTLSMetricsAfterHandshake(t *testing.T) { +func TestTLSConfig_AfterHandshake(t *testing.T) { + // TODO(s.chzhen): Consider using [agdtest.PrometheusRegisterer]. + reg := prometheus.NewRegistry() + m, err := metrics.NewTLSConfig(metrics.Namespace(), reg) + require.NoError(t, err) + serverName := "test_server" devDomains := []string{"d.adguard-dns.com"} dnsNames := []string{ @@ -87,18 +92,19 @@ func TestTLSMetricsAfterHandshake(t *testing.T) { cert := tls.Certificate{Leaf: &x509Cert} - listener := metrics.TLSMetricsAfterHandshake( + listener := m.AfterHandshake( "", serverName, tc.devDomains, []tls.Certificate{cert}, ) - err := listener(tls.ConnectionState{ServerName: tc.connectionServerName}) + err = listener(tls.ConnectionState{ServerName: tc.connectionServerName}) require.NoError(t, err) - metricFamilies, err := prometheus.DefaultGatherer.Gather() + var metricFamilies []*io_prometheus_client.MetricFamily + metricFamilies, err = reg.Gather() require.NoError(t, err) require.NotNil(t, metricFamilies) @@ -140,11 +146,14 @@ func findLabel(ms []*io_prometheus_client.Metric, label string) (ok bool) { return false } -func TestTLSMetricsBeforeHandshake(t *testing.T) { - f := metrics.TLSMetricsBeforeHandshake("srv-name") +func TestTLSConfig_BeforeHandshake(t *testing.T) { + reg := prometheus.NewRegistry() + m, err := metrics.NewTLSConfig(metrics.Namespace(), reg) + require.NoError(t, err) + + f := m.BeforeHandshake("srv-name") var conf *tls.Config - var err error require.NotPanics(t, func() { conf, err = f(&tls.ClientHelloInfo{ SupportedProtos: []string{"\xC0\xC1\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF"}, diff --git a/internal/optlog/optlog.go b/internal/optlog/optlog.go index 3884f5f..8f03e90 100644 --- a/internal/optlog/optlog.go +++ b/internal/optlog/optlog.go @@ -7,37 +7,9 @@ import ( "github.com/AdguardTeam/golibs/log" ) -// Debug1 is an ugly hack to prevent [log.Debug] from allocating. -func Debug1[T1 any](msg string, arg1 T1) { - if log.GetLevel() >= log.DEBUG { - log.Debug(msg, arg1) - } -} - -// Debug2 is an ugly hack to prevent [log.Debug] from allocating. -func Debug2[T1, T2 any](msg string, arg1 T1, arg2 T2) { - if log.GetLevel() >= log.DEBUG { - log.Debug(msg, arg1, arg2) - } -} - // Debug3 is an ugly hack to prevent [log.Debug] from allocating. func Debug3[T1, T2, T3 any](msg string, arg1 T1, arg2 T2, arg3 T3) { if log.GetLevel() >= log.DEBUG { log.Debug(msg, arg1, arg2, arg3) } } - -// Debug4 is an ugly hack to prevent [log.Debug] from allocating. -func Debug4[T1, T2, T3, T4 any](msg string, arg1 T1, arg2 T2, arg3 T3, arg4 T4) { - if log.GetLevel() >= log.DEBUG { - log.Debug(msg, arg1, arg2, arg3, arg4) - } -} - -// Error2 is an ugly hack to prevent [log.Error] from allocating. -func Error2[T1, T2 any](msg string, arg1 T1, arg2 T2) { - if log.GetLevel() >= log.ERROR { - log.Error(msg, arg1, arg2) - } -} diff --git a/internal/optslog/optslog.go b/internal/optslog/optslog.go index b373b36..ca4b9c7 100644 --- a/internal/optslog/optslog.go +++ b/internal/optslog/optslog.go @@ -13,7 +13,7 @@ import ( ) // Trace1 is an optimized version of [slog.Logger.Log] that prevents it from -// from allocating when debugging is not necessary. +// allocating when debugging is not necessary. func Trace1[T1 any](ctx context.Context, l *slog.Logger, msg, name1 string, arg1 T1) { if l.Enabled(ctx, slogutil.LevelTrace) { l.Log(ctx, slogutil.LevelTrace, msg, name1, arg1) @@ -21,7 +21,7 @@ func Trace1[T1 any](ctx context.Context, l *slog.Logger, msg, name1 string, arg1 } // Trace2 is an optimized version of [slog.Logger.Log] that prevents it from -// from allocating when debugging is not necessary. +// allocating when debugging is not necessary. func Trace2[T1, T2 any]( ctx context.Context, l *slog.Logger, @@ -35,7 +35,7 @@ func Trace2[T1, T2 any]( } // Trace3 is an optimized version of [slog.Logger.Log] that prevents it from -// from allocating when debugging is not necessary. +// allocating when debugging is not necessary. func Trace3[T1, T2, T3 any]( ctx context.Context, l *slog.Logger, @@ -50,7 +50,7 @@ func Trace3[T1, T2, T3 any]( } // Debug1 is an optimized version of [slog.Logger.DebugContext] that prevents it -// from from allocating when debugging is not necessary. +// from allocating when debugging is not necessary. func Debug1[T1 any](ctx context.Context, l *slog.Logger, msg, name1 string, arg1 T1) { if l.Enabled(ctx, slog.LevelDebug) { l.DebugContext(ctx, msg, name1, arg1) @@ -58,7 +58,7 @@ func Debug1[T1 any](ctx context.Context, l *slog.Logger, msg, name1 string, arg1 } // Debug2 is an optimized version of [slog.Logger.DebugContext] that prevents it -// from from allocating when debugging is not necessary. +// from allocating when debugging is not necessary. func Debug2[T1, T2 any]( ctx context.Context, l *slog.Logger, @@ -72,7 +72,7 @@ func Debug2[T1, T2 any]( } // Debug3 is an optimized version of [slog.Logger.DebugContext] that prevents it -// from from allocating when debugging is not necessary. +// from allocating when debugging is not necessary. func Debug3[T1, T2, T3 any]( ctx context.Context, l *slog.Logger, @@ -87,7 +87,7 @@ func Debug3[T1, T2, T3 any]( } // Debug4 is an optimized version of [slog.Logger.DebugContext] that prevents it -// from from allocating when debugging is not necessary. +// from allocating when debugging is not necessary. func Debug4[T1, T2, T3, T4 any]( ctx context.Context, l *slog.Logger, @@ -101,3 +101,17 @@ func Debug4[T1, T2, T3, T4 any]( l.DebugContext(ctx, msg, name1, arg1, name2, arg2, name3, arg3, name4, arg4) } } + +// Warn2 is an optimized version of [slog.Logger.WarnContext] that prevents it +// from allocating when debugging is not necessary. +func Warn2[T1, T2 any]( + ctx context.Context, + l *slog.Logger, + msg string, + name1 string, arg1 T1, + name2 string, arg2 T2, +) { + if l.Enabled(ctx, slog.LevelWarn) { + l.WarnContext(ctx, msg, name1, arg1, name2, arg2) + } +} diff --git a/internal/profiledb/internal/filecachepb/filecache.pb.go b/internal/profiledb/internal/filecachepb/filecache.pb.go index a7dce21..10a5db1 100644 --- a/internal/profiledb/internal/filecachepb/filecache.pb.go +++ b/internal/profiledb/internal/filecachepb/filecache.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.34.2 -// protoc v5.27.1 +// protoc-gen-go v1.35.1 +// protoc v5.28.3 // source: filecache.proto package filecachepb @@ -35,11 +35,9 @@ type FileCache struct { func (x *FileCache) Reset() { *x = FileCache{} - if protoimpl.UnsafeEnabled { - mi := &file_filecache_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_filecache_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *FileCache) String() string { @@ -50,7 +48,7 @@ func (*FileCache) ProtoMessage() {} func (x *FileCache) ProtoReflect() protoreflect.Message { mi := &file_filecache_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -129,11 +127,9 @@ type Profile struct { func (x *Profile) Reset() { *x = Profile{} - if protoimpl.UnsafeEnabled { - mi := &file_filecache_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_filecache_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Profile) String() string { @@ -144,7 +140,7 @@ func (*Profile) ProtoMessage() {} func (x *Profile) ProtoReflect() protoreflect.Message { mi := &file_filecache_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -371,11 +367,9 @@ type ParentalProtectionSettings struct { func (x *ParentalProtectionSettings) Reset() { *x = ParentalProtectionSettings{} - if protoimpl.UnsafeEnabled { - mi := &file_filecache_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_filecache_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ParentalProtectionSettings) String() string { @@ -386,7 +380,7 @@ func (*ParentalProtectionSettings) ProtoMessage() {} func (x *ParentalProtectionSettings) ProtoReflect() protoreflect.Message { mi := &file_filecache_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -455,11 +449,9 @@ type SafeBrowsingSettings struct { func (x *SafeBrowsingSettings) Reset() { *x = SafeBrowsingSettings{} - if protoimpl.UnsafeEnabled { - mi := &file_filecache_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_filecache_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *SafeBrowsingSettings) String() string { @@ -470,7 +462,7 @@ func (*SafeBrowsingSettings) ProtoMessage() {} func (x *SafeBrowsingSettings) ProtoReflect() protoreflect.Message { mi := &file_filecache_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -523,11 +515,9 @@ type ParentalProtectionSchedule struct { func (x *ParentalProtectionSchedule) Reset() { *x = ParentalProtectionSchedule{} - if protoimpl.UnsafeEnabled { - mi := &file_filecache_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_filecache_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ParentalProtectionSchedule) String() string { @@ -538,7 +528,7 @@ func (*ParentalProtectionSchedule) ProtoMessage() {} func (x *ParentalProtectionSchedule) ProtoReflect() protoreflect.Message { mi := &file_filecache_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -620,11 +610,9 @@ type DayRange struct { func (x *DayRange) Reset() { *x = DayRange{} - if protoimpl.UnsafeEnabled { - mi := &file_filecache_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_filecache_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *DayRange) String() string { @@ -635,7 +623,7 @@ func (*DayRange) ProtoMessage() {} func (x *DayRange) ProtoReflect() protoreflect.Message { mi := &file_filecache_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -675,11 +663,9 @@ type BlockingModeCustomIP struct { func (x *BlockingModeCustomIP) Reset() { *x = BlockingModeCustomIP{} - if protoimpl.UnsafeEnabled { - mi := &file_filecache_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_filecache_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *BlockingModeCustomIP) String() string { @@ -690,7 +676,7 @@ func (*BlockingModeCustomIP) ProtoMessage() {} func (x *BlockingModeCustomIP) ProtoReflect() protoreflect.Message { mi := &file_filecache_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -727,11 +713,9 @@ type BlockingModeNXDOMAIN struct { func (x *BlockingModeNXDOMAIN) Reset() { *x = BlockingModeNXDOMAIN{} - if protoimpl.UnsafeEnabled { - mi := &file_filecache_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_filecache_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *BlockingModeNXDOMAIN) String() string { @@ -742,7 +726,7 @@ func (*BlockingModeNXDOMAIN) ProtoMessage() {} func (x *BlockingModeNXDOMAIN) ProtoReflect() protoreflect.Message { mi := &file_filecache_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -765,11 +749,9 @@ type BlockingModeNullIP struct { func (x *BlockingModeNullIP) Reset() { *x = BlockingModeNullIP{} - if protoimpl.UnsafeEnabled { - mi := &file_filecache_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_filecache_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *BlockingModeNullIP) String() string { @@ -780,7 +762,7 @@ func (*BlockingModeNullIP) ProtoMessage() {} func (x *BlockingModeNullIP) ProtoReflect() protoreflect.Message { mi := &file_filecache_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -803,11 +785,9 @@ type BlockingModeREFUSED struct { func (x *BlockingModeREFUSED) Reset() { *x = BlockingModeREFUSED{} - if protoimpl.UnsafeEnabled { - mi := &file_filecache_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_filecache_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *BlockingModeREFUSED) String() string { @@ -818,7 +798,7 @@ func (*BlockingModeREFUSED) ProtoMessage() {} func (x *BlockingModeREFUSED) ProtoReflect() protoreflect.Message { mi := &file_filecache_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -849,11 +829,9 @@ type Device struct { func (x *Device) Reset() { *x = Device{} - if protoimpl.UnsafeEnabled { - mi := &file_filecache_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_filecache_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Device) String() string { @@ -864,7 +842,7 @@ func (*Device) ProtoMessage() {} func (x *Device) ProtoReflect() protoreflect.Message { mi := &file_filecache_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -942,11 +920,9 @@ type AccessSettings struct { func (x *AccessSettings) Reset() { *x = AccessSettings{} - if protoimpl.UnsafeEnabled { - mi := &file_filecache_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_filecache_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AccessSettings) String() string { @@ -957,7 +933,7 @@ func (*AccessSettings) ProtoMessage() {} func (x *AccessSettings) ProtoReflect() protoreflect.Message { mi := &file_filecache_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1018,11 +994,9 @@ type CidrRange struct { func (x *CidrRange) Reset() { *x = CidrRange{} - if protoimpl.UnsafeEnabled { - mi := &file_filecache_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_filecache_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *CidrRange) String() string { @@ -1033,7 +1007,7 @@ func (*CidrRange) ProtoMessage() {} func (x *CidrRange) ProtoReflect() protoreflect.Message { mi := &file_filecache_proto_msgTypes[12] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1076,11 +1050,9 @@ type AuthenticationSettings struct { func (x *AuthenticationSettings) Reset() { *x = AuthenticationSettings{} - if protoimpl.UnsafeEnabled { - mi := &file_filecache_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_filecache_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AuthenticationSettings) String() string { @@ -1091,7 +1063,7 @@ func (*AuthenticationSettings) ProtoMessage() {} func (x *AuthenticationSettings) ProtoReflect() protoreflect.Message { mi := &file_filecache_proto_msgTypes[13] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1149,11 +1121,9 @@ type RateLimitSettings struct { func (x *RateLimitSettings) Reset() { *x = RateLimitSettings{} - if protoimpl.UnsafeEnabled { - mi := &file_filecache_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_filecache_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RateLimitSettings) String() string { @@ -1164,7 +1134,7 @@ func (*RateLimitSettings) ProtoMessage() {} func (x *RateLimitSettings) ProtoReflect() protoreflect.Message { mi := &file_filecache_proto_msgTypes[14] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1493,188 +1463,6 @@ func file_filecache_proto_init() { if File_filecache_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_filecache_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*FileCache); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_filecache_proto_msgTypes[1].Exporter = func(v any, i int) any { - switch v := v.(*Profile); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_filecache_proto_msgTypes[2].Exporter = func(v any, i int) any { - switch v := v.(*ParentalProtectionSettings); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_filecache_proto_msgTypes[3].Exporter = func(v any, i int) any { - switch v := v.(*SafeBrowsingSettings); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_filecache_proto_msgTypes[4].Exporter = func(v any, i int) any { - switch v := v.(*ParentalProtectionSchedule); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_filecache_proto_msgTypes[5].Exporter = func(v any, i int) any { - switch v := v.(*DayRange); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_filecache_proto_msgTypes[6].Exporter = func(v any, i int) any { - switch v := v.(*BlockingModeCustomIP); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_filecache_proto_msgTypes[7].Exporter = func(v any, i int) any { - switch v := v.(*BlockingModeNXDOMAIN); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_filecache_proto_msgTypes[8].Exporter = func(v any, i int) any { - switch v := v.(*BlockingModeNullIP); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_filecache_proto_msgTypes[9].Exporter = func(v any, i int) any { - switch v := v.(*BlockingModeREFUSED); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_filecache_proto_msgTypes[10].Exporter = func(v any, i int) any { - switch v := v.(*Device); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_filecache_proto_msgTypes[11].Exporter = func(v any, i int) any { - switch v := v.(*AccessSettings); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_filecache_proto_msgTypes[12].Exporter = func(v any, i int) any { - switch v := v.(*CidrRange); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_filecache_proto_msgTypes[13].Exporter = func(v any, i int) any { - switch v := v.(*AuthenticationSettings); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_filecache_proto_msgTypes[14].Exporter = func(v any, i int) any { - switch v := v.(*RateLimitSettings); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } file_filecache_proto_msgTypes[1].OneofWrappers = []any{ (*Profile_BlockingModeCustomIp)(nil), (*Profile_BlockingModeNxdomain)(nil), diff --git a/internal/querylog/querylog_test.go b/internal/querylog/querylog_test.go index ff7996a..085b257 100644 --- a/internal/querylog/querylog_test.go +++ b/internal/querylog/querylog_test.go @@ -1,21 +1,15 @@ package querylog_test import ( - "testing" "time" "github.com/AdguardTeam/AdGuardDNS/internal/agd" "github.com/AdguardTeam/AdGuardDNS/internal/filter" "github.com/AdguardTeam/AdGuardDNS/internal/geoip" "github.com/AdguardTeam/AdGuardDNS/internal/querylog" - "github.com/AdguardTeam/golibs/testutil" "github.com/miekg/dns" ) -func TestMain(m *testing.M) { - testutil.DiscardLogOutput(m) -} - // testRequestID is the common request ID for tests. var testRequestID = agd.NewRequestID() diff --git a/internal/tlsconfig/manager.go b/internal/tlsconfig/manager.go new file mode 100644 index 0000000..f6e35c0 --- /dev/null +++ b/internal/tlsconfig/manager.go @@ -0,0 +1,286 @@ +package tlsconfig + +import ( + "cmp" + "context" + "crypto/tls" + "fmt" + "io" + "log/slog" + "maps" + "os" + "path/filepath" + "slices" + "strings" + "sync" + + "github.com/AdguardTeam/AdGuardDNS/internal/agdservice" + "github.com/AdguardTeam/AdGuardDNS/internal/errcoll" + "github.com/AdguardTeam/golibs/errors" +) + +// 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) +} + +// DefaultManagerConfig is the configuration structure for [DefaultManager]. +// +// TODO(s.chzhen): Use it. +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 errcoll.Interface + + // Metrics is used to collect TLS related statistics. + Metrics RefreshMetrics + + // KeyLogFilename, if not empty, is the name of the TLS key log file. + KeyLogFilename string + + // SessionTicketPaths are paths to files containing the TLS session tickets. + 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 +} + +// NewDefaultManager returns a new initialized *DefaultManager. +func NewDefaultManager(conf *DefaultManagerConfig) (m *DefaultManager, err error) { + var kl io.Writer + fn := conf.KeyLogFilename + if fn != "" { + kl, err = tlsKeyLogWriter(fn) + if err != nil { + return nil, fmt.Errorf("initializing tls key log writer: %w", err) + } + } + + return &DefaultManager{ + mu: &sync.Mutex{}, + logger: conf.Logger, + errColl: conf.ErrColl, + metrics: conf.Metrics, + keyLogWriter: kl, + configs: make(map[certWithKey]*tls.Config), + sessTicketPaths: conf.SessionTicketPaths, + }, nil +} + +// type check +var _ Manager = (*DefaultManager)(nil) + +// Add implements the [Manager] interface for *DefaultManager. +func (m *DefaultManager) Add( + ctx context.Context, + certPath string, + keyPath string, +) (conf *tls.Config, err error) { + ck := certWithKey{ + certPath: certPath, + keyPath: keyPath, + } + + m.mu.Lock() + defer m.mu.Unlock() + + if conf = m.configs[ck]; conf != nil { + return conf, nil + } + + return m.add(ctx, ck) +} + +// 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) + if err != nil { + return nil, fmt.Errorf("loading certificate: %w", err) + } + + authAlgo := cert.Leaf.PublicKeyAlgorithm.String() + 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 + } + + m.logger.InfoContext(ctx, "refreshed config", "cert", ck.certPath, "key", ck.keyPath) + + return conf, nil + } + + 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, + } + + m.configs[ck] = conf + + m.logger.InfoContext(ctx, "added config", "cert", ck.certPath, "key", ck.keyPath) + + return conf, nil +} + +// type check +var _ agdservice.Refresher = (*DefaultManager)(nil) + +// Refresh implements the [agdservice.Refresher] interface for *DefaultManager. +func (m *DefaultManager) Refresh(ctx context.Context) (err error) { + m.logger.DebugContext(ctx, "refresh started") + defer m.logger.DebugContext(ctx, "refresh finished") + + defer func() { + if err != nil { + errcoll.Collect(ctx, m.errColl, m.logger, "cerificate refresh failed", err) + } + }() + + m.mu.Lock() + 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) + } + + 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)) + + return nil +} + +// 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 + +// sessionTicket is a type alias for a single TLS session ticket. +type sessionTicket = [sessTickLen]byte + +// RotateTickets rereads and resets TLS session tickets. +func (m *DefaultManager) RotateTickets(ctx context.Context) (err error) { + m.logger.DebugContext(ctx, "ticket rotation started") + defer m.logger.DebugContext(ctx, "ticket rotation finished") + + files := m.sessTicketPaths + if len(files) == 0 { + return nil + } + + defer func() { + if err != nil { + m.metrics.SetSessionTicketRotationStatus(ctx, false) + errcoll.Collect(ctx, m.errColl, m.logger, "ticket rotation failed", err) + } + }() + + tickets := make([]sessionTicket, 0, len(files)) + for _, fileName := range files { + var ticket sessionTicket + ticket, err = readSessionTicketKey(fileName) + if err != nil { + return fmt.Errorf("reading sesion ticket: %w", err) + } + + tickets = append(tickets, ticket) + } + + m.mu.Lock() + defer m.mu.Unlock() + + for _, conf := range m.configs { + conf.SetSessionTicketKeys(tickets) + } + + m.logger.InfoContext( + ctx, + "ticket rotation successful", + "num_configs", len(m.configs), + "num_tickets", len(tickets), + ) + + m.metrics.SetSessionTicketRotationStatus(ctx, true) + + return nil +} + +// readSessionTicketKey reads a single TLS session ticket from a file. +func readSessionTicketKey(fn string) (ticket sessionTicket, 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 ticket, fmt.Errorf("reading session ticket: %w", err) + } + + tickLen := len(b) + if tickLen != sessTickLen { + return ticket, fmt.Errorf( + "session ticket in %s: bad len %d, want %d", + fn, + tickLen, + sessTickLen, + ) + } + + return sessionTicket(b), nil +} + +// tlsKeyLogWriter returns a writer for logging TLS secrets to keyLogFilename. +func tlsKeyLogWriter(keyLogFilename string) (kl io.Writer, 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 { + // Don't wrap the error, because it's informative enough as is. + return nil, err + } + + return kl, nil +} diff --git a/internal/tlsconfig/manager_test.go b/internal/tlsconfig/manager_test.go new file mode 100644 index 0000000..60d2bcc --- /dev/null +++ b/internal/tlsconfig/manager_test.go @@ -0,0 +1,175 @@ +package tlsconfig_test + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "encoding/pem" + "math/big" + "os" + "path/filepath" + "testing" + "time" + + "github.com/AdguardTeam/AdGuardDNS/internal/agdtest" + "github.com/AdguardTeam/AdGuardDNS/internal/tlsconfig" + "github.com/AdguardTeam/golibs/logutil/slogutil" + "github.com/AdguardTeam/golibs/testutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// testTimeout is the common timeout for tests and contexts. +const testTimeout = 1 * time.Second + +// newCertAndKey is a helper function that generates certificate and key. +func newCertAndKey(tb testing.TB, n int64) (certDER []byte, key *rsa.PrivateKey) { + tb.Helper() + + key, err := rsa.GenerateKey(rand.Reader, 2048) + require.NoError(tb, err) + + certTmpl := &x509.Certificate{ + SerialNumber: big.NewInt(n), + } + + certDER, err = x509.CreateCertificate(rand.Reader, certTmpl, certTmpl, &key.PublicKey, key) + require.NoError(tb, err) + + return certDER, key +} + +// writeCertAndKey is a helper function that writes certificate and key to +// specified paths. +func writeCertAndKey( + tb testing.TB, + certDER []byte, + certPath string, + key *rsa.PrivateKey, + keyPath string, +) { + tb.Helper() + + certFile, err := os.OpenFile(certPath, os.O_WRONLY|os.O_CREATE, 0o600) + require.NoError(tb, err) + + defer func() { + err = certFile.Close() + require.NoError(tb, err) + }() + + err = pem.Encode(certFile, &pem.Block{Type: "CERTIFICATE", Bytes: certDER}) + require.NoError(tb, err) + + keyFile, err := os.OpenFile(keyPath, os.O_WRONLY|os.O_CREATE, 0o600) + require.NoError(tb, err) + + defer func() { + err = keyFile.Close() + require.NoError(tb, err) + }() + + err = pem.Encode(keyFile, &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(key), + }) + require.NoError(tb, err) +} + +// writeSesionKey is a helper function that writes generated session key to +// specified path. +func writeSessionKey(tb testing.TB, sessKeyPath string) { + tb.Helper() + + var sessKey [32]byte + _, err := rand.Read(sessKey[:]) + require.NoError(tb, err) + + keyFile, err := os.OpenFile(sessKeyPath, os.O_WRONLY|os.O_CREATE, 0o600) + require.NoError(tb, err) + + defer func() { + err = keyFile.Close() + require.NoError(tb, err) + }() + + _, err = keyFile.Write(sessKey[:]) + require.NoError(tb, err) +} + +func TestDefaultManager_Refresh(t *testing.T) { + t.Parallel() + + const ( + snBefore int64 = 1 + snAfter int64 = 2 + ) + + m, err := tlsconfig.NewDefaultManager(&tlsconfig.DefaultManagerConfig{ + Logger: slogutil.NewDiscardLogger(), + ErrColl: agdtest.NewErrorCollector(), + Metrics: tlsconfig.EmptyRefreshMetrics{}, + }) + require.NoError(t, err) + + certDER, key := newCertAndKey(t, snBefore) + + tmpDir := t.TempDir() + certPath := filepath.Join(tmpDir, "cert.pem") + keyPath := filepath.Join(tmpDir, "key.pem") + + writeCertAndKey(t, certDER, certPath, key, keyPath) + + ctx := testutil.ContextWithTimeout(t, testTimeout) + conf, err := m.Add(ctx, certPath, keyPath) + require.NoError(t, err) + + cert, err := conf.GetCertificate(&tls.ClientHelloInfo{}) + require.NoError(t, err) + + assert.Equal(t, snBefore, cert.Leaf.SerialNumber.Int64()) + + certDER, key = newCertAndKey(t, snAfter) + writeCertAndKey(t, certDER, certPath, key, keyPath) + + 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()) +} + +func TestDefaultManager_RotateTickets(t *testing.T) { + t.Parallel() + + tmpDir := t.TempDir() + sessKeyPath := filepath.Join(tmpDir, "sess.key") + writeSessionKey(t, sessKeyPath) + + m, err := tlsconfig.NewDefaultManager(&tlsconfig.DefaultManagerConfig{ + Logger: slogutil.NewDiscardLogger(), + ErrColl: agdtest.NewErrorCollector(), + Metrics: tlsconfig.EmptyRefreshMetrics{}, + SessionTicketPaths: []string{sessKeyPath}, + }) + require.NoError(t, err) + + certDER, key := newCertAndKey(t, 1) + + certPath := filepath.Join(tmpDir, "cert.pem") + keyPath := filepath.Join(tmpDir, "key.pem") + + writeCertAndKey(t, certDER, certPath, key, keyPath) + + ctx := testutil.ContextWithTimeout(t, testTimeout) + _, err = m.Add(ctx, certPath, keyPath) + require.NoError(t, err) + + err = m.RotateTickets(ctx) + require.NoError(t, err) + + // TODO(s.chzhen): Find a way to test session ticket changes. +} diff --git a/internal/tlsconfig/metrics.go b/internal/tlsconfig/metrics.go new file mode 100644 index 0000000..30b0096 --- /dev/null +++ b/internal/tlsconfig/metrics.go @@ -0,0 +1,92 @@ +package tlsconfig + +import ( + "context" + "crypto/tls" + "time" +) + +// 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 + // [tls.Config.GetConfigForClient]. f must not be nil. + BeforeHandshake(proto string) (f func(*tls.ClientHelloInfo) (c *tls.Config, err error)) + + // AfterHandshake returns a function that needs to be passed to + // [tls.Config.VerifyConnection]. f must not be nil. + AfterHandshake( + proto string, + srvName string, + devDomains []string, + 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) + + // SetSessionTicketRotationStatus sets the TLS session ticket rotation + // status. + SetSessionTicketRotationStatus(ctx context.Context, enabled bool) +} + +// EmptyMetrics is the implementation of the [Metrics] interface that does +// nothing. +type EmptyMetrics struct{} + +// type check +var _ Metrics = EmptyMetrics{} + +// BeforeHandshake implements the [Metrics] interface for EmptyMetrics by +// returning a function that does nothing. +func (EmptyMetrics) BeforeHandshake( + _ string, +) (f func(info *tls.ClientHelloInfo) (c *tls.Config, err error)) { + return func(info *tls.ClientHelloInfo) (*tls.Config, error) { + return nil, nil + } +} + +// AfterHandshake implements the [Metrics] interface for EmptyMetrics by +// returning a function that does nothing. +func (EmptyMetrics) AfterHandshake( + _ string, + _ string, + _ []string, + _ []tls.Certificate, +) (f func(s tls.ConnectionState) (err error)) { + return func(tls.ConnectionState) error { + return nil + } +} + +// SetCertificateInfo implements the [Metrics] interface for EmptyMetrics. +func (EmptyMetrics) SetCertificateInfo(_ context.Context, _, _ string, _ time.Time) {} + +// 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 new file mode 100644 index 0000000..83f34f4 --- /dev/null +++ b/internal/tlsconfig/tlsconfig.go @@ -0,0 +1,3 @@ +// Package tlsconfig contains TLS related interfaces, helpers, and +// implementations. +package tlsconfig diff --git a/internal/tools/go.mod b/internal/tools/go.mod index 7a5eaed..0a02d17 100644 --- a/internal/tools/go.mod +++ b/internal/tools/go.mod @@ -1,32 +1,32 @@ module github.com/AdguardTeam/AdGuardDNS/internal/tools -go 1.23.1 +go 1.23.2 require ( github.com/fzipp/gocyclo v0.6.0 github.com/golangci/misspell v0.6.0 github.com/gordonklaus/ineffassign v0.1.0 github.com/jstemmer/go-junit-report/v2 v2.1.0 - github.com/kisielk/errcheck v1.7.0 + github.com/kisielk/errcheck v1.8.0 github.com/securego/gosec/v2 v2.21.4 github.com/uudashr/gocognit v1.1.3 - golang.org/x/tools v0.25.0 + golang.org/x/tools v0.26.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.34.2 + google.golang.org/protobuf v1.35.1 honnef.co/go/tools v0.5.1 mvdan.cc/gofumpt v0.7.0 - mvdan.cc/sh/v3 v3.9.0 + mvdan.cc/sh/v3 v3.10.0 mvdan.cc/unparam v0.0.0-20240917084806-57a3b4290ba3 ) require ( - cloud.google.com/go v0.115.1 // indirect + cloud.google.com/go v0.116.0 // indirect cloud.google.com/go/ai v0.8.2 // indirect - cloud.google.com/go/auth v0.9.5 // indirect + cloud.google.com/go/auth v0.9.9 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect cloud.google.com/go/compute/metadata v0.5.2 // indirect - cloud.google.com/go/longrunning v0.6.1 // indirect + cloud.google.com/go/longrunning v0.6.2 // 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 @@ -44,27 +44,27 @@ require ( 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.55.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 // indirect - go.opentelemetry.io/otel v1.30.0 // indirect - go.opentelemetry.io/otel/metric v1.30.0 // indirect - go.opentelemetry.io/otel/trace v1.30.0 // indirect - golang.org/x/crypto v0.27.0 // indirect - golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect - golang.org/x/exp/typeparams v0.0.0-20240909161429-701f63a606c0 // 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.29.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.25.0 // indirect - golang.org/x/telemetry v0.0.0-20240927214544-e9e6960092dd // indirect - golang.org/x/term v0.24.0 // indirect - golang.org/x/text v0.18.0 // indirect - golang.org/x/time v0.6.0 // indirect - google.golang.org/api v0.199.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240924160255-9d4c2d233b61 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 // indirect - google.golang.org/grpc v1.67.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 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 16b8dc8..779bc3f 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.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ= -cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc= +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.5 h1:4CTn43Eynw40aFVr3GpPqsQponx2jv0BQpjvajsbbzw= -cloud.google.com/go/auth v0.9.5/go.mod h1:Xo0n7n66eHyOWWCnitop6870Ilwo3PiZyodVkkH1xWM= +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/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.1 h1:lOLTFxYpr8hcRtcwWir5ITh1PAKUD/sG2lKrTSYjyMc= -cloud.google.com/go/longrunning v0.6.1/go.mod h1:nHISoOZpBcmlwbJmiVk5oDRz0qG/ZxPynEGs1iZ79s0= +cloud.google.com/go/longrunning v0.6.2 h1:xjDfh1pQcWPEvnfjZmwjKQEcHnpz6lHjfy7Fo0MK+hc= +cloud.google.com/go/longrunning v0.6.2/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= @@ -91,8 +91,8 @@ github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5 github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= github.com/jstemmer/go-junit-report/v2 v2.1.0 h1:X3+hPYlSczH9IMIpSC9CQSZA0L+BipYafciZUWHEmsc= github.com/jstemmer/go-junit-report/v2 v2.1.0/go.mod h1:mgHVr7VUo5Tn8OLVr1cKnLuEy0M92wdRntM99h7RkgQ= -github.com/kisielk/errcheck v1.7.0 h1:+SbscKmWJ5mOK/bO1zS60F5I9WwZDWOfRsC4RwfwRV0= -github.com/kisielk/errcheck v1.7.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ= +github.com/kisielk/errcheck v1.8.0 h1:ZX/URYa7ilESY19ik/vBmCn6zdGQLxACwjAcWbHlYlg= +github.com/kisielk/errcheck v1.8.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -125,26 +125,26 @@ 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.55.0 h1:hCq2hNMwsegUvPzI7sPOvtO9cqyy5GbWt/Ybp2xrx8Q= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0/go.mod h1:LqaApwGx/oUmzsbqxkzuBvyoPpkxk3JQWnqfVrJ3wCA= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 h1:ZIg3ZT/aQ7AfKqdwp7ECpOK6vHqquXXuyTjIO8ZdmPs= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0/go.mod h1:DQAwmETtZV00skUwgD6+0U89g80NKsJE3DCKeLLPQMI= -go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts= -go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= -go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w= -go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ= -go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc= -go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= +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= 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.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +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-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= -golang.org/x/exp/typeparams v0.0.0-20240909161429-701f63a606c0 h1:bVwtbF629Xlyxk6xLQq2TDYmqP0uiWaet5LwRebuY0k= -golang.org/x/exp/typeparams v0.0.0-20240909161429-701f63a606c0/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +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/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= @@ -159,8 +159,8 @@ 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.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +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/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= @@ -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.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/telemetry v0.0.0-20240927214544-e9e6960092dd h1:cSUM3UgI7q2QZ4WBwDOGo5eFhZG4eGUkpdFporYHwpQ= -golang.org/x/telemetry v0.0.0-20240927214544-e9e6960092dd/go.mod h1:PsFMgI0jiuY7j+qwXANuh9a/x5kQESTSnRow3gapUyk= +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/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -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 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= 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.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= -golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +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.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.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= -golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= +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/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.199.0 h1:aWUXClp+VFJmqE0JPvpZOK3LDQMyFKYIow4etYd9qxs= -google.golang.org/api v0.199.0/go.mod h1:ohG4qSztDJmZdjK/Ar6MhbAmb/Rpi4JHOqagsh90K28= +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/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-20240924160255-9d4c2d233b61 h1:pAjq8XSSzXoP9ya73v/w+9QEAAJNluLrpmMq5qFJQNY= -google.golang.org/genproto/googleapis/api v0.0.0-20240924160255-9d4c2d233b61/go.mod h1:O6rP0uBq4k0mdi/b4ZEMAZjkhYWhS815kCvaMha4VN8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 h1:N9BgCIAUvn/M+p4NJccWPWb3BWh88+zyL0ll9HgbEeM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +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/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.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw= -google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +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/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.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +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 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= @@ -249,7 +249,7 @@ mvdan.cc/editorconfig v0.3.0 h1:D1D2wLYEYGpawWT5SpM5pRivgEgXjtEXwC9MWhEY0gQ= mvdan.cc/editorconfig v0.3.0/go.mod h1:NcJHuDtNOTEJ6251indKiWuzK6+VcrMuLzGMLKBFupQ= mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU= mvdan.cc/gofumpt v0.7.0/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo= -mvdan.cc/sh/v3 v3.9.0 h1:it14fyjCdQUk4jf/aYxLO3FG8jFarR9GzMCtnlvvD7c= -mvdan.cc/sh/v3 v3.9.0/go.mod h1:cdBk8bgoiBI7lSZqK5JhUuq7OB64VQ7fgm85xelw3Nk= +mvdan.cc/sh/v3 v3.10.0 h1:v9z7N1DLZ7owyLM/SXZQkBSXcwr2IGMm2LY2pmhVXj4= +mvdan.cc/sh/v3 v3.10.0/go.mod h1:z/mSSVyLFGZzqb3ZIKojjyqIx/xbmz/UHdCSv9HmqXY= mvdan.cc/unparam v0.0.0-20240917084806-57a3b4290ba3 h1:YkmTN1n5U60NM02j7TCSWRlW3fqNiuXe/eVXf0dLFN8= mvdan.cc/unparam v0.0.0-20240917084806-57a3b4290ba3/go.mod h1:z5yboO1sP1Q9pcfvS597TpfbNXQjphDlkCJHzt13ybc= diff --git a/internal/websvc/blockpage_test.go b/internal/websvc/blockpage_test.go index 1314926..fa14bc2 100644 --- a/internal/websvc/blockpage_test.go +++ b/internal/websvc/blockpage_test.go @@ -12,6 +12,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp" "github.com/AdguardTeam/AdGuardDNS/internal/websvc" "github.com/AdguardTeam/golibs/httphdr" + "github.com/AdguardTeam/golibs/netutil/urlutil" "github.com/AdguardTeam/golibs/testutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -122,7 +123,7 @@ func TestBlockPageServers_gzip(t *testing.T) { } u := &url.URL{ - Scheme: "http", + Scheme: urlutil.SchemeHTTP, Host: addr.String(), Path: "/", } diff --git a/internal/websvc/handler_test.go b/internal/websvc/handler_test.go index a790631..881dee4 100644 --- a/internal/websvc/handler_test.go +++ b/internal/websvc/handler_test.go @@ -11,6 +11,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp" "github.com/AdguardTeam/AdGuardDNS/internal/websvc" "github.com/AdguardTeam/golibs/httphdr" + "github.com/AdguardTeam/golibs/netutil/urlutil" "github.com/AdguardTeam/golibs/testutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -25,7 +26,7 @@ func TestService_ServeHTTP(t *testing.T) { }) rootRedirectURL := &url.URL{ - Scheme: "http", + Scheme: urlutil.SchemeHTTP, Host: "adguard-dns.com", Path: "/", } @@ -73,7 +74,7 @@ func assertResponse( t.Helper() r := httptest.NewRequest(http.MethodGet, (&url.URL{ - Scheme: "http", + Scheme: urlutil.SchemeHTTP, Host: "127.0.0.1", Path: path, }).String(), strings.NewReader("")) diff --git a/internal/websvc/linkip_internal_test.go b/internal/websvc/linkip_internal_test.go index 8a4f1dc..46aad12 100644 --- a/internal/websvc/linkip_internal_test.go +++ b/internal/websvc/linkip_internal_test.go @@ -12,6 +12,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp" "github.com/AdguardTeam/AdGuardDNS/internal/agdtest" "github.com/AdguardTeam/golibs/httphdr" + "github.com/AdguardTeam/golibs/netutil/urlutil" "github.com/AdguardTeam/golibs/testutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -107,8 +108,8 @@ func TestLinkedIPProxy_ServeHTTP(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { r := httptest.NewRequest(tc.method, (&url.URL{ - Scheme: "http", - Host: "www.example.com", + Scheme: urlutil.SchemeHTTP, + Host: "link-ip.example", Path: tc.path, }).String(), strings.NewReader("")) @@ -117,7 +118,7 @@ func TestLinkedIPProxy_ServeHTTP(t *testing.T) { r.Header.Set(httphdr.Forwarded, "1.1.1.1") r.Header.Set(httphdr.TrueClientIP, "1.1.1.1") r.Header.Set(httphdr.XForwardedFor, "1.1.1.1") - r.Header.Set(httphdr.XForwardedHost, "forward.example.org") + r.Header.Set(httphdr.XForwardedHost, "forward.example") r.Header.Set(httphdr.XForwardedProto, "https") r.Header.Set(httphdr.XRealIP, "1.1.1.1") diff --git a/internal/websvc/websvc_test.go b/internal/websvc/websvc_test.go index c25efea..2a6158c 100644 --- a/internal/websvc/websvc_test.go +++ b/internal/websvc/websvc_test.go @@ -11,6 +11,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/agdhttp" "github.com/AdguardTeam/AdGuardDNS/internal/websvc" "github.com/AdguardTeam/golibs/httphdr" + "github.com/AdguardTeam/golibs/netutil/urlutil" "github.com/AdguardTeam/golibs/testutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -62,7 +63,7 @@ func TestService_NonDoH(t *testing.T) { } resp, err := client.Get((&url.URL{ - Scheme: "http", + Scheme: urlutil.SchemeHTTP, Host: nonDoHPort.String(), Path: "/other", }).String()) @@ -89,7 +90,7 @@ func assertContent(t *testing.T, addr netip.AddrPort, path string, status int, e var body []byte u := &url.URL{ - Scheme: "http", + Scheme: urlutil.SchemeHTTP, Host: addr.String(), Path: path, } diff --git a/scripts/backend/dns.go b/scripts/backend/dns.go new file mode 100644 index 0000000..3c83b89 --- /dev/null +++ b/scripts/backend/dns.go @@ -0,0 +1,202 @@ +package main + +import ( + "context" + "fmt" + "io" + "log/slog" + "net/netip" + "strconv" + "time" + + "github.com/AdguardTeam/AdGuardDNS/internal/backendpb" + "github.com/AdguardTeam/golibs/errors" + "github.com/AdguardTeam/golibs/httphdr" + "github.com/AdguardTeam/golibs/logutil/slogutil" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/emptypb" +) + +// mockDNSServiceServer is the mock [backendpb.DNSServiceServer]. +type mockDNSServiceServer struct { + backendpb.UnimplementedDNSServiceServer + log *slog.Logger +} + +// newMockDNSServiceServer creates a new instance of *mockDNSServiceServer. +func newMockDNSServiceServer(log *slog.Logger) (srv *mockDNSServiceServer) { + return &mockDNSServiceServer{ + log: log, + } +} + +// type check +var _ backendpb.DNSServiceServer = (*mockDNSServiceServer)(nil) + +// CreateDeviceByHumanId implements the [backendpb.DNSServiceServer] interface +// for *mockDNSServiceServer. +// +//lint:ignore ST1003 The name is necessary for the interface. +func (s *mockDNSServiceServer) CreateDeviceByHumanId( + ctx context.Context, + req *backendpb.CreateDeviceRequest, +) (resp *backendpb.CreateDeviceResponse, err error) { + md, _ := metadata.FromIncomingContext(ctx) + s.log.InfoContext( + ctx, + "creating by id", + "auth", md.Get(httphdr.Authorization), + "req", req, + ) + + p := newDNSProfile() + + return &backendpb.CreateDeviceResponse{ + Device: p.Devices[1], + }, nil +} + +// GetDNSProfiles implements the [backendpb.DNSServiceServer] interface for +// *mockDNSServiceServer +func (s *mockDNSServiceServer) GetDNSProfiles( + req *backendpb.DNSProfilesRequest, + srv grpc.ServerStreamingServer[backendpb.DNSProfile], +) (err error) { + ctx := srv.Context() + md, _ := metadata.FromIncomingContext(ctx) + s.log.InfoContext( + ctx, + "getting dns profiles", + "auth", md.Get(httphdr.Authorization), + "sync_time", req.SyncTime.AsTime(), + ) + + t := time.Now() + syncTime := strconv.FormatInt(t.UnixMilli(), 10) + trailerMD := metadata.MD{ + "sync_time": []string{syncTime}, + } + + srv.SetTrailer(trailerMD) + err = srv.Send(newDNSProfile()) + if err != nil { + s.log.WarnContext(ctx, "sending dns profile", slogutil.KeyError, err) + } + + return nil +} + +// SaveDevicesBillingStat implements the [backendpb.DNSServiceServer] interface +// for *mockDNSServiceServer +func (s *mockDNSServiceServer) SaveDevicesBillingStat( + srv grpc.ClientStreamingServer[backendpb.DeviceBillingStat, emptypb.Empty], +) (err error) { + ctx := srv.Context() + md, _ := metadata.FromIncomingContext(ctx) + s.log.InfoContext(ctx, "saving devices", "auth", md.Get(httphdr.Authorization)) + + for { + var bs *backendpb.DeviceBillingStat + bs, err = srv.Recv() + if err != nil { + if errors.Is(err, io.EOF) { + return srv.SendAndClose(&emptypb.Empty{}) + } else { + return fmt.Errorf("receiving billing stat: %w", err) + } + } + + s.log.InfoContext(ctx, "saving billing stat", "device_id", bs.DeviceId) + } +} + +// newDNSProfile returns a mock instance of [*backendpb.DNSProfile]. +func newDNSProfile() (dp *backendpb.DNSProfile) { + dayRange := &backendpb.DayRange{ + Start: durationpb.New(0), + End: durationpb.New(59 * time.Minute), + } + + devices := []*backendpb.DeviceSettings{{ + Id: "test", + Name: "test-name", + FilteringEnabled: false, + LinkedIp: []byte{1, 1, 1, 1}, + DedicatedIps: [][]byte{{127, 0, 0, 1}}, + }, { + Id: "auto", + Name: "My Device X-10", + HumanIdLower: "my-device-x--10", + }} + + return &backendpb.DNSProfile{ + DnsId: "mock1234", + FilteringEnabled: true, + QueryLogEnabled: true, + Deleted: false, + AutoDevicesEnabled: true, + IpLogEnabled: true, + SafeBrowsing: &backendpb.SafeBrowsingSettings{ + Enabled: true, + BlockDangerousDomains: true, + BlockNrd: false, + }, + Parental: &backendpb.ParentalSettings{ + Enabled: false, + BlockAdult: false, + GeneralSafeSearch: false, + YoutubeSafeSearch: false, + BlockedServices: []string{"youtube"}, + Schedule: &backendpb.ScheduleSettings{ + Tmz: "GMT", + WeeklyRange: &backendpb.WeeklyRange{ + Sun: nil, + Mon: dayRange, + Tue: dayRange, + Wed: dayRange, + Thu: dayRange, + Fri: dayRange, + Sat: nil, + }, + }, + }, + Access: &backendpb.AccessSettings{ + AllowlistCidr: []*backendpb.CidrRange{{ + Address: netip.MustParseAddr("1.1.1.0").AsSlice(), + Prefix: 24, + }}, + BlocklistCidr: []*backendpb.CidrRange{{ + Address: netip.MustParseAddr("2.2.2.0").AsSlice(), + Prefix: 24, + }}, + AllowlistAsn: []uint32{1}, + BlocklistAsn: []uint32{2}, + BlocklistDomainRules: []string{"block.test"}, + Enabled: true, + }, + RuleLists: &backendpb.RuleListsSettings{ + Enabled: true, + Ids: []string{"1"}, + }, + Devices: devices, + CustomRules: []string{"||example.org^"}, + FilteredResponseTtl: durationpb.New(10 * time.Second), + BlockPrivateRelay: true, + BlockFirefoxCanary: true, + BlockingMode: &backendpb.DNSProfile_BlockingModeCustomIp{ + BlockingModeCustomIp: &backendpb.BlockingModeCustomIP{ + Ipv4: []byte{1, 2, 3, 4}, + }, + }, + RateLimit: &backendpb.RateLimitSettings{ + ClientCidr: []*backendpb.CidrRange{{ + Address: netip.MustParseAddr("3.3.3.0").AsSlice(), + Prefix: 24, + }}, + Rps: 100, + Enabled: true, + }, + } +} diff --git a/scripts/backend/main.go b/scripts/backend/main.go index d586ae9..c794fd0 100644 --- a/scripts/backend/main.go +++ b/scripts/backend/main.go @@ -1,33 +1,22 @@ -// backend contains mock GRPC server for BILLSTAT_URL and PROFILES_URL -// endpoints. +// main implements a single mock GRPC server for backend services defined by +// BILLSTAT_URL, PROFILES_URL, and REMOTE_KV_URL environment variables. package main import ( - "context" - "fmt" - "io" - "log/slog" "net" - "net/netip" "os" - "strconv" - "time" "github.com/AdguardTeam/AdGuardDNS/internal/backendpb" - "github.com/AdguardTeam/golibs/errors" - "github.com/AdguardTeam/golibs/httphdr" "github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/osutil" "google.golang.org/grpc" - "google.golang.org/grpc/metadata" - "google.golang.org/protobuf/types/known/durationpb" - "google.golang.org/protobuf/types/known/emptypb" ) func main() { l := slogutil.New(nil) const listenAddr = "localhost:6062" + lsnr, err := net.Listen("tcp", listenAddr) if err != nil { l.Error("getting listener", slogutil.KeyError, err) @@ -36,10 +25,14 @@ func main() { } grpcSrv := grpc.NewServer() - srv := &mockDNSServiceServer{ - log: slogutil.New(nil), - } - backendpb.RegisterDNSServiceServer(grpcSrv, srv) + dnsSrv := newMockDNSServiceServer(l.With(slogutil.KeyPrefix, "dns")) + backendpb.RegisterDNSServiceServer(grpcSrv, dnsSrv) + + kvSrv := newMockRemoteKVServiceServer(l.With(slogutil.KeyPrefix, "remote_kv")) + backendpb.RegisterRemoteKVServiceServer(grpcSrv, kvSrv) + + rateLimitSrv := newMockRateLimitServiceServer(l.With(slogutil.KeyPrefix, "rate_limiter")) + backendpb.RegisterRateLimitServiceServer(grpcSrv, rateLimitSrv) l.Info("staring serving", "laddr", listenAddr) err = grpcSrv.Serve(lsnr) @@ -49,178 +42,3 @@ func main() { os.Exit(osutil.ExitCodeFailure) } } - -// mockDNSServiceServer is the mock [backendpb.DNSServiceServer]. -type mockDNSServiceServer struct { - backendpb.UnimplementedDNSServiceServer - log *slog.Logger -} - -// type check -var _ backendpb.DNSServiceServer = (*mockDNSServiceServer)(nil) - -// CreateDeviceByHumanId implements the [backendpb.DNSServiceServer] interface -// for *mockDNSServiceServer. -// -//lint:ignore ST1003 The name is necessary for the interface. -func (s *mockDNSServiceServer) CreateDeviceByHumanId( - ctx context.Context, - req *backendpb.CreateDeviceRequest, -) (resp *backendpb.CreateDeviceResponse, err error) { - md, _ := metadata.FromIncomingContext(ctx) - s.log.InfoContext( - ctx, - "creating by id", - "auth", md.Get(httphdr.Authorization), - "req", req, - ) - - p := newDNSProfile() - - return &backendpb.CreateDeviceResponse{ - Device: p.Devices[1], - }, nil -} - -// GetDNSProfiles implements the [backendpb.DNSServiceServer] interface for -// *mockDNSServiceServer -func (s *mockDNSServiceServer) GetDNSProfiles( - req *backendpb.DNSProfilesRequest, - srv grpc.ServerStreamingServer[backendpb.DNSProfile], -) (err error) { - ctx := srv.Context() - md, _ := metadata.FromIncomingContext(ctx) - s.log.InfoContext( - ctx, - "getting dns profiles", - "auth", md.Get(httphdr.Authorization), - "sync_time", req.SyncTime.AsTime(), - ) - - t := time.Now() - syncTime := strconv.FormatInt(t.UnixMilli(), 10) - trailerMD := metadata.MD{ - "sync_time": []string{syncTime}, - } - - srv.SetTrailer(trailerMD) - err = srv.Send(newDNSProfile()) - if err != nil { - s.log.WarnContext(ctx, "sending dns profile", slogutil.KeyError, err) - } - - return nil -} - -// SaveDevicesBillingStat implements the [backendpb.DNSServiceServer] interface -// for *mockDNSServiceServer -func (s *mockDNSServiceServer) SaveDevicesBillingStat( - srv grpc.ClientStreamingServer[backendpb.DeviceBillingStat, emptypb.Empty], -) (err error) { - ctx := srv.Context() - md, _ := metadata.FromIncomingContext(ctx) - s.log.InfoContext(ctx, "saving devices", "auth", md.Get(httphdr.Authorization)) - - for { - var bs *backendpb.DeviceBillingStat - bs, err = srv.Recv() - if err != nil { - if errors.Is(err, io.EOF) { - return srv.SendAndClose(&emptypb.Empty{}) - } else { - return fmt.Errorf("receiving billing stat: %w", err) - } - } - - s.log.InfoContext(ctx, "saving billing stat", "device_id", bs.DeviceId) - } -} - -// newDNSProfile returns a mock instance of [*backendpb.DNSProfile]. -func newDNSProfile() (dp *backendpb.DNSProfile) { - dayRange := &backendpb.DayRange{ - Start: durationpb.New(0), - End: durationpb.New(59 * time.Minute), - } - - devices := []*backendpb.DeviceSettings{{ - Id: "test", - Name: "test-name", - FilteringEnabled: false, - LinkedIp: []byte{1, 1, 1, 1}, - DedicatedIps: [][]byte{{127, 0, 0, 1}}, - }, { - Id: "auto", - Name: "My Device X-10", - HumanIdLower: "my-device-x--10", - }} - - return &backendpb.DNSProfile{ - DnsId: "mock1234", - FilteringEnabled: true, - QueryLogEnabled: true, - Deleted: false, - AutoDevicesEnabled: true, - IpLogEnabled: true, - SafeBrowsing: &backendpb.SafeBrowsingSettings{ - Enabled: true, - BlockDangerousDomains: true, - BlockNrd: false, - }, - Parental: &backendpb.ParentalSettings{ - Enabled: false, - BlockAdult: false, - GeneralSafeSearch: false, - YoutubeSafeSearch: false, - BlockedServices: []string{"youtube"}, - Schedule: &backendpb.ScheduleSettings{ - Tmz: "GMT", - WeeklyRange: &backendpb.WeeklyRange{ - Sun: nil, - Mon: dayRange, - Tue: dayRange, - Wed: dayRange, - Thu: dayRange, - Fri: dayRange, - Sat: nil, - }, - }, - }, - Access: &backendpb.AccessSettings{ - AllowlistCidr: []*backendpb.CidrRange{{ - Address: netip.MustParseAddr("1.1.1.0").AsSlice(), - Prefix: 24, - }}, - BlocklistCidr: []*backendpb.CidrRange{{ - Address: netip.MustParseAddr("2.2.2.0").AsSlice(), - Prefix: 24, - }}, - AllowlistAsn: []uint32{1}, - BlocklistAsn: []uint32{2}, - BlocklistDomainRules: []string{"block.test"}, - Enabled: true, - }, - RuleLists: &backendpb.RuleListsSettings{ - Enabled: true, - Ids: []string{"1"}, - }, - Devices: devices, - CustomRules: []string{"||example.org^"}, - FilteredResponseTtl: durationpb.New(10 * time.Second), - BlockPrivateRelay: true, - BlockFirefoxCanary: true, - BlockingMode: &backendpb.DNSProfile_BlockingModeCustomIp{ - BlockingModeCustomIp: &backendpb.BlockingModeCustomIP{ - Ipv4: []byte{1, 2, 3, 4}, - }, - }, - RateLimit: &backendpb.RateLimitSettings{ - ClientCidr: []*backendpb.CidrRange{{ - Address: netip.MustParseAddr("3.3.3.0").AsSlice(), - Prefix: 24, - }}, - Rps: 100, - Enabled: true, - }, - } -} diff --git a/scripts/backend/ratelimiter.go b/scripts/backend/ratelimiter.go new file mode 100644 index 0000000..b41decb --- /dev/null +++ b/scripts/backend/ratelimiter.go @@ -0,0 +1,51 @@ +package main + +import ( + "context" + "log/slog" + + "github.com/AdguardTeam/AdGuardDNS/internal/backendpb" + "github.com/AdguardTeam/golibs/httphdr" + "github.com/AdguardTeam/golibs/netutil" + "google.golang.org/grpc/metadata" +) + +// mockRateLimitServiceServer is the mock [backendpb.RateLimiteServiceServer]. +type mockRateLimitServiceServer struct { + backendpb.UnimplementedRateLimitServiceServer + log *slog.Logger +} + +// newMockRateLimitServiceServer creates a new instance of +// *mockRateLimitServiceServer. +func newMockRateLimitServiceServer(log *slog.Logger) (srv *mockRateLimitServiceServer) { + return &mockRateLimitServiceServer{ + log: log, + } +} + +// type check +var _ backendpb.RateLimitServiceServer = (*mockRateLimitServiceServer)(nil) + +// Get implements the [backendpb.RateLimitServiceServer] interface for +// *mockRateLimitServiceServer. +func (s *mockRateLimitServiceServer) GetRateLimitSettings( + ctx context.Context, + req *backendpb.RateLimitSettingsRequest, +) (resp *backendpb.RateLimitSettingsResponse, err error) { + md, _ := metadata.FromIncomingContext(ctx) + + s.log.InfoContext( + ctx, + "getting", + "auth", md.Get(httphdr.Authorization), + "req", req, + ) + + return &backendpb.RateLimitSettingsResponse{ + AllowedSubnets: []*backendpb.CidrRange{{ + Address: netutil.IPv4Localhost().AsSlice(), + Prefix: 8, + }}, + }, nil +} diff --git a/scripts/backend/remotekv.go b/scripts/backend/remotekv.go new file mode 100644 index 0000000..53ce6e6 --- /dev/null +++ b/scripts/backend/remotekv.go @@ -0,0 +1,83 @@ +package main + +import ( + "context" + "log/slog" + "time" + + "github.com/AdguardTeam/AdGuardDNS/internal/backendpb" + "github.com/AdguardTeam/golibs/httphdr" + "github.com/patrickmn/go-cache" + "google.golang.org/grpc/metadata" +) + +// mockRemoteKVServiceServer is the mock [backendpb.RemoteKVServiceServer]. +type mockRemoteKVServiceServer struct { + backendpb.UnimplementedRemoteKVServiceServer + log *slog.Logger + strg *cache.Cache +} + +// newMockRemoteKVServiceServer creates a new instance of +// *mockRemoteKVServiceServer. +func newMockRemoteKVServiceServer(log *slog.Logger) (srv *mockRemoteKVServiceServer) { + const ( + defaultCacheExp = 30 * time.Second + defaultCacheGC = 1 * time.Minute + ) + + return &mockRemoteKVServiceServer{ + log: log, + strg: cache.New(defaultCacheExp, defaultCacheGC), + } +} + +// type check +var _ backendpb.RemoteKVServiceServer = (*mockRemoteKVServiceServer)(nil) + +// Get implements the [backendpb.RemoteKVServiceServer] interface for +// *mockRemoteKVServiceServer. +func (s *mockRemoteKVServiceServer) Get( + ctx context.Context, + req *backendpb.RemoteKVGetRequest, +) (resp *backendpb.RemoteKVGetResponse, err error) { + md, _ := metadata.FromIncomingContext(ctx) + s.log.InfoContext( + ctx, + "getting", + "auth", md.Get(httphdr.Authorization), + "req", req, + ) + + resp = &backendpb.RemoteKVGetResponse{ + Value: &backendpb.RemoteKVGetResponse_Empty{}, + } + + val, ok := s.strg.Get(req.Key) + if ok { + resp.Value = &backendpb.RemoteKVGetResponse_Data{ + Data: val.([]byte), + } + } + + return resp, nil +} + +// Set implements the [backendpb.RemoteKVServiceServer] interface for +// *mockRemoteKVServiceServer. +func (s *mockRemoteKVServiceServer) Set( + ctx context.Context, + req *backendpb.RemoteKVSetRequest, +) (resp *backendpb.RemoteKVSetResponse, err error) { + md, _ := metadata.FromIncomingContext(ctx) + s.log.InfoContext( + ctx, + "setting", + "auth", md.Get(httphdr.Authorization), + "req", req, + ) + + s.strg.Set(req.Key, req.Data, req.Ttl.AsDuration()) + + return &backendpb.RemoteKVSetResponse{}, err +}