diff --git a/.gitignore b/.gitignore index 41cb9f1..7b641af 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ +# This comment is used to simplify checking local copies of the file. Bump +# this number every time a significant change is made to this file. +# +# AdGuard-Project-Version: 3 + # Please, DO NOT put your text editors' temporary files here. The more are # added, the harder it gets to maintain and manage projects' gitignores. Put # them into your global gitignore file instead. @@ -6,12 +11,15 @@ # # Only build, run, and test outputs here. Sorted. With negations at the # bottom to make sure they take effect. +*.exe *.out *.test /bin/ /filters/ /github-mirror/ +/test-reports/ /test/ +/tmp/ AdGuardDNS agdns asn.mmdb diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d01da9..74897e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ 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-2998 / Build 1042 + +- Profiles’ file cache version has been incremented. The new field `StandardEnabled` has been added to access’ object. + ## AGDNS-3018 / Build 1033 - The environment variables `DNSCHECK_KV_TTL`, `DNSCHECK_KV_TYPE` have been added. diff --git a/Makefile b/Makefile index cd01e3f..0dd7888 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.24.5 +GOTOOLCHAIN = go1.24.6 RACE = 0 REVISION = $${REVISION:-$$(git rev-parse --short HEAD)} VERSION = 0 diff --git a/README.md b/README.md index e4d20a5..25ebbe4 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,6 @@ You can sign up for a personal AdGuard DNS account and get access to the followi ## Software license -Copyright (C) 2022-2024 AdGuard Software Ltd. +Copyright (C) 2022-2025 AdGuard Software Ltd. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3. diff --git a/doc/debughttp.md b/doc/debughttp.md index c84640a..e23caab 100644 --- a/doc/debughttp.md +++ b/doc/debughttp.md @@ -112,6 +112,7 @@ Supported IDs: - `profiledb_full` - `profiledb` - `rulestat` +- `standard_profile_access` - `ticket_rotator` - `tlsconfig` - `websvc` diff --git a/doc/environment.md b/doc/environment.md index a12d0c2..c778541 100644 --- a/doc/environment.md +++ b/doc/environment.md @@ -65,6 +65,11 @@ AdGuard DNS uses [environment variables][wiki-env] to store some of the more sen - [`SESSION_TICKET_REFRESH_INTERVAL`](#SESSION_TICKET_REFRESH_INTERVAL) - [`SESSION_TICKET_TYPE`](#SESSION_TICKET_TYPE) - [`SESSION_TICKET_URL`](#SESSION_TICKET_URL) +- [`STANDARD_ACCESS_API_KEY`](#STANDARD_ACCESS_API_KEY) +- [`STANDARD_ACCESS_REFRESH_INTERVAL`](#STANDARD_ACCESS_REFRESH_INTERVAL) +- [`STANDARD_ACCESS_TIMEOUT`](#STANDARD_ACCESS_TIMEOUT) +- [`STANDARD_ACCESS_TYPE`](#STANDARD_ACCESS_TYPE) +- [`STANDARD_ACCESS_URL`](#STANDARD_ACCESS_URL) - [`SSL_KEY_LOG_FILE`](#SSL_KEY_LOG_FILE) - [`VERBOSE`](#VERBOSE) - [`WEB_STATIC_DIR_ENABLED`](#WEB_STATIC_DIR_ENABLED) @@ -535,6 +540,36 @@ The base backend URL used as a TLS session ticket storage, when [`SESSION_TICKET **Default:** **Unset.** +## `STANDARD_ACCESS_API_KEY` + +The API key to use when authenticating requests to the standard access settings storage API, if [`STANDARD_ACCESS_TYPE`](#STANDARD_ACCESS_TYPE) is set to `backend`. The API key should be valid as defined by [RFC 6750]. + +**Default:** **Unset.** + +## `STANDARD_ACCESS_REFRESH_INTERVAL` + +The interval between standard access settings updates, when [`STANDARD_ACCESS_TYPE`](#STANDARD_ACCESS_TYPE) is set to `backend`, as a human-readable duration. + +**Default:** **Unset.** + +## `STANDARD_ACCESS_TIMEOUT` + +The timeout for standard access settings updates, when [`STANDARD_ACCESS_TYPE`](#STANDARD_ACCESS_TYPE) is set to `backend`, as a human-readable duration. + +**Default:** **Unset.** + +## `STANDARD_ACCESS_TYPE` + +The type of standard access settings storage. Its possible values are: `off` and `backend`. When set to `backend`, the [`STANDARD_ACCESS_API_KEY`](#STANDARD_ACCESS_API_KEY), [`STANDARD_ACCESS_REFRESH_INTERVAL`](#STANDARD_ACCESS_REFRESH_INTERVAL), [`STANDARD_ACCESS_TIMEOUT`](#STANDARD_ACCESS_TIMEOUT), and [`STANDARD_ACCESS_URL`](#STANDARD_ACCESS_URL) variables are required. + +**Default:** **Unset.** + +## `STANDARD_ACCESS_URL` + +The base backend URL used as a standard access settings storage, when [`STANDARD_ACCESS_TYPE`](#STANDARD_ACCESS_TYPE) is set to `backend`. Supports gRPC(S) (`grpc://` and`grpcs://`) URLs. See the [external API requirements section][ext-backend-dnscheck]. + +**Default:** **Unset.** + ## `SSL_KEY_LOG_FILE` If set, TLS key logs are written to this file to allow other programs (i.e. Wireshark) to decrypt packets. **Must only be used for debug purposes**. diff --git a/go.mod b/go.mod index 3bb4a5f..4e4af79 100644 --- a/go.mod +++ b/go.mod @@ -1,79 +1,80 @@ module github.com/AdguardTeam/AdGuardDNS -go 1.24.5 +go 1.24.6 require ( // NOTE: Do not change the pseudoversion. github.com/AdguardTeam/AdGuardDNS/internal/dnsserver v0.0.0-00010101000000-000000000000 - github.com/AdguardTeam/golibs v0.32.15 - github.com/AdguardTeam/urlfilter v0.20.0 + github.com/AdguardTeam/golibs v0.34.0 + github.com/AdguardTeam/urlfilter v0.21.0 github.com/ameshkov/dnscrypt/v2 v2.4.0 github.com/axiomhq/hyperloglog v0.2.5 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.34.0 + github.com/getsentry/sentry-go v0.35.1 github.com/gomodule/redigo v1.9.2 + github.com/google/go-cmp v0.7.0 github.com/google/renameio/v2 v2.0.0 - github.com/miekg/dns v1.1.66 + github.com/miekg/dns v1.1.68 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.22.0 + github.com/prometheus/client_golang v1.23.0 github.com/prometheus/client_model v0.6.2 - github.com/prometheus/common v0.64.0 - github.com/quic-go/quic-go v0.52.0 - github.com/stretchr/testify v1.10.0 - golang.org/x/crypto v0.39.0 - golang.org/x/net v0.41.0 - golang.org/x/sys v0.34.0 + github.com/prometheus/common v0.65.0 + github.com/quic-go/quic-go v0.54.0 + github.com/stretchr/testify v1.11.1 + github.com/viktordanov/golang-lru v0.5.6 + golang.org/x/crypto v0.41.0 + golang.org/x/net v0.43.0 + golang.org/x/sys v0.35.0 golang.org/x/time v0.12.0 - google.golang.org/grpc v1.73.0 - google.golang.org/protobuf v1.36.6 + google.golang.org/grpc v1.75.0 + google.golang.org/protobuf v1.36.8 gopkg.in/yaml.v2 v2.4.0 ) require ( - cloud.google.com/go v0.121.3 // indirect + cloud.google.com/go v0.121.6 // indirect cloud.google.com/go/ai v0.12.1 // indirect - cloud.google.com/go/auth v0.16.2 // indirect + cloud.google.com/go/auth v0.16.5 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect - cloud.google.com/go/compute/metadata v0.7.0 // indirect + cloud.google.com/go/compute/metadata v0.8.0 // indirect cloud.google.com/go/longrunning v0.6.7 // indirect github.com/BurntSushi/toml v1.5.0 // indirect github.com/ameshkov/dnsstamps v1.0.3 // indirect + github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/caarlos0/env/v11 v11.3.1 // indirect github.com/ccojocar/zxcvbn-go v1.0.4 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fzipp/gocyclo v0.6.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/golangci/misspell v0.7.0 // indirect github.com/google/generative-ai-go v0.20.1 // indirect - github.com/google/go-cmp v0.7.0 // indirect - github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a // indirect + github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect - github.com/googleapis/gax-go/v2 v2.14.2 // indirect + github.com/googleapis/gax-go/v2 v2.15.0 // indirect github.com/gookit/color v1.5.4 // indirect - github.com/gordonklaus/ineffassign v0.1.0 // indirect + github.com/gordonklaus/ineffassign v0.2.0 // indirect github.com/jstemmer/go-junit-report/v2 v2.1.0 // indirect github.com/kamstrup/intmap v0.5.1 // indirect github.com/kisielk/errcheck v1.9.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/onsi/ginkgo/v2 v2.23.4 // indirect + github.com/onsi/ginkgo/v2 v2.25.1 // indirect github.com/panjf2000/ants/v2 v2.11.3 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/procfs v0.16.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/procfs v0.17.0 // indirect github.com/quic-go/qpack v0.5.1 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect - github.com/securego/gosec/v2 v2.22.5 // indirect + github.com/securego/gosec/v2 v2.22.8 // indirect github.com/uudashr/gocognit v1.2.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect @@ -82,21 +83,20 @@ require ( go.opentelemetry.io/otel v1.37.0 // indirect go.opentelemetry.io/otel/metric v1.37.0 // indirect go.opentelemetry.io/otel/trace v1.37.0 // indirect - go.uber.org/automaxprocs v1.6.0 // indirect - go.uber.org/mock v0.5.2 // indirect - golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect - golang.org/x/exp/typeparams v0.0.0-20250620022241-b7579e27df2b // indirect - golang.org/x/mod v0.26.0 // indirect + go.uber.org/mock v0.6.0 // indirect + golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect + golang.org/x/exp/typeparams v0.0.0-20250819193227-8b4c13bb791b // indirect + golang.org/x/mod v0.27.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.16.0 // indirect - golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b // indirect - golang.org/x/term v0.33.0 // indirect - golang.org/x/text v0.27.0 // indirect - golang.org/x/tools v0.34.0 // indirect + golang.org/x/telemetry v0.0.0-20250822161441-f407b8c191ff // indirect + golang.org/x/term v0.34.0 // indirect + golang.org/x/text v0.28.0 // indirect + golang.org/x/tools v0.36.0 // indirect golang.org/x/vuln v1.1.4 // indirect - google.golang.org/api v0.241.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect + google.golang.org/api v0.248.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 // indirect google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect honnef.co/go/tools v0.6.1 // indirect diff --git a/go.sum b/go.sum index ab675e3..ad8f376 100644 --- a/go.sum +++ b/go.sum @@ -1,27 +1,31 @@ -cloud.google.com/go v0.121.3 h1:84RD+hQXNdY5Sw/MWVAx5O9Aui/rd5VQ9HEcdN19afo= -cloud.google.com/go v0.121.3/go.mod h1:6vWF3nJWRrEUv26mMB3FEIU/o1MQNVPG1iHdisa2SJc= +cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c= +cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI= cloud.google.com/go/ai v0.12.1 h1:m1n/VjUuHS+pEO/2R4/VbuuEIkgk0w67fDQvFaMngM0= cloud.google.com/go/ai v0.12.1/go.mod h1:5vIPNe1ZQsVZqCliXIPL4QnhObQQY4d9hAGHdVc4iw4= -cloud.google.com/go/auth v0.16.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4= -cloud.google.com/go/auth v0.16.2/go.mod h1:sRBas2Y1fB1vZTdurouM0AzuYQBMZinrUYL8EufhtEA= +cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI= +cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= -cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU= -cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo= +cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA= +cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw= cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE= cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY= -github.com/AdguardTeam/golibs v0.32.15 h1:arDRDWiZCH3g5Onr8AqMnOHhaOppNoBpgC3DNhmeDeA= -github.com/AdguardTeam/golibs v0.32.15/go.mod h1:G9CzUOzx87J+2u+eClJrrwWD7lMbROvuUnT8uvDUzIA= -github.com/AdguardTeam/urlfilter v0.20.0 h1:X32qiuVCVd8WDYCEsbdZKfXMzwdVqrdulamtUi4rmzs= -github.com/AdguardTeam/urlfilter v0.20.0/go.mod h1:gjrywLTxfJh6JOkwi9SU+frhP7kVVEZ5exFGkR99qpk= +github.com/AdguardTeam/golibs v0.34.0 h1:JQK024DkTYxE7vsPVsYsoyDHW/53Nun7OYb9qscniK8= +github.com/AdguardTeam/golibs v0.34.0/go.mod h1:K4C2EbfSEM1zY5YXoti9SfbTAHN/kIX97LpDtCwORrM= +github.com/AdguardTeam/urlfilter v0.21.0 h1:ThIxiP7yoaXt8JTEroGQeU5ftQSoFpUq+t1L+TIx2pA= +github.com/AdguardTeam/urlfilter v0.21.0/go.mod h1:xoZ3AF5qpE9ngbbeSShY9hgVeyHtm9MdH5xH1u714Wg= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= +github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/ameshkov/dnscrypt/v2 v2.4.0 h1:if6ZG2cuQmcP2TwSY+D0+8+xbPfoatufGlOQTMNkI9o= github.com/ameshkov/dnscrypt/v2 v2.4.0/go.mod h1:WpEFV2uhebXb8Jhes/5/fSdpmhGV8TL22RDaeWwV6hI= github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo= github.com/ameshkov/dnsstamps v1.0.3/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A= github.com/axiomhq/hyperloglog v0.2.5 h1:Hefy3i8nAs8zAI/tDp+wE7N+Ltr8JnwiW3875pvl0N8= github.com/axiomhq/hyperloglog v0.2.5/go.mod h1:DLUK9yIzpU5B6YFLjxTIcbHu1g4Y1WQb1m5RH3radaM= +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw= @@ -36,16 +40,16 @@ github.com/ccojocar/zxcvbn-go v1.0.4 h1:FWnCIRMXPj43ukfX000kvBZvV6raSxakYr1nzyNr github.com/ccojocar/zxcvbn-go v1.0.4/go.mod h1:3GxGX+rHmueTUMvm5ium7irpyjmm7ikxYFOSJB21Das= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33 h1:ucRHb6/lvW/+mTEIGbvhcYU3S8+uSNkuMjx/qZFfhtM= github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= -github.com/getsentry/sentry-go v0.34.0 h1:1FCHBVp8TfSc8L10zqSwXUZNiOSF+10qw4czjarTiY4= -github.com/getsentry/sentry-go v0.34.0/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE= +github.com/getsentry/sentry-go v0.35.1 h1:iopow6UVLE2aXu46xKVIs8Z9D/YZkJrHkgozrxa+tOQ= +github.com/getsentry/sentry-go v0.35.1/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE= 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.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -72,8 +76,8 @@ github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786/go.mod h1:apVn github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18= -github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= +github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY= +github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg= @@ -84,12 +88,12 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= -github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0= -github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w= +github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= +github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= -github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s= -github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= +github.com/gordonklaus/ineffassign v0.2.0 h1:Uths4KnmwxNJNzq87fwQQDDnbNb7De00VOk9Nu0TySs= +github.com/gordonklaus/ineffassign v0.2.0/go.mod h1:TIpymnagPSexySzs7F9FnO1XFTy8IT3a59vmZp5Y9Lw= 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/kamstrup/intmap v0.5.1 h1:ENGAowczZA+PJPYYlreoqJvWgQVtAmX1l899WfYFVK0= @@ -106,14 +110,14 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE= -github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE= +github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= +github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= -github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= -github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= -github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= +github.com/onsi/ginkgo/v2 v2.25.1 h1:Fwp6crTREKM+oA6Cz4MsO8RhKQzs2/gOIVOUscMAfZY= +github.com/onsi/ginkgo/v2 v2.25.1/go.mod h1:ppTWQ1dh9KM/F1XgpeRqelR+zHVwV81DGRSDnFxK7Sk= +github.com/onsi/gomega v1.38.0 h1:c/WX+w8SLAinvuKKQFh77WEucCnPk4j2OTUr7lt7BeY= +github.com/onsi/gomega v1.38.0/go.mod h1:OcXcwId0b9QsE7Y49u+BTrL4IdKOBOKnD6VQNTJEB6o= github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE= github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8= github.com/panjf2000/ants/v2 v2.11.3 h1:AfI0ngBoXJmYOpDh9m516vjqoUu2sLrIVgppI9TZVpg= @@ -124,47 +128,46 @@ github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4 github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/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/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= -github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= -github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= -github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= +github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQPGO4= -github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= -github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= -github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= +github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= +github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= 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.52.0 h1:/SlHrCRElyaU6MaEPKqKr9z83sBg2v4FLLvWM+Z47pA= -github.com/quic-go/quic-go v0.52.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ= +github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= +github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/securego/gosec/v2 v2.22.5 h1:ySws9uwOeE42DsG54v2moaJfh7r08Ev7SAYJuoMDfRA= -github.com/securego/gosec/v2 v2.22.5/go.mod h1:AWfgrFsVewk5LKobsPWlygCHt8K91boVPyL6GUZG5NY= +github.com/securego/gosec/v2 v2.22.8 h1:3NMpmfXO8wAVFZPNsd3EscOTa32Jyo6FLLlW53bexMI= +github.com/securego/gosec/v2 v2.22.8/go.mod h1:ZAw8K2ikuH9qDlfdV87JmNghnVfKB1XC7+TVzk6Utto= github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/uudashr/gocognit v1.2.0 h1:3BU9aMr1xbhPlvJLSydKwdLN3tEUUrzPSSM8S4hDYRA= github.com/uudashr/gocognit v1.2.0/go.mod h1:k/DdKPI6XBZO1q7HgoV2juESI2/Ofj9AcHPZhBBdrTU= +github.com/viktordanov/golang-lru v0.5.6 h1:wEyMgglEo5IZ7Maxeh8E2jCPskpQnt6FJAYl1/TJ6ac= +github.com/viktordanov/golang-lru v0.5.6/go.mod h1:R91CBCcMhp6TYUy8NHP/PJ09sk5BTDwb8KMO21CELes= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= @@ -187,72 +190,56 @@ go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= -go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= -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.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= -golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= -golang.org/x/exp/typeparams v0.0.0-20250620022241-b7579e27df2b h1:KdrhdYPDUvJTvrDK9gdjfFd6JTk8vA1WJoldYSi0kHo= -golang.org/x/exp/typeparams v0.0.0-20250620022241-b7579e27df2b/go.mod h1:LKZHyeOpPuZcMgxeHjJp4p5yvxrCX1xDvH10zYHhjjQ= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= -golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= -golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0= +golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4= +golang.org/x/exp/typeparams v0.0.0-20250819193227-8b4c13bb791b h1:GU1ttDuJS89SePnuEsEuLj7dMMFP2JkGsDV1Z51iDXo= +golang.org/x/exp/typeparams v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4Mzdyp/6jzw9auFDJ3OMF5qksa7UvPnzKqTVGcb04ms= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-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.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b h1:DU+gwOBXU+6bO0sEyO7o/NeMlxZxCZEvI7v+J4a1zRQ= -golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= -golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= -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.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/telemetry v0.0.0-20250822161441-f407b8c191ff h1:ey0rzo8V0Po6WkHMbhGVL6zNkuYDBe9iP5toIRchj9Q= +golang.org/x/telemetry v0.0.0-20250822161441-f407b8c191ff/go.mod h1:JIJwPkb04vX0KeIBbQ7epGtgIjA8ihHbsAtW4A/lIQ4= +golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -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.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= -golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= +golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= golang.org/x/vuln v1.1.4 h1:Ju8QsuyhX3Hk8ma3CesTbO8vfJD9EvUBgHvkxHBzj0I= golang.org/x/vuln v1.1.4/go.mod h1:F+45wmU18ym/ca5PLTPLsSzr2KppzswxPP603ldA67s= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.241.0 h1:QKwqWQlkc6O895LchPEDUSYr22Xp3NCxpQRiWTB6avE= -google.golang.org/api v0.241.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50= -google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78= -google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk= -google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU= -google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= -google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/api v0.248.0 h1:hUotakSkcwGdYUqzCRc5yGYsg4wXxpkKlW5ryVqvC1Y= +google.golang.org/api v0.248.0/go.mod h1:yAFUAF56Li7IuIQbTFoLwXTCI6XCFKueOlS7S9e4F9k= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= +google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1 h1:APHvLLYBhtZvsbnpkfknDZ7NyH4z5+ub/I0u8L3Oz6g= +google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1/go.mod h1:xUjFWUnWDpZ/C0Gu0qloASKFb6f8/QXiiXhSPFsD668= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 h1:pmJpJEvT846VzausCQ5d7KreSROcDqmO388w5YbnltA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= +google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= +google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= 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 v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= +google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= 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 804fc4e..03ae774 100644 --- a/go.work +++ b/go.work @@ -1,4 +1,4 @@ -go 1.24.5 +go 1.24.6 use ( . diff --git a/go.work.sum b/go.work.sum index 5be767c..f0e2372 100644 --- a/go.work.sum +++ b/go.work.sum @@ -11,6 +11,7 @@ cel.dev/expr v0.20.0 h1:OunBvVCfvpWlt4dN7zg3FM6TDkzOePe1+foGJ9AXeeI= cel.dev/expr v0.20.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cel.dev/expr v0.23.0 h1:wUb94w6OYQS4uXraxo9U+wUAs9jT47Xvl4iPgAwM2ss= cel.dev/expr v0.23.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= +cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= 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= @@ -183,6 +184,7 @@ cloud.google.com/go/storage v1.51.0/go.mod h1:YEJfu/Ki3i5oHC/7jyTgsGZwdQ8P9hqMqv cloud.google.com/go/storage v1.53.0 h1:gg0ERZwL17pJ+Cz3cD2qS60w1WMDnwcm5YPAIQBHUAw= cloud.google.com/go/storage v1.53.0/go.mod h1:7/eO2a/srr9ImZW9k5uufcNahT2+fPb8w5it1i5boaA= cloud.google.com/go/storage v1.55.0/go.mod h1:ztSmTTwzsdXe5syLVS0YsbFxXuvEmEyZj7v7zChEmuY= +cloud.google.com/go/storage v1.56.0/go.mod h1:Tpuj6t4NweCLzlNbw9Z9iwxEkrSem20AetIeH/shgVU= cloud.google.com/go/storagetransfer v1.10.4/go.mod h1:vef30rZKu5HSEf/x1tK3WfWrL0XVoUQN/EPDRGPzjZs= cloud.google.com/go/talent v1.6.6/go.mod h1:y/WQDKrhVz12WagoarpAIyKKMeKGKHWPoReZ0g8tseQ= cloud.google.com/go/texttospeech v1.7.5/go.mod h1:tzpCuNWPwrNJnEa4Pu5taALuZL4QRRLcb+K9pbhXT6M= @@ -232,6 +234,8 @@ github.com/AdguardTeam/golibs v0.32.11/go.mod h1:LXr0gqqZuVpt+L+bP3Nnr0/CecLmm3r 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= +github.com/AdguardTeam/urlfilter v0.21.0 h1:ThIxiP7yoaXt8JTEroGQeU5ftQSoFpUq+t1L+TIx2pA= +github.com/AdguardTeam/urlfilter v0.21.0/go.mod h1:xoZ3AF5qpE9ngbbeSShY9hgVeyHtm9MdH5xH1u714Wg= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= @@ -251,12 +255,15 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0/go.mod h1:2bIszWvQRlJVmJLiuLhukLImRjKPcYdzzsx6darK02A= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0/go.mod h1:Cz6ft6Dkn3Et6l2v2a9/RpN7epQ1GtDlO6lj8bEcOvw= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0/go.mod h1:ZV4VOm0/eHR06JLrXWe09068dHpr3TRpY9Uo7T+anuA= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 h1:fYE9p3esPxA/C0rQ0AHhP0drtPXDRhaWiwg1DPqO7IU= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0/go.mod h1:BnBReJLvVYx2CS/UHOgVz2BXKXD9wsQPxZug20nZhd0= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 h1:6/0iUd0xrnX7qt+mLNRwg5c0PGv8wpE8K90ryANQwMI= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0/go.mod h1:cSgYe11MCNYunTnRXrKiR/tHc0eoKjICUuWpNZoVCOo= github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk= github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -312,6 +319,7 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= @@ -362,6 +370,7 @@ github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k= github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= 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= @@ -380,7 +389,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -453,6 +461,7 @@ github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc= github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= +github.com/go-jose/go-jose/v4 v4.1.1/go.mod h1:BdsZGqgdO3b6tTc6LSE56wcDbMMLuPsw5d4ZD5f94kA= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -514,6 +523,7 @@ github.com/golang/glog v1.2.3 h1:oDTdz9f5VGVVNGu/Q7UXKWYsD0873HXLHdJUNBsSEKM= github.com/golang/glog v1.2.3/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/glog v1.2.4 h1:CNNw5U8lSiiBk7druxtSHHTsRWcxKoac6kZKm2peBBc= github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -1150,7 +1160,6 @@ go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxt go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= -go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= @@ -1161,6 +1170,7 @@ go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9f go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os= go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo= +go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -1704,6 +1714,7 @@ google.golang.org/genproto/googleapis/bytestream v0.0.0-20250512202823-5a2f75b73 google.golang.org/genproto/googleapis/bytestream v0.0.0-20250528174236-200df99c418a/go.mod h1:h6yxum/C2qRb4txaZRLDHK8RyS0H/o2oEDeKY4onY/Y= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250603155806-513f23925822 h1:zWFRixYR5QlotL+Uv3YfsPRENIrQFXiGs+iwqel6fOQ= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250603155806-513f23925822/go.mod h1:h6yxum/C2qRb4txaZRLDHK8RyS0H/o2oEDeKY4onY/Y= +google.golang.org/genproto/googleapis/bytestream v0.0.0-20250818200422-3122310a409c/go.mod h1:1kGGe25NDrNJYgta9Rp2QLLXWS1FLVMMXNvihbhK0iE= 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= google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= diff --git a/internal/access/access.go b/internal/access/access.go index 7e0dbce..3a716d2 100644 --- a/internal/access/access.go +++ b/internal/access/access.go @@ -4,10 +4,10 @@ package access import ( "fmt" "net/netip" - "strings" + "github.com/AdguardTeam/AdGuardDNS/internal/agdurlflt" "github.com/AdguardTeam/golibs/netutil" - "github.com/AdguardTeam/golibs/stringutil" + "github.com/AdguardTeam/golibs/syncutil" "github.com/AdguardTeam/urlfilter" "github.com/AdguardTeam/urlfilter/filterlist" ) @@ -32,25 +32,28 @@ type Interface interface { type Global struct { blockedHostsEng *urlfilter.DNSEngine blockedNets netutil.SubnetSet + reqPool *syncutil.Pool[urlfilter.DNSRequest] + resPool *syncutil.Pool[urlfilter.DNSResult] } -// NewGlobal create a new Global from provided parameters. +// NewGlobal creates a new *Global from provided parameters. func NewGlobal(blockedDomains []string, blockedSubnets []netip.Prefix) (g *Global, err error) { g = &Global{ blockedNets: netutil.SliceSubnetSet(blockedSubnets), + reqPool: syncutil.NewPool(func() (req *urlfilter.DNSRequest) { + return &urlfilter.DNSRequest{} + }), + resPool: syncutil.NewPool(func() (v *urlfilter.DNSResult) { + return &urlfilter.DNSResult{} + }), } - b := &strings.Builder{} - for _, h := range blockedDomains { - stringutil.WriteToBuilder(b, strings.ToLower(h), "\n") - } - - lists := []filterlist.RuleList{ - &filterlist.StringRuleList{ + lists := []filterlist.Interface{ + filterlist.NewBytes(&filterlist.BytesConfig{ ID: blocklistFilterID, - RulesText: b.String(), + RulesText: agdurlflt.RulesToBytesLower(blockedDomains), IgnoreCosmetic: true, - }, + }), } rulesStrg, err := filterlist.NewRuleStorage(lists) @@ -68,16 +71,37 @@ var _ Interface = (*Global)(nil) // IsBlockedHost implements the [Interface] interface for *Global. func (g *Global) IsBlockedHost(host string, qt uint16) (blocked bool) { - res, matched := g.blockedHostsEng.MatchRequest(&urlfilter.DNSRequest{ - Hostname: host, - DNSType: qt, - }) + return matchBlocked(host, qt, g.blockedHostsEng, g.reqPool, g.resPool) +} - if matched && res.NetworkRule != nil { +// matchBlocked is a helper function that handles matching of request using DNS +// engines and pools of requests and results. engine, reqPool, and resPool must +// not be nil. +func matchBlocked( + host string, + qt uint16, + engine *urlfilter.DNSEngine, + reqPool *syncutil.Pool[urlfilter.DNSRequest], + resPool *syncutil.Pool[urlfilter.DNSResult], +) (blocked bool) { + req := reqPool.Get() + defer reqPool.Put(req) + + req.Reset() + req.Hostname = host + req.DNSType = qt + + res := resPool.Get() + defer resPool.Put(res) + + res.Reset() + + blocked = engine.MatchRequestInto(req, res) + if blocked && res.NetworkRule != nil { return !res.NetworkRule.Whitelist } - return matched + return blocked } // IsBlockedIP implements the [Interface] interface for *Global. diff --git a/internal/access/access_test.go b/internal/access/access_test.go index 23cb4f2..55b9a47 100644 --- a/internal/access/access_test.go +++ b/internal/access/access_test.go @@ -17,7 +17,59 @@ const testTimeout = 1 * time.Second // testAccessMtrc is the common profile access engine metrics for tests. var testAccessMtrc = access.EmptyProfileMetrics{} -func TestGlobal_IsBlockedHost(t *testing.T) { +// testCases is the list of test cases for the [IsBlocked] function. +var testCases = []struct { + want assert.BoolAssertionFunc + name string + host string + qt uint16 +}{{ + want: assert.False, + name: "pass", + host: "pass.test", + qt: dns.TypeA, +}, { + want: assert.True, + name: "blocked_domain_a", + host: "block.test", + qt: dns.TypeA, +}, { + want: assert.True, + name: "blocked_domain_https", + host: "block.test", + qt: dns.TypeHTTPS, +}, { + want: assert.True, + name: "uppercase_domain", + host: "uppercase.test", + qt: dns.TypeHTTPS, +}, { + want: assert.False, + name: "pass_qt", + host: "block_aaaa.test", + qt: dns.TypeA, +}, { + want: assert.True, + name: "block_qt", + host: "block_aaaa.test", + qt: dns.TypeAAAA, +}, { + want: assert.True, + name: "allowlist_block", + host: "block.allowlist.test", + qt: dns.TypeA, +}, { + want: assert.False, + name: "allowlist_test", + host: "allow.allowlist.test", + qt: dns.TypeA, +}} + +// newTestGlobal is a test helper that returns a new [access.Global] with test +// rules. +func newTestGlobal(t testing.TB) (global *access.Global) { + t.Helper() + global, err := access.NewGlobal([]string{ "block.test", "UPPERCASE.test", @@ -27,52 +79,13 @@ func TestGlobal_IsBlockedHost(t *testing.T) { }, nil) require.NoError(t, err) - testCases := []struct { - want assert.BoolAssertionFunc - name string - host string - qt uint16 - }{{ - want: assert.False, - name: "pass", - host: "pass.test", - qt: dns.TypeA, - }, { - want: assert.True, - name: "blocked_domain_A", - host: "block.test", - qt: dns.TypeA, - }, { - want: assert.True, - name: "blocked_domain_HTTPS", - host: "block.test", - qt: dns.TypeHTTPS, - }, { - want: assert.True, - name: "uppercase_domain", - host: "uppercase.test", - qt: dns.TypeHTTPS, - }, { - want: assert.False, - name: "pass_qt", - host: "block_aaaa.test", - qt: dns.TypeA, - }, { - want: assert.True, - name: "block_qt", - host: "block_aaaa.test", - qt: dns.TypeAAAA, - }, { - want: assert.True, - name: "allowlist_block", - host: "block.allowlist.test", - qt: dns.TypeA, - }, { - want: assert.False, - name: "allowlist_test", - host: "allow.allowlist.test", - qt: dns.TypeA, - }} + return global +} + +func TestGlobal_IsBlockedHost(t *testing.T) { + t.Parallel() + + global := newTestGlobal(t) for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { @@ -83,9 +96,11 @@ func TestGlobal_IsBlockedHost(t *testing.T) { } func TestGlobal_IsBlockedIP(t *testing.T) { + t.Parallel() + global, err := access.NewGlobal([]string{}, []netip.Prefix{ - netip.MustParsePrefix("1.1.1.1/32"), - netip.MustParsePrefix("2.2.2.0/8"), + netip.MustParsePrefix("192.0.2.1/32"), + netip.MustParsePrefix("198.51.100.0/24"), }) require.NoError(t, err) @@ -96,19 +111,19 @@ func TestGlobal_IsBlockedIP(t *testing.T) { }{{ want: assert.False, name: "pass", - ip: netip.MustParseAddr("1.1.1.0"), + ip: netip.MustParseAddr("192.0.2.0"), }, { want: assert.True, name: "block_ip", - ip: netip.MustParseAddr("1.1.1.1"), + ip: netip.MustParseAddr("192.0.2.1"), }, { want: assert.False, name: "pass_subnet", - ip: netip.MustParseAddr("1.2.2.2"), + ip: netip.MustParseAddr("198.51.101.1"), }, { want: assert.True, name: "block_subnet", - ip: netip.MustParseAddr("2.2.2.2"), + ip: netip.MustParseAddr("198.51.100.1"), }} for _, tc := range testCases { @@ -118,3 +133,77 @@ func TestGlobal_IsBlockedIP(t *testing.T) { }) } } + +func BenchmarkGlobal_IsBlockedHost(b *testing.B) { + global := newTestGlobal(b) + + for _, tc := range testCases { + b.Run(tc.name, func(b *testing.B) { + var blocked bool + + b.ReportAllocs() + for b.Loop() { + blocked = global.IsBlockedHost(tc.host, tc.qt) + } + + tc.want(b, blocked) + }) + } + + // Most recent results: + // + // goos: linux + // goarch: amd64 + // pkg: github.com/AdguardTeam/AdGuardDNS/internal/access + // cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics + // BenchmarkGlobal_IsBlockedHost/pass-16 2313513 515.0 ns/op 16 B/op 1 allocs/op + // BenchmarkGlobal_IsBlockedHost/blocked_domain_a-16 1604049 683.4 ns/op 24 B/op 1 allocs/op + // BenchmarkGlobal_IsBlockedHost/blocked_domain_https-16 1981204 597.7 ns/op 24 B/op 1 allocs/op + // BenchmarkGlobal_IsBlockedHost/uppercase_domain-16 2093197 590.5 ns/op 24 B/op 1 allocs/op + // BenchmarkGlobal_IsBlockedHost/pass_qt-16 1961065 653.3 ns/op 24 B/op 1 allocs/op + // BenchmarkGlobal_IsBlockedHost/block_qt-16 768783 1567 ns/op 24 B/op 1 allocs/op + // BenchmarkGlobal_IsBlockedHost/allowlist_block-16 759159 1890 ns/op 32 B/op 1 allocs/op + // BenchmarkGlobal_IsBlockedHost/allowlist_test-16 371722 3170 ns/op 32 B/op 1 allocs/op +} + +func BenchmarkGlobal_IsBlockedIP(b *testing.B) { + global, err := access.NewGlobal([]string{}, []netip.Prefix{ + netip.MustParsePrefix("192.0.2.0/24"), + }) + require.NoError(b, err) + + b.Run("pass", func(b *testing.B) { + ip := netip.MustParseAddr("192.0.3.0") + + var blocked bool + + b.ReportAllocs() + for b.Loop() { + blocked = global.IsBlockedIP(ip) + } + + assert.False(b, blocked) + }) + + b.Run("block", func(b *testing.B) { + ip := netip.MustParseAddr("192.0.2.0") + + var blocked bool + + b.ReportAllocs() + for b.Loop() { + blocked = global.IsBlockedIP(ip) + } + + assert.True(b, blocked) + }) + + // Most recent results: + // + // goos: linux + // goarch: amd64 + // pkg: github.com/AdguardTeam/AdGuardDNS/internal/access + // cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics + // BenchmarkGlobal_IsBlockedIP/pass-16 100000000 10.18 ns/op 0 B/op 0 allocs/op + // BenchmarkGlobal_IsBlockedIP/block-16 141876058 8.545 ns/op 0 B/op 0 allocs/op +} diff --git a/internal/access/blocker.go b/internal/access/blocker.go new file mode 100644 index 0000000..8d0c63b --- /dev/null +++ b/internal/access/blocker.go @@ -0,0 +1,38 @@ +package access + +import ( + "context" + "net/netip" + + "github.com/AdguardTeam/AdGuardDNS/internal/geoip" + "github.com/miekg/dns" +) + +// Blocker is the interface to control DNS resolution access. +type Blocker interface { + // IsBlocked returns true if the req should be blocked. req must not be + // nil, and req.Question must have one item. + IsBlocked( + ctx context.Context, + req *dns.Msg, + rAddr netip.AddrPort, + l *geoip.Location, + ) (isBlocked bool) +} + +// EmptyBlocker is an empty [Blocker] implementation that does nothing. +type EmptyBlocker struct{} + +// type check +var _ Blocker = EmptyBlocker{} + +// IsBlocked implements the [Blocker] interface for EmptyBlocker. It always +// returns false. +func (EmptyBlocker) IsBlocked( + _ context.Context, + _ *dns.Msg, + _ netip.AddrPort, + _ *geoip.Location, +) (isBlocked bool) { + return false +} diff --git a/internal/access/engine.go b/internal/access/engine.go index 2ca8db0..5a6bd35 100644 --- a/internal/access/engine.go +++ b/internal/access/engine.go @@ -3,12 +3,12 @@ package access import ( "context" "fmt" - "strings" "sync" "time" "github.com/AdguardTeam/AdGuardDNS/internal/agdnet" - "github.com/AdguardTeam/golibs/stringutil" + "github.com/AdguardTeam/AdGuardDNS/internal/agdurlflt" + "github.com/AdguardTeam/golibs/syncutil" "github.com/AdguardTeam/urlfilter" "github.com/AdguardTeam/urlfilter/filterlist" "github.com/miekg/dns" @@ -18,18 +18,26 @@ import ( // // TODO(a.garipov): Replace/merge with [custom.Filter]. type blockedHostEngine struct { - metrics ProfileMetrics - lazyEngine *urlfilter.DNSEngine initOnce *sync.Once + lazyEngine *urlfilter.DNSEngine + reqPool *syncutil.Pool[urlfilter.DNSRequest] + resPool *syncutil.Pool[urlfilter.DNSResult] + metrics ProfileMetrics rules []string } // newBlockedHostEngine creates a new blockedHostEngine. mtrc must not be nil. func newBlockedHostEngine(mtrc ProfileMetrics, rules []string) (e *blockedHostEngine) { return &blockedHostEngine{ - metrics: mtrc, - rules: rules, initOnce: &sync.Once{}, + reqPool: syncutil.NewPool(func() (req *urlfilter.DNSRequest) { + return &urlfilter.DNSRequest{} + }), + resPool: syncutil.NewPool(func() (v *urlfilter.DNSResult) { + return &urlfilter.DNSResult{} + }), + metrics: mtrc, + rules: rules, } } @@ -48,31 +56,20 @@ func (e *blockedHostEngine) isBlocked(ctx context.Context, req *dns.Msg) (blocke }) q := req.Question[0] - res, matched := e.lazyEngine.MatchRequest(&urlfilter.DNSRequest{ - Hostname: agdnet.NormalizeQueryDomain(q.Name), - DNSType: q.Qtype, - }) - if matched && res.NetworkRule != nil { - return !res.NetworkRule.Whitelist - } + host := agdnet.NormalizeQueryDomain(q.Name) - return matched + return matchBlocked(host, q.Qtype, e.lazyEngine, e.reqPool, e.resPool) } // init returns new properly initialized dns engine. func (e *blockedHostEngine) init() (eng *urlfilter.DNSEngine) { - b := &strings.Builder{} - for _, h := range e.rules { - stringutil.WriteToBuilder(b, strings.ToLower(h), "\n") - } - - lists := []filterlist.RuleList{ - &filterlist.StringRuleList{ + lists := []filterlist.Interface{ + filterlist.NewBytes(&filterlist.BytesConfig{ ID: blocklistFilterID, - RulesText: b.String(), + RulesText: agdurlflt.RulesToBytesLower(e.rules), IgnoreCosmetic: true, - }, + }), } rulesStrg, err := filterlist.NewRuleStorage(lists) diff --git a/internal/access/engine_internal_test.go b/internal/access/engine_internal_test.go index 40d8472..0165777 100644 --- a/internal/access/engine_internal_test.go +++ b/internal/access/engine_internal_test.go @@ -109,3 +109,46 @@ func TestBlockedHostEngine_IsBlocked_concurrent(t *testing.T) { wg.Wait() } + +func BenchmarkBlockedHostEngine_IsBlocked(b *testing.B) { + engine := newBlockedHostEngine(EmptyProfileMetrics{}, []string{ + "block.test", + }) + + ctx := testutil.ContextWithTimeout(b, testTimeout) + + b.Run("pass", func(b *testing.B) { + req := dnsservertest.NewReq("pass.test", dns.TypeA, dns.ClassINET) + + var blocked bool + + b.ReportAllocs() + for b.Loop() { + blocked = engine.isBlocked(ctx, req) + } + + assert.False(b, blocked) + }) + + b.Run("block", func(b *testing.B) { + req := dnsservertest.NewReq("block.test", dns.TypeA, dns.ClassINET) + + var blocked bool + + b.ReportAllocs() + for b.Loop() { + blocked = engine.isBlocked(ctx, req) + } + + assert.True(b, blocked) + }) + + // Most recent results: + // + // goos: linux + // goarch: amd64 + // pkg: github.com/AdguardTeam/AdGuardDNS/internal/access + // cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics + // BenchmarkBlockedHostEngine_IsBlocked/pass-16 3362199 369.1 ns/op 16 B/op 1 allocs/op + // BenchmarkBlockedHostEngine_IsBlocked/block-16 2299890 510.6 ns/op 24 B/op 1 allocs/op +} diff --git a/internal/access/profile.go b/internal/access/profile.go index 620b31d..10e72cf 100644 --- a/internal/access/profile.go +++ b/internal/access/profile.go @@ -6,6 +6,8 @@ import ( "slices" "github.com/AdguardTeam/AdGuardDNS/internal/geoip" + "github.com/AdguardTeam/golibs/syncutil" + "github.com/AdguardTeam/urlfilter" "github.com/miekg/dns" ) @@ -14,18 +16,13 @@ type Profile interface { // Config returns the profile access configuration. Config() (conf *ProfileConfig) - // IsBlocked returns true if the req should be blocked. req must not be - // nil, and req.Question must have one item. - IsBlocked( - ctx context.Context, - req *dns.Msg, - rAddr netip.AddrPort, - l *geoip.Location, - ) (blocked bool) + Blocker } -// EmptyProfile is an empty profile implementation that does nothing. -type EmptyProfile struct{} +// EmptyProfile is an empty [Profile] implementation that does nothing. +type EmptyProfile struct { + EmptyBlocker +} // type check var _ Profile = EmptyProfile{} @@ -34,17 +31,6 @@ var _ Profile = EmptyProfile{} // returns nil. func (EmptyProfile) Config() (conf *ProfileConfig) { return nil } -// IsBlocked implements the [Profile] interface for EmptyProfile. It always -// returns false. -func (EmptyProfile) IsBlocked( - _ context.Context, - _ *dns.Msg, - _ netip.AddrPort, - _ *geoip.Location, -) (blocked bool) { - return false -} - // ProfileConfig is a profile specific access configuration. // // NOTE: Do not change fields of this structure without incrementing @@ -64,14 +50,23 @@ type ProfileConfig struct { // BlocklistDomainRules is slice of rules to match requests. BlocklistDomainRules []string + + // StandardEnabled controls whether the profile should also apply standard + // access settings. + StandardEnabled bool } // DefaultProfile controls profile specific IP and client blocking that take // place before all other processing. DefaultProfile is safe for concurrent // use. type DefaultProfile struct { + standard Blocker + blockedHostsEng *blockedHostEngine + reqPool *syncutil.Pool[urlfilter.DNSRequest] + resPool *syncutil.Pool[urlfilter.DNSResult] + allowedNets []netip.Prefix blockedNets []netip.Prefix @@ -80,6 +75,8 @@ type DefaultProfile struct { blockedASN []geoip.ASN blocklistDomainRules []string + + standardEnabled bool } // defaultProfileConfig is the configuration for the default access for @@ -89,21 +86,42 @@ type defaultProfileConfig struct { // nil and must be valid. conf *ProfileConfig + // reqPool is the pool of URLFilter request data to use and reuse during + // filtering. It must not be nil. + reqPool *syncutil.Pool[urlfilter.DNSRequest] + + // resPool is the pool of URLFilter result data to use and reuse during + // filtering. It must not be nil. + resPool *syncutil.Pool[urlfilter.DNSResult] + // metrics is used for the collection of the profile access engine // statistics. It must not be nil. metrics ProfileMetrics + + // standard is the standard access blocker to use. + standard Blocker } // newDefaultProfile creates a new *DefaultProfile. conf is assumed to be // valid. mtrc must not be nil. func newDefaultProfile(c *defaultProfileConfig) (p *DefaultProfile) { return &DefaultProfile{ - allowedNets: c.conf.AllowedNets, - blockedNets: c.conf.BlockedNets, - allowedASN: c.conf.AllowedASN, - blockedASN: c.conf.BlockedASN, + standard: c.standard, + + blockedHostsEng: newBlockedHostEngine(c.metrics, c.conf.BlocklistDomainRules), + + reqPool: c.reqPool, + resPool: c.resPool, + + allowedNets: c.conf.AllowedNets, + blockedNets: c.conf.BlockedNets, + + allowedASN: c.conf.AllowedASN, + blockedASN: c.conf.BlockedASN, + blocklistDomainRules: c.conf.BlocklistDomainRules, - blockedHostsEng: newBlockedHostEngine(c.metrics, c.conf.BlocklistDomainRules), + + standardEnabled: c.conf.StandardEnabled, } } @@ -118,10 +136,14 @@ func (p *DefaultProfile) Config() (conf *ProfileConfig) { AllowedASN: slices.Clone(p.allowedASN), BlockedASN: slices.Clone(p.blockedASN), BlocklistDomainRules: slices.Clone(p.blocklistDomainRules), + StandardEnabled: p.standardEnabled, } } -// IsBlocked implements the [Profile] interface for *DefaultProfile. +// type check +var _ Blocker = (*DefaultProfile)(nil) + +// IsBlocked implements the [Blocker] interface for *DefaultProfile. func (p *DefaultProfile) IsBlocked( ctx context.Context, req *dns.Msg, @@ -130,7 +152,9 @@ func (p *DefaultProfile) IsBlocked( ) (blocked bool) { ip := rAddr.Addr() - return p.isBlockedByNets(ip, l) || p.isBlockedByHostsEng(ctx, req) + return p.isBlockedByNets(ip, l) || + p.isBlockedByHostsEng(ctx, req) || + p.standard.IsBlocked(ctx, req, rAddr, l) } // isBlockedByNets returns true if ip or l is blocked by current profile. @@ -163,28 +187,3 @@ func matchASNs(asns []geoip.ASN, l *geoip.Location) (ok bool) { func (p *DefaultProfile) isBlockedByHostsEng(ctx context.Context, req *dns.Msg) (blocked bool) { return p.blockedHostsEng.isBlocked(ctx, req) } - -// ProfileConstructor creates default access managers for profiles. -// -// TODO(a.garipov): Add global standard rules for profile access managers here -// as well. -type ProfileConstructor struct { - metrics ProfileMetrics -} - -// NewProfileConstructor returns a properly initialized *ProfileConstructor. -// mtrc must not be nil. -func NewProfileConstructor(mtrc ProfileMetrics) (c *ProfileConstructor) { - return &ProfileConstructor{ - metrics: mtrc, - } -} - -// New creates a new access manager for a profile based on the configuration. -// conf must not be nil and must be valid. -func (c *ProfileConstructor) New(conf *ProfileConfig) (p *DefaultProfile) { - return newDefaultProfile(&defaultProfileConfig{ - conf: conf, - metrics: c.metrics, - }) -} diff --git a/internal/access/profile_test.go b/internal/access/profile_test.go index 831bd7a..f692509 100644 --- a/internal/access/profile_test.go +++ b/internal/access/profile_test.go @@ -13,30 +13,49 @@ import ( ) func TestDefaultProfile_Config(t *testing.T) { + t.Parallel() + conf := &access.ProfileConfig{ - AllowedNets: []netip.Prefix{netip.MustParsePrefix("1.1.1.0/24")}, - BlockedNets: []netip.Prefix{netip.MustParsePrefix("2.2.2.0/24")}, + AllowedNets: []netip.Prefix{netip.MustParsePrefix("192.0.2.1/32")}, + BlockedNets: []netip.Prefix{netip.MustParsePrefix("192.0.2.2/32")}, AllowedASN: []geoip.ASN{1}, BlockedASN: []geoip.ASN{1, 2}, BlocklistDomainRules: []string{"block.test"}, + StandardEnabled: true, } - cons := access.NewProfileConstructor(testAccessMtrc) + cons := access.NewProfileConstructor(&access.ProfileConstructorConfig{ + Metrics: testAccessMtrc, + Standard: access.EmptyBlocker{}, + }) + a := cons.New(conf) got := a.Config() - assert.Equal(t, conf.AllowedNets, got.AllowedNets) - assert.Equal(t, conf.BlockedNets, got.BlockedNets) - assert.Equal(t, conf.AllowedASN, got.AllowedASN) - assert.Equal(t, conf.BlockedASN, got.BlockedASN) - assert.Equal(t, conf.BlocklistDomainRules, got.BlocklistDomainRules) + assert.Equal(t, conf, got) } func TestDefaultProfile_IsBlocked(t *testing.T) { - passAddrPort := netip.MustParseAddrPort("3.3.3.3:3333") + t.Parallel() + + passAddrPort := netip.MustParseAddrPort("192.0.2.3:3333") + + std := access.NewStandardBlocker(&access.StandardBlockerConfig{ + AllowedNets: []netip.Prefix{netip.MustParsePrefix("192.0.2.10/32")}, + BlockedNets: []netip.Prefix{netip.MustParsePrefix("192.0.2.20/32")}, + AllowedASN: []geoip.ASN{10}, + BlockedASN: []geoip.ASN{10, 20}, + BlocklistDomainRules: []string{ + "block.std.test", + "UPPERCASE.STD.test", + "||block_aaaa.std.test^$dnstype=AAAA", + "||allowlist.std.test^", + "@@||allow.allowlist.std.test^", + }, + }) conf := &access.ProfileConfig{ - AllowedNets: []netip.Prefix{netip.MustParsePrefix("1.1.1.1/32")}, - BlockedNets: []netip.Prefix{netip.MustParsePrefix("1.1.1.0/24")}, + AllowedNets: []netip.Prefix{netip.MustParsePrefix("192.0.2.1/32")}, + BlockedNets: []netip.Prefix{netip.MustParsePrefix("192.0.2.2/32")}, AllowedASN: []geoip.ASN{1}, BlockedASN: []geoip.ASN{1, 2}, BlocklistDomainRules: []string{ @@ -46,9 +65,13 @@ func TestDefaultProfile_IsBlocked(t *testing.T) { "||allowlist.test^", "@@||allow.allowlist.test^", }, + StandardEnabled: true, } - cons := access.NewProfileConstructor(testAccessMtrc) + cons := access.NewProfileConstructor(&access.ProfileConstructorConfig{ + Metrics: testAccessMtrc, + Standard: std, + }) a := cons.New(conf) testCases := []struct { @@ -117,21 +140,21 @@ func TestDefaultProfile_IsBlocked(t *testing.T) { }, { want: assert.False, name: "pass_ip", - rAddr: netip.MustParseAddrPort("1.1.1.1:57"), + rAddr: netip.MustParseAddrPort("192.0.2.1:57"), host: "pass.test", qt: dns.TypeA, loc: nil, }, { want: assert.True, name: "block_subnet", - rAddr: netip.MustParseAddrPort("1.1.1.2:57"), + rAddr: netip.MustParseAddrPort("192.0.2.2:57"), host: "pass.test", qt: dns.TypeA, loc: nil, }, { want: assert.False, name: "pass_subnet", - rAddr: netip.MustParseAddrPort("1.2.2.2:57"), + rAddr: netip.MustParseAddrPort("192.0.2.1:57"), host: "pass.test", qt: dns.TypeA, loc: nil, @@ -156,10 +179,103 @@ func TestDefaultProfile_IsBlocked(t *testing.T) { host: "pass.test", qt: dns.TypeA, loc: &geoip.Location{ASN: 2}, + }, { + want: assert.True, + name: "standard_blocked_domain_A", + host: "block.std.test", + qt: dns.TypeA, + rAddr: passAddrPort, + loc: nil, + }, { + want: assert.True, + name: "standard_blocked_domain_HTTPS", + host: "block.std.test", + qt: dns.TypeHTTPS, + rAddr: passAddrPort, + loc: nil, + }, { + want: assert.True, + name: "standard_uppercase_domain", + host: "uppercase.std.test", + qt: dns.TypeHTTPS, + rAddr: passAddrPort, + loc: nil, + }, { + want: assert.False, + name: "standard_pass_qt", + host: "block_aaaa.std.test", + qt: dns.TypeA, + rAddr: passAddrPort, + loc: nil, + }, { + want: assert.True, + name: "standard_block_qt", + host: "block_aaaa.std.test", + qt: dns.TypeAAAA, + rAddr: passAddrPort, + loc: nil, + }, { + want: assert.True, + name: "standard_allowlist_block", + host: "block.allowlist.std.test", + qt: dns.TypeA, + rAddr: passAddrPort, + loc: nil, + }, { + want: assert.False, + name: "standard_allowlist_test", + host: "allow.allowlist.std.test", + qt: dns.TypeA, + rAddr: passAddrPort, + loc: nil, + }, { + want: assert.False, + name: "standard_pass_ip", + rAddr: netip.MustParseAddrPort("192.0.2.21:57"), + host: "pass.std.test", + qt: dns.TypeA, + loc: nil, + }, { + want: assert.True, + name: "standard_block_subnet", + rAddr: netip.MustParseAddrPort("192.0.2.20:57"), + host: "pass.std.test", + qt: dns.TypeA, + loc: nil, + }, { + want: assert.False, + name: "standard_pass_subnet", + rAddr: netip.MustParseAddrPort("192.0.2.11:57"), + host: "pass.std.test", + qt: dns.TypeA, + loc: nil, + }, { + want: assert.True, + name: "standard_block_host_pass_asn", + rAddr: passAddrPort, + host: "block.std.test", + qt: dns.TypeA, + loc: &geoip.Location{ASN: 10}, + }, { + want: assert.False, + name: "standard_pass_asn", + rAddr: passAddrPort, + host: "pass.std.test", + qt: dns.TypeA, + loc: &geoip.Location{ASN: 10}, + }, { + want: assert.True, + name: "standard_block_asn", + rAddr: passAddrPort, + host: "pass.std.test", + qt: dns.TypeA, + loc: &geoip.Location{ASN: 20}, }} for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { + t.Parallel() + req := dnsservertest.NewReq(tc.host, tc.qt, dns.ClassINET) ctx := testutil.ContextWithTimeout(t, testTimeout) @@ -170,6 +286,8 @@ func TestDefaultProfile_IsBlocked(t *testing.T) { } func TestDefaultProfile_IsBlocked_prefixAllowlist(t *testing.T) { + t.Parallel() + conf := &access.ProfileConfig{ AllowedNets: []netip.Prefix{ netip.MustParsePrefix("2.2.2.0/24"), @@ -181,7 +299,10 @@ func TestDefaultProfile_IsBlocked_prefixAllowlist(t *testing.T) { BlocklistDomainRules: nil, } - cons := access.NewProfileConstructor(testAccessMtrc) + cons := access.NewProfileConstructor(&access.ProfileConstructorConfig{ + Metrics: testAccessMtrc, + Standard: access.EmptyBlocker{}, + }) a := cons.New(conf) testCases := []struct { @@ -212,6 +333,8 @@ func TestDefaultProfile_IsBlocked_prefixAllowlist(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { + t.Parallel() + req := dnsservertest.NewReq("pass.test", dns.TypeA, dns.ClassINET) ctx := testutil.ContextWithTimeout(t, testTimeout) @@ -238,7 +361,10 @@ func BenchmarkDefaultProfile_IsBlocked(b *testing.B) { }, } - cons := access.NewProfileConstructor(testAccessMtrc) + cons := access.NewProfileConstructor(&access.ProfileConstructorConfig{ + Metrics: testAccessMtrc, + Standard: access.EmptyBlocker{}, + }) a := cons.New(conf) ctx := testutil.ContextWithTimeout(b, testTimeout) @@ -271,10 +397,10 @@ func BenchmarkDefaultProfile_IsBlocked(b *testing.B) { // Most recent results: // - // goos: darwin - // goarch: amd64 - // pkg: github.com/AdguardTeam/AdGuardDNS/internal/access - // cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz - // BenchmarkDefaultProfile_IsBlocked/pass-12 2761741 421.9 ns/op 96 B/op 2 allocs/op - // BenchmarkDefaultProfile_IsBlocked/block-12 2143516 556.1 ns/op 128 B/op 4 allocs/op + // goos: linux + // goarch: amd64 + // pkg: github.com/AdguardTeam/AdGuardDNS/internal/access + // cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics + // BenchmarkDefaultProfile_IsBlocked/pass-16 2679700 468.8 ns/op 16 B/op 1 allocs/op + // BenchmarkDefaultProfile_IsBlocked/block-16 2081113 576.4 ns/op 24 B/op 1 allocs/op } diff --git a/internal/access/profileconstructor.go b/internal/access/profileconstructor.go new file mode 100644 index 0000000..240abd8 --- /dev/null +++ b/internal/access/profileconstructor.go @@ -0,0 +1,57 @@ +package access + +import ( + "github.com/AdguardTeam/golibs/syncutil" + "github.com/AdguardTeam/urlfilter" +) + +// ProfileConstructorConfig is the configuration for the [ProfileConstructor]. +type ProfileConstructorConfig struct { + // Metrics is used for the collection of the statistics of profile access + // managers. It must not be nil. + Metrics ProfileMetrics + + // Standard is the standard blocker for all profiles which have enabled this + // feature. It must not be nil. + Standard Blocker +} + +// ProfileConstructor creates default access managers for profiles. +type ProfileConstructor struct { + reqPool *syncutil.Pool[urlfilter.DNSRequest] + resPool *syncutil.Pool[urlfilter.DNSResult] + metrics ProfileMetrics + standard Blocker +} + +// NewProfileConstructor returns a properly initialized *ProfileConstructor. +// conf must not be nil. +func NewProfileConstructor(conf *ProfileConstructorConfig) (c *ProfileConstructor) { + return &ProfileConstructor{ + reqPool: syncutil.NewPool(func() (req *urlfilter.DNSRequest) { + return &urlfilter.DNSRequest{} + }), + resPool: syncutil.NewPool(func() (v *urlfilter.DNSResult) { + return &urlfilter.DNSResult{} + }), + metrics: conf.Metrics, + standard: conf.Standard, + } +} + +// New creates a new access manager for a profile based on the configuration. +// conf must not be nil and must be valid. +func (c *ProfileConstructor) New(conf *ProfileConfig) (p *DefaultProfile) { + var standard Blocker = EmptyBlocker{} + if conf.StandardEnabled { + standard = c.standard + } + + return newDefaultProfile(&defaultProfileConfig{ + conf: conf, + reqPool: c.reqPool, + resPool: c.resPool, + metrics: c.metrics, + standard: standard, + }) +} diff --git a/internal/access/standardaccess.go b/internal/access/standardaccess.go new file mode 100644 index 0000000..bcad9b2 --- /dev/null +++ b/internal/access/standardaccess.go @@ -0,0 +1,183 @@ +package access + +import ( + "context" + "net/netip" + "slices" + "sync" + + "github.com/AdguardTeam/AdGuardDNS/internal/agdnet" + "github.com/AdguardTeam/AdGuardDNS/internal/agdurlflt" + "github.com/AdguardTeam/AdGuardDNS/internal/geoip" + "github.com/AdguardTeam/golibs/errors" + "github.com/AdguardTeam/golibs/syncutil" + "github.com/AdguardTeam/urlfilter" + "github.com/AdguardTeam/urlfilter/filterlist" + "github.com/miekg/dns" +) + +// StandardSetter is the interface for setting the standard access blocker +// configuration. +type StandardSetter interface { + // SetConfig sets the configuration for the standard access blocker. conf + // must not be nil. Fields of conf must not be modified after calling this + // method. It must be safe for concurrent use. + SetConfig(conf *StandardBlockerConfig) +} + +// EmptyStandard is an empty [StandardSetter] implementation that does nothing. +type EmptyStandard struct{} + +// type check +var _ StandardSetter = EmptyStandard{} + +// SetConfig implements the [StandardSetter] interface for EmptyStandard. It +// always returns false. +func (EmptyStandard) SetConfig(_ *StandardBlockerConfig) {} + +// StandardBlockerConfig is the configuration structure for the standard access +// blocker. +type StandardBlockerConfig struct { + // AllowedNets are the networks allowed for DNS resolution. If empty or + // nil, all networks are allowed, except those blocked by BlockedNets. + AllowedNets []netip.Prefix + + // BlockedNets are the networks blocked for DNS resolution. If empty or + // nil, all networks are allowed, except those allowed by AllowedNets. + BlockedNets []netip.Prefix + + // AllowedASN are the ASNs allowed for DNS resolution. If empty or nil, all + // ASNs are allowed, except those blocked by BlockedASN. + AllowedASN []geoip.ASN + + // BlockedASN are the ASNs blocked for DNS resolution. If empty or nil, all + // ASNs are allowed, except those allowed by AllowedASN. + BlockedASN []geoip.ASN + + // BlocklistDomainRules are the rules blocking the domains. If empty or + // nil, no domains are blocked. + BlocklistDomainRules []string +} + +// StandardBlocker is the dynamic [Blocker] implementation with standard +// access settings. +type StandardBlocker struct { + reqPool *syncutil.Pool[urlfilter.DNSRequest] + resPool *syncutil.Pool[urlfilter.DNSResult] + + // mu protects all fields below. + mu *sync.RWMutex + + blockedHostsEng *urlfilter.DNSEngine + + allowedNets []netip.Prefix + blockedNets []netip.Prefix + + // TODO(d.kolyshev): Change to map[geoip.ASN]unit to improve performance. + allowedASN []geoip.ASN + blockedASN []geoip.ASN +} + +// NewStandardBlocker creates a new StandardBlocker instance. conf must not be +// nil. +func NewStandardBlocker(conf *StandardBlockerConfig) (s *StandardBlocker) { + s = &StandardBlocker{ + reqPool: syncutil.NewPool(func() (req *urlfilter.DNSRequest) { + return &urlfilter.DNSRequest{} + }), + resPool: syncutil.NewPool(func() (v *urlfilter.DNSResult) { + return &urlfilter.DNSResult{} + }), + + mu: &sync.RWMutex{}, + } + + s.SetConfig(conf) + + return s +} + +// type check +var _ StandardSetter = (*StandardBlocker)(nil) + +// SetConfig implements the [StandardSetter] interface for *StandardBlocker. +func (b *StandardBlocker) SetConfig(c *StandardBlockerConfig) { + lists := []filterlist.Interface{ + filterlist.NewBytes(&filterlist.BytesConfig{ + ID: blocklistFilterID, + RulesText: agdurlflt.RulesToBytesLower(c.BlocklistDomainRules), + IgnoreCosmetic: true, + }), + } + + // Should never panic, since the storage has only one list. + rulesStrg := errors.Must(filterlist.NewRuleStorage(lists)) + eng := urlfilter.NewDNSEngine(rulesStrg) + + b.mu.Lock() + defer b.mu.Unlock() + + b.blockedHostsEng = eng + b.allowedNets = c.AllowedNets + b.blockedNets = c.BlockedNets + b.allowedASN = c.AllowedASN + b.blockedASN = c.BlockedASN +} + +// type check +var _ Blocker = (*StandardBlocker)(nil) + +// IsBlocked implements the [Blocker] interface for *StandardBlocker. +func (b *StandardBlocker) IsBlocked( + _ context.Context, + req *dns.Msg, + rAddr netip.AddrPort, + l *geoip.Location, +) (blocked bool) { + b.mu.RLock() + defer b.mu.RUnlock() + + ip := rAddr.Addr() + + return b.isBlockedByNets(ip, l) || b.isBlockedByHostsEng(req) +} + +// isBlockedByNets returns true if ip or l is blocked by current configuration. +func (b *StandardBlocker) isBlockedByNets(ip netip.Addr, l *geoip.Location) (blocked bool) { + if matchASNs(b.allowedASN, l) || matchNets(b.allowedNets, ip) { + return false + } + + return matchASNs(b.blockedASN, l) || matchNets(b.blockedNets, ip) +} + +// isBlockedByHostsEng returns true if the req is blocked by blocklist domain +// rules. req must have exactly one question. +func (b *StandardBlocker) isBlockedByHostsEng(req *dns.Msg) (blocked bool) { + q := req.Question[0] + + host := agdnet.NormalizeQueryDomain(q.Name) + + return matchBlocked(host, q.Qtype, b.blockedHostsEng, b.reqPool, b.resPool) +} + +// Equal returns true if c and other are equal. nil is only equal to other nil. +func (c *StandardBlockerConfig) Equal(other *StandardBlockerConfig) (ok bool) { + if c == nil { + return other == nil + } else if other == nil { + return false + } + + switch { + case + !slices.Equal(c.AllowedNets, other.AllowedNets), + !slices.Equal(c.BlockedNets, other.BlockedNets), + !slices.Equal(c.AllowedASN, other.AllowedASN), + !slices.Equal(c.BlockedASN, other.BlockedASN), + !slices.Equal(c.BlocklistDomainRules, other.BlocklistDomainRules): + return false + default: + return true + } +} diff --git a/internal/access/standardaccess_test.go b/internal/access/standardaccess_test.go new file mode 100644 index 0000000..9e6df63 --- /dev/null +++ b/internal/access/standardaccess_test.go @@ -0,0 +1,58 @@ +package access_test + +import ( + "net/netip" + "testing" + + "github.com/AdguardTeam/AdGuardDNS/internal/access" + "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest" + "github.com/AdguardTeam/golibs/testutil" + "github.com/miekg/dns" + "github.com/stretchr/testify/assert" +) + +func BenchmarkStandardBlocker_IsBlocked(b *testing.B) { + blocker := access.NewStandardBlocker(&access.StandardBlockerConfig{ + BlocklistDomainRules: []string{ + "block.test", + }, + }) + + ctx := testutil.ContextWithTimeout(b, testTimeout) + remoteAddr := netip.AddrPort{} + + b.Run("pass", func(b *testing.B) { + req := dnsservertest.NewReq("pass.test", dns.TypeA, dns.ClassINET) + + var blocked bool + + b.ReportAllocs() + for b.Loop() { + blocked = blocker.IsBlocked(ctx, req, remoteAddr, nil) + } + + assert.False(b, blocked) + }) + + b.Run("block", func(b *testing.B) { + req := dnsservertest.NewReq("block.test", dns.TypeA, dns.ClassINET) + + var blocked bool + + b.ReportAllocs() + for b.Loop() { + blocked = blocker.IsBlocked(ctx, req, remoteAddr, nil) + } + + assert.True(b, blocked) + }) + + // Most recent results: + // + // goos: linux + // goarch: amd64 + // pkg: github.com/AdguardTeam/AdGuardDNS/internal/access + // cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics + // BenchmarkStandardBlocker_IsBlocked/pass-16 3009312 378.2 ns/op 16 B/op 1 allocs/op + // BenchmarkStandardBlocker_IsBlocked/block-16 2518006 421.9 ns/op 24 B/op 1 allocs/op +} diff --git a/internal/agdcache/agdcache_test.go b/internal/agdcache/agdcache_test.go new file mode 100644 index 0000000..0b093ba --- /dev/null +++ b/internal/agdcache/agdcache_test.go @@ -0,0 +1,13 @@ +package agdcache_test + +import "time" + +// Constants used in tests. +const ( + key = "key" + val = 123 + + nonExistingKey = "nonExistingKey" + + expDuration = 100 * time.Millisecond +) diff --git a/internal/agdcache/default.go b/internal/agdcache/default.go new file mode 100644 index 0000000..122ea28 --- /dev/null +++ b/internal/agdcache/default.go @@ -0,0 +1,126 @@ +package agdcache + +import ( + "fmt" + "sync" + "time" + + "github.com/AdguardTeam/golibs/timeutil" + "github.com/viktordanov/golang-lru/simplelru" +) + +// Config is a configuration structure of a cache. +type Config struct { + // Clock is used to get current time for expiration. It must not be nil. + Clock timeutil.Clock + + // Count is the maximum number of elements to keep in the cache. It must be + // positive. + // + // TODO(a.garipov): Make uint64. + Count int +} + +// entry is an entry of the cache with expiration. +type entry[T any] struct { + // val is the value of the entry. + val T + + // expiration is the expiration unix time in nanoseconds. Zero means no + // expiration. It's an int64 in optimization purposes. + expiration int64 +} + +// Default is an implementation of a thread safe, fixed size LRU cache with +// expiration. +type Default[K comparable, T any] struct { + // cacheMu protects cache. + cacheMu *sync.RWMutex + + cache *simplelru.LRU[K, entry[T]] + clock timeutil.Clock +} + +// New returns a new initialized *Default cache and error, if any. +func New[K comparable, T any](conf *Config) (c *Default[K, T], err error) { + lru, err := simplelru.NewLRU[K, entry[T]](conf.Count, nil) + if err != nil { + return nil, fmt.Errorf("agdcache: creating lru: %w", err) + } + + return &Default[K, T]{ + cache: lru, + clock: conf.Clock, + cacheMu: &sync.RWMutex{}, + }, nil +} + +// type check +var _ Interface[any, any] = (*Default[any, any])(nil) + +// Set implements the [Interface] interface for *Default. +func (c *Default[K, T]) Set(key K, val T) { + c.cacheMu.Lock() + defer c.cacheMu.Unlock() + + // Not a pointer, but the value is used in optimization purposes. + e := entry[T]{ + val: val, + } + + c.cache.Add(key, e) +} + +// SetWithExpire implements the [Interface] interface for *Default. +func (c *Default[K, T]) SetWithExpire(key K, val T, duration time.Duration) { + c.cacheMu.Lock() + defer c.cacheMu.Unlock() + + e := entry[T]{ + val: val, + expiration: c.clock.Now().Add(duration).UnixNano(), + } + + c.cache.Add(key, e) +} + +// Get implements the [Interface] interface for *Default. It returns the value +// and whether the key was found. Removes the key from the cache if it has +// expired. +func (c *Default[K, T]) Get(key K) (val T, ok bool) { + // TODO(a.garipov): Optimize, use RLock. + c.cacheMu.Lock() + defer c.cacheMu.Unlock() + + e, ok := c.cache.Get(key) + if !ok { + return val, false + } + + if e.expiration > 0 && c.clock.Now().UnixNano() > e.expiration { + c.cache.Remove(key) + + return val, false + } + + return e.val, true +} + +// type check +var _ Clearer = (*Default[any, any])(nil) + +// Clear implements the [Interface] interface for *Default. +func (c *Default[K, T]) Clear() { + c.cacheMu.Lock() + defer c.cacheMu.Unlock() + + c.cache.Purge() +} + +// Len implements the [Interface] interface for *Default. +func (c *Default[K, T]) Len() (n int) { + c.cacheMu.RLock() + defer c.cacheMu.RUnlock() + + return c.cache.Len() +} diff --git a/internal/agdcache/default_test.go b/internal/agdcache/default_test.go new file mode 100644 index 0000000..bfbcae6 --- /dev/null +++ b/internal/agdcache/default_test.go @@ -0,0 +1,182 @@ +package agdcache_test + +import ( + "testing" + "time" + + "github.com/AdguardTeam/AdGuardDNS/internal/agdcache" + "github.com/AdguardTeam/golibs/testutil/faketime" + "github.com/AdguardTeam/golibs/timeutil" + "github.com/bluele/gcache" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestDefault(t *testing.T) { + var ( + testTimeNow = time.Now() + nowLater = testTimeNow.Add(2 * expDuration) + ) + + clock := &faketime.Clock{ + OnNow: func() (now time.Time) { return testTimeNow }, + } + + cache, err := agdcache.New[string, int](&agdcache.Config{ + Clock: clock, + Count: 10, + }) + require.NoError(t, err) + + cache.Set(key, val) + assert.Equal(t, 1, cache.Len()) + + v, ok := cache.Get(key) + assert.Equal(t, val, v) + assert.True(t, ok) + + v, ok = cache.Get(nonExistingKey) + assert.Equal(t, 0, v) + assert.False(t, ok) + + cache.Clear() + assert.Equal(t, 0, cache.Len()) + + cache.SetWithExpire(key, val, expDuration) + assert.Equal(t, 1, cache.Len()) + + v, ok = cache.Get(key) + assert.Equal(t, val, v) + assert.True(t, ok) + + clock.OnNow = func() (now time.Time) { return nowLater } + + v, ok = cache.Get(key) + assert.Equal(t, 0, v) + assert.False(t, ok) + + assert.Equal(t, 0, cache.Len()) +} + +func BenchmarkDefault(b *testing.B) { + var ok bool + + b.Run("set", func(b *testing.B) { + cache := newDefault(b) + + b.ReportAllocs() + for i := 0; b.Loop(); i++ { + cache.Set(i, i) + _, ok = cache.Get(i) + } + + assert.True(b, ok) + }) + + b.Run("set_expire", func(b *testing.B) { + cache := newDefault(b) + + b.ReportAllocs() + for i := 0; b.Loop(); i++ { + cache.SetWithExpire(i, i, 2000) + _, ok = cache.Get(i) + } + + assert.True(b, ok) + }) + + // Most recent results: + // + // goos: darwin + // goarch: arm64 + // pkg: github.com/AdguardTeam/AdGuardDNS/internal/agdcache + // cpu: Apple M1 Pro + // BenchmarkDefault/set-8 7764472 138.6 ns/op 56 B/op 2 allocs/op + // BenchmarkDefault/set_expire-8 4727664 246.5 ns/op 56 B/op 2 allocs/op +} + +func FuzzDefault(f *testing.F) { + const ( + size = 1_000 + secondsSeed = uint(1) + ) + + f.Add("key", 1, secondsSeed, 1) + f.Add("key", 1, secondsSeed, 2) + f.Add("key", 1, secondsSeed, 3) + + now := time.Now() + + f.Fuzz(func(t *testing.T, key string, val int, seconds uint, op int) { + clock := &faketime.Clock{ + OnNow: func() (n time.Time) { + return now + }, + } + + cache, err := agdcache.New[string, int](&agdcache.Config{ + Clock: clock, + Count: size, + }) + require.NoError(t, err) + + gCache := gcache.New(size).LRU().Clock(clock).Build() + + switch { + case op%2 == 0: + cache.Set(key, val) + err = gCache.Set(key, val) + require.NoError(t, err) + case op%3 == 0: + dur := time.Duration(seconds) * time.Second + + cache.SetWithExpire(key, val, dur) + err = gCache.SetWithExpire(key, val, dur) + require.NoError(t, err) + case op%5 == 0: + cache.Clear() + gCache.Purge() + } + + clock.OnNow = func() (n time.Time) { + return now.Add(1 * time.Second) + } + + got, ok := cache.Get(key) + gGot, err := gCache.Get(key) + gVal, gValOk := gGot.(int) + if !gValOk { + gVal = 0 + } + + require.Equalf( + t, + err == nil, + ok, + "key %q, val %d, dur %d, op %d: incorrect ok", + key, val, seconds, op, + ) + require.Equalf( + t, + gVal, + got, + "key %q, val %d, dur %d, op %d: incorrect val", + key, val, seconds, op, + ) + + l := cache.Len() + goL := gCache.Len(false) + require.Equal(t, l, goL) + }) +} + +// newDefault returns a new cache for testing. +func newDefault(tb testing.TB) (cache *agdcache.Default[int, int]) { + cache, err := agdcache.New[int, int](&agdcache.Config{ + Clock: timeutil.SystemClock{}, + Count: 10_000, + }) + require.NoError(tb, err) + + return cache +} diff --git a/internal/agdcache/lru_test.go b/internal/agdcache/lru_test.go index bdd9a90..b2b91f3 100644 --- a/internal/agdcache/lru_test.go +++ b/internal/agdcache/lru_test.go @@ -8,13 +8,6 @@ import ( ) func TestLRU(t *testing.T) { - const ( - key = "key" - val = 123 - - nonExistingKey = "nonExistingKey" - ) - cache := agdcache.NewLRU[string, int](&agdcache.LRUConfig{ Count: 10, }) @@ -35,3 +28,71 @@ func TestLRU(t *testing.T) { assert.Equal(t, 0, cache.Len()) } + +func BenchmarkLRU(b *testing.B) { + cache := agdcache.NewLRU[int, int](&agdcache.LRUConfig{ + Count: 10_000, + }) + + var ok bool + + b.ReportAllocs() + for i := 0; b.Loop(); i++ { + cache.Set(i, i) + _, ok = cache.Get(i) + } + + assert.True(b, ok) + + // Most recent results: + // + // goos: darwin + // goarch: arm64 + // pkg: github.com/AdguardTeam/AdGuardDNS/internal/agdcache + // cpu: Apple M1 Pro + // BenchmarkLRU-8 5104281 207.2 ns/op 136 B/op 5 allocs/op +} + +func BenchmarkLRU_expire(b *testing.B) { + cache := agdcache.NewLRU[int, int](&agdcache.LRUConfig{ + Count: 10_000, + }) + + var ok bool + + b.Run("default_expire", func(b *testing.B) { + b.ReportAllocs() + for i := 0; b.Loop(); i++ { + cache.Set(i, i) + _, ok = cache.Get(i) + } + + assert.True(b, ok) + + // Most recent results: + // + // goos: darwin + // goarch: arm64 + // pkg: github.com/AdguardTeam/AdGuardDNS/internal/agdcache + // cpu: Apple M1 Pro + // BenchmarkLRU_expire/default_expire-8 4883622 208.6 ns/op 136 B/op 5 allocs/op + }) + + b.Run("set_expire", func(b *testing.B) { + b.ReportAllocs() + for i := 0; b.Loop(); i++ { + cache.SetWithExpire(i, i, 2000) + _, ok = cache.Get(i) + } + + assert.True(b, ok) + + // Most recent results: + // + // goos: darwin + // goarch: arm64 + // pkg: github.com/AdguardTeam/AdGuardDNS/internal/agdcache + // cpu: Apple M1 Pro + // BenchmarkLRU_expire/set_expire-8 3620727 328.7 ns/op 160 B/op 5 allocs/op + }) +} diff --git a/internal/agdtest/interface.go b/internal/agdtest/interface.go index 1220572..43d7bd6 100644 --- a/internal/agdtest/interface.go +++ b/internal/agdtest/interface.go @@ -2,7 +2,6 @@ package agdtest import ( "context" - "fmt" "net" "net/netip" "time" @@ -23,6 +22,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/remotekv" "github.com/AdguardTeam/AdGuardDNS/internal/rulestat" "github.com/AdguardTeam/golibs/netutil" + "github.com/AdguardTeam/golibs/testutil" "github.com/miekg/dns" "github.com/prometheus/client_golang/prometheus" ) @@ -191,8 +191,8 @@ func (c *ErrorCollector) Collect(ctx context.Context, err error) { // NewErrorCollector returns a new *ErrorCollector all methods of which panic. func NewErrorCollector() (c *ErrorCollector) { return &ErrorCollector{ - OnCollect: func(_ context.Context, err error) { - panic(fmt.Errorf("unexpected call to ErrorCollector.Collect(%v)", err)) + OnCollect: func(ctx context.Context, err error) { + panic(testutil.UnexpectedCall(ctx, err)) }, } } @@ -291,13 +291,13 @@ func (g *GeoIP) SubnetByLocation( 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)) + panic(testutil.UnexpectedCall(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)) + panic(testutil.UnexpectedCall(l, fam)) }, } } @@ -390,50 +390,41 @@ func (db *ProfileDB) ProfileByLinkedIP( func NewProfileDB() (db *ProfileDB) { return &ProfileDB{ OnCreateAutoDevice: func( - _ context.Context, + ctx 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, - )) + panic(testutil.UnexpectedCall(ctx, id, humanID, devType)) }, OnProfileByDedicatedIP: func( - _ context.Context, + ctx context.Context, ip netip.Addr, ) (p *agd.Profile, d *agd.Device, err error) { - panic(fmt.Errorf("unexpected call to ProfileDB.ProfileByDedicatedIP(%v)", ip)) + panic(testutil.UnexpectedCall(ctx, ip)) }, OnProfileByDeviceID: func( - _ context.Context, + ctx context.Context, id agd.DeviceID, ) (p *agd.Profile, d *agd.Device, err error) { - panic(fmt.Errorf("unexpected call to ProfileDB.ProfileByDeviceID(%v)", id)) + panic(testutil.UnexpectedCall(ctx, id)) }, OnProfileByHumanID: func( - _ context.Context, + ctx 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, - )) + panic(testutil.UnexpectedCall(ctx, profID, humanID)) }, OnProfileByLinkedIP: func( - _ context.Context, + ctx context.Context, ip netip.Addr, ) (p *agd.Profile, d *agd.Device, err error) { - panic(fmt.Errorf("unexpected call to ProfileDB.ProfileByLinkedIP(%v)", ip)) + panic(testutil.UnexpectedCall(ctx, ip)) }, } } @@ -475,16 +466,16 @@ func (s *ProfileStorage) Profiles( func NewProfileStorage() (s *ProfileStorage) { return &ProfileStorage{ OnCreateAutoDevice: func( - _ context.Context, + ctx context.Context, req *profiledb.StorageCreateAutoDeviceRequest, ) (resp *profiledb.StorageCreateAutoDeviceResponse, err error) { - panic(fmt.Errorf("unexpected call to ProfileStorage.CreateAutoDevice(%v)", req)) + panic(testutil.UnexpectedCall(ctx, req)) }, OnProfiles: func( - _ context.Context, + ctx context.Context, req *profiledb.StorageProfilesRequest, ) (resp *profiledb.StorageProfilesResponse, err error) { - panic(fmt.Errorf("unexpected call to ProfileStorage.Profiles(%v)", req)) + panic(testutil.UnexpectedCall(ctx, req)) }, } } @@ -587,14 +578,14 @@ func (l *RateLimit) CountResponses(ctx context.Context, req *dns.Msg, ip netip.A func NewRateLimit() (c *RateLimit) { return &RateLimit{ OnIsRateLimited: func( - _ context.Context, + ctx 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)) + panic(testutil.UnexpectedCall(ctx, req, addr)) }, - OnCountResponses: func(_ context.Context, resp *dns.Msg, addr netip.Addr) { - panic(fmt.Errorf("unexpected call to RateLimit.CountResponses(%v, %v)", resp, addr)) + OnCountResponses: func(ctx context.Context, resp *dns.Msg, addr netip.Addr) { + panic(testutil.UnexpectedCall(ctx, resp, addr)) }, } } diff --git a/internal/agdtest/profile.go b/internal/agdtest/profile.go new file mode 100644 index 0000000..d62073f --- /dev/null +++ b/internal/agdtest/profile.go @@ -0,0 +1,31 @@ +package agdtest + +import ( + "reflect" + "testing" + + "github.com/AdguardTeam/AdGuardDNS/internal/access" + gocmp "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" +) + +// AssertEqualProfile compares two values while ignoring internal details of +// some fields of profiles, such as pools. +func AssertEqualProfile(tb testing.TB, want, got any) (ok bool) { + tb.Helper() + + exportAll := gocmp.Exporter(func(_ reflect.Type) (ok bool) { return true }) + + defAccCmp := gocmp.Comparer(func(want, got *access.DefaultProfile) (ok bool) { + return gocmp.Equal(want.Config(), got.Config(), exportAll) + }) + + diff := gocmp.Diff(want, got, defAccCmp, exportAll) + if diff == "" { + return true + } + + // Use assert.Failf instead of tb.Errorf to get a more consistent error + // message. + return assert.Failf(tb, "not equal", "got: %+v\nwant: %+v\ndiff: %s", got, want, diff) +} diff --git a/internal/agdurlflt/agdurlflt.go b/internal/agdurlflt/agdurlflt.go new file mode 100644 index 0000000..3c567eb --- /dev/null +++ b/internal/agdurlflt/agdurlflt.go @@ -0,0 +1,68 @@ +// Package agdurlflt contains utilities for the urlfilter module. +package agdurlflt + +import ( + "bytes" + "unicode" +) + +// RulesLen returns the length of the byte buffer necessary to write ruleStrs, +// separated by a newline, to it. +func RulesLen[S ~string](ruleStrs []S) (l int) { + if len(ruleStrs) == 0 { + return 0 + } + + for _, s := range ruleStrs { + l += len(s) + len("\n") + } + + return l +} + +// RulesToBytes writes ruleStrs to a byte slice and returns it. +// +// TODO(a.garipov): Consider moving to golibs or urlfilter. +func RulesToBytes[S ~string](ruleStrs []S) (b []byte) { + l := RulesLen(ruleStrs) + if l == 0 { + return nil + } + + buf := bytes.NewBuffer(make([]byte, 0, l)) + for _, s := range ruleStrs { + _, _ = buf.WriteString(string(s)) + _ = buf.WriteByte('\n') + } + + return buf.Bytes() +} + +// RulesToBytesLower writes lowercase versions of ruleStrs to a byte slice and +// returns it. +// +// NOTE: Do not use this for rules that can include dnsrewrite modifiers, since +// their DNS types are case-sensitive. +// +// TODO(a.garipov): Consider moving to golibs or urlfilter. +func RulesToBytesLower(ruleStrs []string) (b []byte) { + l := RulesLen(ruleStrs) + if l == 0 { + return nil + } + + buf := bytes.NewBuffer(make([]byte, 0, l)) + for _, s := range ruleStrs { + for _, c := range s { + // NOTE: Theoretically there might be cases where a lowercase + // version of a rune takes up more space or less space than an + // uppercase one, but that doesn't matter since we're using a + // bytes.Buffer and rules generally are ASCII-only. + _, _ = buf.WriteRune(unicode.ToLower(c)) + } + + _ = buf.WriteByte('\n') + } + + return buf.Bytes() +} diff --git a/internal/agdurlflt/agdurlflt_test.go b/internal/agdurlflt/agdurlflt_test.go new file mode 100644 index 0000000..0ca02d6 --- /dev/null +++ b/internal/agdurlflt/agdurlflt_test.go @@ -0,0 +1,57 @@ +package agdurlflt_test + +import ( + "strings" + "testing" + + "github.com/AdguardTeam/AdGuardDNS/internal/agdurlflt" + "github.com/stretchr/testify/require" +) + +// testRulesStrs are the common filtering rules for tests. +var testRulesStrs = []string{ + `||blocked.example^`, + `@@||allowed.example^`, + `||dnsrewrite.example^$dnsrewrite=192.0.2.1`, +} + +// testRulesData is the data of [testRulesStrs] as bytes. +var testRulesData = []byte(strings.Join(testRulesStrs, "\n") + "\n") + +func BenchmarkRulesToBytes(b *testing.B) { + var got []byte + + b.ReportAllocs() + for b.Loop() { + got = agdurlflt.RulesToBytes(testRulesStrs) + } + + require.Equal(b, testRulesData, got) + + // Most recent results: + // + // goos: linux + // goarch: amd64 + // pkg: github.com/AdguardTeam/AdGuardDNS/internal/agdurlflt + // cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics + // BenchmarkRulesToBytes-16 7925872 145.3 ns/op 96 B/op 1 allocs/op +} + +func BenchmarkRulesToBytesLower(b *testing.B) { + var got []byte + + b.ReportAllocs() + for b.Loop() { + got = agdurlflt.RulesToBytesLower(testRulesStrs) + } + + require.Equal(b, testRulesData, got) + + // Most recent results: + // + // goos: linux + // goarch: amd64 + // pkg: github.com/AdguardTeam/AdGuardDNS/internal/agdurlflt + // cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics + // BenchmarkRulesToBytesLower-16 1000000 1188 ns/op 96 B/op 1 allocs/op +} diff --git a/internal/backendpb/backendpb_internal_test.go b/internal/backendpb/backendpb_internal_test.go index c77af5c..6a6c862 100644 --- a/internal/backendpb/backendpb_internal_test.go +++ b/internal/backendpb/backendpb_internal_test.go @@ -58,4 +58,7 @@ var TestLogger = slogutil.NewDiscardLogger() // TestProfileAccessConstructor is the common constructor of profile access // managers for tests -var TestProfileAccessConstructor = access.NewProfileConstructor(access.EmptyProfileMetrics{}) +var TestProfileAccessConstructor = access.NewProfileConstructor(&access.ProfileConstructorConfig{ + Metrics: access.EmptyProfileMetrics{}, + Standard: access.EmptyBlocker{}, +}) diff --git a/internal/backendpb/billstat_test.go b/internal/backendpb/billstat_test.go index 5ebefa0..d8f4681 100644 --- a/internal/backendpb/billstat_test.go +++ b/internal/backendpb/billstat_test.go @@ -47,14 +47,14 @@ func TestBillStat_Upload(t *testing.T) { ctx context.Context, req *backendpb.CreateDeviceRequest, ) (resp *backendpb.CreateDeviceResponse, err error) { - panic("not implemented") + panic(testutil.UnexpectedCall(ctx, req)) }, OnGetDNSProfiles: func( req *backendpb.DNSProfilesRequest, srv grpc.ServerStreamingServer[backendpb.DNSProfile], ) (err error) { - panic("not implemented") + panic(testutil.UnexpectedCall(req, srv)) }, OnSaveDevicesBillingStat: func( diff --git a/internal/backendpb/dns.pb.go b/internal/backendpb/dns.pb.go index 0b3069d..d51192c 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.36.6 -// protoc v6.31.0 +// protoc-gen-go v1.36.8 +// protoc v6.32.0 // source: dns.proto package backendpb @@ -1056,7 +1056,7 @@ func (x *ParentalSettings) GetSchedule() *ScheduleSettings { type ScheduleSettings struct { state protoimpl.MessageState `protogen:"open.v1"` Tmz string `protobuf:"bytes,1,opt,name=tmz,proto3" json:"tmz,omitempty"` - WeeklyRange *WeeklyRange `protobuf:"bytes,2,opt,name=weeklyRange,proto3" json:"weeklyRange,omitempty"` + WeeklyRange *WeeklyRange `protobuf:"bytes,2,opt,name=weekly_range,json=weeklyRange,proto3" json:"weekly_range,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -2810,7 +2810,7 @@ var File_dns_proto protoreflect.FileDescriptor const file_dns_proto_rawDesc = "" + "\n" + - "\tdns.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1bgoogle/protobuf/empty.proto\"\x1a\n" + + "\tdns.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x1a\n" + "\x18RateLimitSettingsRequest\"P\n" + "\x19RateLimitSettingsResponse\x123\n" + "\x0fallowed_subnets\x18\x01 \x03(\v2\n" + @@ -2897,10 +2897,10 @@ const file_dns_proto_rawDesc = "" + "\x13general_safe_search\x18\x03 \x01(\bR\x11generalSafeSearch\x12.\n" + "\x13youtube_safe_search\x18\x04 \x01(\bR\x11youtubeSafeSearch\x12)\n" + "\x10blocked_services\x18\x05 \x03(\tR\x0fblockedServices\x12-\n" + - "\bschedule\x18\x06 \x01(\v2\x11.ScheduleSettingsR\bschedule\"T\n" + + "\bschedule\x18\x06 \x01(\v2\x11.ScheduleSettingsR\bschedule\"U\n" + "\x10ScheduleSettings\x12\x10\n" + - "\x03tmz\x18\x01 \x01(\tR\x03tmz\x12.\n" + - "\vweeklyRange\x18\x02 \x01(\v2\f.WeeklyRangeR\vweeklyRange\"\xd8\x01\n" + + "\x03tmz\x18\x01 \x01(\tR\x03tmz\x12/\n" + + "\fweekly_range\x18\x02 \x01(\v2\f.WeeklyRangeR\vweeklyRange\"\xd8\x01\n" + "\vWeeklyRange\x12\x1b\n" + "\x03mon\x18\x01 \x01(\v2\t.DayRangeR\x03mon\x12\x1b\n" + "\x03tue\x18\x02 \x01(\v2\t.DayRangeR\x03tue\x12\x1b\n" + @@ -3019,8 +3019,7 @@ const file_dns_proto_rawDesc = "" + "\x13CustomDomainService\x12_\n" + "\x1agetCustomDomainCertificate\x12\x1f.CustomDomainCertificateRequest\x1a .CustomDomainCertificateResponse2Z\n" + "\x14SessionTicketService\x12B\n" + - "\x11getSessionTickets\x12\x15.SessionTicketRequest\x1a\x16.SessionTicketResponseB=\n" + - "!com.adguard.backend.dns.generatedB\x10DNSProfilesProtoP\x01\xa2\x02\x03DNSb\x06proto3" + "\x11getSessionTickets\x12\x15.SessionTicketRequest\x1a\x16.SessionTicketResponseb\x06proto3" var ( file_dns_proto_rawDescOnce sync.Once @@ -3111,7 +3110,7 @@ var file_dns_proto_depIdxs = []int32{ 45, // 20: CustomDomain.current:type_name -> CustomDomain.Current 24, // 21: DeviceSettings.authentication:type_name -> AuthenticationSettings 13, // 22: ParentalSettings.schedule:type_name -> ScheduleSettings - 14, // 23: ScheduleSettings.weeklyRange:type_name -> WeeklyRange + 14, // 23: ScheduleSettings.weekly_range:type_name -> WeeklyRange 15, // 24: WeeklyRange.mon:type_name -> DayRange 15, // 25: WeeklyRange.tue:type_name -> DayRange 15, // 26: WeeklyRange.wed:type_name -> DayRange diff --git a/internal/backendpb/dns.proto b/internal/backendpb/dns.proto index 867effc..a17d761 100644 --- a/internal/backendpb/dns.proto +++ b/internal/backendpb/dns.proto @@ -1,118 +1,107 @@ syntax = "proto3"; import "google/protobuf/duration.proto"; -import "google/protobuf/timestamp.proto"; import "google/protobuf/empty.proto"; +import "google/protobuf/timestamp.proto"; -option java_multiple_files = true; -option java_package = "com.adguard.backend.dns.generated"; -option java_outer_classname = "DNSProfilesProto"; -option objc_class_prefix = "DNS"; +// TODO(a.garipov): Expand the documentation and make it consistent. service DNSService { - /* - Gets DNS profiles. + Gets DNS profiles. - Field "sync_time" in DNSProfilesRequest - pass to return the latest updates after this time moment. + Field "sync_time" in DNSProfilesRequest - pass to return the latest updates after this time moment. - The trailers headers will include a "sync_time", given in milliseconds, - that should be used for subsequent incremental DNS profile synchronization requests. + The trailers headers will include a "sync_time", given in milliseconds, + that should be used for subsequent incremental DNS profile synchronization requests. - This method may return the following errors: - - RateLimitedError: If too many "full sync" concurrent requests are made. - - AuthenticationFailedError: If the authentication failed. + This method may return the following errors: + - RateLimitedError: If too many "full sync" concurrent requests are made. + - AuthenticationFailedError: If the authentication failed. */ rpc getDNSProfiles(DNSProfilesRequest) returns (stream DNSProfile); /* - Stores devices activity. + Stores devices activity. - This method may return the following errors: - - AuthenticationFailedError: If the authentication failed. + This method may return the following errors: + - AuthenticationFailedError: If the authentication failed. */ rpc saveDevicesBillingStat(stream DeviceBillingStat) returns (google.protobuf.Empty); /* - Create device by "human_id". + Create device by "human_id". - This method may return the following errors: - - RateLimitedError: If the request was made too frequently and the client must wait before retrying. - - DeviceQuotaExceededError: If the client has exceeded its quota for creating devices. - - BadRequestError: If the request is invalid: DNS server does not exist, creation of auto-devices is disabled or human_id validation failed. - - AuthenticationFailedError: If the authentication failed. + This method may return the following errors: + - RateLimitedError: If the request was made too frequently and the client must wait before retrying. + - DeviceQuotaExceededError: If the client has exceeded its quota for creating devices. + - BadRequestError: If the request is invalid: DNS server does not exist, creation of auto-devices is disabled or human_id validation failed. + - AuthenticationFailedError: If the authentication failed. */ rpc createDeviceByHumanId(CreateDeviceRequest) returns (CreateDeviceResponse); } service RateLimitService { - /* - Gets rate limit settings. + Gets rate limit settings. */ rpc getRateLimitSettings(RateLimitSettingsRequest) returns (RateLimitSettingsResponse); /* - Gets global access settings. + Gets global access settings. */ rpc getGlobalAccessSettings(GlobalAccessSettingsRequest) returns (GlobalAccessSettingsResponse); } service RemoteKVService { - /** - Get the value for the specified key. + Get the value for the specified key. - This method may return the following errors: - - AuthenticationFailedError: If the authentication failed. - */ + This method may return the following errors: + - AuthenticationFailedError: If the authentication failed. + */ rpc get(RemoteKVGetRequest) returns (RemoteKVGetResponse); /** - Set the value for the specified key. + 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. - */ + 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); } service CustomDomainService { - /* - Get certificate for custom domain. + Get certificate for custom domain. - This method may return the following errors: - - AuthenticationFailedError: If the authentication failed. - - BadRequestError: If the request is invalid: cert_name is empty. - - NotFoundError: If the certificate could not be found. + This method may return the following errors: + - AuthenticationFailedError: If the authentication failed. + - BadRequestError: If the request is invalid: cert_name is empty or no certificate found. + - NotFoundError: If the certificate was not found. + - RateLimitedError: If the request was made too frequently and the client must wait before retrying. */ rpc getCustomDomainCertificate(CustomDomainCertificateRequest) returns (CustomDomainCertificateResponse); } service SessionTicketService { + /* + Gets session ticket for the current date - /* - Gets session ticket for the current date - - This method may return the following errors: - - AuthenticationFailedError: If the authentication failed. - */ - rpc getSessionTickets(SessionTicketRequest) returns (SessionTicketResponse); + This method may return the following errors: + - AuthenticationFailedError: If the authentication failed. + */ + rpc getSessionTickets(SessionTicketRequest) returns (SessionTicketResponse); } -message RateLimitSettingsRequest { - -} +message RateLimitSettingsRequest {} message RateLimitSettingsResponse { repeated CidrRange allowed_subnets = 1; } -message GlobalAccessSettingsRequest { - -} +message GlobalAccessSettingsRequest {} message GlobalAccessSettingsResponse { AccessSettings standard = 1; @@ -164,18 +153,17 @@ message DNSProfile { } message DeviceSettingsChange { - message Deleted { - string device_id = 1; + string device_id = 1; } message Upserted { - DeviceSettings device = 1; + DeviceSettings device = 1; } oneof change { - Deleted deleted = 1; - Upserted upserted = 2; + Deleted deleted = 1; + Upserted upserted = 2; } } @@ -237,7 +225,7 @@ message ParentalSettings { message ScheduleSettings { string tmz = 1; - WeeklyRange weeklyRange = 2; + WeeklyRange weekly_range = 2; } message WeeklyRange { @@ -369,9 +357,7 @@ message RemoteKVSetRequest { google.protobuf.Duration ttl = 3; } -message RemoteKVSetResponse { - -} +message RemoteKVSetResponse {} message CustomDomainCertificateRequest { string cert_name = 1; @@ -385,10 +371,10 @@ message CustomDomainCertificateResponse { message SessionTicketRequest {} message SessionTicketResponse { - repeated SessionTicket tickets = 1; + repeated SessionTicket tickets = 1; } message SessionTicket { - string name = 1; - bytes data = 2; + string name = 1; + bytes data = 2; } diff --git a/internal/backendpb/dns_grpc.pb.go b/internal/backendpb/dns_grpc.pb.go index f1d8738..33694c9 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 v6.31.0 +// - protoc v6.32.0 // source: dns.proto package backendpb @@ -554,8 +554,9 @@ type CustomDomainServiceClient interface { // // This method may return the following errors: // - AuthenticationFailedError: If the authentication failed. - // - BadRequestError: If the request is invalid: cert_name is empty. - // - NotFoundError: If the certificate could not be found. + // - BadRequestError: If the request is invalid: cert_name is empty or no certificate found. + // - NotFoundError: If the certificate was not found. + // - RateLimitedError: If the request was made too frequently and the client must wait before retrying. GetCustomDomainCertificate(ctx context.Context, in *CustomDomainCertificateRequest, opts ...grpc.CallOption) (*CustomDomainCertificateResponse, error) } @@ -585,8 +586,9 @@ type CustomDomainServiceServer interface { // // This method may return the following errors: // - AuthenticationFailedError: If the authentication failed. - // - BadRequestError: If the request is invalid: cert_name is empty. - // - NotFoundError: If the certificate could not be found. + // - BadRequestError: If the request is invalid: cert_name is empty or no certificate found. + // - NotFoundError: If the certificate was not found. + // - RateLimitedError: If the request was made too frequently and the client must wait before retrying. GetCustomDomainCertificate(context.Context, *CustomDomainCertificateRequest) (*CustomDomainCertificateResponse, error) mustEmbedUnimplementedCustomDomainServiceServer() } diff --git a/internal/backendpb/metrics.go b/internal/backendpb/metrics.go index 9b84ce0..b2953f0 100644 --- a/internal/backendpb/metrics.go +++ b/internal/backendpb/metrics.go @@ -182,3 +182,22 @@ func (EmptyTicketStorageMetrics) SetTicketsState(_ context.Context, _ float64) { // ObserveUpdate implements the [TicketStorageMetrics] interface for // EmptyTicketStorageMetrics. func (EmptyTicketStorageMetrics) ObserveUpdate(_ context.Context, _ time.Duration, _ error) {} + +// StandardAccessMetrics is an interface that is used for the collection of +// standard access statistics. +type StandardAccessMetrics interface { + // ObserveUpdate sets the duration of the standard access settings update + // operation. + ObserveUpdate(ctx context.Context, dur time.Duration, err error) +} + +// EmptyStandardAccessMetrics is the implementation of the +// [StandardAccessMetrics] interface that does nothing. +type EmptyStandardAccessMetrics struct{} + +// type check +var _ StandardAccessMetrics = EmptyStandardAccessMetrics{} + +// ObserveUpdate implements the [StandardAccessMetrics] interface for +// EmptyStandardAccessMetrics. +func (EmptyStandardAccessMetrics) ObserveUpdate(_ context.Context, _ time.Duration, _ error) {} diff --git a/internal/backendpb/profile.go b/internal/backendpb/profile.go index 857b47f..ff000cf 100644 --- a/internal/backendpb/profile.go +++ b/internal/backendpb/profile.go @@ -88,6 +88,7 @@ func (x *AccessSettings) toInternal( logger *slog.Logger, errColl errcoll.Interface, cons *access.ProfileConstructor, + standardEnabled bool, ) (a access.Profile) { if x == nil || !x.Enabled { return access.EmptyProfile{} @@ -99,9 +100,32 @@ func (x *AccessSettings) toInternal( AllowedASN: asnToInternal(x.AllowlistAsn), BlockedASN: asnToInternal(x.BlocklistAsn), BlocklistDomainRules: x.BlocklistDomainRules, + StandardEnabled: standardEnabled, }) } +// toStandardConfig converts protobuf access settings to an internal structure. +// If x is nil, toStandardConfig returns nil. +func (x *AccessSettings) toStandardConfig( + ctx context.Context, + logger *slog.Logger, + errColl errcoll.Interface, +) (a *access.StandardBlockerConfig) { + if x == nil || !x.Enabled { + logger.WarnContext(ctx, "received disabled standard access settings") + + return nil + } + + return &access.StandardBlockerConfig{ + AllowedNets: cidrRangeToInternal(ctx, errColl, logger, x.AllowlistCidr), + BlockedNets: cidrRangeToInternal(ctx, errColl, logger, x.BlocklistCidr), + AllowedASN: asnToInternal(x.AllowlistAsn), + BlockedASN: asnToInternal(x.BlocklistAsn), + BlocklistDomainRules: x.BlocklistDomainRules, + } +} + // cidrRangeToInternal is a helper that converts a slice of CidrRange to the // slice of [netip.Prefix]. func cidrRangeToInternal( diff --git a/internal/backendpb/profilestorage.go b/internal/backendpb/profilestorage.go index baa735c..a885f7a 100644 --- a/internal/backendpb/profilestorage.go +++ b/internal/backendpb/profilestorage.go @@ -304,6 +304,14 @@ func (s *ProfileStorage) newProfile( Enabled: customEnabled, } + accessProf := p.Access.toInternal( + ctx, + s.logger, + s.errColl, + s.profAccessCons, + p.StandardAccessSettingsEnabled, + ) + return &agd.Profile{ CustomDomains: p.CustomDomain.toInternal(ctx, s.errColl, s.logger), DeviceIDs: container.NewMapSet(deviceIDs...), @@ -313,7 +321,7 @@ func (s *ProfileStorage) newProfile( RuleList: p.RuleLists.toInternal(ctx, s.errColl, s.logger), SafeBrowsing: p.SafeBrowsing.toInternal(), }, - Access: p.Access.toInternal(ctx, s.logger, s.errColl, s.profAccessCons), + Access: accessProf, BlockingMode: m, Ratelimiter: p.RateLimit.toInternal(ctx, s.errColl, s.logger, s.respSzEst), AccountID: accID, diff --git a/internal/backendpb/profilestorage_internal_test.go b/internal/backendpb/profilestorage_internal_test.go index eb83834..9bb6910 100644 --- a/internal/backendpb/profilestorage_internal_test.go +++ b/internal/backendpb/profilestorage_internal_test.go @@ -56,7 +56,7 @@ func TestProfileStorage_NewProfile(t *testing.T) { ) require.NoError(t, err) - assert.Equal(t, newProfile(t), got) + agdtest.AssertEqualProfile(t, newProfile(t), got) assert.Equal(t, newDevices(t), gotDevices) assert.Equal(t, wantDevChg, gotDevChg) }) @@ -95,7 +95,7 @@ func TestProfileStorage_NewProfile(t *testing.T) { errCollErr, ) - assert.Equal(t, newProfile(t), got) + agdtest.AssertEqualProfile(t, newProfile(t), got) assert.Equal(t, newDevices(t), gotDevices) assert.Equal(t, wantDevChg, gotDevChg) }) @@ -135,7 +135,10 @@ func TestProfileStorage_NewProfile(t *testing.T) { errCollErr, ) - assert.NotEqual(t, newProfile(t), got) + wantProf := newProfile(t) + wantProf.DeviceIDs.Delete(TestDeviceID) + + agdtest.AssertEqualProfile(t, wantProf, got) assert.NotEqual(t, newDevices(t), gotDevices) assert.Len(t, gotDevices, 3) assert.Equal(t, wantDevChg, gotDevChg) @@ -250,7 +253,7 @@ func TestProfileStorage_NewProfile(t *testing.T) { wantProf := newProfile(t) wantProf.BlockingMode = &dnsmsg.BlockingModeNullIP{} - assert.Equal(t, wantProf, got) + agdtest.AssertEqualProfile(t, wantProf, got) assert.Equal(t, newDevices(t), gotDevices) assert.Equal(t, wantDevChg, gotDevChg) }) diff --git a/internal/backendpb/profilestorage_test.go b/internal/backendpb/profilestorage_test.go index 4f7a34e..d37c6bc 100644 --- a/internal/backendpb/profilestorage_test.go +++ b/internal/backendpb/profilestorage_test.go @@ -52,13 +52,13 @@ func TestProfileStorage_CreateAutoDevice(t *testing.T) { req *backendpb.DNSProfilesRequest, srv grpc.ServerStreamingServer[backendpb.DNSProfile], ) (err error) { - panic("not implemented") + panic(testutil.UnexpectedCall(req, srv)) }, OnSaveDevicesBillingStat: func( srv grpc.ClientStreamingServer[backendpb.DeviceBillingStat, emptypb.Empty], ) (err error) { - panic("not implemented") + panic(testutil.UnexpectedCall(srv)) }, } @@ -114,10 +114,10 @@ func BenchmarkProfileStorage_Profiles(b *testing.B) { srv := &testDNSServiceServer{ OnCreateDeviceByHumanId: func( - _ context.Context, - _ *backendpb.CreateDeviceRequest, - ) (_ *backendpb.CreateDeviceResponse, _ error) { - panic("not implemented") + ctx context.Context, + req *backendpb.CreateDeviceRequest, + ) (resp *backendpb.CreateDeviceResponse, err error) { + panic(testutil.UnexpectedCall(ctx, req)) }, OnGetDNSProfiles: func( @@ -131,9 +131,9 @@ func BenchmarkProfileStorage_Profiles(b *testing.B) { }, OnSaveDevicesBillingStat: func( - _ grpc.ClientStreamingServer[backendpb.DeviceBillingStat, emptypb.Empty], - ) (_ error) { - panic("not implemented") + srv grpc.ClientStreamingServer[backendpb.DeviceBillingStat, emptypb.Empty], + ) (err error) { + panic(testutil.UnexpectedCall(srv)) }, } diff --git a/internal/backendpb/ratelimiter_test.go b/internal/backendpb/ratelimiter_test.go index 488f359..51bbd40 100644 --- a/internal/backendpb/ratelimiter_test.go +++ b/internal/backendpb/ratelimiter_test.go @@ -2,7 +2,6 @@ package backendpb_test import ( "context" - "fmt" "net/netip" "testing" @@ -38,10 +37,10 @@ func TestRateLimiter_Refresh(t *testing.T) { }, // TODO(e.burkov): Use and test. OnGetGlobalAccessSettings: func( - _ context.Context, - _ *backendpb.GlobalAccessSettingsRequest, - ) (_ *backendpb.GlobalAccessSettingsResponse, _ error) { - panic(fmt.Errorf("unexpected call to GetGlobalAccessSettings")) + ctx context.Context, + req *backendpb.GlobalAccessSettingsRequest, + ) (resp *backendpb.GlobalAccessSettingsResponse, err error) { + panic(testutil.UnexpectedCall(ctx, req)) }, } diff --git a/internal/backendpb/standardaccess.go b/internal/backendpb/standardaccess.go new file mode 100644 index 0000000..162b7a4 --- /dev/null +++ b/internal/backendpb/standardaccess.go @@ -0,0 +1,96 @@ +package backendpb + +import ( + "context" + "fmt" + "log/slog" + "net/url" + "time" + + "github.com/AdguardTeam/AdGuardDNS/internal/access" + "github.com/AdguardTeam/AdGuardDNS/internal/errcoll" + "github.com/AdguardTeam/AdGuardDNS/internal/filter/filterstorage" +) + +// StandardAccessConfig is the configuration structure for the business logic +// backend standard profile access service. +type StandardAccessConfig struct { + // Logger is used for logging the operation of the standard access service. + // It must not be nil. + Logger *slog.Logger + + // GRPCMetrics is used for the collection of the protobuf communication + // statistics. + GRPCMetrics GRPCMetrics + + // Metrics is used to collect standard access service statistics. + Metrics StandardAccessMetrics + + // ErrColl is used to collect errors during procedure calls. + 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 +} + +// StandardAccess is the implementation of the [service.Refresher] interface +// that retrieves the standard access settings from the business logic backend. +type StandardAccess struct { + logger *slog.Logger + grpcMetrics GRPCMetrics + metrics StandardAccessMetrics + errColl errcoll.Interface + client RateLimitServiceClient + apiKey string +} + +// NewStandardAccess creates a new properly initialized standard access service. +// c must not be nil. +func NewStandardAccess(c *StandardAccessConfig) (a *StandardAccess, 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 &StandardAccess{ + logger: c.Logger, + grpcMetrics: c.GRPCMetrics, + metrics: c.Metrics, + errColl: c.ErrColl, + client: NewRateLimitServiceClient(client), + apiKey: c.APIKey, + }, nil +} + +// type check +var _ filterstorage.StandardAccessStorage = (*StandardAccess)(nil) + +// Config retrieves the standard access settings from the business logic +// backend. +func (a *StandardAccess) Config(ctx context.Context) (c *access.StandardBlockerConfig, err error) { + ctx = ctxWithAuthentication(ctx, a.apiKey) + req := &GlobalAccessSettingsRequest{} + + start := time.Now() + defer func() { + // TODO(e.burkov): Consider separating metrics for networking and + // decoding. + a.metrics.ObserveUpdate(ctx, time.Since(start), err) + }() + + resp, err := a.client.GetGlobalAccessSettings(ctx, req) + if err != nil { + return nil, fmt.Errorf( + "loading global access settings: %w", + fixGRPCError(ctx, a.grpcMetrics, err), + ) + } + + return resp.GetStandard().toStandardConfig(ctx, a.logger, a.errColl), nil +} diff --git a/internal/bindtodevice/manager_linux.go b/internal/bindtodevice/manager_linux.go index 91badea..823c189 100644 --- a/internal/bindtodevice/manager_linux.go +++ b/internal/bindtodevice/manager_linux.go @@ -225,7 +225,7 @@ var _ service.Interface = (*Manager)(nil) // // TODO(a.garipov): Consider an interface solution instead of the nil exception. // -// TODO(a.garipov): Use the context for cancelation. +// TODO(a.garipov): Use the context for cancellation. func (m *Manager) Start(ctx context.Context) (err error) { if m == nil { return nil @@ -262,7 +262,7 @@ func (m *Manager) Start(ctx context.Context) (err error) { // // TODO(a.garipov): Consider waiting for all sockets to close. // -// TODO(a.garipov): Use the context for cancelation. +// TODO(a.garipov): Use the context for cancellation. // // TODO(a.garipov): Consider an interface solution instead of the nil exception. func (m *Manager) Shutdown(_ context.Context) (err error) { diff --git a/internal/bindtodevice/socket_linux_internal_test.go b/internal/bindtodevice/socket_linux_internal_test.go index 02a7349..731d18d 100644 --- a/internal/bindtodevice/socket_linux_internal_test.go +++ b/internal/bindtodevice/socket_linux_internal_test.go @@ -406,6 +406,7 @@ func TestListenControlWithSO(t *testing.T) { ) require.NotNil(t, lc) + // TODO(a.garipov): Move to golibs. type syscallConner interface { SyscallConn() (c syscall.RawConn, err error) } @@ -414,9 +415,10 @@ func TestListenControlWithSO(t *testing.T) { c, err := lc.ListenPacket(context.Background(), "udp", "0.0.0.0:0") require.NoError(t, err) require.NotNil(t, c) - require.Implements(t, (*syscallConner)(nil), c) - sc, err := c.(syscallConner).SyscallConn() + scConner := testutil.RequireTypeAssert[syscallConner](t, c) + + sc, err := scConner.SyscallConn() require.NoError(t, err) err = sc.Control(func(fd uintptr) { @@ -440,9 +442,10 @@ func TestListenControlWithSO(t *testing.T) { c, err := lc.Listen(context.Background(), "tcp", "0.0.0.0:0") require.NoError(t, err) require.NotNil(t, c) - require.Implements(t, (*syscallConner)(nil), c) - sc, err := c.(syscallConner).SyscallConn() + scConner := testutil.RequireTypeAssert[syscallConner](t, c) + + sc, err := scConner.SyscallConn() require.NoError(t, err) err = sc.Control(func(fd uintptr) { diff --git a/internal/cmd/access.go b/internal/cmd/access.go index 486efad..5e9699a 100644 --- a/internal/cmd/access.go +++ b/internal/cmd/access.go @@ -6,6 +6,12 @@ import ( "github.com/AdguardTeam/golibs/validate" ) +// Possible values of the STANDARD_ACCESS_TYPE environment variable. +const ( + standardAccessOff = "off" + standardAccessBackend = "backend" +) + // accessConfig is the configuration that controls IP and hosts blocking. type accessConfig struct { // BlockedQuestionDomains is a list of AdBlock rules used to block access. diff --git a/internal/cmd/builder.go b/internal/cmd/builder.go index 8401d8c..f0aeafe 100644 --- a/internal/cmd/builder.go +++ b/internal/cmd/builder.go @@ -57,16 +57,17 @@ import ( // Constants that define debug identifiers for the debug HTTP service. const ( - debugIDAllowlist = "allowlist" - debugIDBillStat = "billstat" - debugIDCustomDomainDB = "custom_domain_db" - debugIDGeoIP = "geoip" - debugIDProfileDB = "profiledb" - debugIDProfileDBFull = "profiledb_full" - debugIDRuleStat = "rulestat" - debugIDTLSConfig = "tlsconfig" - debugIDTicketRotator = "ticket_rotator" - debugIDWebSvc = "websvc" + debugIDAllowlist = "allowlist" + debugIDBillStat = "billstat" + debugIDCustomDomainDB = "custom_domain_db" + debugIDGeoIP = "geoip" + debugIDProfileDB = "profiledb" + debugIDProfileDBFull = "profiledb_full" + debugIDRuleStat = "rulestat" + debugIDStandardProfileAccess = "standard_profile_access" + debugIDTLSConfig = "tlsconfig" + debugIDTicketRotator = "ticket_rotator" + debugIDWebSvc = "websvc" // debugIDPrefixPlugin is the prefix for plugin debug identifiers. debugIDPrefixPlugin = "plugin/" @@ -97,6 +98,7 @@ type builder struct { promRegisterer prometheus.Registerer rand *rand.Rand sigHdlr *service.SignalHandler + standardAccess access.Blocker // The fields below are initialized later by calling the builder's methods. // Keep them sorted. @@ -318,9 +320,9 @@ func (b *builder) initAdultBlocking( return nil } - b.adultBlockingHashes, err = hashprefix.NewStorage("") + b.adultBlockingHashes, err = hashprefix.NewStorage(nil) if err != nil { - // Don't expect errors here because we pass an empty string. + // Expect no errors here because we pass a nil. panic(err) } @@ -420,7 +422,7 @@ func (b *builder) initNewRegDomains( return nil } - b.newRegDomainsHashes, err = hashprefix.NewStorage("") + b.newRegDomainsHashes, err = hashprefix.NewStorage(nil) if err != nil { // Don't expect errors here because we pass an empty string. panic(err) @@ -508,7 +510,7 @@ func (b *builder) initSafeBrowsing( return nil } - b.safeBrowsingHashes, err = hashprefix.NewStorage("") + b.safeBrowsingHashes, err = hashprefix.NewStorage(nil) if err != nil { // Don't expect errors here because we pass an empty string. panic(err) @@ -581,6 +583,83 @@ func (b *builder) initSafeBrowsing( return nil } +// initStandardAccess initializes the standard access settings. +// +// The following methods must be called before this one: +// - [builder.initGRPCMetrics] +func (b *builder) initStandardAccess(ctx context.Context) (err error) { + switch typ := b.env.StandardAccessType; typ { + case standardAccessOff: + b.standardAccess = access.EmptyBlocker{} + + return nil + case standardAccessBackend: + // Go on. + // + // TODO(e.burkov): Extract the initialization logic to a separate + // function. + default: + panic(fmt.Errorf("env STANDARD_ACCESS_TYPE: %w: %q", errors.ErrBadEnumValue, typ)) + } + + stdAcc := access.NewStandardBlocker(&access.StandardBlockerConfig{}) + b.standardAccess = stdAcc + + mtrc, err := metrics.NewBackendStandardAccess(b.mtrcNamespace, b.promRegisterer) + if err != nil { + return fmt.Errorf("initializing standard access metrics: %w", err) + } + + strg, err := backendpb.NewStandardAccess(&backendpb.StandardAccessConfig{ + Endpoint: &b.env.StandardAccessURL.URL, + GRPCMetrics: b.backendGRPCMtrc, + Metrics: mtrc, + Logger: b.baseLogger.With(slogutil.KeyPrefix, "standard_access_storage"), + ErrColl: b.errColl, + APIKey: b.env.StandardAccessAPIKey, + }) + if err != nil { + return fmt.Errorf("initializing standard access storage: %w", err) + } + + updater, err := filterstorage.NewStandardAccess(ctx, &filterstorage.StandardAccessConfig{ + BaseLogger: b.baseLogger, + Logger: b.baseLogger.With(slogutil.KeyPrefix, "standard_access_updater"), + Getter: strg, + Setter: stdAcc, + CacheDir: b.env.FilterCachePath, + }) + if err != nil { + return fmt.Errorf("initializing standard access updater: %w", err) + } + + err = updater.Refresh(ctx) + if err != nil { + return fmt.Errorf("initializing standard access updater: %w", err) + } + + refrWorker := service.NewRefreshWorker(&service.RefreshWorkerConfig{ + Clock: timeutil.SystemClock{}, + ContextConstructor: contextutil.NewTimeoutConstructor( + time.Duration(b.env.StandardAccessTimeout), + ), + ErrorHandler: newSlogErrorHandler(b.baseLogger, "standard_access_refresh"), + Refresher: updater, + Schedule: timeutil.NewConstSchedule(time.Duration(b.env.StandardAccessRefreshIvl)), + RefreshOnShutdown: false, + }) + err = refrWorker.Start(context.WithoutCancel(ctx)) + if err != nil { + return fmt.Errorf("starting standard access refresher: %w", err) + } + + b.sigHdlr.AddService(refrWorker) + + b.debugRefrs[debugIDStandardProfileAccess] = updater + + return nil +} + // initFilterStorage initializes and refreshes the filter storage. It also adds // the refresher with ID [filter.StoragePrefix] to the debug refreshers. // @@ -598,7 +677,7 @@ func (b *builder) initFilterStorage(ctx context.Context) (err error) { b.filterStorage, err = filterstorage.New(&filterstorage.Config{ BaseLogger: b.baseLogger, Logger: b.baseLogger.With(slogutil.KeyPrefix, filter.StoragePrefix), - BlockedServices: &filterstorage.ConfigBlockedServices{ + BlockedServices: &filterstorage.BlockedServicesConfig{ IndexURL: blockedSvcIdxURL, // TODO(a.garipov): Consider adding a separate parameter here. IndexMaxSize: c.MaxSize, @@ -612,15 +691,15 @@ func (b *builder) initFilterStorage(ctx context.Context) (err error) { ResultCacheEnabled: c.RuleListCache.Enabled, Enabled: bool(b.env.BlockedServiceEnabled), }, - Custom: &filterstorage.ConfigCustom{ + Custom: &filterstorage.CustomConfig{ CacheCount: c.CustomFilterCacheSize, }, - HashPrefix: &filterstorage.ConfigHashPrefix{ + HashPrefix: &filterstorage.HashPrefixConfig{ Adult: b.adultBlocking, Dangerous: b.safeBrowsing, NewlyRegistered: b.newRegDomains, }, - RuleLists: &filterstorage.ConfigRuleLists{ + RuleLists: &filterstorage.RuleListsConfig{ IndexURL: &b.env.FilterIndexURL.URL, // TODO(a.garipov): Consider adding a separate parameter here. IndexMaxSize: c.MaxSize, @@ -686,14 +765,14 @@ func (b *builder) newSafeSearchConfig( u *urlutil.URL, id filter.ID, enabled bool, -) (c *filterstorage.ConfigSafeSearch) { +) (c *filterstorage.SafeSearchConfig) { if !enabled { - return &filterstorage.ConfigSafeSearch{} + return &filterstorage.SafeSearchConfig{} } fltConf := b.conf.Filters - return &filterstorage.ConfigSafeSearch{ + return &filterstorage.SafeSearchConfig{ URL: &u.URL, ID: id, // TODO(a.garipov): Consider adding a separate parameter here. @@ -1148,6 +1227,7 @@ func (b *builder) initGRPCMetrics(ctx context.Context) (err error) { case b.profilesEnabled, b.env.SessionTicketType == sessionTicketRemote, + b.env.StandardAccessType == standardAccessBackend, b.env.DNSCheckKVType == kvModeBackend, b.env.RateLimitAllowlistType == rlAllowlistTypeBackend: // Go on. @@ -1262,7 +1342,10 @@ func (b *builder) initProfileDB(ctx context.Context) (err error) { return fmt.Errorf("registering profile access engine metrics: %w", err) } - profAccessCons := access.NewProfileConstructor(profileMtrc) + profAccessCons := access.NewProfileConstructor(&access.ProfileConstructorConfig{ + Metrics: profileMtrc, + Standard: b.standardAccess, + }) backendProfileDBMtrc, err := metrics.NewBackendProfileDB(b.mtrcNamespace, b.promRegisterer) if err != nil { diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go index 7146c22..e3bb189 100644 --- a/internal/cmd/cmd.go +++ b/internal/cmd/cmd.go @@ -112,6 +112,8 @@ func Main(plugins *plugin.Registry) { errors.Check(b.initGRPCMetrics(ctx)) + errors.Check(b.initStandardAccess(ctx)) + errors.Check(b.initTLSManager(ctx)) errors.Check(b.initCustomDomainDB(ctx)) diff --git a/internal/cmd/env.go b/internal/cmd/env.go index 574f2a6..a7681c2 100644 --- a/internal/cmd/env.go +++ b/internal/cmd/env.go @@ -49,6 +49,7 @@ type environment struct { RuleStatURL *urlutil.URL `env:"RULESTAT_URL"` SafeBrowsingURL *urlutil.URL `env:"SAFE_BROWSING_URL"` SessionTicketURL *urlutil.URL `env:"SESSION_TICKET_URL"` + StandardAccessURL *urlutil.URL `env:"STANDARD_ACCESS_URL"` YoutubeSafeSearchURL *urlutil.URL `env:"YOUTUBE_SAFE_SEARCH_URL"` BackendRateLimitAPIKey string `env:"BACKEND_RATELIMIT_API_KEY"` @@ -74,6 +75,8 @@ type environment struct { SessionTicketCachePath string `env:"SESSION_TICKET_CACHE_PATH"` SessionTicketIndexName string `env:"SESSION_TICKET_INDEX_NAME"` SessionTicketType string `env:"SESSION_TICKET_TYPE"` + StandardAccessAPIKey string `env:"STANDARD_ACCESS_API_KEY"` + StandardAccessType string `env:"STANDARD_ACCESS_TYPE"` // TODO(a.garipov): Consider renaming to "WEB_STATIC_PATH" or something // similar. @@ -83,9 +86,11 @@ type environment struct { ProfilesMaxRespSize datasize.ByteSize `env:"PROFILES_MAX_RESP_SIZE" envDefault:"64MB"` - CustomDomainsRefreshIvl timeutil.Duration `env:"CUSTOM_DOMAINS_REFRESH_INTERVAL"` - DNSCheckKVTTL timeutil.Duration `env:"DNSCHECK_KV_TTL"` - SessionTicketRefreshIvl timeutil.Duration `env:"SESSION_TICKET_REFRESH_INTERVAL"` + CustomDomainsRefreshIvl timeutil.Duration `env:"CUSTOM_DOMAINS_REFRESH_INTERVAL"` + DNSCheckKVTTL timeutil.Duration `env:"DNSCHECK_KV_TTL"` + SessionTicketRefreshIvl timeutil.Duration `env:"SESSION_TICKET_REFRESH_INTERVAL"` + StandardAccessRefreshIvl timeutil.Duration `env:"STANDARD_ACCESS_REFRESH_INTERVAL"` + StandardAccessTimeout timeutil.Duration `env:"STANDARD_ACCESS_TIMEOUT"` // TODO(a.garipov): Rename to DNSCHECK_CACHE_KV_COUNT? DNSCheckCacheKVSize int `env:"DNSCHECK_CACHE_KV_SIZE"` @@ -152,6 +157,7 @@ func (envs *environment) Validate() (err error) { errs = envs.validateDNSCheck(errs) errs = envs.validateRateLimit(errs) errs = envs.validateSessionTickets(errs) + errs = envs.validateStandardAccess(errs) errs = envs.validateRateLimitURLs(errs) return errors.Join(errs...) @@ -328,12 +334,12 @@ func (envs *environment) validateRateLimit(errs []error) (res []error) { func (envs *environment) validateSessionTickets(errs []error) (res []error) { res = errs - err := validate.Positive("env SESSION_TICKET_REFRESH_INTERVAL", envs.SessionTicketRefreshIvl) + err := validate.NotEmpty("env SESSION_TICKET_TYPE", envs.SessionTicketType) if err != nil { - res = append(res, err) + return append(res, err) } - err = validate.NotEmpty("env SESSION_TICKET_TYPE", envs.SessionTicketType) + err = validate.Positive("env SESSION_TICKET_REFRESH_INTERVAL", envs.SessionTicketRefreshIvl) if err != nil { return append(res, err) } @@ -342,24 +348,52 @@ func (envs *environment) validateSessionTickets(errs []error) (res []error) { case sessionTicketLocal: return res case sessionTicketRemote: - // Go on. + res = append( + res, + validate.NotEmpty("env SESSION_TICKET_API_KEY", envs.SessionTicketAPIKey), + validate.NotEmpty("env SESSION_TICKET_CACHE_PATH", envs.SessionTicketCachePath), + validate.NotEmpty("env SESSION_TICKET_INDEX_NAME", envs.SessionTicketIndexName), + ) + + if err = validate.NotNil("env SESSION_TICKET_URL", envs.SessionTicketURL); err != nil { + res = append(res, err) + } else if err = urlutil.ValidateGRPCURL(&envs.SessionTicketURL.URL); err != nil { + res = append(res, fmt.Errorf("env SESSION_TICKET_URL: %w", err)) + } default: err = fmt.Errorf("env SESSION_TICKET_TYPE: %w: %q", errors.ErrBadEnumValue, typ) return append(res, err) } - res = append( - res, - validate.NotEmpty("env SESSION_TICKET_API_KEY", envs.SessionTicketAPIKey), - validate.NotEmpty("env SESSION_TICKET_CACHE_PATH", envs.SessionTicketCachePath), - validate.NotEmpty("env SESSION_TICKET_INDEX_NAME", envs.SessionTicketIndexName), - ) + return res +} - if err = validate.NotNil("env SESSION_TICKET_URL", envs.SessionTicketURL); err != nil { - res = append(res, err) - } else if err = urlutil.ValidateGRPCURL(&envs.SessionTicketURL.URL); err != nil { - res = append(res, fmt.Errorf("env SESSION_TICKET_URL: %w", err)) +// validateStandardAccess appends validation errors to the given errs if +// environment variables for standard access contain errors. +func (envs *environment) validateStandardAccess(errs []error) (res []error) { + res = errs + + switch typ := envs.StandardAccessType; typ { + case standardAccessOff: + return res + case standardAccessBackend: + res = append( + res, + validate.NotEmpty("env STANDARD_ACCESS_API_KEY", envs.StandardAccessAPIKey), + validate.Positive("env STANDARD_ACCESS_REFRESH_INTERVAL", envs.StandardAccessRefreshIvl), + validate.Positive("env STANDARD_ACCESS_TIMEOUT", envs.StandardAccessTimeout), + ) + + if err := validate.NotNil("env STANDARD_ACCESS_URL", envs.StandardAccessURL); err != nil { + res = append(res, err) + } else if err = urlutil.ValidateGRPCURL(&envs.StandardAccessURL.URL); err != nil { + res = append(res, fmt.Errorf("env STANDARD_ACCESS_URL: %w", err)) + } + default: + err := fmt.Errorf("env STANDARD_ACCESS_TYPE: %w: %q", errors.ErrBadEnumValue, typ) + + return append(res, err) } return res diff --git a/internal/connlimiter/limiter_test.go b/internal/connlimiter/limiter_test.go index 8d6ba2f..2b8b089 100644 --- a/internal/connlimiter/limiter_test.go +++ b/internal/connlimiter/limiter_test.go @@ -34,20 +34,21 @@ func TestLimiter(t *testing.T) { Resume: 1, }) + // TODO(a.garipov): Add fakenet.NewConn to golibs. conn := &fakenet.Conn{ OnClose: func() (err error) { return nil }, - OnLocalAddr: func() (laddr net.Addr) { panic("not implemented") }, - OnRead: func(b []byte) (n int, err error) { panic("not implemented") }, + OnLocalAddr: func() (laddr net.Addr) { panic(testutil.UnexpectedCall()) }, + OnRead: func(b []byte) (n int, err error) { panic(testutil.UnexpectedCall(b)) }, OnRemoteAddr: func() (addr net.Addr) { return &net.TCPAddr{ IP: netutil.IPv4Localhost().AsSlice(), Port: 1234, } }, - OnSetDeadline: func(t time.Time) (err error) { panic("not implemented") }, - OnSetReadDeadline: func(t time.Time) (err error) { panic("not implemented") }, - OnSetWriteDeadline: func(t time.Time) (err error) { panic("not implemented") }, - OnWrite: func(b []byte) (n int, err error) { panic("not implemented") }, + OnSetDeadline: func(t time.Time) (err error) { panic(testutil.UnexpectedCall(t)) }, + OnSetReadDeadline: func(t time.Time) (err error) { panic(testutil.UnexpectedCall(t)) }, + OnSetWriteDeadline: func(t time.Time) (err error) { panic(testutil.UnexpectedCall(t)) }, + OnWrite: func(b []byte) (n int, err error) { panic(testutil.UnexpectedCall(b)) }, } lsnr := &fakenet.Listener{ diff --git a/internal/connlimiter/listenconfig_test.go b/internal/connlimiter/listenconfig_test.go index 32f775b..9834b62 100644 --- a/internal/connlimiter/listenconfig_test.go +++ b/internal/connlimiter/listenconfig_test.go @@ -10,30 +10,32 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/connlimiter" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver" "github.com/AdguardTeam/golibs/logutil/slogutil" + "github.com/AdguardTeam/golibs/testutil" "github.com/AdguardTeam/golibs/testutil/fakenet" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestListenConfig(t *testing.T) { + // TODO(a.garipov): Add fakenet.NewPacketConn to golibs. pc := &fakenet.PacketConn{ - OnClose: func() (_ error) { panic("not implemented") }, - OnLocalAddr: func() (_ net.Addr) { panic("not implemented") }, - OnReadFrom: func(_ []byte) (_ int, _ net.Addr, _ error) { - panic("not implemented") + OnClose: func() (err error) { panic(testutil.UnexpectedCall()) }, + OnLocalAddr: func() (laddr net.Addr) { panic(testutil.UnexpectedCall()) }, + OnReadFrom: func(b []byte) (n int, addr net.Addr, err error) { + panic(testutil.UnexpectedCall(b)) }, - OnSetDeadline: func(_ time.Time) (_ error) { panic("not implemented") }, - OnSetReadDeadline: func(_ time.Time) (_ error) { panic("not implemented") }, - OnSetWriteDeadline: func(_ time.Time) (_ error) { panic("not implemented") }, - OnWriteTo: func(_ []byte, _ net.Addr) (_ int, _ error) { - panic("not implemented") + OnSetDeadline: func(t time.Time) (err error) { panic(testutil.UnexpectedCall(t)) }, + OnSetReadDeadline: func(t time.Time) (err error) { panic(testutil.UnexpectedCall(t)) }, + OnSetWriteDeadline: func(t time.Time) (err error) { panic(testutil.UnexpectedCall(t)) }, + OnWriteTo: func(b []byte, addr net.Addr) (n int, err error) { + panic(testutil.UnexpectedCall(b, addr)) }, } lsnr := &fakenet.Listener{ - OnAccept: func() (_ net.Conn, _ error) { panic("not implemented") }, - OnAddr: func() (_ net.Addr) { panic("not implemented") }, - OnClose: func() (_ error) { return nil }, + OnAccept: func() (c net.Conn, err error) { panic(testutil.UnexpectedCall()) }, + OnAddr: func() (addr net.Addr) { panic(testutil.UnexpectedCall()) }, + OnClose: func() (err error) { return nil }, } c := &agdtest.ListenConfig{ diff --git a/internal/debugsvc/debugsvc.go b/internal/debugsvc/debugsvc.go index 61d6c24..a593ef6 100644 --- a/internal/debugsvc/debugsvc.go +++ b/internal/debugsvc/debugsvc.go @@ -1,4 +1,6 @@ // Package debugsvc contains the debug HTTP API of AdGuard DNS. +// +// TODO(a.garipov): Add standard or custom metrics. package debugsvc import ( @@ -130,7 +132,7 @@ var _ service.Interface = (*Service)(nil) // // TODO(a.garipov): Wait for the services to go online. // -// TODO(a.garipov): Use the context for cancelation. +// TODO(a.garipov): Use the context for cancellation. func (svc *Service) Start(ctx context.Context) (err error) { for _, srv := range svc.servers { go runServer(ctx, svc.logger, srv) diff --git a/internal/debugsvc/debugsvc_test.go b/internal/debugsvc/debugsvc_test.go index 1f55e9a..8099753 100644 --- a/internal/debugsvc/debugsvc_test.go +++ b/internal/debugsvc/debugsvc_test.go @@ -99,10 +99,10 @@ func TestService_Start(t *testing.T) { // yet, check for it in periodically. var resp *http.Response healthCheckURL := srvURL.JoinPath(debugsvc.PathPatternHealthCheck) - require.EventuallyWithT(t, func(ct *assert.CollectT) { + require.EventuallyWithT(t, func(c *assert.CollectT) { var getErr error resp, getErr = client.Get(ctx, healthCheckURL) - assert.NoError(t, getErr) + assert.NoError(c, getErr) }, testTimeout, testTimeout/10) body := readRespBody(t, resp) diff --git a/internal/dnsserver/cache/cache.go b/internal/dnsserver/cache/cache.go index 9738e17..aa01778 100644 --- a/internal/dnsserver/cache/cache.go +++ b/internal/dnsserver/cache/cache.go @@ -24,8 +24,9 @@ import ( // // TODO(a.garipov): Extract cache logic to golibs. type Middleware struct { - logger *slog.Logger - metrics MetricsListener + logger *slog.Logger + metrics MetricsListener + // TODO(d.kolyshev): Use [agdcache.Default]. cache gcache.Cache cacheMinTTL time.Duration overrideTTL bool diff --git a/internal/dnsserver/dnsserver.go b/internal/dnsserver/dnsserver.go index 6b301ef..b95520e 100644 --- a/internal/dnsserver/dnsserver.go +++ b/internal/dnsserver/dnsserver.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022-2024 AdGuard Software Ltd. +// Copyright (C) 2022-2025 AdGuard Software Ltd. // // This program is free software: you can redistribute it and/or modify it under // the terms of the GNU Affero General Public License as published by the Free diff --git a/internal/dnsserver/dnsservertest/handler.go b/internal/dnsserver/dnsservertest/handler.go index 940afe0..0eedd51 100644 --- a/internal/dnsserver/dnsservertest/handler.go +++ b/internal/dnsserver/dnsservertest/handler.go @@ -2,12 +2,12 @@ package dnsservertest import ( "context" - "fmt" "time" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver" "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/netutil" + "github.com/AdguardTeam/golibs/testutil" "github.com/miekg/dns" ) @@ -59,8 +59,7 @@ func NewDefaultHandlerWithCount(recordsCount int) (h dnsserver.Handler) { // 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)) + panic(testutil.UnexpectedCall(ctx, rw, req)) } return dnsserver.HandlerFunc(f) diff --git a/internal/dnsserver/doc.go b/internal/dnsserver/doc.go index 9c39802..cf44e39 100644 --- a/internal/dnsserver/doc.go +++ b/internal/dnsserver/doc.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022-2024 AdGuard Software Ltd. +// Copyright (C) 2022-2025 AdGuard Software Ltd. // // This program is free software: you can redistribute it and/or modify it under // the terms of the GNU Affero General Public License as published by the Free diff --git a/internal/dnsserver/go.mod b/internal/dnsserver/go.mod index f53dfe2..1354ec4 100644 --- a/internal/dnsserver/go.mod +++ b/internal/dnsserver/go.mod @@ -1,46 +1,42 @@ module github.com/AdguardTeam/AdGuardDNS/internal/dnsserver -go 1.24.5 +go 1.24.6 require ( - github.com/AdguardTeam/golibs v0.32.15 + github.com/AdguardTeam/golibs v0.34.0 github.com/ameshkov/dnscrypt/v2 v2.4.0 github.com/ameshkov/dnsstamps v1.0.3 github.com/bluele/gcache v0.0.2 github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 - github.com/miekg/dns v1.1.66 + github.com/miekg/dns v1.1.68 github.com/panjf2000/ants/v2 v2.11.3 github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible - github.com/prometheus/client_golang v1.22.0 - github.com/quic-go/quic-go v0.52.0 - github.com/stretchr/testify v1.10.0 - golang.org/x/net v0.41.0 - golang.org/x/sys v0.33.0 + github.com/prometheus/client_golang v1.23.0 + github.com/quic-go/quic-go v0.54.0 + github.com/stretchr/testify v1.11.1 + golang.org/x/net v0.43.0 + golang.org/x/sys v0.35.0 ) require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/go-task/slim-sprig/v3 v3.0.0 // indirect - github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // 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.23.4 // indirect - github.com/onsi/gomega v1.37.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.64.0 // indirect - github.com/prometheus/procfs v0.16.1 // indirect + github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/procfs v0.17.0 // indirect github.com/quic-go/qpack v0.5.1 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect - go.uber.org/automaxprocs v1.6.0 // indirect - go.uber.org/mock v0.5.2 // indirect - golang.org/x/crypto v0.39.0 // indirect - golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect - golang.org/x/mod v0.25.0 // indirect - golang.org/x/sync v0.15.0 // indirect - golang.org/x/text v0.26.0 // indirect - golang.org/x/tools v0.34.0 // indirect - google.golang.org/protobuf v1.36.6 // indirect + go.uber.org/mock v0.6.0 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect + golang.org/x/mod v0.27.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/text v0.28.0 // indirect + golang.org/x/tools v0.36.0 // indirect + google.golang.org/protobuf v1.36.8 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/internal/dnsserver/go.sum b/internal/dnsserver/go.sum index caae2e1..b2b555d 100644 --- a/internal/dnsserver/go.sum +++ b/internal/dnsserver/go.sum @@ -1,5 +1,5 @@ -github.com/AdguardTeam/golibs v0.32.15 h1:arDRDWiZCH3g5Onr8AqMnOHhaOppNoBpgC3DNhmeDeA= -github.com/AdguardTeam/golibs v0.32.15/go.mod h1:G9CzUOzx87J+2u+eClJrrwWD7lMbROvuUnT8uvDUzIA= +github.com/AdguardTeam/golibs v0.34.0 h1:JQK024DkTYxE7vsPVsYsoyDHW/53Nun7OYb9qscniK8= +github.com/AdguardTeam/golibs v0.34.0/go.mod h1:K4C2EbfSEM1zY5YXoti9SfbTAHN/kIX97LpDtCwORrM= github.com/ameshkov/dnscrypt/v2 v2.4.0 h1:if6ZG2cuQmcP2TwSY+D0+8+xbPfoatufGlOQTMNkI9o= github.com/ameshkov/dnscrypt/v2 v2.4.0/go.mod h1:WpEFV2uhebXb8Jhes/5/fSdpmhGV8TL22RDaeWwV6hI= github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo= @@ -12,76 +12,65 @@ github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 h1:6lhrsTEnloDPXye github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= -github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18= -github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE= -github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE= +github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= +github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= -github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= -github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= -github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/panjf2000/ants/v2 v2.11.3 h1:AfI0ngBoXJmYOpDh9m516vjqoUu2sLrIVgppI9TZVpg= github.com/panjf2000/ants/v2 v2.11.3/go.mod h1:8u92CYMUc6gyvTIw8Ru7Mt7+/ESnJahz5EVtqfrilek= github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible h1:IWzUvJ72xMjmrjR9q3H1PF+jwdN0uNQiR2t1BLNalyo= github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= -github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= -github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= -github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= +github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQPGO4= -github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= -github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= -github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= +github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= +github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= 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.52.0 h1:/SlHrCRElyaU6MaEPKqKr9z83sBg2v4FLLvWM+Z47pA= -github.com/quic-go/quic-go v0.52.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ= +github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= +github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= -go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= -go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= -go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= -golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= -golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= -golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= -golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= -golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= -golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= -golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= -golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= -golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= -golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0= +golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= +google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= 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/netext/listenconfig_unix_test.go b/internal/dnsserver/netext/listenconfig_unix_test.go index d9e3155..3e8cc16 100644 --- a/internal/dnsserver/netext/listenconfig_unix_test.go +++ b/internal/dnsserver/netext/listenconfig_unix_test.go @@ -9,6 +9,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext" "github.com/AdguardTeam/golibs/errors" + "github.com/AdguardTeam/golibs/testutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/sys/unix" @@ -18,6 +19,7 @@ func TestDefaultListenConfigWithOOB(t *testing.T) { lc := netext.DefaultListenConfigWithOOB(nil) require.NotNil(t, lc) + // TODO(a.garipov): Move to golibs. type syscallConner interface { SyscallConn() (c syscall.RawConn, err error) } @@ -26,9 +28,10 @@ func TestDefaultListenConfigWithOOB(t *testing.T) { c, err := lc.ListenPacket(context.Background(), "udp4", "127.0.0.1:0") require.NoError(t, err) require.NotNil(t, c) - require.Implements(t, (*syscallConner)(nil), c) - sc, err := c.(syscallConner).SyscallConn() + scConner := testutil.RequireTypeAssert[syscallConner](t, c) + + sc, err := scConner.SyscallConn() require.NoError(t, err) err = sc.Control(func(fd uintptr) { @@ -51,9 +54,10 @@ func TestDefaultListenConfigWithOOB(t *testing.T) { require.NoError(t, err) require.NotNil(t, c) - require.Implements(t, (*syscallConner)(nil), c) - sc, err := c.(syscallConner).SyscallConn() + scConner := testutil.RequireTypeAssert[syscallConner](t, c) + + sc, err := scConner.SyscallConn() require.NoError(t, err) err = sc.Control(func(fd uintptr) { @@ -86,9 +90,10 @@ func TestDefaultListenConfigWithSO(t *testing.T) { c, err := lc.ListenPacket(context.Background(), "udp4", "127.0.0.1:0") require.NoError(t, err) require.NotNil(t, c) - require.Implements(t, (*syscallConner)(nil), c) - sc, err := c.(syscallConner).SyscallConn() + scConner := testutil.RequireTypeAssert[syscallConner](t, c) + + sc, err := scConner.SyscallConn() require.NoError(t, err) err = sc.Control(func(fd uintptr) { @@ -119,9 +124,10 @@ func TestDefaultListenConfigWithSO(t *testing.T) { require.NoError(t, err) require.NotNil(t, c) - require.Implements(t, (*syscallConner)(nil), c) - sc, err := c.(syscallConner).SyscallConn() + scConner := testutil.RequireTypeAssert[syscallConner](t, c) + + sc, err := scConner.SyscallConn() require.NoError(t, err) err = sc.Control(func(fd uintptr) { diff --git a/internal/dnsserver/serverhttps_test.go b/internal/dnsserver/serverhttps_test.go index bf7f766..4911937 100644 --- a/internal/dnsserver/serverhttps_test.go +++ b/internal/dnsserver/serverhttps_test.go @@ -527,7 +527,7 @@ func createDoH3Client( _ string, tlsCfg *tls.Config, cfg *quic.Config, - ) (c quic.EarlyConnection, e error) { + ) (c *quic.Conn, e error) { return quic.DialAddrEarly(ctx, httpsAddr.String(), tlsCfg, cfg) }, QUICConfig: quicConfig, diff --git a/internal/dnsserver/serverquic.go b/internal/dnsserver/serverquic.go index 3f7d4ef..c814ef1 100644 --- a/internal/dnsserver/serverquic.go +++ b/internal/dnsserver/serverquic.go @@ -316,7 +316,7 @@ func (s *ServerQUIC) acceptQUICConn( // decremented. func (s *ServerQUIC) serveQUICConnAsync( ctx context.Context, - conn quic.Connection, + conn *quic.Conn, connWg *sync.WaitGroup, ) { defer connWg.Done() @@ -331,7 +331,7 @@ func (s *ServerQUIC) serveQUICConnAsync( // serveQUICConn handles a new QUIC connection. It waits for new streams and // passes them to serveQUICStream. -func (s *ServerQUIC) serveQUICConn(ctx context.Context, conn quic.Connection) (err error) { +func (s *ServerQUIC) serveQUICConn(ctx context.Context, conn *quic.Conn) (err error) { streamWg := &sync.WaitGroup{} defer func() { // Wait until all streams are processed. @@ -347,7 +347,7 @@ func (s *ServerQUIC) serveQUICConn(ctx context.Context, conn quic.Connection) (e // design specifies that for each subsequent query on a QUIC connection // the client MUST select the next available client-initiated // bidirectional stream. - var stream quic.Stream + var stream *quic.Stream acceptCtx, cancel := context.WithDeadline(ctx, time.Now().Add(maxQUICIdleTimeout)) // For some reason AcceptStream below seems to get stuck even when @@ -403,8 +403,8 @@ func (s *ServerQUIC) serveQUICConn(ctx context.Context, conn quic.Connection) (e // be decremented. func (s *ServerQUIC) serveQUICStreamAsync( ctx context.Context, - stream quic.Stream, - conn quic.Connection, + stream *quic.Stream, + conn *quic.Conn, wg *sync.WaitGroup, ) { defer wg.Done() @@ -421,8 +421,8 @@ func (s *ServerQUIC) serveQUICStreamAsync( // and writes back the responses. func (s *ServerQUIC) serveQUICStream( ctx context.Context, - stream quic.Stream, - conn quic.Connection, + stream *quic.Stream, + conn *quic.Conn, ) (err error) { // The server MUST send the response on the same stream, and MUST indicate // through the STREAM FIN mechanism that no further data will be sent on @@ -486,7 +486,7 @@ func (s *ServerQUIC) serveQUICStream( // if anything went wrong. func (s *ServerQUIC) readQUICMsg( ctx context.Context, - stream quic.Stream, + stream *quic.Stream, ) (m *dns.Msg, err error) { bufPtr := s.reqPool.Get() defer s.reqPool.Put(bufPtr) @@ -607,7 +607,7 @@ func isExpectedQUICErr(err error) (ok bool) { } // Catch quic-go's IdleTimeoutError. This error is returned from - // quic.Connection.AcceptStream calls and this is an expected outcome, + // *quic.Conn.AcceptStream calls and this is an expected outcome, // happens all the time with different QUIC clients. var qErr *quic.IdleTimeoutError if errors.As(err, &qErr) { @@ -690,7 +690,7 @@ func validQUICMsg(req *dns.Msg) (ok bool) { // code and logs if it fails to close the connection. func (s *ServerQUIC) closeQUICConn( ctx context.Context, - conn quic.Connection, + conn *quic.Conn, code quic.ApplicationErrorCode, ) { err := conn.CloseWithError(code, "") @@ -725,6 +725,7 @@ func newServerQUICConfig( // quicAddrValidator is a helper struct that holds a small LRU cache of // addresses for which we do not require address validation. type quicAddrValidator struct { + // TODO(d.kolyshev): Use [agdcache.Default]. cache gcache.Cache metrics MetricsListener ttl time.Duration diff --git a/internal/dnsserver/serverquic_test.go b/internal/dnsserver/serverquic_test.go index e04e4ce..49c6ca2 100644 --- a/internal/dnsserver/serverquic_test.go +++ b/internal/dnsserver/serverquic_test.go @@ -86,7 +86,7 @@ func TestServerQUIC_integration_ENDS0Padding(t *testing.T) { conn, err := quic.DialAddr(context.Background(), addr.String(), tlsConfig, nil) require.NoError(t, err) - defer func(conn quic.Connection, code quic.ApplicationErrorCode, s string) { + defer func(conn *quic.Conn, code quic.ApplicationErrorCode, s string) { _ = conn.CloseWithError(code, s) }(conn, 0, "") @@ -196,7 +196,7 @@ func testQUICExchange( return conn.CloseWithError(0, "") }) - defer func(conn quic.Connection, code quic.ApplicationErrorCode, s string) { + defer func(conn *quic.Conn, code quic.ApplicationErrorCode, s string) { _ = conn.CloseWithError(code, s) }(conn, 0, "") @@ -210,7 +210,7 @@ func testQUICExchange( // sendQUICMessage is a test helper that sends a test QUIC message. func sendQUICMessage( - conn quic.Connection, + conn *quic.Conn, req *dns.Msg, ) (resp *dns.Msg, err error) { stream, err := conn.OpenStreamSync(context.Background()) @@ -266,7 +266,7 @@ func sendQUICMessage( // test's t. func requireSendQUICMessage( t testing.TB, - conn quic.Connection, + conn *quic.Conn, req *dns.Msg, ) (resp *dns.Msg) { t.Helper() @@ -279,7 +279,7 @@ func requireSendQUICMessage( // writeQUICStream writes buf to the specified QUIC stream in chunks. This way // it is possible to test how the server deals with chunked DNS messages. -func writeQUICStream(buf []byte, stream quic.Stream) (err error) { +func writeQUICStream(buf []byte, stream *quic.Stream) (err error) { // Send the DNS query to the stream and split it into chunks of up // to 400 bytes. 400 is an arbitrary chosen value. chunkSize := 400 diff --git a/internal/dnssvc/context.go b/internal/dnssvc/context.go index 05f9171..a8ca6f8 100644 --- a/internal/dnssvc/context.go +++ b/internal/dnssvc/context.go @@ -26,7 +26,7 @@ var _ contextutil.Constructor = (*contextConstructor)(nil) // New implements the [contextutil.Constructor] interface for // *contextConstructor. It returns a context with a new [agd.RequestID] as well -// as its timeout and the corresponding cancelation function. +// as its timeout and the corresponding cancellation function. func (c *contextConstructor) New( parent context.Context, ) (ctx context.Context, cancel context.CancelFunc) { diff --git a/internal/dnssvc/dnssvc_test.go b/internal/dnssvc/dnssvc_test.go index 687b34b..41ea323 100644 --- a/internal/dnssvc/dnssvc_test.go +++ b/internal/dnssvc/dnssvc_test.go @@ -80,17 +80,21 @@ func (l *testListener) LocalUDPAddr() (addr net.Addr) { } // newTestListener returns a *testListener all of methods of which panic with -// a "not implemented" message. +// an unexpected call message. func newTestListener() (tl *testListener) { return &testListener{ - onName: func() (_ string) { panic("not implemented") }, - onProto: func() (_ dnsserver.Protocol) { panic("not implemented") }, - onNetwork: func() (_ dnsserver.Network) { panic("not implemented") }, - onAddr: func() (_ string) { panic("not implemented") }, - onStart: func(_ context.Context) (err error) { panic("not implemented") }, - onShutdown: func(_ context.Context) (err error) { panic("not implemented") }, - onLocalUDPAddr: func() (_ net.Addr) { panic("not implemented") }, - onLocalTCPAddr: func() (_ net.Addr) { panic("not implemented") }, + onName: func() (name string) { panic(testutil.UnexpectedCall()) }, + onProto: func() (proto dnsserver.Protocol) { panic(testutil.UnexpectedCall()) }, + onNetwork: func() (n dnsserver.Network) { panic(testutil.UnexpectedCall()) }, + onAddr: func() (addr string) { panic(testutil.UnexpectedCall()) }, + onStart: func(ctx context.Context) (err error) { + panic(testutil.UnexpectedCall(ctx)) + }, + onShutdown: func(ctx context.Context) (err error) { + panic(testutil.UnexpectedCall(ctx)) + }, + onLocalUDPAddr: func() (addr net.Addr) { panic(testutil.UnexpectedCall()) }, + onLocalTCPAddr: func() (addr net.Addr) { panic(testutil.UnexpectedCall()) }, } } diff --git a/internal/dnssvc/handler.go b/internal/dnssvc/handler.go index 8c45634..d52ad98 100644 --- a/internal/dnssvc/handler.go +++ b/internal/dnssvc/handler.go @@ -17,6 +17,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/ecscache" "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/logutil/slogutil" + "github.com/AdguardTeam/golibs/timeutil" // TODO(e.burkov): Move registering of the metrics to another package to // avoid dependency on the metrics package. @@ -123,6 +124,7 @@ func wrapPreUpstreamMw( cacheMw := ecscache.NewMiddleware(&ecscache.MiddlewareConfig{ Metrics: mtrc, + Clock: timeutil.SystemClock{}, Cloner: c.Cloner, Logger: c.BaseLogger.With(slogutil.KeyPrefix, "ecscache"), CacheManager: c.CacheManager, diff --git a/internal/dnssvc/handler_test.go b/internal/dnssvc/handler_test.go index 2594ab8..0f024a0 100644 --- a/internal/dnssvc/handler_test.go +++ b/internal/dnssvc/handler_test.go @@ -26,36 +26,38 @@ 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") }, + OnIsBlockedHost: func(host string, qt uint16) (blocked bool) { + panic(testutil.UnexpectedCall(host, qt)) + }, + OnIsBlockedIP: func(ip netip.Addr) (blocked bool) { panic(testutil.UnexpectedCall(ip)) }, } billStat := &agdtest.BillStatRecorder{ OnRecord: func( - _ context.Context, - _ agd.DeviceID, - _ geoip.Country, - _ geoip.ASN, - _ time.Time, - _ agd.Protocol, + ctx context.Context, + id agd.DeviceID, + ctry geoip.Country, + asn geoip.ASN, + start time.Time, + proto agd.Protocol, ) { - panic("not implemented") + panic(testutil.UnexpectedCall(ctx, id, ctry, asn, start, proto)) }, } dnsCk := &agdtest.DNSCheck{ OnCheck: func( - _ context.Context, - _ *dns.Msg, - _ *agd.RequestInfo, + ctx context.Context, + req *dns.Msg, + ri *agd.RequestInfo, ) (resp *dns.Msg, err error) { - panic("not implemented") + panic(testutil.UnexpectedCall(ctx, req, ri)) }, } dnsDB := &agdtest.DNSDB{ - OnRecord: func(_ context.Context, _ *dns.Msg, _ *agd.RequestInfo) { - panic("not implemented") + OnRecord: func(ctx context.Context, resp *dns.Msg, ri *agd.RequestInfo) { + panic(testutil.UnexpectedCall(ctx, resp, ri)) }, } @@ -76,30 +78,30 @@ func TestNewHandlers(t *testing.T) { } fltStrg := &agdtest.FilterStorage{ - OnForConfig: func(_ context.Context, _ filter.Config) (f filter.Interface) { - panic("not implemented") + OnForConfig: func(ctx context.Context, c filter.Config) (f filter.Interface) { + panic(testutil.UnexpectedCall(ctx, c)) }, - OnHasListID: func(_ filter.ID) (ok bool) { panic("not implemented") }, + OnHasListID: func(id filter.ID) (ok bool) { panic(testutil.UnexpectedCall(id)) }, } hashMatcher := &agdtest.HashMatcher{ OnMatchByPrefix: func( - _ context.Context, - _ string, + ctx context.Context, + host string, ) (hashes []string, matched bool, err error) { - panic("not implemented") + panic(testutil.UnexpectedCall(ctx, host)) }, } queryLog := &agdtest.QueryLog{ - OnWrite: func(_ context.Context, _ *querylog.Entry) (err error) { - panic("not implemented") + OnWrite: func(ctx context.Context, e *querylog.Entry) (err error) { + panic(testutil.UnexpectedCall(ctx, e)) }, } ruleStat := &agdtest.RuleStat{ - OnCollect: func(_ context.Context, _ filter.ID, _ filter.RuleText) { - panic("not implemented") + OnCollect: func(ctx context.Context, id filter.ID, text filter.RuleText) { + panic(testutil.UnexpectedCall(ctx, id, text)) }, } diff --git a/internal/dnssvc/integration_test.go b/internal/dnssvc/integration_test.go index 9e6b2bd..91f8e43 100644 --- a/internal/dnssvc/integration_test.go +++ b/internal/dnssvc/integration_test.go @@ -136,7 +136,7 @@ func newTestService( OnForConfig: func(_ context.Context, _ filter.Config) (f filter.Interface) { return flt }, - OnHasListID: func(_ filter.ID) (ok bool) { panic("not implemented") }, + OnHasListID: func(id filter.ID) (ok bool) { panic(testutil.UnexpectedCall(id)) }, } var ql querylog.Interface = &agdtest.QueryLog{ @@ -397,8 +397,8 @@ func TestService_Wrap(t *testing.T) { Rule: cnameRule, }, nil }, - OnFilterResponse: func(_ context.Context, _ *filter.Response) (filter.Result, error) { - panic("not implemented") + OnFilterResponse: func(ctx context.Context, resp *filter.Response) (filter.Result, error) { + panic(testutil.UnexpectedCall(ctx, resp)) }, } diff --git a/internal/dnssvc/internal/devicefinder/devicefinder_test.go b/internal/dnssvc/internal/devicefinder/devicefinder_test.go index 365fe00..54716ba 100644 --- a/internal/dnssvc/internal/devicefinder/devicefinder_test.go +++ b/internal/dnssvc/internal/devicefinder/devicefinder_test.go @@ -265,9 +265,9 @@ func assertEqualResult(tb testing.TB, want, got agd.DeviceResult) { } } -// newDefault is is a helper for creating the device finders for tests. c may -// be nil, and all zero-value fields in c are replaced with defaults for tests. -// The default server is [srvDoH]. +// newDefault is a helper for creating device finders for tests. c may be nil, +// and all zero-value fields in c are replaced with defaults for tests. The +// default server is [srvDoH]. func newDefault(tb testing.TB, c *devicefinder.Config) (f *devicefinder.Default) { tb.Helper() @@ -303,12 +303,12 @@ func TestDefault_Find_dnscrypt(t *testing.T) { func BenchmarkDefault(b *testing.B) { profDB := &agdtest.ProfileDB{ OnCreateAutoDevice: func( - _ context.Context, - _ agd.ProfileID, - _ agd.HumanID, - _ agd.DeviceType, + ctx context.Context, + profID agd.ProfileID, + humanID agd.HumanID, + typ agd.DeviceType, ) (p *agd.Profile, d *agd.Device, err error) { - panic("not implemented") + panic(testutil.UnexpectedCall(ctx, profID, humanID, typ)) }, OnProfileByDedicatedIP: func( diff --git a/internal/dnssvc/internal/mainmw/mainmw_test.go b/internal/dnssvc/internal/mainmw/mainmw_test.go index 878cfa7..362a0d7 100644 --- a/internal/dnssvc/internal/mainmw/mainmw_test.go +++ b/internal/dnssvc/internal/mainmw/mainmw_test.go @@ -69,14 +69,14 @@ func TestMiddleware_Wrap(t *testing.T) { var ( billStatNotImp = &agdtest.BillStatRecorder{ OnRecord: func( - _ context.Context, - _ agd.DeviceID, - _ geoip.Country, - _ geoip.ASN, - _ time.Time, - _ agd.Protocol, + ctx context.Context, + id agd.DeviceID, + ctry geoip.Country, + asn geoip.ASN, + start time.Time, + proto agd.Protocol, ) { - panic("not implemented") + panic(testutil.UnexpectedCall(ctx, id, ctry, asn, start, proto)) }, } @@ -114,7 +114,7 @@ func TestMiddleware_Wrap(t *testing.T) { OnForConfig: func(_ context.Context, _ filter.Config) (f filter.Interface) { return flt }, - OnHasListID: func(_ filter.ID) (ok bool) { panic("not implemented") }, + OnHasListID: func(id filter.ID) (ok bool) { panic(testutil.UnexpectedCall(id)) }, } geoIP := agdtest.NewGeoIP() @@ -392,14 +392,14 @@ func TestMiddleware_Wrap_filtering(t *testing.T) { var ( billStatNotImp = &agdtest.BillStatRecorder{ OnRecord: func( - _ context.Context, - _ agd.DeviceID, - _ geoip.Country, - _ geoip.ASN, - _ time.Time, - _ agd.Protocol, + ctx context.Context, + id agd.DeviceID, + ctry geoip.Country, + asn geoip.ASN, + start time.Time, + proto agd.Protocol, ) { - panic("not implemented") + panic(testutil.UnexpectedCall(ctx, id, ctry, asn, start, proto)) }, } @@ -672,7 +672,7 @@ func TestMiddleware_Wrap_filtering(t *testing.T) { OnForConfig: func(_ context.Context, _ filter.Config) (f filter.Interface) { return flt }, - OnHasListID: func(_ filter.ID) (ok bool) { panic("not implemented") }, + OnHasListID: func(id filter.ID) (ok bool) { panic(testutil.UnexpectedCall(id)) }, } q := tc.req.Question[0] diff --git a/internal/ecscache/cache.go b/internal/ecscache/cache.go index ec2a44f..022527f 100644 --- a/internal/ecscache/cache.go +++ b/internal/ecscache/cache.go @@ -2,14 +2,10 @@ package ecscache import ( "context" - "encoding/binary" - "hash/maphash" "net/netip" "time" - "github.com/AdguardTeam/AdGuardDNS/internal/agdcache" "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" - "github.com/AdguardTeam/golibs/mathutil" "github.com/miekg/dns" ) @@ -44,12 +40,12 @@ type cacheRequest struct { // support ECS, isECSDependent is true. cr, cr.req, and cr.subnet must not be // nil. func (mw *Middleware) get( - ctx context.Context, + _ context.Context, req *dns.Msg, cr *cacheRequest, ) (resp *dns.Msg, isECSDependent bool) { - key := mw.toCacheKey(cr, false) - item, ok := mw.itemFromCache(ctx, mw.cache, key, cr) + key := newCacheKey(cr, false) + item, ok := mw.cache.Get(key) if ok { return fromCacheItem(item, mw.cloner, req, cr.reqDO), false } else if cr.isECSDeclined { @@ -57,8 +53,8 @@ func (mw *Middleware) get( } // Try ECS-aware cache. - key = mw.toCacheKey(cr, true) - item, ok = mw.itemFromCache(ctx, mw.ecsCache, key, cr) + key = newCacheKey(cr, true) + item, ok = mw.ecsCache.Get(key) if ok { return fromCacheItem(item, mw.cloner, req, cr.reqDO), true } @@ -66,67 +62,6 @@ func (mw *Middleware) get( return nil, false } -// 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 (mw *Middleware) itemFromCache( - ctx context.Context, - cache agdcache.Interface[uint64, *cacheItem], - key uint64, - cr *cacheRequest, -) (item *cacheItem, ok bool) { - item, ok = cache.Get(key) - if !ok { - return nil, false - } - - // Check for cache key collisions. - if item.host != cr.host { - mw.logger.WarnContext(ctx, "cache collision", "item", item, "host", cr.host) - - return nil, false - } - - return item, true -} - -// hashSeed is the seed used by all hashes to create hash keys. -var hashSeed = maphash.MakeSeed() - -// toCacheKey returns the appropriate cache key for msg. msg must have one -// question record. subnet must not be nil. -func (mw *Middleware) toCacheKey(cr *cacheRequest, respIsECSDependent bool) (key uint64) { - // Use maphash explicitly instead of using a key structure to reduce - // allocations and optimize interface conversion up the stack. - // - // TODO(a.garipov, e.burkov): Consider just using struct as a key. - h := &maphash.Hash{} - h.SetSeed(hashSeed) - - _, _ = h.WriteString(cr.host) - - // Save on allocations by reusing a buffer. - var buf [6]byte - binary.LittleEndian.PutUint16(buf[:2], cr.qType) - binary.LittleEndian.PutUint16(buf[2:4], cr.qClass) - - buf[4] = mathutil.BoolToNumber[byte](cr.reqDO) - - addr := cr.subnet.Addr() - buf[5] = mathutil.BoolToNumber[byte](addr.Is6()) - - _, _ = h.Write(buf[:]) - - if respIsECSDependent { - _, _ = h.Write(addr.AsSlice()) - _ = h.WriteByte(byte(cr.subnet.Bits())) - } else { - _ = h.WriteByte(mathutil.BoolToNumber[byte](cr.isECSDeclined)) - } - - return h.Sum64() -} - // set saves resp to the cache if it's cacheable. If msg cannot be cached, it // is ignored. func (mw *Middleware) set(resp *dns.Msg, cr *cacheRequest, respIsECSDependent bool) { @@ -146,11 +81,55 @@ func (mw *Middleware) set(resp *dns.Msg, cr *cacheRequest, respIsECSDependent bo dnsmsg.SetMinTTL(resp, uint32(exp.Seconds())) } - key := mw.toCacheKey(cr, respIsECSDependent) + key := newCacheKey(cr, respIsECSDependent) + cache.SetWithExpire(key, &cacheItem{ + msg: mw.cloner.Clone(resp), + when: mw.clock.Now(), + }, exp) +} - cachedResp := mw.cloner.Clone(resp) +// cacheKey represents a key used in the cache. +type cacheKey struct { + // host is a non-FQDN version of a cached hostname. + host string - cache.SetWithExpire(key, toCacheItem(cachedResp, cr.host), exp) + // subnet is the network of the country the DNS request came from determined + // with GeoIP. + subnet netip.Prefix + + // qType is the question type of the DNS request. + qType uint16 + + // qClass is the class of the DNS request. + qClass uint16 + + // reqDO is the state of DNSSEC OK bit from the DNS request. + reqDO bool + + // isECSDeclined reflects if the client explicitly restricts using its + // information in EDNS client subnet option as per RFC 7871. + // + // See https://datatracker.ietf.org/doc/html/rfc7871#section-7.1.2. + isECSDeclined bool +} + +// newCacheKey returns the appropriate cache key for msg. msg must have one +// question record. cr must not be nil. +func newCacheKey(cr *cacheRequest, respIsECSDependent bool) (key cacheKey) { + key = cacheKey{ + host: cr.host, + qType: cr.qType, + qClass: cr.qClass, + reqDO: cr.reqDO, + } + + if respIsECSDependent { + key.subnet = cr.subnet + } else { + key.isECSDeclined = cr.isECSDeclined + } + + return key } // cacheItem represents an item that we will store in the cache. @@ -160,19 +139,6 @@ type cacheItem struct { // msg is the cached DNS message. msg *dns.Msg - - // host is the cached normalized hostname for later cache key collision - // checks. - host string -} - -// toCacheItem creates a *cacheItem from a DNS message. -func toCacheItem(resp *dns.Msg, host string) (item *cacheItem) { - return &cacheItem{ - msg: resp, - when: time.Now(), - host: host, - } } // fromCacheItem creates a response from the cached item. item, cloner, and req diff --git a/internal/ecscache/cache_internal_test.go b/internal/ecscache/cache_internal_test.go index 0fac69b..8e8e048 100644 --- a/internal/ecscache/cache_internal_test.go +++ b/internal/ecscache/cache_internal_test.go @@ -6,26 +6,35 @@ import ( "testing" "github.com/AdguardTeam/AdGuardDNS/internal/agdcache" + "github.com/AdguardTeam/AdGuardDNS/internal/agdtest" "github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest" + "github.com/AdguardTeam/golibs/logutil/slogutil" + "github.com/AdguardTeam/golibs/timeutil" "github.com/miekg/dns" "github.com/stretchr/testify/assert" ) -func BenchmarkMiddleware_Get(b *testing.B) { - mw := &Middleware{ - cache: agdcache.NewLRU[uint64, *cacheItem](&agdcache.LRUConfig{ - Count: 10, - }), - ecsCache: agdcache.NewLRU[uint64, *cacheItem](&agdcache.LRUConfig{ - Count: 10, - }), - } +func BenchmarkMiddleware(b *testing.B) { + mw := NewMiddleware(&MiddlewareConfig{ + Metrics: EmptyMetrics{}, + Clock: timeutil.SystemClock{}, + Cloner: agdtest.NewCloner(), + Logger: slogutil.NewDiscardLogger(), + CacheManager: agdcache.EmptyManager{}, + GeoIP: agdtest.NewGeoIP(), + NoECSCount: 100, + ECSCount: 100, + }) const ( host = "benchmark.example" fqdn = host + "." + + defaultTTL uint32 = 3600 ) + reqAddr := netip.MustParseAddr("1.2.3.4") + req := dnsservertest.NewReq(fqdn, dns.TypeA, dns.ClassINET) cr := &cacheRequest{ host: host, @@ -34,6 +43,9 @@ func BenchmarkMiddleware_Get(b *testing.B) { qClass: dns.ClassINET, reqDO: true, } + resp := dnsservertest.NewResp(dns.RcodeSuccess, req, dnsservertest.SectionAnswer{ + dnsservertest.NewA(host, defaultTTL, reqAddr), + }) ctx := context.Background() @@ -41,16 +53,18 @@ func BenchmarkMiddleware_Get(b *testing.B) { b.ReportAllocs() for b.Loop() { + mw.set(resp, cr, true) + msg, _ = mw.get(ctx, req, cr) } - assert.Nil(b, msg) + assert.NotNil(b, msg) // Most recent results: // // goos: darwin - // goarch: amd64 + // goarch: arm64 // pkg: github.com/AdguardTeam/AdGuardDNS/internal/ecscache - // cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz - // BenchmarkMiddleware_Get-12 5855624 195.1 ns/op 16 B/op 2 allocs/op + // cpu: Apple M1 Pro + // BenchmarkMiddleware_Get-8 1647064 726.8 ns/op 568 B/op 12 allocs/op } diff --git a/internal/ecscache/ecsblocklist.go b/internal/ecscache/ecsblocklist.go index ea6595f..4ebc3b8 100644 --- a/internal/ecscache/ecsblocklist.go +++ b/internal/ecscache/ecsblocklist.go @@ -7,103 +7,367 @@ import "github.com/AdguardTeam/golibs/container" // FakeECSFQDNs contains all domains that indicate ECS support, but in fact // don't have one. var FakeECSFQDNs = container.NewMapSet( - "01-cdn.videodb.cloud.", "0272ac85-5199-4024-a555-397c3d825d95.prmutv.co.", "0cf.io.", "0cf17917-395b-4f25-91cc-db3bdd6044b0.prmutv.co.", "1.eu.ntp.huan.tv.", + "1024sj.com.", "103.chtsite.com.", - "11-alarm-mop.meshare.com.", - "11222.cn.", - "11ipip.com.", "12-alarm-mop.meshare.com.", - "123ipip.icu.", "126.com.", "126.net.", "127.net.", "13-alarm-mop.meshare.com.", - "1337.abcvg.info.", - "14-alarm-mop.meshare.com.", + "15969-ipv4v6e.clump.dprodmgd104.aa-rt.sharepoint.com.", "163.com.", "163jiasu.com.", "163yun.com.", + "166.net.", "1688.com.", - "178448.com.", - "189cube.com.", "18ea70d2d9a945cfb97d818ba71817dc.pacloudflare.com.", "1adr.com.", "1ato.com.", "1osb.com.", "2.401402081.west-gcloud.codm.activision.com.", - "2.497948935.codmsg.gcloudcs.com.", "2.realtime.services.box.net.", - "2345.cc.", - "289d106c-df24-4cd9-a9fa-753e928c23ad.prmutv.co.", + "21cn.com.", + "2591j46p8g-3.algolianet.com.", "2acdb9b66bb242618283aadb21ede6c1.pacloudflare.com.", + "2e4b93d1-a8ae-4a89-8885-6109135ac0de.prmutv.co.", "2gis.ru.", "2owo1y.n0qq3z.com.", - "31gamestudio.com.", - "33ipip.top.", + "340.ncsis.gov.", "345ddz.com.", "360safe.com.", "3a6b0682-f3e1-4576-a706-5eb4101b9cc3.prmutv.co.", "3d2fb0bd-52fc-4b75-aaf5-2d436c172540.prmutv.co.", - "3gcj.com.", + "3dmxku.com.", "4.401402081.west-gcloud.codm.activision.com.", - "4.497948935.codmsg.gcloudcs.com.", "4.adsco.re.", "401402081.west-gcloud.codm.activision.com.", - "497948935.codmsg.gcloudcs.com.", - "500audio.com.", - "51jiaoxi.com.", + "4513764478418944-content.customer.pendo.io.", + "4b32bb64ce554875ae3f8836479c89d4.pacloudflare.com.", + "4dmax.space.", + "507b28fb-2ef1-4c34-8bda-ba32030bb199.prmutv.co.", "520.jp.", - "5f876161-9740-4cc8-9b64-4585990b2690.prmutv.co.", - "5k6ph8.qz94.com.", - "5lgx5w5cgs9pyyu8u.ay.delivery.", + "5d79bce7-5d2b-427e-a6c4-b89b6c7bf048.prmutv.co.", "6.401402081.west-gcloud.codm.activision.com.", "6093eccf-6734-4877-ac8b-83d6d0e27b46.prmutv.co.", - "64.adsco.re.", - "64ypd.6w9hps.com.", - "65-66.com.", "66yahoo.com.", + "68547f8f-2fd8-4ff3-9b63-51e86e2edee8.prmutv.co.", "7c7b02d4bc3d48dd81a7c7738d4de1ab.pacloudflare.com.", - "88a66e5c-8fe8-48af-9c6c-3ec3f4983aad.prmutv.co.", "8x8.com.cdn.cloudflare.net.", + "920.ncsis.gov.", "998law.com.", - "9game.cn.", - "a-ap.1rx.io.", + "9pumbooc7hqedhjmeavbpmp5ik9u0ngr-data.customer.pendo.io.", "a-api.anthropic.com.", "a-cdn.anthropic.com.", "a-m-p.xyz.", + "a-yz.com.", "a.appbaqend.com.", "a.audrte.com.", + "a.bringads.ru.", + "a.getepic.com.", + "a.myisolved.com.", "a.nel.cloudflare.com.", "a.nitropay.com.", - "a.quora.com.", "a.simplemdm.com.", - "a.utraff.com.", - "a.yximgs.com.cdn.dnsv1.com.", - "a40.usablenet.com.", + "a048.casalemedia.com.", + "a087.casalemedia.com.", + "a1477.casalemedia.com.", + "a1478.casalemedia.com.", + "a1479.casalemedia.com.", + "a1518.casalemedia.com.", + "a1523.casalemedia.com.", + "a1527.casalemedia.com.", + "a1537.casalemedia.com.", + "a1544.casalemedia.com.", + "a1549.casalemedia.com.", + "a1556.casalemedia.com.", + "a1577.casalemedia.com.", + "a1623.casalemedia.com.", + "a1635.casalemedia.com.", + "a1637.casalemedia.com.", + "a1667.casalemedia.com.", + "a1678.casalemedia.com.", + "a1681.casalemedia.com.", + "a1684.casalemedia.com.", + "a1687.casalemedia.com.", + "a1688.casalemedia.com.", + "a1689.casalemedia.com.", + "a1690.casalemedia.com.", + "a1691.casalemedia.com.", + "a1692.casalemedia.com.", + "a22711502979.cdn.optimizely.com.", + "a252.casalemedia.com.", + "a25758810713.cdn.optimizely.com.", + "a2806.casalemedia.com.", + "a2811.casalemedia.com.", + "a2828.casalemedia.com.", + "a2836.casalemedia.com.", + "a2839.casalemedia.com.", + "a2849.casalemedia.com.", + "a2850.casalemedia.com.", + "a2854.casalemedia.com.", + "a2859.casalemedia.com.", + "a2869.casalemedia.com.", + "a2884.casalemedia.com.", + "a2898.casalemedia.com.", + "a2900.casalemedia.com.", + "a2901.casalemedia.com.", + "a2916.casalemedia.com.", + "a2928.casalemedia.com.", + "a2935.casalemedia.com.", + "a2941.casalemedia.com.", + "a2944.casalemedia.com.", + "a2a5c7f9-3fa0-4182-889a-15aa61acf59b.prmutv.co.", + "a364.casalemedia.com.", + "a366.casalemedia.com.", + "a375.casalemedia.com.", + "a389.casalemedia.com.", + "a392.casalemedia.com.", + "a410.casalemedia.com.", + "a416.casalemedia.com.", + "a4621041136.cdn.optimizely.com.", + "a5551.casalemedia.com.", + "a5557.casalemedia.com.", + "a5558.casalemedia.com.", + "a5560.casalemedia.com.", + "a5561.casalemedia.com.", + "a5562.casalemedia.com.", + "a5563.casalemedia.com.", + "a5564.casalemedia.com.", + "a5565.casalemedia.com.", + "a5567.casalemedia.com.", + "a5568.casalemedia.com.", + "a5569.casalemedia.com.", + "a5570.casalemedia.com.", + "a5572.casalemedia.com.", + "a5573.casalemedia.com.", + "a5574.casalemedia.com.", + "a5575.casalemedia.com.", + "a5576.casalemedia.com.", + "a5578.casalemedia.com.", + "a5579.casalemedia.com.", + "a5581.casalemedia.com.", + "a5582.casalemedia.com.", + "a5584.casalemedia.com.", + "a5585.casalemedia.com.", + "a5586.casalemedia.com.", + "a5587.casalemedia.com.", + "a5588.casalemedia.com.", + "a5589.casalemedia.com.", + "a5590.casalemedia.com.", + "a5591.casalemedia.com.", + "a5593.casalemedia.com.", + "a5594.casalemedia.com.", + "a5595.casalemedia.com.", + "a5596.casalemedia.com.", + "a5599.casalemedia.com.", + "a55a84b3-9632-4869-b625-3d8ef43ed18d.prmutv.co.", + "a5600.casalemedia.com.", + "a5601.casalemedia.com.", + "a5602.casalemedia.com.", + "a5603.casalemedia.com.", + "a5604.casalemedia.com.", + "a5605.casalemedia.com.", + "a5606.casalemedia.com.", + "a5607.casalemedia.com.", + "a5608.casalemedia.com.", + "a5609.casalemedia.com.", + "a5610.casalemedia.com.", + "a5611.casalemedia.com.", + "a5612.casalemedia.com.", + "a5613.casalemedia.com.", + "a5614.casalemedia.com.", + "a5615.casalemedia.com.", + "a5616.casalemedia.com.", + "a5617.casalemedia.com.", + "a5618.casalemedia.com.", + "a5619.casalemedia.com.", + "a5620.casalemedia.com.", + "a5621.casalemedia.com.", + "a5622.casalemedia.com.", + "a5623.casalemedia.com.", + "a5624.casalemedia.com.", + "a5626.casalemedia.com.", + "a5627.casalemedia.com.", + "a5628.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.", + "a5671.casalemedia.com.", + "a5672.casalemedia.com.", + "a5673.casalemedia.com.", + "a5675.casalemedia.com.", + "a5676.casalemedia.com.", + "a5677.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.", + "a5688.casalemedia.com.", + "a5689.casalemedia.com.", + "a5690.casalemedia.com.", + "a5692.casalemedia.com.", + "a5693.casalemedia.com.", + "a5694.casalemedia.com.", + "a5695.casalemedia.com.", + "a5696.casalemedia.com.", + "a5697.casalemedia.com.", + "a5698.casalemedia.com.", + "a5699.casalemedia.com.", + "a5700.casalemedia.com.", + "a5701.casalemedia.com.", + "a5702.casalemedia.com.", + "a5704.casalemedia.com.", + "a5705.casalemedia.com.", + "a5706.casalemedia.com.", + "a5707.casalemedia.com.", + "a5708.casalemedia.com.", + "a5709.casalemedia.com.", + "a5710.casalemedia.com.", + "a572.casalemedia.com.", + "a5731.casalemedia.com.", + "a5732.casalemedia.com.", + "a5733.casalemedia.com.", + "a5735.casalemedia.com.", + "a5736.casalemedia.com.", + "a5737.casalemedia.com.", + "a5738.casalemedia.com.", + "a5739.casalemedia.com.", + "a5740.casalemedia.com.", + "a5741.casalemedia.com.", + "a5742.casalemedia.com.", + "a5743.casalemedia.com.", + "a5744.casalemedia.com.", + "a5745.casalemedia.com.", + "a5746.casalemedia.com.", + "a5747.casalemedia.com.", + "a5748.casalemedia.com.", + "a5749.casalemedia.com.", + "a5750.casalemedia.com.", + "a5751.casalemedia.com.", + "a5752.casalemedia.com.", + "a5753.casalemedia.com.", + "a5754.casalemedia.com.", + "a5755.casalemedia.com.", + "a5756.casalemedia.com.", + "a5758.casalemedia.com.", + "a5759.casalemedia.com.", + "a576.casalemedia.com.", + "a5763.casalemedia.com.", + "a5764.casalemedia.com.", + "a5765.casalemedia.com.", + "a5766.casalemedia.com.", + "a5767.casalemedia.com.", + "a5768.casalemedia.com.", + "a5769.casalemedia.com.", + "a577.casalemedia.com.", + "a5770.casalemedia.com.", + "a5771.casalemedia.com.", + "a5772.casalemedia.com.", + "a5773.casalemedia.com.", + "a5774.casalemedia.com.", + "a5775.casalemedia.com.", + "a5776.casalemedia.com.", + "a5777.casalemedia.com.", + "a5778.casalemedia.com.", + "a5779.casalemedia.com.", + "a578.casalemedia.com.", + "a5780.casalemedia.com.", + "a5781.casalemedia.com.", + "a5782.casalemedia.com.", + "a5783.casalemedia.com.", + "a5785.casalemedia.com.", + "a5786.casalemedia.com.", + "a5787.casalemedia.com.", + "a5788.casalemedia.com.", + "a5789.casalemedia.com.", + "a5790.casalemedia.com.", + "a5791.casalemedia.com.", + "a5792.casalemedia.com.", + "a5793.casalemedia.com.", + "a5794.casalemedia.com.", + "a5795.casalemedia.com.", + "a5796.casalemedia.com.", + "a5797.casalemedia.com.", + "a5798.casalemedia.com.", + "a5799.casalemedia.com.", + "a5800.casalemedia.com.", + "a5801.casalemedia.com.", + "a5802.casalemedia.com.", + "a5803.casalemedia.com.", + "a5804.casalemedia.com.", + "a5805.casalemedia.com.", + "a5806.casalemedia.com.", + "a5809.casalemedia.com.", + "a5810.casalemedia.com.", + "a5811.casalemedia.com.", + "a5813.casalemedia.com.", + "a5814.casalemedia.com.", + "a5815.casalemedia.com.", + "a5856.casalemedia.com.", + "a5857.casalemedia.com.", + "a5858.casalemedia.com.", + "a5859.casalemedia.com.", + "a5860.casalemedia.com.", + "a5861.casalemedia.com.", + "a5862.casalemedia.com.", + "a5863.casalemedia.com.", + "a5864.casalemedia.com.", + "a5865.casalemedia.com.", + "a5866.casalemedia.com.", + "a5867.casalemedia.com.", + "a5868.casalemedia.com.", + "a5869.casalemedia.com.", + "a5870.casalemedia.com.", + "a5871.casalemedia.com.", + "a5872.casalemedia.com.", + "a5873.casalemedia.com.", + "a5874.casalemedia.com.", + "a5875.casalemedia.com.", + "a5876.casalemedia.com.", + "a5877.casalemedia.com.", + "a5878.casalemedia.com.", + "a5880.casalemedia.com.", + "a5881.casalemedia.com.", + "a5882.casalemedia.com.", + "a5883.casalemedia.com.", + "a5884.casalemedia.com.", + "a5885.casalemedia.com.", + "a5tfzk.doordash.com.", "aa.online-metrix.net.", "aa.quantummetric.com.", - "aaa.a-mo.net.", "ab.qq.com.", "aba2c424-419a-4d03-9aed-2dca8a7139e4.prmutv.co.", - "abcvg.info.", - "abeacdataonrt-stsdk.vivo.com.cn.", "abroad.apilocate.amap.com.", "abtest-aws-us-east-01.saas.sensorsdata.com.", "accela.com.", "accentuate.io.", - "accenture.com.", "access.mp.lura.live.", - "account-devices-gdo.myq-cloud.com.", "account.box.com.", + "account.cpanel.net.", + "account.meraki.com.", + "account.premierleague.com.", "accountapi.agoda.com.", "accounts.pointclickcare.com.", "accounts.shopify.com.", + "accounts.superhuman.com.", + "accurint.com.", "acdn.tinkoff.ru.", - "acehardware.groupbycloud.com.", "acfun.cn.", "acgvideo.com.", "acme-v02.api.letsencrypt.org.", @@ -121,118 +385,121 @@ var FakeECSFQDNs = container.NewMapSet( "acr13.prd.dc13.adaptiva.cloud.", "acr14.prd.dc14.adaptiva.cloud.", "acrobits.cz.", - "actualized.org.", + "activitymonitor-westus.classroom.cloud.", "ad-host-backup-america.oss-us-west-1.aliyuncs.com.", "ad-host-backup-asia.oss-ap-southeast-1.aliyuncs.com.", "ad-host-backup-europe.oss-eu-central-1.aliyuncs.com.", - "ad.nvdvr.cn.", "adapex.io.", "adaptiva.cloud.", "adash.man.aliyuncs.com.", "adblock.telemetry.getadblock.com.", "adder.feeder.co.", + "additionfi.com.", + "adexchangeclear.com.", + "adguard.com.", "adiam.tech.", "aditude.io.", - "adlog.vivo.com.cn.", "adm.wsms.haplat.net.", "admbk.wsms.haplat.net.", - "admin.cloud.microsoft.", "admin.shopify.com.", "admixer.com.", + "adopt.jaggaer.com.", "ads.playstream.media.", + "ads.themoneytizer.com.", "adsco.re.", "adsinteractive.com.", - "adstag0102.xyz.", "adstatistics.av380.net.", - "adsukses.online.", "adtarget.market.", + "adtech.ink.", + "adtidy.org.", "advantage.purpleguys.com.", "advantage2.purpleguys.com.", "advertise.cloudlinks.cn.", "adview.com.", - "adx-event-service.trackeradx.com.", - "adx-in.ads.heytapmobile.com.", - "adx-sg-req.mosspf.net.", "adxpremium.services.", - "aerocomminc.com.", + "aeries.com.", "afd-lnkd.www.linkedin.com.", - "affpa.top.", "afss.zhiqinyun.cn.", "ag.dns-finder.com.", + "agelesslivingny.com.", "agent-healthcheck.defensx.com.", "agent-logos.storage.googleapis.com.", + "agent.marketingcloudfx.com.", "agentio.com.", "agents.fxpasu01.manage.microsoft.us.", + "ai-assistant.foxit.com.", "ai-voice.cloudbirds.cn.", + "ai.finalsite.com.", "aicdn.com.", "aid.send.microad.jp.", "aidata.io.", + "aidcgroup.net.", + "air-quality-api.open-meteo.com.", "airasia.com.", - "aisee.tv.", + "airtory.com.", "ajcloud.net.", + "akrdinfo.cn.", "akulaku.com.", "alarm.wsms.haplat.net.", "alarmbk.wsms.haplat.net.", + "alb.mobile.devnet.helium.wtf.", "albany.remotepc.com.", "album-sg01a.ocloud.heytapmobi.com.", - "alburychurches.org.", "alfasense.com.", - "ali-alarm-us.oss-us-east-1.aliyuncs.com.", "alibaba-inc.com.", "alibabachengdun.com.", "alibabacorp.com.", "alibabausercontent.com.", - "alicloudapi.com.", "alikunlun.com.", "alikunlun.net.", "alive1.cloudbirds.cn.", "alive2.cloudbirds.cn.", "alive3.cloudbirds.cn.", "aliyuncs.com.", - "allelcoelec.jp.", - "allkarupspc.com.", + "aloyoga.com.", "alpha1-ap-public.val.qq.com.", + "alpha1-nj-gp4-mix2.val.qq.com.", "ambari.dvr163.com.", "amdcopen.m.taobao.com.", - "amdcopen.m.taobao.com.gds.alibabadns.com.", - "aminer.cn.", + "americanchemicalsociety-my.sharepoint.com.", + "ameritas-my.sharepoint.com.", + "amp-static.cbsnews.com.", "amp.permutive.com.", "amplitudelab.usemotion.com.", "ams-api.video.intl.xiaomi.com.", - "ams-l7-apm.slb.xiaomi.com.", + "ams-howl.api.intl.miui.com.", "ams2-cdn.2gis.com.", "amsterdam.remotepc.com.", - "amuseddie.com.", "an3762.ci.managedwhitelisting.com.", + "analytics-2.athome.com.", "analytics-debugger.com.", "analytics-ingress-global.bitmovin.com.", "analytics.languagetoolplus.com.", "analytics.pdf24.org.", "analytics.qumucloud.com.", + "analytics.talbots.com.", "android.crashsight.wetest.net.", - "anfensi.com.", "ankara.remotepc.com.", "anlian.co.", "announce.torrentsmd.com.", - "announcekit.co.", + "announcekit.app.", "anthropic.com.", "antstream.com.", "aocde.com.", - "aon.com.", - "api-analytics-us3.zepp.com.", - "api-app-slow.bitkeep.fun.", + "ap.sd-rtn.com.", "api-asyncgw-gcc-teams.usgovtrafficmanager.net.", "api-auth.flysleep.cn.", + "api-auth.zztfly.com.", "api-decider.vsco.co.", "api-eu1.hubspot.com.", "api-glb-aapne1a.smoot.apple.com.", "api-glb-aapne1c.smoot.apple.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-asae1b.smoot.apple.com.", + "api-glb-aeuw1b.smoot.apple.com.", "api-glb-ause1a.smoot.apple.com.", "api-glb-ause1b.smoot.apple.com.", "api-glb-ause1c.smoot.apple.com.", @@ -242,16 +509,13 @@ var FakeECSFQDNs = container.NewMapSet( "api-glb-ausw2b.smoot.apple.com.", "api-glb-ausw2c.smoot.apple.com.", "api-mayi.django.t.taobao.com.", - "api-mifit-cn3.zepp.com.", "api-mifit-us3.zepp.com.", - "api-php2-ovh.keepsolid.com.", - "api-php4-ovh.keepsolid.com.", - "api-php9-ovh.keepsolid.com.", "api-player.musicstylingonline.com.", "api-preview.luckyorange.com.", "api-private.atlassian.com.", + "api-prod-2.optisigns.com.", + "api-proxy.conveythis.com.", "api-safari-aapse1c.smoot.apple.com.", - "api-safari-aeun1a.smoot.apple.com.", "api-safari-aeun1b.smoot.apple.com.", "api-safari-aeus2a.smoot.apple.com.", "api-safari-aeus2b.smoot.apple.com.", @@ -264,130 +528,134 @@ var FakeECSFQDNs = container.NewMapSet( "api-safari-ause2c.smoot.apple.com.", "api-safari-ausw2b.smoot.apple.com.", "api-safari-ausw2c.smoot.apple.com.", + "api-service.shein.com.cdn.cloudflare.net.", "api-sh.django.t.taobao.com.gds.alibabadns.com.", - "api-stream.twitter.com.", + "api-spotlight-ause1a.smoot.apple.com.", + "api-spotlight-ause1b.smoot.apple.com.", + "api-spotlight-ause1c.smoot.apple.com.", + "api-spotlight-ause2a.smoot.apple.com.", + "api-spotlight-ause2b.smoot.apple.com.", + "api-spotlight-ause2c.smoot.apple.com.", + "api-us.aosulife.com.", + "api-us.mida.so.", "api-user.dalyfeds.com.", - "api.2gis.ru.", + "api-westus.classroom.cloud.", "api.56myu5u3v.com.", + "api.adguard.org.", + "api.agent.dcca.dell.com.", "api.al-array.com.", "api.ams.gcc.teams.microsoft.com.", + "api.anthropic.com.", "api.atlassian.com.", "api.auth.2gis.com.", "api.bobble.ai.", "api.box.com.", "api.btloader.com.", "api.bugreport.huorong.cn.", - "api.c1al9.com.", "api.cdo.oppomobile.com.", "api.cloud.tenda.com.cn.", "api.config-security.com.", + "api.crazygames.com.", "api.crobox.com.", "api.crush.163.com.", - "api.dashboard.3dos.io.", + "api.cyberghostvpn.com.", "api.dealersocket.com.", - "api.dewmobile.net.", + "api.f6kyf.com.", "api.facemojikeyboard.com.", - "api.fouanalytics.com.", + "api.funneljourney.io.", "api.gelighting.com.", "api.glanceapis.com.", "api.gleap.io.", "api.gravitec.media.", - "api.hcaptcha.com.", - "api.hive.blog.", "api.internal.temp-mail.io.", "api.ipgeolocation.io.", - "api.k.163.com.", - "api.kinogram.best.", + "api.kingdata.ksyun.com.", "api.letsplaywell.com.", "api.lightboxcdn.com.", - "api.mida.so.", - "api.mintkeyboard.com.", + "api.linkr.com.", + "api.mavenoid.com.", "api.moyoung.com.", "api.nkryu17dc.com.", "api.onedrive.com.", "api.open-meteo.com.", + "api.owhealth.com.cdn.cloudflare.net.", "api.permutive.app.", "api.permutive.com.", - "api.pgf-thek63.com.", + "api.pixelpioneerss.com.", + "api.poki.com.", "api.polaris.al-array.com.", "api.popin.cc.", "api.production.gstcpx.site.", + "api.queryly.com.", "api.ringcentral.biz.", "api.rollbar.com.", - "api.sbz.vn.", "api.sdk.cqsjd.xyz.", - "api.smartdeploy.com.", "api.snapchat.com.", - "api.sound-machine.com.", - "api.stiven-king.com.", + "api.tamosplayer.com.", "api.tracking.al-array.com.", "api.transitapp.com.", - "api.ttwifi.net.", + "api.transmitsecurity.io.cdn.cloudflare.net.", "api.unity.com.", "api.us.xiaoyi.com.", - "api.userlike.com.", + "api.usercentrics.eu.", "api.vieon.vn.", "api.vsco.co.", "api.x1skf.com.", - "api.xiaoyi.com.", - "api.yosmart.com.", "api.zmcyu9ypy.com.", "api000.backblazeb2.com.", "api001.backblazeb2.com.", - "api002.backblazeb2.com.", "apiblink.ru.", "apiisgp.ezvizlife.com.", "apiisgp.hik-connect.com.", "apis.live.net.", - "apk.v-mate.mobi.", - "apm-api.mihoyo.com.", - "apollo.archive.org.", + "apixeu.b2c.com.", "app-atl.five9.com.", "app-scl.five9.com.", - "app.anaplan.com.", "app.box.com.", "app.cybba.solutions.", "app.ee-share.com.", + "app.eu.securiti.ai.", "app.firmguard.com.", - "app.iherb.com.", "app.pdq.com.", "app.pendo.qgenda.com.", "app.pendo.uptodate.com.", "app.sealsubscriptions.com.", - "app.snapchat.com.", + "app.talkingpts.org.", + "app.usercentrics.eu.", "app.zoom.us.", "appdump.nie.easebar.com.", - "appleimap.163.com.", + "appliedcloudservices.com.", "applog.matrix.easebar.com.", "appocean.media.", - "appprd.american-time.com.", + "apps-ds.shopifynetwork.com.", "apps.samsung.com.", + "apps.shopify.com.", "apps.wix.com.", "appstoreonrt-stsdk.vivo.com.cn.", + "appstoreort-stsdk.vivo.com.cn.", "aralego.net.", "arbitrum.io.", "arenabg.com.", - "arenahll-h5.uatext66ap.com.", + "aristotleinsight.com.", + "arkdhs-my.sharepoint.com.", + "arm-frontdoor-edge-geo.trafficmanager.net.", "arm1.maxhost.io.", - "armexdef.uk.", + "armexfort.uk.", "arms-retcode-sg.aliyuncs.com.", - "articles.ipt.pw.", - "artsy.net.", + "article.writeswonder.com.", "as-api.asm.skype.com.", "as-prod.asyncgw.teams.microsoft.com.", "asanalytics.booking.com.", - "asgnotice.bidmatrixdsp.com.", - "asheville.remotepc.com.", + "asc-a.hbwrapper.com.", "asia-browser.vivoglobal.com.", "asia-ex-adlog.vivoglobal.com.", - "asia-rommc-api.vivoglobal.com.", + "asia-minor-appstore.vivoglobal.com.", "asia-timer-appstore.vivoglobal.com.", "asia-timesync.vivoglobal.com.", "asia-upload-appstore.vivoglobal.com.", "asia-usrsys-api.vivoglobal.com.", "asia-wifi.vivoglobal.com.", "asia.remotepc.com.", - "asklib.com.", "asm-api-golocal-geo-am-teams.trafficmanager.net.", "asm-api-golocal-geo-as-teams.trafficmanager.net.", "asm-api-golocal-geo-eu-teams.trafficmanager.net.", @@ -400,106 +668,102 @@ var FakeECSFQDNs = container.NewMapSet( "asset.fwpub1.com.", "asset.fwscripts.com.", "assets.api.stairwell.com.", - "assets.api.useinsider.com.", - "assets.windscribe.com.", - "astemo-am.spectrum.colortokens.com.", + "assets.id.me.", + "assets.ovid.com.", + "assets.secure.checkout.visa.com.", "async-motiondetection-us-1d.oss-us-west-1.aliyuncs.com.", "asyncim.zoom.us.", - "ati.com.", - "atianqi.com.", + "atka.aibansg.com.", "atlanta.remotepc.com.", "atlanta3.remotepc.com.", "atlanta4.remotepc.com.", "atlas.shopifysvc.com.", "atlassian.net.", + "atlassianblog.wpengine.com.", + "atm-broker-ws-1314297-sg.vasdgame.com.", + "atom-tag-acc-real-hwp.gamedistribution.com.", "au-prod.asyncgw.teams.microsoft.com.", "au.ff.avast.sec.miui.com.", "auckland.remotepc.com.", - "audienceye.com.", + "audio.vivintsky.com.", "audiostatlog.cc.easebar.com.", - "audiouser.cc.easebar.com.", "audrte.com.", "australiaeast.api.cognitive.microsoft.com.", "auth-l7.bereal.com.", "auth.atlassian.com.", + "auth.prod.ims.adobejanus.com.", "auto-load-balancer.likr.com.tw.", - "autocounter.idealmedia.io.", - "autohome.com.cn.", - "automate.heart.net.", "automate.itsasap.com.", - "avatar.us.kwcdn.com.", + "avigilon.com.", "aws-aus-e-rdvz.g.nssvc.net.", "aws-bz-s-rdvz.g.nssvc.net.", - "aws-in-w-rdvz.g.nssvc.net.", "aws-us-e-rdvz.g.nssvc.net.", "aws-us-nc-rdvz.g.nssvc.net.", + "ayads.co.", "az-us-e-rdvz.g.nssvc.net.", "az-us-sc-features.netscalergateway.net.", - "az-us-sc-rdvz.g.nssvc.net.", "azchicago.remotepc.com.", "azchicago2.remotepc.com.", + "azure-reg.c.nssvc.net.", "azurgames.com.", - "b1-data.ads.heytapmobi.com.", - "b1-nldc1.zemanta.com.", "b2b.filesyscrm.com.", - "b2b168.com.", "bablosoft.com.", "backend.deepl.com.", "badambiz.com.", "badoo.app.", + "baganintel.com.", "bahrain.remotepc.com.", - "baike.so.com.", "baishan-cloud.net.", - "baixing.com.", "balancer.api.net2fast.com.", + "ballstate-my.sharepoint.com.", "baltimore.remotepc.com.", "bam.nr-data.net.cdn.cloudflare.net.", - "bancomermovil.com.", "bangalore2.remotepc.com.", "bangalore3.remotepc.com.", "bangalore4.remotepc.com.", - "baron.cgee.net.", + "bankozarks.sharepoint.com.", + "bankwithunited-my.sharepoint.com.", "barstoolsports.com.", + "batesvilletechnology.com.", "bbvaexperience.com.", - "bcit.ca.", "bd1cec50-00d1-4ce9-9572-785857419a1e.prmutv.co.", + "bdreporting.com.", "be.nortic.ogtic.gob.do.", "be1213.am.managedwhitelisting.com.", "beacon.qq.com.", "beacons4.gvt2.com.", "beacons5.gvt2.com.", + "becn.sharepoint.com.", "bee.tc.easebar.com.", "beizi.biz.", "belgium.remotepc.com.", "belgrad.remotepc.com.", "bend.remotepc.com.", - "betlive.com.", "bgtfs.transitapp.com.", "bi-tracker-global.rivergame.net.", - "biddr.brealtime.com.", - "bidmatrixdsp.com.", "bidster.net.", "bifrost.vivaldi.com.", "bigdata.talkie-ai.com.", - "biggaboo.com.", "bik.gov.tr.", - "bike100.tw.", "bildanat.com.", - "birdeye.so.", - "birdsshopping.com.", - "biz.everychina.com.", - "bizcommunity.com.", + "bistro-la-nature.com.", + "bitdefender.com.cdn.cloudflare.net.", + "bithumb.com.", + "bitmovin.com.", "bj-td-menta-01-callback.advlion.com.", + "bl.listrakbi.com.", "black-cat.crypto.com.", "blackboard.com.", "blackbox.dropbox-dns.com.", - "bluetracktor.com.", + "blacknut.net.", + "bloomerang.co.", + "bluebuffalo.com.", "bluffdale.remotepc.com.", "bmaus.bumble.com.", "boardgamearena.com.", + "boardgamearena.net.", "bobble.ai.", - "bokep.work.", - "bookmarking.site.", + "bobid-ip.hybrid.ai.", "books-analytics-events.apple.com.", "books-personalization-server.apple.com.", "booksy.com.", @@ -510,30 +774,28 @@ var FakeECSFQDNs = container.NewMapSet( "box.com.", "box.net.", "br.chat.si.riotgames.com.", - "brands-prod.cdn-tinkoff.ru.", "bratislava.remotepc.com.", "brazilsouth.api.cognitive.microsoft.com.", - "breaktime.com.tw.", "breitbart.com.", + "bringads.ru.", "broadstreetads.com.", + "broker-projectd.vasdgame.com.", "broker-ws-prod-cag-sg.vasdgame.com.", - "broker.jagat.io.", - "bt.ktrackers.com.", + "bsclink.cn.", + "bsgslb.cn.", + "bsgslb.com.", "bt.moack.co.kr.", - "bt1.archive.org.", - "btc-europe.com.", "btinstall-artifacts.bittorrent.com.", "btrace.qq.com.", - "bts-la.ucweb.com.", "bucharest.remotepc.com.", "budapest.remotepc.com.", "buenosaires.remotepc.com.", "bugly.qcloud.com.", + "bugly.weplayer.cc.", "bugreport.huorong.cn.", - "builds.crazygames.com.", + "bx01.optimix.cn.", "bytecdn.cn.", "bytedance.net.", - "bzroofn.com.", "c.4dex.io.", "c.4dex.tech.", "c.51y5.net.", @@ -541,30 +803,28 @@ var FakeECSFQDNs = container.NewMapSet( "c.appbaqend.com.", "c.pub.network.", "c1.ttcache.com.", + "c10.patreonusercontent.com.", "c2.ttcache.com.", "c2c.wechat.com.", "c3.ttcache.com.", "c4.ttcache.com.", - "ca-mon-gcp-r001.router.teamviewer.com.", "ca-prod.asyncgw.teams.microsoft.com.", - "ca-tor-anx-r004.router.teamviewer.com.", - "ca-tor-anx-r007.router.teamviewer.com.", - "ca-van-anx-r005.router.teamviewer.com.", + "ca.4dmax.space.", "ca.rogers.rcs.telephony.goog.", "ca000.backblaze.com.", "ca001.backblaze.com.", - "ca002.backblaze.com.", "ca80a1adb12a4fbdac5ffcbc944e9a61.pacloudflare.com.", "cachenetworks.com.", + "caixa.gov.br.", "caldav.163.com.", "california.remotepc.com.", "caltech.edu.", + "campuslabsengage.com.", "camstore.vsco.co.", "canada.remotepc.com.", "canadacentral.api.cognitive.microsoft.com.", "canadaeast.api.cognitive.microsoft.com.", "canberra.remotepc.com.", - "cangura.com.", "cantor-lite-api.vsco.co.", "capetown.remotepc.com.", "capig.stape.id.", @@ -573,33 +833,43 @@ var FakeECSFQDNs = container.NewMapSet( "cardiff.remotepc.com.", "care.novaicare.com.", "cartx.cloud.", + "cas-us8.wfs.cloud.", + "cask.qg2.apps.qualys.com.", + "cask.qg3.apps.qualys.com.", + "cask.qg4.apps.qualys.com.", "cat1.hbwrapper.com.", "cat2.hbwrapper.com.", "cat3.hbwrapper.com.", - "catholichealth.net.", - "cbox.im.", "cbox.ws.", - "cbsipv4.shuzilm.cn.", "cc.easebar.com.", - "cd.elements.video.", + "ccare2.ccr1.com.", + "ccf.ivalua.com.", + "ccs.c.nssvc.net.", + "cdn-audio-gcp-media.getepic.com.", + "cdn-c.skcrtxr.com.", "cdn-cname.pendo.io.", - "cdn-profiles.tunein.com.", - "cdn-st.rutubelist.ru.", + "cdn-gcp-media-drm.getepic.com.", + "cdn-gcp-media.getepic.com.", + "cdn-gop.garenanow.com.", + "cdn-probe-jobs.mrgcdn.ru.", "cdn-us.algoliaradar.com.", - "cdn.adfinity.pro.", + "cdn-video-gcp-media.getepic.com.", "cdn.adjust.com.", "cdn.adtarget.market.", - "cdn.adx.ws.", + "cdn.aimtell.com.cdn.cloudflare.net.", "cdn.ap.bittorrent.com.", "cdn.chargeafter.com.", + "cdn.conveythis.com.", "cdn.deepintent.com.", + "cdn.flightradar24.com.", "cdn.ftd.agency.", - "cdn.fuseplatform.net.cdn.cloudflare.net.", "cdn.groupbycloud.com.", "cdn.hw.gcloudcs.com.", "cdn.inappstory.ru.", "cdn.instapagemetrics.com.", + "cdn.justuno.com.", "cdn.lgrckt-in.com.", + "cdn.minga.io.", "cdn.nmcorp.video.", "cdn.pandora.xiaomi.com.", "cdn.pendo.uptodate.com.", @@ -609,22 +879,20 @@ var FakeECSFQDNs = container.NewMapSet( "cdn.sierrapacificgroup.com.", "cdn.skcrtxr.com.", "cdn.static.linkr.com.", - "cdn.storageimagedisplay.com.", "cdn.t-bank-app.ru.", "cdn.t-bank-app.su.", "cdn.tapdb-dev.com.", - "cdn.tbank.ru.", - "cdn.upcbroadband.com.", + "cdn.us.minga.io.", "cdn.uxfeedback.ru.", - "cdn.viqeo.tv.", - "cdn01.humeysha.com.", + "cdn.viously.com.", "cdn02.humeysha.com.", - "cdn03.humeysha.com.", - "cdn04.humeysha.com.", + "cdn05.humeysha.com.", "cdn1.wixdns.net.", - "cdn3.uxfeedback.ru.", + "cdn11.bigcommerce.com.cdn.cloudflare.net.", + "cdn3.fireworktv.com.", "cdn4.fireworktv.com.", "cdnapps.avada.io.", + "cdnfp.accurint.com.", "cdngslb.com.", "cdntm.hsbc.co.uk.", "cdnwidget.com.", @@ -633,114 +901,124 @@ var FakeECSFQDNs = container.NewMapSet( "cedexis-test.com.", "cedock.com.", "cef.com.br.", - "census.shodan.io.", + "center00.deltaork.com.", + "center01.deltaork.com.", + "center02.deltaork.com.", + "center03.deltaork.com.", + "center04.deltaork.com.", + "center05.deltaork.com.", + "center06.deltaork.com.", + "center07.deltaork.com.", + "center08.deltaork.com.", + "center09.deltaork.com.", + "center10.deltaork.com.", + "center11.deltaork.com.", + "center12.deltaork.com.", + "center13.deltaork.com.", + "center14.deltaork.com.", + "center15.deltaork.com.", + "center16.deltaork.com.", + "center17.deltaork.com.", + "center18.deltaork.com.", + "center19.deltaork.com.", "centralindia.api.cognitive.microsoft.com.", "centralus.api.cognitive.microsoft.com.", "centrexit.com.", + "cf-3ip.web.us-east-1.prod.diagnostic.networking.aws.dev.", "cfa.fidelity.com.", - "cgee.net.", - "cgi.twns.qq.com.", + "challenges.app.", "chanjet.com.", "chargeafter.com.", "charlotte.remotepc.com.", + "chaseprod.quantummetric.com.", + "chat.openai.com.", "check2.lloydsbank.co.uk.", "check2.zennolab.com.", - "checkout-sdk.bigcommerce.com.", "checkout.shopify.com.", - "chek.zennolab.com.", + "checkout.www.deepl.com.", "chekfast.zennolab.com.", - "chem17.com.", - "chemsrc.com.", "chennai.remotepc.com.", "chicago2.remotepc.com.", + "china-dcr-ccl-zj-b.haplat.net.", "chinaccl-a.haplat.net.", "chinaccl-b.haplat.net.", "chinaccl-c.haplat.net.", - "chinatimes.com.", - "choperella.com.", "chrome.com.", "chtsite.com.", + "chub1.imp.uk.contentkeeper.net.", + "chub1.imp.us.contentkeeper.net.", + "chub2.imp.uk.contentkeeper.net.", + "chub2.imp.us.contentkeeper.net.", "cicd-release-api.dalyfeds.com.", - "cis.citrix.com.", - "citizenmaths.com.", + "cimcsouth.com.", + "cisa.gov.", "citrix-cloud-content.customer.pendo.io.", "citrix-cloud-data.customer.pendo.io.", + "citrix-sharefile-content.customer.pendo.io.", "citrix-sharefile-data.customer.pendo.io.", "citrix.com.", "cl-data.ads.heytapmobi.com.", + "claphands20.com.", "claude.ai.", "cleveland.remotepc.com.", - "click-v4.cldirplarimo.com.", - "click-v4.ecxclk.com.", + "click-v4.clkoplardir.com.", + "click-v4.exmainclckback.com.", "click-v4.junclikrmedi.com.", - "click-v4.preclksize.com.", + "click-v4.mainexclkdir.com.", "clickiocmp.com.", "client-log.box.com.", "client-tracking.omiapp.me.", "client-updates.lumu.io.", - "client.brightvpn.com.", - "client.mail.163.com.", "client.otcollector.com.", "clientlogsf.music.163.com.", + "clipchamp.com.", "cloud-agent.policypak.com.", "cloud-config-service.rtc.aliyuncs.com.", "cloud-links.net.", - "cloud-x-21-srb.rufowupe.xyz.", - "cloud-x-21-srb.tedumari.xyz.", - "cloud-x-21-srb.wutirase.xyz.", - "cloud-x-21-srb.zewaduko.xyz.", - "cloud.huawei.asia.", - "cloud.ibm.com.", + "cloud-x-21-srb.pugiwora.xyz.", + "cloud.huawei.ru.", "cloud.v-key.com.", "cloud.vmp.onezapp.com.", "cloudbirds.cn.", "cloudlinks.cn.", - "clzymbeai00003p6q8pmnae1v.d.jitsu.com.", "cm-x.mgid.com.", - "cm7mjur1o00003p6r7lio27sb.d.jitsu.com.", - "cmgate.vip.qq.com.", "cmpassport.com.", - "cms.ejoyspace.com.", + "cn-hangzhou.oss-cdn.aliyun-inc.com.", "cn-mib2high-mbbservices.audi-connect.cn.", "cn-mib2plus.mbbservices-1a.audi-connect.cn.", "cn.cdn.dnsv1.com.", - "cn.cdn.dnsv1.com.cn.", - "cn.gcloudcs.com.", "cname.pendo.io.", "cname.shopify.com.", - "cnplug.ttlock.com.", + "cnhtcsales.com.", "cnt.woxh.world.", "cnzz.com.", - "co-vcode-od.vivoglobal.com.", - "coastalbreed.com.", "codacloud.net.", "code42.com.", - "codelibrary.amlegal.com.", - "codmsg.gcloudcs.com.", - "coindesk.com.", - "collect.trendyol.com.", - "collector-direct.xhamster.com.", + "codepush.akulaku.net.", "collector.agentio.com.", + "collector.prn.fyi.", "collector.quillbot.com.", "collector.vhx.tv.", "collector.wdp.brave.com.", "collector.woxh.world.", "collector.xhamster.com.", "collector.xhamster.desi.", - "colortokens.com.", "columbus.remotepc.com.", + "columbusoh.infinitecampus.org.", "com.cdn.dnsv1.com.", "com.cdn.dnsv1.com.cn.", "com.yangyi19.com.", "cometglobal.cf.t3cloud.pb.com.", + "cometservd1.pb.com.", + "comfrt-us.attn.tv.", "comicbook.com.", - "commimg.kwcdn.com.cdn.cloudflare.net.", + "commercial.ocsp.identrust.com.", "common-afd.fe.1drv.com.", "common-afdrk.fe.1drv.com.", + "common-geo.wac.trafficmanager.net.", "company.rt.ru.", - "compellent.com.", "compiles.overleafusercontent.com.", - "comserver.us1.mspa.n-able.com.", + "concursolutions.report-uri.com.", "config-alps.y5en.com.", "config-security.com.", "config.a-m-p.xyz.", @@ -749,86 +1027,104 @@ var FakeECSFQDNs = container.NewMapSet( "config.content-settings.com.", "config.office.net.", "config.silk.al-array.com.", - "config.smooch.io.", "config.y5en.com.", "config2.cmpassport.com.", "configdl.teamviewer.com.", - "configv2.unityads.unitychina.cn.", - "connect.thescore.com.", "connectad.io.", "connectivitycheck.unisoc.com.", "connectivitycheck.unisoc.com.c.yundunwaf5.com.", + "connector.msappproxy.net.", + "contact-ldap.8x8.com.", "contacts.zoho.com.", + "content.adopt.jaggaer.com.", + "content.assist.chromeriver.com.", + "content.bhrpendo.bamboohr.com.", + "content.dap.paylocity.com.", + "content.data.aleks.com.", + "content.data.mheducation.com.", "content.discover.com.", "content.discovercard.com.", + "content.ebanking-services.com.", + "content.engage.starrez.com.", + "content.fisglobal.com.", "content.gap.com.", + "content.help.explorelearning.com.", + "content.maxconnector.com.", + "content.pendo.brightlysoftware.com.", "content.pendo.careporthealth.com.", + "content.pendo.follettdestiny.com.", "content.pendo.saashr.com.", "content.productinsights.blackline.com.", + "content.tracking.billtrust.com.", + "content22.bmo.com.", "content22.citicards.com.", "content22.online.citi.com.", "contentexchange.me.", "control-out.mna.qq.com.", "conversion-assistant.apps.seabroadnet.com.", - "conversionbear.com.", + "cookiereports.com.", "coolccloud.com.", "coolkit.cc.", - "coopcowboys.com.", "copenhagen.remotepc.com.", "cordial.com.", "core.iprom.net.", "core.omiapp.me.", - "cortacreativo.com.", "cosmos.azure.com.", "countly.mail.163.com.", + "courts.ca.gov.", "cpisolutions.com.", "cpm.appocean.media.", "cpm.aserve1.net.", "cpm.qortex.ai.", "cpm.vuukle.net.", + "cpr-pusa01.app.blackbaud.net.", "cpx-research.com.", "crash.xiaohongshu.com.", "crashlytics.com.", "crashsight.qq.com.", "crashsight.wetest.net.", - "crc-engines.api.ksztone.com.", "crealitycloud.com.", - "creative.bbrdbr.com.", - "creative.rmhfrtnd.com.", "creator.pdf24.org.", + "creditmaven.com.", + "credova.com.", "crlocsp.cn.", "croatia.remotepc.com.", "crobox.com.", "crobox.io.", + "crossborder-integration.global-e.com.", "crossforward.com.", "crowd.transitapp.com.", "cs.globalsun.io.", "cs.nex8.net.", "cs.playdigo.com.", - "cs13.pikabu.ru.", - "cs44.pikabu.ru.", "csdn.net.", "cstb.adsinteractive.com.", + "cstse02.ultipro.com.", + "cstsew02.ultipro.com.", "cstsn02.ultipro.com.", - "csyzf.com.cn.", "cta-eu1.hubspot.com.", "ctmail.com.", "ctripins.com.", + "ctrk.klclick.com.", "cts.appmeta.store.", "ctt-er.hnzkclouds.com.", - "cucloud.cn.", + "ctx-akadns-features.netscalergateway.net.", + "cummins365-my.sharepoint.com.", + "custom-cf.video.twimg.com.cdn.cloudflare.net.", "customer.gopayapi.com.", "customer.homedepot.com.", + "cvs.quantummetric.com.", + "cwa-ma01.ntiva.com.", + "cwa-mw01.ntiva.com.", "cwmpb-as.ruijienetworks.com.", - "cws.conviva.com.", - "cz88.net.", + "cybersource.com.", + "cza.crazygames.com.", "czechrepublic.remotepc.com.", - "czyyhgd.com.", + "czhuaqiang.com.", "d.docs.live.net.", "d.pub.network.", "d2fb08da-1c03-4c8a-978f-ad8a96b4c31f.partner.permutive.app.", "d2fb08da-1c03-4c8a-978f-ad8a96b4c31f.prmutv.co.", - "d3-pr-cu-tm-secapi.trafficmanager.net.", "d5p.de17a.com.", "d6691a17-6fdb-4d26-85d6-b3dd27f55f08.prmutv.co.", "d837da8d.cloudsrv.minerva-labs.com.", @@ -837,51 +1133,62 @@ var FakeECSFQDNs = container.NewMapSet( "da.mossru.com.", "dacdn.visualwebsiteoptimizer.com.", "daemon.nanoleaf.me.", - "dailynewshungary.com.", "dal-cdsltm-2b.dataremote.com.", "dallas.remotepc.com.", "dallas2.remotepc.com.", "dallas3.remotepc.com.", "dallas4.remotepc.com.", "dallas5.remotepc.com.", - "dangkexuexi.com.", "daraz.com.", - "darkness.services.", "dashi.163.com.", "data-detect.nie.easebar.com.", + "data-feed.flightradar24.com.", "data-tr.rethinkad.com.", "data.analytics.clockwisemd.com.", - "data.armexdef.uk.", + "data.analytics.thomsonreuters.com.", + "data.analytics.ux.quickbase.com.", + "data.armexfort.uk.", "data.assist.chromeriver.com.", "data.bhrpendo.bamboohr.com.", "data.cw.edgenuity.com.", "data.dap.paylocity.com.", + "data.data.aleks.com.", "data.data.mheducation.com.", + "data.guide-app.zoominfo.co.", "data.hockeystack.com.", + "data.info.costar.com.", + "data.ipd.goto.com.", "data.kuiniuca.com.", "data.meitu.com.", + "data.p-a.egnyte.com.", + "data.p3nd0.sproutsocial.com.", + "data.pathfinder.gohighlevel.com.", + "data.pendo-cobalt.westlaw.com.", + "data.pendo-tracking.seismic.com.", "data.pendo.brightlysoftware.com.", "data.pendo.careporthealth.com.", + "data.pendo.follettdestiny.com.", + "data.pendo.gomotive.com.", + "data.pendo.looker.app.", + "data.pendo.progresslearning.com.", "data.pendo.saashr.com.", "data.pendo.udemy.com.", "data.pendoanalytics.dayforcehcm.com.", + "data.productanalytics.coconutcalendar.com.", "data.productinsights.blackline.com.", + "data.riotgames.com.cdn.cloudflare.net.", + "data.tracking.billtrust.com.", "data00.adlooxtracking.com.", - "dataannotation.tech.", "datacollection.uve.weibo.com.", "datahub.ultimate-guitar.com.", "datasec-kmsex-cn.heytapmobi.com.", "datasink.cloudlinks.cn.", "datasite.com.", "datatheorem.com.", - "daydreamingland.com.", "dayunlinks.cn.", - "dayviews.com.", "dbankcdn.ru.cdn.dnsv1.com.", - "dc-device-service.apple.com.", "dc-device-service.v.aaplimg.com.", "dc-dragate-cn.heytapmobi.com.", - "dc-o.api.leiniao.com.", "dc.ads.linkedin.com.", "dc.di.atlas.samsung.com.", "dc.dqa.samsung.com.", @@ -901,115 +1208,109 @@ var FakeECSFQDNs = container.NewMapSet( "dc13.adaptiva.cloud.", "dc14.adaptiva.cloud.", "dca-cdsltm-2b.dataremote.com.", - "dcmdaa.51y5.net.", + "dcs-live-uc1.mp.lura.live.", + "dcs-live-ue1.mp.lura.live.", + "dcs-live-ue4.mp.lura.live.", + "dcs-live-uw1.mp.lura.live.", + "dcs-live.mp.lura.live.", + "dcs-mcdn.mp.lura.live.", + "dcs-png.mp.lura.live.", "dcs-vod.mp.lura.live.", + "dcs.mp.lura.live.", "dcs4-vod.mp.lura.live.", - "ddimg.cn.", + "ddata.huntingtonbank.com.", "ddnsclient.ivview.net.", - "de-muc-anx-c023.carouter.teamviewer.com.", "de-prod.asyncgw.teams.microsoft.com.", - "de.api.io.mi.com.", "de.dt.rcs.telephony.goog.", "dealmoon.com.", "debugbear.com.", "decagon.ai.", - "deepseek.com.cdn.cloudflare.net.", "delivery.upremium.asia.", - "deliveryhero.io.", "dellsupportcenter.com.", "delta.quantummetric.com.", - "demeter-int-ecom-collect.trendyol.com.", - "demeter-tr-core-collect.trendyol.com.", "denver.remotepc.com.", + "desktop.gonitro.com.", "desync.com.", "dev-silent-upgrade.cloudbirds.cn.", "dev.av380.net.", - "dev.sgp.hik-connect.com.", "dev.visualwebsiteoptimizer.com.", - "dev5.keepsolid.com.", "devc.flysleep.cn.", - "develop.muxintu.com.", + "devc.zztfly.com.", + "deviceapi.ca1.absolute.com.", "deviceops.hstgps.com.", - "deviceservices-external.apple.com.", "devicetrust.com.", - "dewasyscrm.com.", - "dewmobile.net.", - "dewu.com.", - "dhs.gov.", - "dht.transmissionbt.com.", + "devs-api.poki.com.", + "diagnostics.api.speechify.com.", "dialpad.com.", + "dicontent.ckapis.com.", "dict.deepl.com.", "dict.ntes53.netease.com.", "dictvip-business.youdao.com.", "digiboy.ir.", "digitalcardservice.com.", "dir.4.401402081.west-gcloud.codm.activision.com.", - "dir.4.497948935.codmsg.gcloudcs.com.", + "directadvert.ru.", "discovery.ringcentral.biz.", - "discoverygc.com.", "dispatcher.omiapp.me.", - "dispatchosglobal.yuanshen.com.", - "dissertationtopic.net.", + "distservp1.pb.com.", "divide.dalyfeds.com.", "dl2.discordapp.net.", - "dlg.3yjt.com.", - "dlg.kelikeliq.com.", - "dlink.com.", + "dler.com.", "dls-udc.dqa.samsung.com.", "dls.di.atlas.samsung.com.", "dm-us.hybrid.ai.", - "dm.slim02.jp.", + "dm.hybrid.ai.", + "dm.wo.com.cn.", + "dmv.ca.gov.", "dns-e.ns4v.icu.", "dns-finder.com.", "dns-tunnel-check.googlezip.net.", - "dns.anlian.co.", "dns.rubyfish.cn.", "dns1.nettica.com.", - "doccafe.com.", - "docs.getxray.app.", + "doceditor.wrike.com.", "docs.live.net.", "docs.zoom.us.", "doctrack.fda.gov.ph.", "document360.io.", - "donate.ssl.xmrig.com.", "donewyork1.remotepc.com.", "donewyork2.remotepc.com.", "donewyork3.remotepc.com.", - "donnermann-und-partner.de.", + "dorpat-use.geo.iponweb.net.", + "dorpat-usw.geo.iponweb.net.", "dosfo1.remotepc.com.", "dosfo2.remotepc.com.", "doublenix.com.", - "doubtnut.com.", "douyin.starrydyn.com.", - "down.taodocs.com.", "download.2.401402081.west-gcloud.codm.activision.com.", - "download.2.497948935.codmsg.gcloudcs.com.", "download.gep.mtss.gov.pt.", "downloadcenter.genetec.com.", + "dp.barclaysus.com.", + "dpf.authorize.net.", "dpool.sina.com.cn.", "dreame.tech.", "drfdisvc.walmart.com.", + "drown.org.", "ds.gsma.com.", "dsa-eu.hybrid.ai.", - "dsa-us.hybrid.ai.", "dsp-ap.eskimi.com.", "dsp-trk.eskimi.com.", "dsp-trvm.eskimi.com.", "dstillery.com.", - "dts.euume.com.", "dtscout.com.", - "dualspaceapi.com.", "dubai.remotepc.com.", "dublin.remotepc.com.", "dun.163yun.com.", "duolingo.cn.", "dz.cyberhaven.io.", "dzfread.cn.", - "e-hentai.org.", + "e-signlive.com.", "e.189.cn.", "e.cdnwidget.com.", + "e.postpilot.com.", "e.userflow.com.", "e.viously.com.", + "e1cef1f0-495f-4973-ba1c-880786e73a66.prmutv.co.", + "e1prodappsapimgmtgw.azure-api.net.", "e2c1.gcp.gvt2.com.", "e2c10.gcp.gvt2.com.", "e2c11.gcp.gvt2.com.", @@ -1092,35 +1393,26 @@ var FakeECSFQDNs = container.NewMapSet( "e2c82.gcp.gvt2.com.", "e2c83.gcp.gvt2.com.", "e2c9.gcp.gvt2.com.", + "e43.ultipro.com.", "e488cdb0-e7cb-4d91-9648-60d437d8e491.prmutv.co.", "e5de3d23065c4748b155c28e6fa36f3e.pacloudflare.com.", "eafddirect.msedge.net.", + "easel.teacherspayteachers.com.", "easeus.com.", "eastasia.api.cognitive.microsoft.com.", "eastmoney.com.", "eastus.api.cognitive.microsoft.com.", "eastus2.api.cognitive.microsoft.com.", "easycounter.com.", + "ecatholic.com.", + "eccdnx.com.", "echo.websocket.org.", "ecouser.net.", - "ecs-gallatin-c2s.trafficmanager.net.", - "ecs.tagtoo.co.", - "ectransistors.com.", + "ed.link.", "edevice.toshiba-solutions.com.", - "edge-msk-1.kinescopecdn.net.", - "edge-msk-2.kinescopecdn.net.", - "edge-msk-3.kinescopecdn.net.", - "edge-msk-4.kinescopecdn.net.", - "edge-msk-5.kinescopecdn.net.", - "edge-msk-6.kinescopecdn.net.", - "edge-msk-7.kinescopecdn.net.", - "edge-msk-8.kinescopecdn.net.", - "edge-msk-9.kinescopecdn.net.", "edge-term4-atl1.roblox.com.", - "edge-term4-cdg2.roblox.com.", "edge-term4-dfw2.roblox.com.", "edge-term4-fra2.roblox.com.", - "edge-term4-fra4.roblox.com.", "edge-term4-iad4.roblox.com.", "edge-term4-lax2.roblox.com.", "edge-term4-lax4.roblox.com.", @@ -1131,77 +1423,76 @@ var FakeECSFQDNs = container.NewMapSet( "edge-term4-ord2.roblox.com.", "edge-term4-sea1.roblox.com.", "edge-term4-sin2.roblox.com.", + "edge-term4-sin4.roblox.com.", "edge-term4-sin6.roblox.com.", - "edge.rgi.pmc.pay.riotgames.com.", "edgecdn.ru.", "edgedl.me.gvt1.com.", "edgelocation.ivanticloud.com.", "editor.wix.com.", "edna.id.", "edrawsoft.com.", - "edreams.es.", + "edubit.vn.", "education-certification.youdao.com.", "ee-share.com.", "efercro.com.", - "egs-platform-service.store.epicgames.com.", - "ei.dyn-rev.app.", - "ejoyspace.com.", - "elcompanies.com.", + "efs.ultipro.com.", + "egateway.ultipro.com.", + "ei.5gtb.com.", "elemecdn.com.", - "elite-hunterz.com.", - "email-tu.998law.com.", + "elitebidder.com.", "emails.zappos.com.", + "embed.ly.cdn.cloudflare.net.", + "en.linguee.com.", "en.woxh.world.", "endpointprotector.com.", "engage.wixapps.net.", "enplug.com.", "ent.box.com.", - "entitlements.auth.riotgames.com.cdn.cloudflare.net.", - "entrance-exam.net.", + "envoy-ios-prod.getepic.com.", + "epdg.vowifi.cspire.com.", + "epic.us.production.appliedcloudplatform.com.", + "eponesh.com.", "eportal.fda.gov.ph.", "epubgame.com.", "eqoe.cn.", + "erpapimanagementservice.azure-api.net.", "error-analytics-production.shopifysvc.com.", "error-analytics-sessions-production.shopifysvc.com.", "errortracking-lb.deepl.com.", "errortracking.deepl.com.", "esignlive.com.", "essence.com.", - "etracker.com.", + "ethicagarden.com.", "etsv2.datalake.gameloft.com.", "eu-aa.online-metrix.net.", "eu-api.asm.skype.com.", "eu-prod.asyncgw.teams.microsoft.com.", - "eu-us.consentmanager.net.", "eu.global.market.xiaomi.com.", - "eu.iot.dreame.tech.", + "eu.inspi-dsp.com.", "eu.statusapi.micloud.xiaomi.net.", "eu.whatfix.com.", "eu1.badoo.com.", "eu1.bumble.com.", "euler-saas-cn.heytapmobi.com.", - "europe-west1-itv-ds-prd.cloudfunctions.net.", "europe-west1-skyuk-uk-pa-tds-prod.cloudfunctions.net.", "europe.remotepc.com.", "euw1.chat.si.riotgames.com.", "euw1.s.seedtag.com.", "eve.gameloft.com.", - "event.meloshort.com.", - "event.sdk.cqsjd.xyz.", - "event.togothermany.com.", - "events.attentivemobile.com.", + "event.evtm.53.com.", "events.glanceapis.com.", "events.jotform.com.", + "events.usermaven.com.", "everestsystemsco.com.", - "everychina.com.", "evtrust.com.", "exappupgrade.vivoglobal.com.", "exceptions-eu1.hubspot.com.", "exodus.desync.com.", - "exoticindiaart.com.", "exp.host.", "expedia.quantummetric.com.", "experimentation-grpc.deepl.com.", + "expf.ru.", + "explicit-explicit.bing.net.trafficmanager.net.", "expo.olo.com.", "extension.savvy.security.", "extensions.shopifycdn.com.", @@ -1210,12 +1501,12 @@ var FakeECSFQDNs = container.NewMapSet( "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-det1-1.xx.fbcdn.net.", "external-dfw5-1.xx.fbcdn.net.", "external-dfw5-2.xx.fbcdn.net.", "external-dfw5-3.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.", @@ -1233,6 +1524,7 @@ var FakeECSFQDNs = container.NewMapSet( "external-mia3-2.xx.fbcdn.net.", "external-mia3-3.xx.fbcdn.net.", "external-mia5-1.xx.fbcdn.net.", + "external-mia5-2.xx.fbcdn.net.", "external-msp1-1.xx.fbcdn.net.", "external-ord5-1.xx.fbcdn.net.", "external-ord5-2.xx.fbcdn.net.", @@ -1241,48 +1533,43 @@ var FakeECSFQDNs = container.NewMapSet( "external-sea1-1.xx.fbcdn.net.", "external-sea5-1.xx.fbcdn.net.", "external-sjc3-1.xx.fbcdn.net.", - "external-vie1-1.xx.fbcdn.net.", + "external-sjc6-1.xx.fbcdn.net.", "f002.backblazeb2.com.", - "f150forum.com.", - "f389d50a-32e0-478b-9d4b-2d4592528bea.prmutv.co.", - "f95zone.to.", "fa3fca7ce79f4b81a39f216e916397d5.pacloudflare.com.", - "faas.marktplaats.nl.", "factset.com.", + "fanatics.ent.box.com.", "fanyiegg.youdao.com.", - "fast.genomics.lbl.gov.", - "fastball-gateway.mlb.com.", + "fastenal-my.sharepoint.com.", + "fdccpaadaptor.forddirectservices.com.", "fdl.flysleep.cn.", - "feed.mp.lura.live.", + "fdl.zztfly.com.", + "features.netscalergateway.net.", "feeder.co.", - "feelinsonice.l.google.com.", - "feishu.cn.", - "fema.gov.", "fengkongcloud.com.", - "fenxi.com.", "fgwn01.ultipro.com.", "fi.telephony.goog.", "field59.com.", - "file.3gcj.com.", - "file.myqcloud.com.cdn.dnsv1.com.", - "file.sgpjbg.com.", + "fil-pusa01.app.blackbaud.net.", "filemail.com.", + "files.ivaws.com.", "files.jotform.com.", "filesyscrm.com.", - "filters.adavoid.org.", + "finalforms.com.", "finalsite.com.", "finalsite.net.", "fireeye.com.", + "fireflies.ai.", + "fit-pay.com.", "five9.com.", - "flashjoin.net.", "flip.to.", "flixcdn.com.", "floors.nitropay.com.", - "flr-eu3.cbox.ws.", "flypgs.com.", "fm.printaudit.com.", "fmsi-lts.com.", "foisonad.com.", + "followlyrics.com.", + "fonts.cdnfonts.com.", "fonts.shopify.com.", "fonts.shopifycdn.com.", "forcesafesearch.google.com.", @@ -1290,7 +1577,6 @@ var FakeECSFQDNs = container.NewMapSet( "forms-eu1.hscollectedforms.net.", "forms-eu1.hsforms.com.", "forms.shopifyapps.com.", - "forthepeople.zoom.us.", "fortisimperious.com.", "fortworth.remotepc.com.", "foundation-ipv4.youdao.com.", @@ -1312,66 +1598,59 @@ var FakeECSFQDNs = container.NewMapSet( "fp-us-xfinity.rcs.telephony.goog.", "fp.ca.rogers.rcs.telephony.goog.", "fp.de.dt.rcs.telephony.goog.", + "fp.marketingcloudfx.com.", "fp.ps.easebar.com.", + "fp.ups.com.", "fp.us.tracfone.rcs.telephony.goog.", "fpdlp.applxweb.com.", - "fr-par-anx-c031.carouter.teamviewer.com.", + "fpf.hybrid.ai.", "fr-prod.asyncgw.teams.microsoft.com.", "fr.resolver.msg.global.xiaomi.net.", - "fragpunk.com.", "fran.frvr.com.", "francecentral.api.cognitive.microsoft.com.", "franecki.net.", "frankfurt.remotepc.com.", - "free.publictracker.xyz.", + "free-sg.cyrohost.in.", "fremont.remotepc.com.", + "friends.crazygames.com.", + "frontend-event-collector.shopifysvc.com.", + "fs.ultiproworkplace.com.", + "ftke02.ultipro.com.", + "ftkew02.ultipro.com.", "ftkn01.ultipro.com.", "ftkn02.ultipro.com.", - "ftpm.amd.com.", - "fuyexuetang.com.", "fxltsbl.com.", "g-galleryapi.micloud.xiaomi.net.", "g.galleryapi.micloud.xiaomi.net.", + "g10102301085.co.", "g10498469755.co.", - "g4.algbid.app.", "g4.bidbrain.app.", "g4.mongobrain.app.", "g4.rtbrain.app.", - "g83naxx1ena.appdump.nie.easebar.com.", "ga.badambiz.com.", - "galaxyappstore.com.", "gallery.vcmdiawe.com.", "galleryn0.vcmdiawe.com.", "galleryn1.vcmdiawe.com.", - "galleryn10.vcmdiawe.com.", - "galleryn11.vcmdiawe.com.", - "galleryn12.vcmdiawe.com.", - "galleryn13.vcmdiawe.com.", "galleryn2.vcmdiawe.com.", "galleryn3.vcmdiawe.com.", + "game-api-acc-real-hwp.gamedistribution.com.", "game.zuiqiangyingyu.net.", - "gameconfig.ivymobile.com.", + "gamechanger.quantummetric.com.", "gameloft.com.", "gamemonkey.org.", - "gamersdream.shop.", - "games.crazygames.com.", - "gateway-2.eu-west-1.production.jet-external.com.", - "gateway.apphud.com.", + "gamepigeon.net.", + "gateway.ultiproworkplace.com.", "gateway.zscalerone.net.", - "gatochino.com.", - "gb-vodafone.rcs.telephony.goog.", - "gb.ee.rcs.telephony.goog.", "gb.o2.rcs.telephony.goog.", - "gccmod.ecs.office.com.", "gcdn.co.", "gcloud.qq.com.", "gcloudcs.com.", "gcloudsdk.com.", - "gcs.sc-cdn.net.", "gdid.datalake.gameloft.com.", "geappl.io.", "geniex.com.", "geniusmonkey.com.", + "gentil.info.", "geo-dra.platform.hicloud.com.", "geoip.apps.getjoy.ai.", "geoplugin.net.", @@ -1379,31 +1658,33 @@ var FakeECSFQDNs = container.NewMapSet( "germanywestcentral.api.cognitive.microsoft.com.", "getadmiral.com.", "getbutton.io.", + "getepic.com.", "getjoy.ai.", "getnodejs.com.", "getshop.tv.", "gettopple.com.", "gifshow.com.", "gigabyte.com.", - "gingerscraps.net.", "giraff.io.", - "gismeteo.net.", "gitlab.com.", "gjapplog.ucweb.com.", - "gkzhan.com.", "gla.gameloft.com.", - "glife.arenaplus.net.", + "gleap.io.", + "glennedia.com.", + "glic-my.sharepoint.com.", + "global-all.g.nssvc.net.", + "global.datasite.com.", "global.gme.qcloud.com.", + "global.jns.swiftserve.com.", "globalsigncdn.com.cdn.cloudflare.net.", "globalsun.io.", "gme.qcloud.com.", - "go.accessacloud.com.", - "go.bluetracktor.com.", - "go.hpyjmp.com.", + "gmfinancial.com.", + "go.myavlive.com.", "go2yd.com.", "goadserver.com.", "goaffpro.com.", - "goods-vod.kwcdn.com.", + "gohighlevel.com.", "google.org.", "googledomains.com.", "googlesync.permutive.com.", @@ -1412,59 +1693,63 @@ var FakeECSFQDNs = container.NewMapSet( "gov-bam.nr-data.net.cdn.cloudflare.net.", "gr-api.gotii.com.", "graph-us.avepointonlineservices.com.", - "gravite.net.", + "graphql.usercentrics.eu.", "gravitec.net.", + "greatwolf-my.sharepoint.com.", "greenville.remotepc.com.", - "grin.com.", "grist.org.", - "gromelink-link-sg.sgameglobal.com.cdn.cloudflare.net.", + "gropulse.com.", "group-ib.com.", + "grouponeauto-my.sharepoint.com.", + "grouponeauto.sharepoint.com.", "grow.me.", "growone.sg.", - "growthbook-proxy.lenta.tech.", "grpc.vivintsky.com.", - "gs-dra.game.dbankcloud.com.", "gslb.finzfin.com.", "gslb.sgw.shopeemobile.com.", - "gtimg.cn.", + "gss.gaijin.net.", "gtm.deepl.com.", "gtm.quillbot.com.", "gtm.shopify.com.", "gtm3.shopify.com.", - "gtp.gr.", - "guid.tpns.sgp.tencent.com.", - "gulamerah.online.", "gwadar.cn.", "gyazo.com.", "gz0.googleusercontent.com.", "h-5h8i3ud8.online-metrix.net.", + "h-adp.online-metrix.net.", "h-discover.online-metrix.net.", "h-ebay.online-metrix.net.", "h-homedepot.online-metrix.net.", - "h-hsbcmx.online-metrix.net.", "h-online.citi.online-metrix.net.", - "h-rbs-bank.online-metrix.net.", "h-sdk.online-metrix.net.", "h-signifyd.online-metrix.net.", + "h-ups.online-metrix.net.", + "h-v60nf4oj-qfp.online-metrix.net.", "h-walmart.online-metrix.net.", "h.online-metrix.net.", "h104216-dcdn.mp.lura.live.", "h104216-fcdn.mp.lura.live.", + "h104216-hcdn.mp.lura.live.", "h107833-ecdn.mp.lura.live.", "h64.online-metrix.net.", "hamina.remotepc.com.", + "handshake-production-cdn.joinhandshake.com.", + "hapi.teamviewer.com.", + "hapsee.cn.", "hapseemate.cn.", "harry.lu.", - "haskell.org.", "hawaii.remotepc.com.", "hbopenbid-apac-v2.pubmnet.com.", "hbwrapper.com.", - "hbzhan.com.", - "healthlincchc.org.", + "health.avid.com.", "hecheck.bitmyanmar.info.", "hetangsmart.com.", "heytapdownload.com.", "heytapmobi.com.", + "hft-prod.actioniq.mr-in.com.", + "hidanoriko.net.", + "highwire.org.", + "hiido.com.", "hisearch-dra.dt.dbankcloud.com.", "hismarttv.com.", "hits.getelevar.com.", @@ -1474,14 +1759,14 @@ var FakeECSFQDNs = container.NewMapSet( "hls.xingyouc.com.", "hnzkclouds.com.", "holykjvbible.com.", + "home.imgsmail.ru.", "homedepot-app.quantummetric.com.", "homedepot.quantummetric.com.", "honeywell.com.", "hongkong.remotepc.com.", "hornetsecurity.com.", - "hosted.weblate.org.", "hpplay.cn.", - "hsg.tdm.qq.com.", + "hprofits.com.", "hstgps.com.", "html5.qq.com.", "htms.heytapmobi.com.", @@ -1489,91 +1774,85 @@ var FakeECSFQDNs = container.NewMapSet( "huan.tv.", "huaweicloud.com.", "huaweicloudwaf.com.", + "hub.gibraltarsoftware.com.", "hubcloud.com.cn.", "hubspotemail.net.", "huion.cn.", - "humiliationstudies.org.", "humix.com.cdn.cloudflare.net.", "huorong.cn.", - "hw.118114.net.", + "husunward.com.", "hw.gcloudcs.com.", - "hwa.youlesp.com.", "hwapps-o.api.leiniao.com.", "hwmsg-as1-usa-o.api.leiniao.com.", - "hwmsg-as2-asia-o.api.leiniao.com.", - "hwmsg-as2-usa-o.api.leiniao.com.", "hwmsg-as3-usa-o.api.leiniao.com.", "hwmsg-as4-usa-o.api.leiniao.com.", - "hwmsg-as5-asia-o.api.leiniao.com.", - "hwr.youlesp.com.", - "hwtpc-o.api.leiniao.com.", - "hwzz-01.com.", "hybrid.ai.", "i-sg01a.ocloud.heytapmobi.com.", + "i.gstatvb.com.", "i.inspi-dsp.com.", - "i.magazine.heytapmobi.com.", + "i.omiapp.me.", "i.one-bid.com.", "i.qchannel03.cn.", "i.search.heytapmobi.com.", "i.vsco.co.", - "i6-vn.weather.oppomobile.com.", + "i1.nhentai.net.", + "i2.nhentai.net.", + "i4.nhentai.net.", "i8yz83pn.com.", "ialicdn.com.", + "iam-v2.appliedcloudservices.com.", + "iberiaparishsdla.aristotleinsight.com.", "ibit.ly.", "ibm.account.box.com.", "ibm.box.com.", "ibsrv.net.", - "ic-components.com.", + "ic.wps.cn.", "icare.infranetgroup.com.", - "icdn.dantri.com.vn.", "iceudpstat.xdrtc.com.", "ichano.cn.", - "id-ooredoo.rcs.telephony.goog.", + "icon.cdn.prod.atlassian-dev.net.", + "iconmonstr.com.", + "id-minor-appstore.vivoglobal.com.", "id-telkom.rcs.telephony.goog.", "id-timer-appstore.vivoglobal.com.", "id.atlassian.com.", - "id.tuoitre.vn.", + "id.pinterest.com.", "id.woxh.world.", "iddallas1.remotepc.com.", "iddenver.remotepc.com.", - "idealapi.nl.", - "ideasevenmedia.com.", "ident.me.", "ident.mygaru.com.", "idlondon.remotepc.com.", "idnewyork1.remotepc.com.", "idoocloud.com.", - "idqqimg.com.", "idr.cdnwidget.com.", "ids.cdnwidget.com.", "iemiq.com.", "igame.gcloudcs.com.", + "iili.io.", "ijoysoftconnect.com.", - "ikki.youdao.com.", - "ilcats.ru.", "ilog-sea-aliyun.alipayplus.com.", "im.vsco.co.", "im360.sentry.cloudlinux.com.", "image.myqcloud.com.", "image.online.adp.com.", + "image.providesupport.com.", "images.fptplay53.net.", "images.getadmiral.com.", "imap.earthlink.net.", - "img-preview.51jiaoxi.com.", - "img.jigao616.com.", - "img.xjishu.com.", - "img.zhuanlichaxun.net.", - "img0.didiglobal.com.", + "ime-reporter.badambiz.com.", + "img.11467.com.", + "img.3dmxku.com.", "img2021.navyfederal.org.", + "img9.target.com.", "imgs.signifyd.com.", + "imgs2.imgsmail.ru.", + "imgs3.hcaptcha.com.", "immomo.com.", "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.", "imp.adx.roockmobile.com.", - "imp.hifunadx.site.", "impactify.media.", - "impression-forwarder.vibe.co.", "imptrk.siteplug.com.", "in-api.asm.skype.com.", "in-prod.asyncgw.teams.microsoft.com.", @@ -1581,31 +1860,29 @@ var FakeECSFQDNs = container.NewMapSet( "in.gov.", "in.visitors.live.", "inaccuracy.net.", - "includes.ccdc02.com.", + "inc-collabrtc.officeapps.live.com.", "incoming.thunderbird.net.", "indiamart.com.", "indianapolis.remotepc.com.", - "indigitall-cdn.com.", + "indodax.com.", "inf.miui.com.", - "infoldgames.com.", - "infrastructure-command-api.newrelic.com.", + "infinitecampus.com.", + "infinitecampus.org.", + "ingest.quantummetric.com.", "ingest.webeyez.com.", + "ingesteu.quantummetric.com.", + "ingestion.zetta.so.", + "ingress.us2.rum-ingress-coralogix.com.", "inneraudioms.cc.easebar.com.", - "innersloth.com.", "innity.com.", "innity.net.", - "ino.qq.com.", - "ins.connatix.com.", - "insearchofdesign.com.", - "insight.ucweb.com.", - "insightful-updates.io.", - "inskinad.com.", "inspi-dsp.com.", + "inspirebrands.quantummetric.com.", "instantmessaging-pa-jms-ap.googleapis.com.", "instantmessaging-pa-jms-eu.googleapis.com.", "instantmessaging-pa-jms-preprod-us.googleapis.com.", "instantmessaging-pa-jms-us.googleapis.com.", - "internetdownloadmanager.com.", + "instatus.com.", "intl-im-conn.iq.com.", "intuit.zoom.us.", "ios-api-2.duolingo.cn.", @@ -1619,26 +1896,32 @@ var FakeECSFQDNs = container.NewMapSet( "ip-tools.tanjingpaas.com.", "ip0.zenno.services.", "ip1.zenno.services.", - "ipfs.io.", "ipm.atm.youku.com.", + "iprep1.t.ctmail.com.", + "iprep2.t.ctmail.com.", "iprep3.t.ctmail.com.", "iprep4.t.ctmail.com.", + "iprep5.t.ctmail.com.", + "ipres.1.geo.ctmail.com.", + "ipres.2.geo.ctmail.com.", "ipres.3.geo.ctmail.com.", "ipres.4.geo.ctmail.com.", + "ipres.5.geo.ctmail.com.", "iprofiles.apple.com.", + "iprofiles.v.aaplimg.com.", "iprom.net.", "ipt.pw.", + "ipv4.cadc.absolute.com.", + "ipv4.games.", "ipv4.geojs.io.", "ipv4.sdiptest.com.", "ipv4.tracker.harry.lu.", - "ipv6-3.sdiptest.com.", "ipv6-4.sdiptest.com.", "ipv6.shuzilm.cn.", "iq.com.", "ironmountain.cyberhaven.io.", - "isciii.es.", + "iscorp.com.", "isgp.ezvizlife.com.", - "isonaspureaccesscloud.com.", "istanbul.remotepc.com.", "istatmenus.app.", "ita-free-lb.deepl.com.", @@ -1647,40 +1930,34 @@ var FakeECSFQDNs = container.NewMapSet( "itc.cn.", "itch.io.", "itch.zone.", + "itemorder.com.", "itm.cloud.com.", - "itmop.com.", - "itsfreeporn.com.", + "ittpx-asia.eskimi.com.", + "ittpx-us-e.eskimi.com.", "ittpx.eskimi.com.", "itzmx.com.", + "ivalua.com.", "ivview.com.", "ivview.net.", - "ivymobile.com.", - "iybrb.net.", - "izap24.ru.", + "ivytechccofindiana-my.sharepoint.com.", "izatcloud.net.", + "jabfm.org.", "japanwest.api.cognitive.microsoft.com.", "jazzpharmaceuticals.cyberhaven.io.", "jdadelivers.com.", - "jefferycotton.com.", - "jfcdns.com.", - "jfmtechnology.com.", - "jiaochengzhijia.com.", - "jigao616.com.", + "jhahosted.com.", "jilliandescribecompany.com.", - "jinliwujin.com.", "jira.com.", - "jishiyuchat.com.", "jitsu.com.", - "jjsm789.com.", + "jocombssd.aristotleinsight.com.", "johannesburg.remotepc.com.", "johnmuirhealth.com.", + "johnmuirhealth.sharepoint.com.", "joinhandshake.com.", "jp-prod.asyncgw.teams.microsoft.com.", "jp.cinarra.com.", "jp1.chat.si.riotgames.com.", - "jpost.com.", "jpush.cn.", - "jpush.html5.qq.com.", "jpush.io.", "jrustonapps.net.", "js-eu1.hs-analytics.net.", @@ -1690,56 +1967,51 @@ var FakeECSFQDNs = container.NewMapSet( "js-eu1.hscollectedforms.net.", "js-eu1.hsforms.net.", "js-eu1.hubspot.com.", - "jsnflowmeter.com.", - "jtexpress.com.cn.", + "js.eruptr.io.", + "jspm.io.", + "jtt.rsmjournals.com.", "junglefrog.com.", "justice.gov.", - "justwipe.co.uk.", + "jxappgtw.jhahosted.com.", "k-aeu1.contentsquare.net.", "k-aus1.contentsquare.net.", - "k.163.com.", - "k01.mbwbm.org.", - "k8s1-creatives-la.lb.indexww.com.", - "k8s1-creatives-va.lb.indexww.com.", + "k8s1-event-tracker-la.lb.indexww.com.", "k8s1-event-tracker-ny.lb.indexww.com.", "k8s1-event-tracker-sg.lb.indexww.com.", "k8s1-event-tracker-sj.lb.indexww.com.", + "k8s1-event-tracker-ty.lb.indexww.com.", + "k8s1-event-tracker-va.lb.indexww.com.", "k8s1-la-ext-haproxy.lb.indexww.com.", + "k8s1-sj-ext-haproxy.lb.indexww.com.", "ka-aus1.contentsquare.net.", - "kaixinkan.com.cn.", "kajicam.com.", - "kalay.net.cn.", - "kapola.gr.", - "karaoke-lyrics.net.", - "kemnaker.go.id.", + "kaziya-ryokan.com.", + "khabarban.com.", "khronos.org.", - "kidzparel.com.", "kiev.remotepc.com.", + "kilat.io.", "kinogram.best.", + "kiprotect.com.", "kiwisizing.com.", "klagenfurt.remotepc.com.", - "klinci.ru.", - "knoxconnect.net.", "knoxville.remotepc.com.", + "kntxy.com.", "komect.com.", - "komikindo.rip.", "komiku.org.", "koreacentral.api.cognitive.microsoft.com.", - "kotaksecurities.com.", "kstatic.googleusercontent.com.", "ktlt.cdn-vk.ru.", "kunlunaq.com.", "kunlunar.com.", - "kunlunca.com.", "kunluncan.com.", "kunlungr.com.", "kunlunhuf.com.", "kunlunsl.com.", "kunlunso.com.", - "kurogame.com.", + "kunvertads.com.", "kurogame.xyz.", + "kuwo.cn.", "kwimgs.com.", - "l.deepintent.com.", "la.remotepc.com.", "la1.chat.si.riotgames.com.", "la10.remotepc.com.", @@ -1756,55 +2028,50 @@ var FakeECSFQDNs = container.NewMapSet( "lahuashanbx.com.", "lan.sdk.linkedin.com.", "lansing.remotepc.com.", + "larkoffice.com.", "larksuite.com.", "lastwar-game-us-cf-ali.lastwarapp.com.", - "laureatelatammx.sharepoint.com.", "lax.remotepc.com.", "layerxsecurity.com.", - "lazpay-fe-kyc-module-file.oss-ap-southeast-1.aliyuncs.com.", "lb-tencent-sv-3.ubianet.com.", "lb-tencent-sv-4.ubianet.com.", "lbv1.com.", + "ld.aurelius.host.", + "ldap.google.com.", "ldmnq.com.", + "leadmanagerfx.com.", "leihuo.netease.com.", "leiniao.com.", - "lemmy.ca.", "lens.l.google.com.", - "lenta.tech.", "levect.com.", "leveldata.poki.io.", "lexicon.33across.com.", - "lh199607.com.", - "lianmeng.360.cn.", "library.pdq.com.", - "libsyn.com.", "license.gonative.io.", "license.litespeedtech.com.", "licensing.bitmovin.com.", + "licensing.sbullet.com.", "lichess.org.", + "lifestancecom.sharepoint.com.", + "liftdsp.com.", "lightwidget.com.", - "likeevideo.ru.", - "likr.com.tw.", + "lima-tpgateway3.factset.com.", "lima.remotepc.com.", "linguee-lb.linguee.com.", "linguee.com.", - "linguee.fr.", "link-vision-picture-sg-v2.oss-ap-southeast-1.aliyuncs.com.", "linkr.bio.", - "links.officedepot.com.mx.", - "liquipedia.net.", + "linvk.com.", "lisbon.remotepc.com.", "lissabon.remotepc.com.", - "list.tronlink.org.", - "list.winehq.org.", - "listal.com.", "litedev.sgp.ezvizlife.com.", "litedev.sgp.hik-connect.com.", + "litedev.us.hik-connect.com.", "litespeedtech.com.", + "littler-my.sharepoint.com.", + "live-smshopv2.v.trpcdn.net.", "live.126.net.", "live.ngb.haplat.net.", - "live.omiapp.me.", - "live.qcloud.com.", "live2.ngb.haplat.net.", "live3.ngb.haplat.net.", "live4.ngb.haplat.net.", @@ -1823,32 +2090,25 @@ var FakeECSFQDNs = container.NewMapSet( "liveoversea7.ngb.haplat.net.", "liveoversea8.ngb.haplat.net.", "liveoversea9.ngb.haplat.net.", - "livepositively.com.", - "liverpool.groupbycloud.com.", "ljubljana.remotepc.com.", - "loaduk.betfred.com.", + "loandepot.zoom.us.", "local.adguard.org.", - "log-abroad-ups.submitdata.top.", "log-api.newrelic.com.cdn.cloudflare.net.", "log-auth.flysleep.cn.", "log-auth.zztfly.com.", "log.mile.so.", "log.webmaxlogger.net.", - "log.xiaoyi.com.", "log.zoom.us.", - "logctrl.av380.net.", - "logger.moviead55.ru.", + "logging-service-prod.getepic.com.", "logging.mp.lura.live.", "loggly.com.", - "login.loudtalks.com.", + "login.shift.com.", "login.teamviewer.com.", "login.vivaldi.net.", - "logs2.sportslocalmedia.com.", - "logsdata.cxkfwn.com.", + "logo.dev.", "logu.hpplay.cn.", "logus.xiaoyi.com.", "logx.optimizely.com.", - "lokalise.co.", "london.remotepc.com.", "london2.remotepc.com.", "london3.remotepc.com.", @@ -1856,23 +2116,21 @@ var FakeECSFQDNs = container.NewMapSet( "london5.remotepc.com.", "london6.remotepc.com.", "london8.remotepc.com.", - "long.tv.", "look.360.cn.", - "lookchem.com.", "lotus-dsp.ru.", "lpa.ds.gsma.com.", + "lplfinancial.app.box.com.", + "lplfinancial.atlassian.net.", "lsagentrelay.lansweeper.com.", - "ludashi.com.", + "ltfl.librarything.com.", + "lumberjack.vitalsource.com.", "luxembourg.remotepc.com.", - "lx.netease.com.", "lycraservice-pa-cam-prod.googleapis.com.", "lyric.alarmnet.com.", "m-aus1.contentsquare.net.", - "m.betlive.com.", + "m-shindo.com.", "m.csqtrk.net.", - "m.csyzf.com.cn.", "m1.ubianet.com.", - "m110601-fcdn.mp.lura.live.", "m2.ubianet.com.", "m3.ubianet.com.", "m4.ubianet.com.", @@ -1880,24 +2138,26 @@ var FakeECSFQDNs = container.NewMapSet( "m6.ubianet.com.", "macclog-as.rj.link.", "macclog002-as.rj.link.", - "madrapvideos.com.", "madrid.remotepc.com.", - "magic.link.", - "magicacid.cn.", "magichue.net.", "maidenhead.remotepc.com.", + "mail.beehiiv.com.", "mail.superhuman.com.", + "mailinblue.com.", "mailings.lmcdn.ru.", + "maintenanceconnection.com.", "malware-filter.gitlab.io.", - "manage.kmail-lists.com.", + "mam.manage.microsoft.us.", "manage.wix.com.", + "manage1.esna.com.", "management-2.dataremote.com.", + "management.azure.com.", + "management.privatelink.azure.com.", "manassas.remotepc.com.", "manchester.remotepc.com.", - "marketplace.atlassian.com.", + "mapixl.com.", "marmot-cloud.com.", "marseille.remotepc.com.", - "marvelrivals.com.", "master1.teamviewer.com.", "master10.teamviewer.com.", "master11.teamviewer.com.", @@ -1915,31 +2175,18 @@ var FakeECSFQDNs = container.NewMapSet( "master8.teamviewer.com.", "master9.teamviewer.com.", "match.contentexchange.me.", - "material.qq.com.", "matrix.netease.com.", "max-l.mediav.com.", "maxhost.io.", - "mayihu.com.", "mbboauth-1c.prd.cn.vwg-connect.cn.", "mcallen.remotepc.com.", + "mcdermottwillemery.sharepoint.com.", + "mcdn.podbean.com.", + "mcds.dalyfeds.com.", + "mclife.xtools.info.", "mcount.easebar.com.", - "mdap.tngdigital.com.my.", - "mdlg.nc66kljo.com.", - "mdlg.oc1nvtla.com.", - "mdlg.pc2toyuca.com.", - "mdlg.qc3okokla.com.", - "mdlg.rc4kickin.com.", - "mdlg.sc5mnster.com.", - "mdlg.tc6hunda.com.", - "mdlg.uc7oicee.com.", - "mdvdns.com.", "meari-oss-us.oss-us-west-1.aliyuncs.com.", "meari-us.oss-us-west-1.aliyuncs.com.", - "mecha-aus-ping.seasungamescdn.com.", - "mecha-gz-ping.seasungamescdn.com.", - "mecha-ind-ping.seasungamescdn.com.", - "mecha-kr-ping.seasungamescdn.com.", - "medal.tv.", "medellin.remotepc.com.", "media-ams2-1.cdn.whatsapp.net.", "media-ams4-1.cdn.whatsapp.net.", @@ -1947,14 +2194,11 @@ var FakeECSFQDNs = container.NewMapSet( "media-atl3-1.cdn.whatsapp.net.", "media-atl3-2.cdn.whatsapp.net.", "media-atl3-3.cdn.whatsapp.net.", - "media-ber1-1.cdn.whatsapp.net.", "media-bog2-1.cdn.whatsapp.net.", "media-bog2-2.cdn.whatsapp.net.", "media-bos5-1.cdn.whatsapp.net.", "media-bru2-1.cdn.whatsapp.net.", "media-cdg4-1.cdn.whatsapp.net.", - "media-cdg4-2.cdn.whatsapp.net.", - "media-cdg4-3.cdn.whatsapp.net.", "media-cdn.atlassian.com.", "media-cgk1-1.cdn.whatsapp.net.", "media-cgk1-2.cdn.whatsapp.net.", @@ -1974,7 +2218,6 @@ var FakeECSFQDNs = container.NewMapSet( "media-fra5-2.cdn.whatsapp.net.", "media-gru1-1.cdn.whatsapp.net.", "media-gru1-2.cdn.whatsapp.net.", - "media-gru2-1.cdn.whatsapp.net.", "media-gru2-2.cdn.whatsapp.net.", "media-gua1-1.cdn.whatsapp.net.", "media-ham3-1.cdn.whatsapp.net.", @@ -1987,7 +2230,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.", @@ -2033,42 +2275,29 @@ var FakeECSFQDNs = container.NewMapSet( "media-sin6-2.cdn.whatsapp.net.", "media-sin6-3.cdn.whatsapp.net.", "media-sjc3-1.cdn.whatsapp.net.", + "media-sjc6-1.cdn.whatsapp.net.", "media-sof1-1.cdn.whatsapp.net.", "media-sof1-2.cdn.whatsapp.net.", "media-vie1-1.cdn.whatsapp.net.", "media-waw2-1.cdn.whatsapp.net.", - "media-waw2-2.cdn.whatsapp.net.", "media-yyz1-1.cdn.whatsapp.net.", - "media-zrh1-1.cdn.whatsapp.net.", - "media.canva.com.", + "media.ringcentral.com.", + "media.superhuman.com.", "media.tinkoff.ru.", "mediav.com.", "medproad.com.", - "medspanewsletter.com.", - "meinali.com.", "melbourne.remotepc.com.", + "memphis.remotepc.com.", "mentamob.com.", - "mercadolibre.com.ar.", "merchant-analytics-api.shopifyapps.com.", + "messaging-api.shopifyapps.com.", "metadata.decagon.ai.", - "metafilter.com.", - "meteocast.in.", - "meteocast.net.", - "meteotrend.com.", - "metric-api.newrelic.com.cdn.cloudflare.net.", "metrics-dre.dt.dbankcloud.cn.", "metrics-dre.dt.dbankcloud.com.", "metrics-dre.dt.hihonorcloud.com.", - "metrics.aimetric.net.", "metrics5.data.hicloud.com.", "mexicocity.remotepc.com.", - "mftransparency.org.", - "mgspabst.prismray.io.", - "mgsphdr1.prismray.io.", - "mgsppros1.prismray.io.", - "mgsptele.prismray.io.", "mgtv.com.", - "mhtcotton.com.", "miami.remotepc.com.", "miami2.remotepc.com.", "mib2clu8.car-cloud-cn.net.", @@ -2076,21 +2305,21 @@ var FakeECSFQDNs = container.NewMapSet( "microvirt.com.", "mid4.linkedin.com.", "mida.so.", - "migu.cn.", + "middleburycsin.aristotleinsight.com.", "milan.remotepc.com.", + "milestoneinternet.com.cdn.cloudflare.net.", "mimir2.vivaldi.com.", "min-api.cryptocompare.com.", "mini.browser.360.cn.", "minigame.vip.", "mintkeyboard.com.", - "miro.medium.com.", - "mirrors.iu13.net.", - "misacdn.net.", + "miqe.sdxpkgyaq.com.", "mixi.media.", - "mkey.163.com.", "mm.fcix.net.", + "mn365.sharepoint.com.", "mobile-bank.cdn-tinkoff.ru.", "mobile-collector.newrelic.com.cdn.cloudflare.net.", + "mobile-gw-plas.ultipro.com.", "mobile-l7.bereal.com.", "mobile-protect-api.securetheorem.com.", "mobiledataplan-pa.googleapis.com.", @@ -2101,97 +2330,87 @@ var FakeECSFQDNs = container.NewMapSet( "modoro360.com.", "moni-onrt-stsdk.vivo.com.cn.", "monitor.fraudblocker.com.", - "monitoring.qg3.apps.qualys.com.", - "monticello.remotepc.com.", + "monorail-edge.shopifysvc.com.", + "monorail-edge.tm.shopifysvc.com.", + "montage-discovery.displaynote.com.", + "montage-updates.displaynote.com.", "montreal.remotepc.com.", + "monumetric.com.", "motiondetection-us-1d.oss-us-west-1.aliyuncs.com.", "motiondetection-us-7d.oss-us-west-1.aliyuncs.com.", - "motorolasolutions.com.", - "mountainside-medical.com.", "mouser.com.", "mp.360.cn.", - "mpwb.bidlink.top.", - "mq-yl-gw.yosmart.com.", - "mrpahs.com.", "mrswd.wo87sf.com.", "ms-static-images.t-static.ru.", + "ms1app.pb.com.", "msdl.microsoft.com.", "msf.3g.qq.com.", "msg-img-hk.oss-cn-hongkong.aliyuncs.com.", + "msgrt-hwp.gamedistribution.com.", + "msp.meituan.com.", "mu.ariba.com.", "mumbai.remotepc.com.", "mumu.nie.netease.com.", "munich.remotepc.com.", "munimob.com.", - "music.163.com.163jiasu.com.", "musical.ly.", - "musicandmoreinc.com.", "musicstylingonline.com.", - "mx-mex-anx-r009.router.teamviewer.com.", - "mx-p.vivoglobal.com.", - "mx-vcode-api.vivoglobal.com.", - "mx-vcode-od.vivoglobal.com.", - "mx.amx.rcs.telephony.goog.", + "mxp-pusa01.app.blackbaud.net.", "mxptint.net.", "my.apps.factset.com.", "my.getadmiral.com.", "my.nalpeiron.com.", - "my.olo.com.", - "mycharismashop.com.", - "mydrive.connect.aig.", + "myapi.arlo.com.cdn.cloudflare.net.", "myisolved.com.", - "myporn.club.", "myqcloud.com.", "myqcloud.com.cdn.dnsv1.com.", + "mystery-game-tile.poki.io.", "myvscloud.com.", - "n03.mbmyj.org.", - "n04.mbeaj.org.", - "n04.mbmyj.org.", - "n04.mbqgu.org.", - "n04.mbrtz.org.", - "n05.mbzcp.org.", - "n06.mbdny.org.", - "n06.mbznp.org.", - "n07.mbxma.org.", - "n08.mbqgu.org.", - "n09.mbeaj.org.", - "n10.mbwnp.org.", - "n11.mbcej.org.", - "n11.mbwnp.org.", - "n12.mbwww.org.", - "n15.mbdny.org.", - "n20.mbdny.org.", - "n20.mbrtz.org.", - "n27.mbcej.org.", - "n27.mbwww.org.", - "n28.mbwbm.org.", - "n30.mbtmv.org.", - "na-filter-upgrade.huan.tv.", - "na159.epm.cyberark.com.", + "myworkdaycdn.com.cn.", + "n10.xyz.", + "n1022-spare.network-auth.com.", + "n1022.network-auth.com.", + "n1036.network-auth.com.", + "n21.ultipro.com.", + "n219.network-auth.com.", + "n259-spare.network-auth.com.", + "n259.network-auth.com.", + "n32.ultipro.com.", + "n35.ultipro.com.", + "n43foto.com.", + "n44.network-auth.com.", + "n509.network-auth.com.", + "n677.network-auth.com.", + "n686-spare.network-auth.com.", + "n686.network-auth.com.", + "n715-spare.network-auth.com.", + "n715.network-auth.com.", + "n866-spare.network-auth.com.", + "n866.network-auth.com.", + "na171.epm.cyberark.com.", "na2.chat.si.riotgames.com.", "nab.com.au.", + "nagumosan.com.", "namequery.com.", "naperville.remotepc.com.", "napps.zoom.us.", "nashville.remotepc.com.", - "nati.adbase.top.", + "nationalmap.gov.", "native.qq.com.", "nawzryhwatm.broker.amsoveasea.com.", "nc.com.", "ncentral.centrexit.com.", "nearme.com.cn.", "nechicago.remotepc.com.", + "neonataltherapists.com.", "net.cdn.dnsv1.com.", "netapm.music.163.com.", - "netapp.com.", "netease.com.", "netease.im.", - "netops.ecimon.com.", "netpop.app.", "netpresenter.com.", "netsolssl.com.", "network-check.sybo.net.", - "networkofcare.org.", "neworleans.remotepc.com.", "news-client.apple.com.", "news-events.apple.com.", @@ -2199,34 +2418,29 @@ var FakeECSFQDNs = container.NewMapSet( "news-sports-events.apple.com.", "newsletter-edge.apple.com.", "newsroom.bi.", - "newstral.com.", "newtvcdn.com.", "newyork.remotepc.com.", "newyork2.remotepc.com.", "newyork3.remotepc.com.", - "nexedi.cn.", "nexstar.amp.permutive.com.", "nexx360.io.", - "ng.colortokens.com.", + "ng1.angus.mrisoftware.com.", "ngb.haplat.net.", "nginxcloudfileproxy-vtm-online.imoulife.com.", "nice-team.net.", "nie.netease.com.", - "nieapps.com.", - "niplife.com.", + "nitroapps.co.", "nitropay.com.", - "nitropdf.com.", - "nlb-al8f8ly066beiyjhsv.eu-central-1.nlb.aliyuncs.com.", "nmcorp.video.", + "noah.n43foto.com.", "noc.computerhelpnj.com.", "node.setupad.com.", "nogigiddy.com.", "nokia.com.", + "nolagov-my.sharepoint.com.", "noodlemagazine.com.", - "noon.com.", - "nordcurrent.com.", + "nordvpn.com.", "northcentralus.api.cognitive.microsoft.com.", - "northeastshooters.com.", "northeurope.api.cognitive.microsoft.com.", "nortic.ogtic.gob.do.", "norwayeast.api.cognitive.microsoft.com.", @@ -2234,6 +2448,7 @@ var FakeECSFQDNs = container.NewMapSet( "notice.sg.bidder.paddlewaver.com.", "notification889.com.", "notify.music.163.com.", + "novabeyond.com.", "novaicare.com.", "nps.gov.", "ns-cloud-a1.googledomains.com.", @@ -2258,22 +2473,26 @@ var FakeECSFQDNs = container.NewMapSet( "ns-cloud-e4.googledomains.com.", "ns.aliyuncs.com.", "ns.identrust.com.", + "ns1.digitalocean.com.", "ns1.g.aaplimg.com.", "ns1.google.com.", "ns1.identrust.com.", "ns2.cloudflare.net.", + "ns2.digitalocean.com.", "ns2.g.aaplimg.com.", "ns2.google.com.", "ns3.cloudflare.net.", + "ns3.digitalocean.com.", "ns3.g.aaplimg.com.", "ns3.google.com.", "ns4.cloudflare.net.", "ns4.g.aaplimg.com.", "ns4.google.com.", + "ns4v.icu.", "ns5.cloudflare.net.", "nsa.nalpeiron.com.", "ntes53.netease.com.", - "ntp-vcp.21cn.com.", + "ntiva.com.", "ntp.aliyun.com.", "ntp.arlo.com.", "ntp.arlo.com.cdn.cloudflare.net.", @@ -2284,9 +2503,11 @@ var FakeECSFQDNs = container.NewMapSet( "ntp3.aliyun.com.", "ntp4.aliyun.com.", "ntp5.aliyun.com.", + "ntp6.aliyun.com.", "ntp7.aliyun.com.", "nts.xtracloud.net.", "nuremberg.remotepc.com.", + "nv.gov.", "nvdvr.cn.", "nvu-prd.mqtt.ivanticloud.com.", "nwr.mmcdn.com.", @@ -2295,88 +2516,91 @@ var FakeECSFQDNs = container.NewMapSet( "nws.zoom.us.", "nxs.mp.lura.live.", "nycrt.marphezis.com.", - "nyhyarc.com.", + "oauth-login-drcn.platform.dbankcloud.com.", "obihai.telephony.goog.", "obs.ap-southeast-1.myhuaweicloud.com.", "obs.ap-southeast-3.myhuaweicloud.com.", - "obs.eu-west-101.myhuaweicloud.eu.", + "obs.line-apps.com.", "observability-l7.bereal.com.", "obsproject.com.", + "obus-dc136101-cn.heytapmobi.com.", "obus-dc20058-cn.heytapmobi.com.", - "obus-dc2007-cn.heytapmobi.com.", "obus-dc20123-cn.heytapmobi.com.", "obus-dc20157-cn.heytapmobi.com.", - "obus-dc2020-cn.heytapmobi.com.", "obus-dctech-cn.heytapmobi.com.", + "oce.ovid.com.", + "ocean-components.com.", "ocloud.oppomobile.com.", + "ocps-xfer.kronos.net.", "ocsp.identrust.com.", "odrs.fda.gov.ph.", "oec22-normal-alisg.tokopediax.com.", "ogma-l7.bereal.com.", - "okii.com.", "okko.tv.", "ollama.com.", "omaha.formlabs.com.", "omiapp.me.", "on-hwapps-o.api.leiniao.com.", + "onboard.triptease.io.", + "one.newrelic.com.", "onekey1.cmpassport.com.", "oneplus.net.", "onethingpcs.com.", "onezapp.com.", - "onlinemace.com.", + "online-store-web.shopifyapps.com.", "onlinewebfonts.com.", - "onsite-api.listrak.com.", "oozf.9vuyy.xyz.", "op.mykonf.com.", "opamarketplace.com.", - "open.acgtracker.com.", + "open-closed.net.", "open.dstud.io.", "open.oppomobile.com.", - "opencmp.net.", "opendsp.ru.", - "openrice.com.", "opex-service-cn.allawntech.com.", - "oppo.com.", + "opml.radiotime.com.", "oppomobile.com.", "opposhop.cn.", + "opps-api.getwarmly.com.", "optimize.ulinq.asia.", "optimize.urekamedia.com.", "optimizely.com.", "orangehire.com.au.", - "orderonline.id.", + "orchidworld.jp.", "oregon.remotepc.com.", "origin.fe-image-cache-ttp.useast8.byteglb.com.", "orlando.remotepc.com.", - "ort.stsdk.vivo.com.cn.", + "orthoclinical.sharepoint.com.", "os.ydmob.com.", "osaka.remotepc.com.", - "oscar666.com.", - "oss-ap-northeast-1.aliyuncs.com.", "oss-ap-southeast-1.aliyuncs.com.", "oss-ap-southeast-5.aliyuncs.com.", + "oss-cn-beijing.aliyuncs.com.", + "oss-cn-hangzhou.aliyuncs.com.", "oss-cn-hongkong.aliyuncs.com.", + "oss-cn-shanghai.aliyuncs.com.", "oss-cn-shenzhen.aliyuncs.com.", + "oss-cn-zhangjiakou.aliyuncs.com.", "oss-eu-central-1.aliyuncs.com.", "oss-us-east-1.aliyuncs.com.", "oss-us-west-1.aliyuncs.com.", - "oss.aliyuncs.com.", - "otc.t-systems.com.", "otlp-http-production.shopifysvc.com.", + "otsu-jva.com.", "ott-lb.deepl.com.", "ott.deepl.com.", "ovative.com.", - "overflow.biz.", "overleaf.com.", "overleafusercontent.com.", "overseasccl-a.haplat.net.", "overseasccl-b.haplat.net.", - "overseasccl-c.haplat.net.", - "overseasccl-d.haplat.net.", "overseasccl-e.haplat.net.", "ovh.maxhost.io.", + "ownadx-xml.tri.media.", + "oxsquare.net.", "p.adlooxtracking.com.", + "p.myisolved.com.", "p.vsco.co.", "p0-pu-private-useast8.tiktokv.us.", + "p0-tiktok-dm-ttp2-private.tiktokv.us.", "p107609.cedexis-test.com.", "p109522.cedexis-test.com.", "p118600.itm.cloud.com.", @@ -2395,12 +2619,6 @@ var FakeECSFQDNs = container.NewMapSet( "p2p-cal.anker-in.com.", "p2p-ohi-2.anker-in.com.", "p2p-ohi-4.anker-in.com.", - "p2p-par-2.anker-in.com.", - "p2p-par-3.anker-in.com.", - "p2p-par.anker-in.com.", - "p2p-sgp-2.anker-in.com.", - "p2p-sgp.anker-in.com.", - "p2p-sto-3.anker-in.com.", "p2p-vir.anker-in.com.", "p2p.qq.com.", "p2p2-usa.aqara.com.", @@ -2426,15 +2644,19 @@ var FakeECSFQDNs = container.NewMapSet( "p34856.cedexis-test.com.", "p34858.cedexis-test.com.", "p35883.cedexis-test.com.", + "p39266.cedexis-test.com.", "p39604.cedexis-test.com.", "p40255.cedexis-test.com.", + "p40256.cedexis-test.com.", "p40259.cedexis-test.com.", "p40264.cedexis-test.com.", + "p40265.cedexis-test.com.", "p40266.cedexis-test.com.", "p40267.cedexis-test.com.", "p40480.cedexis-test.com.", "p40488.cedexis-test.com.", "p40491.cedexis-test.com.", + "p40952.cedexis-test.com.", "p41237.cedexis-test.com.", "p41238.cedexis-test.com.", "p41905.cedexis-test.com.", @@ -2454,83 +2676,86 @@ var FakeECSFQDNs = container.NewMapSet( "p95708.cedexis-test.com.", "p95711.cedexis-test.com.", "p95722.cedexis-test.com.", + "pa.authenticator.beyondidentity.com.", "paddlewaver.com.", "pai.googlezip.net.", "palermo.remotepc.com.", "palm.tech.", - "panaryglassware.com.", "panorama.wixapps.net.", - "pansousuo.com.", - "papegames.com.", "paris.remotepc.com.", "parlovoz.com.", + "partner-identity.myq-cloud.com.", "pasadena.remotepc.com.", "passportalmsp.com.", - "patentcenter.uspto.gov.", "pay.shopify.com.", + "payment.api.speechify.com.", "payment.omiapp.me.", "pbdlsp1.pb.com.", - "pbe1.chat.si.riotgames.com.", + "pbjs.rbstsystems.live.", "pbs.atmtd.com.", "pbs.btloader.com.", "pbs.nitropay.com.", - "pbs.receptivity.io.", + "pbsj.bricks-co.com.", "pc-store.lenovomm.cn.", "pc.crashsight.wetest.net.", "pc.perfsight.wetest.net.", "pcdn.brave.com.", + "pcworld.com.", "pd.cdnwidget.com.", "pdf24.org.", "pdfforge.org.", - "pe0733.ci.managedwhitelisting.com.", - "peplink.com.", + "pearcommerce.com.", + "people.com.cdn.cloudflare.net.", + "peopleadmin.com.", "perf-eu1.hsforms.com.", "perfsight.qq.com.", "perfsight.wetest.net.", - "permutive.arstechnica.com.", + "perkspot-api.perkspot.com.", "permutive.businessinsider.com.", "perr.brightvpn.com.", "pf.intuit.com.", "pharos.studyquicks.com.", "phonebridge.zoho.com.", - "phonehome.hazelcast.com.", "photoroom.com.", - "pic.rutubelist.ru.", + "piicmgvmss.polaris.com.", "pikabu.ru.", "pin.apiblink.ru.", "pingler.com.", "pingma.qq.com.", "pioneer.ghtinc.com.", + "pirateparty.gr.", "pitk.unioneeu.com.", "pittsburgh.remotepc.com.", "pix.cdnwidget.com.", "pixel.adlooxtracking.ru.", "pixel.dashfi.dev.", "pixel.gliacloud.com.", - "pixivzhan.com.", - "pk-live.cn.", "planner.cloud.microsoft.", "platform-alib.linkedin.cn.", - "playfamily.ru.", + "play-bs-las.livetech.shopee.co.id.", + "playdigo.com.", "playstream.media.", "pllatdpr.cn.", "plrm.zone.", "plt-api-us.xiaoyi.com.", + "pluralsight.com.", "pm.geniusmonkey.com.", - "pod-19-sunco-ws.zendesk.com.", "poizon.com.", "polandcentral.api.cognitive.microsoft.com.", + "policy.cookiereports.com.", + "polling.zoom.us.", "polymarket.com.", + "pool-bid-gce-us.dorpat.iponweb.net.", + "pool-usw.dorpat.iponweb.net.", "pop-convert.com.", - "pop-release.itop.qq.com.", "popt.in.", - "porsche-parts-store.myshopify.com.", "portal.us.ubianet.com.", "portals.mobi.", "portland.remotepc.com.", "posthog.com.", + "postpilot.com.", + "pov.spectrum.net.", "pp.cadc.absolute.com.", - "pp.ringcentral.biz.", "pp.usdc.absolute.com.", "ppgames.net.", "pragmaticplay.net.", @@ -2548,15 +2773,17 @@ var FakeECSFQDNs = container.NewMapSet( "prd.dc13.adaptiva.cloud.", "prd.dc14.adaptiva.cloud.", "prd.pck.netskrt.net.", + "prebid.adipolo.live.", "prebid.anyclip.com.", "prebid.trustedstack.com.", + "premium.xvpn.io.", "presentation-hkg1.turn.com.", - "primevox.net.", "printaudit.com.", + "privacy-proxy.usercentrics.eu.", + "privy.com.", "privy.io.", + "privymktg.com.", "pro-glswish-aks-tm.trafficmanager.net.", - "proballers.com.", - "probe.twitter.com.cdn.cloudflare.net.", "procore.com.", "prod-client-api.v.aaplimg.com.", "prod-default.lb.logrocket.network.", @@ -2565,50 +2792,41 @@ 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-naus-track.popmart.com.", "prod-newsletter-edge.v.aaplimg.com.", - "prod-ssg-launcher-api.amazingseasun.com.", + "prod-tasks.trafficmanager.net.", "prod.api.letsencrypt.org.", + "production.appliedcloudplatform.com.", "productreviews.shopifycdn.com.", + "profile.zoho.com.", "profiler-collector.dalyfeds.com.", - "profiles.zello.com.", - "project-limelight.com.", - "promotionad.nvcam.net.", + "prominder360.com.", + "propertyview.net.", "proquest.com.", "protonvpn.com.", "provaltech.com.", "proxy-safebrowsing.googleapis.com.", "proxy.mob.maps.yandex.net.", "proxy.shopifycdn.com.", - "ps.namequery.com.", - "psychologs.com.", - "psychpulse.com.", + "psav-my.sharepoint.com.", "pub.affilimateapis.com.", + "pub.dev.", "pub.network.", - "pubg1.battleye.com.", "public-api.uxfeedback.ru.", - "public-cdn-s3-us-west-2.oss-us-east-1.aliyuncs.com.", - "publiclog.zhiyan.tencent-cloud.net.", - "publictracker.xyz.", - "pubsub.checkvideo.net.", "puffer.6.401402081.west-gcloud.codm.activision.com.", - "pull-cmaf-l77-va01.tiktokcdn.com.", - "pulse.app-wizard.io.", "punch.p2p.qq.com.", "purpleguys.com.", "push-ads-cn.heytapmobi.com.", "push-row.zui.com.", "push.omiapp.me.", "pushmac.flexibits.com.", - "pushtrs9.push.hicloud.com.", - "puswdsprmtprs.dealersocket.com.", "pvvstream.pro.", "pw.mediav.com.", - "pwnt.bidflex.top.", + "pwa.zoom.us.", "px-intl.ucweb.com.", "px.ads.linkedin.com.", "px4.ads.linkedin.com.", "pxl.stripchat.com.", - "qagpublic.qg1.apps.qualys.ae.", "qagpublic.qg1.apps.qualys.ca.", "qagpublic.qg1.apps.qualys.co.uk.", "qagpublic.qg1.apps.qualys.com.", @@ -2620,23 +2838,25 @@ var FakeECSFQDNs = container.NewMapSet( "qagpublic.qg4.apps.qualys.com.", "qatarcentral.api.cognitive.microsoft.com.", "qc-static.coccoc.com.", - "qcloud-sg-datareceiver.kurogame.xyz.", - "qcloud.com.", "qfp.intuit.com.", "qiezibenpao.com.", "qikify.com.", - "qldzkj.com.", + "qiyeku.cn.", + "qm2.progressive.com.", + "qmi.cdw.com.", + "qntv.io.", "qookkagames.com.", "qpic.cn.cdn.dnsv1.com.", - "qq.com.cn.", "qualcomm.cn.", "qualcomm.com.", - "qualys.ae.", - "quangcao.tuoitre.vn.", + "quantamagazine.org.", "quantummetric.com.", "quicinc.com.", "quickcep.com.", + "quyou8.com.", "qxwz.com.", + "r.3gl.net.", + "r.akulaku.net.", "r.ingest-lr.com.", "r.intake-lr.com.", "r.lgrckt-in.com.", @@ -2648,126 +2868,336 @@ var FakeECSFQDNs = container.NewMapSet( "r.lr-ingest.io.", "r.lr-intake.com.", "r.lrkt-in.com.", - "r.visitstats.com.", - "r1---sn-ab5l6nrs.c.2mdn.net.", + "r.superhuman.com.", + "r1---sn-ab5sznz6.c.2mdn.net.", + "r1---sn-ab5sznzy.c.2mdn.net.", + "r1---sn-bvvbaxivnuxq5uu-p5ie.googlevideo.com.", + "r1---sn-bvvbaxivnuxq5uu-p5il.googlevideo.com.", + "r1---sn-bvvbaxivnuxq5uu-p5is.googlevideo.com.", + "r1---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", "r1---sn-bvvbaxivnuxq5uu-q4fe.googlevideo.com.", "r1---sn-bvvbaxivnuxq5uu-q4fl.googlevideo.com.", + "r1---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "r1---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "r1---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "r1---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "r1---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "r1---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "r1---sn-n2uxaxjvh-j5xl.gvt1.com.", + "r1---sn-nh5gujvh-h4xe.googlevideo.com.", + "r1---sn-nh5gujvh-h4xl.googlevideo.com.", + "r1---sn-nh5gujvh-h4xl.gvt1.com.", + "r1---sn-q4fl6nd7.c.2mdn.net.", + "r1---sn-q4fl6nz7.c.2mdn.net.", + "r1---sn-q4flrne6.c.2mdn.net.", "r1---sn-tn.googlevideo.com.", - "r1---sn-vgqsknlk.c.2mdn.net.", + "r1---sn-vgqskne6.c.2mdn.net.", + "r1---sn-vgqsknlr.c.2mdn.net.", + "r1---sn-vgqsknz6.c.2mdn.net.", + "r1---sn-vgqsknz7.c.2mdn.net.", + "r1---sn-vgqsknzl.c.2mdn.net.", + "r1---sn-vgqsrne6.c.2mdn.net.", + "r1---sn-vgqsrnzr.c.2mdn.net.", + "r1---sn-vgqsrnzz.c.2mdn.net.", "r1.sn-tn.googlevideo.com.", + "r1.visualwebsiteoptimizer.com.", + "r10---sn-bvvbaxivnuxq5uu-p5ie.googlevideo.com.", + "r10---sn-bvvbaxivnuxq5uu-p5il.googlevideo.com.", + "r10---sn-bvvbaxivnuxq5uu-p5is.googlevideo.com.", + "r10---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", + "r10---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "r10---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "r10---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "r10---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "r10---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "r10---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "r11---sn-bvvbaxivnuxq5uu-p5ie.googlevideo.com.", + "r11---sn-bvvbaxivnuxq5uu-p5il.googlevideo.com.", + "r11---sn-bvvbaxivnuxq5uu-p5is.googlevideo.com.", + "r11---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", + "r11---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "r11---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "r11---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "r11---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "r11---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "r11---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "r12---sn-bvvbaxivnuxq5uu-p5ie.googlevideo.com.", + "r12---sn-bvvbaxivnuxq5uu-p5il.googlevideo.com.", + "r12---sn-bvvbaxivnuxq5uu-p5is.googlevideo.com.", + "r12---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", + "r12---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "r12---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "r12---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "r12---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "r12---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "r12---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "r13---sn-bvvbaxivnuxq5uu-p5ie.googlevideo.com.", + "r13---sn-bvvbaxivnuxq5uu-p5il.googlevideo.com.", + "r13---sn-bvvbaxivnuxq5uu-p5is.googlevideo.com.", + "r13---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", + "r13---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "r13---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "r13---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "r13---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "r13---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "r13---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "r2---sn-ab5l6nrd.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-ab5sznzr.c.2mdn.net.", + "r2---sn-ab5sznzy.c.2mdn.net.", + "r2---sn-ab5sznzz.c.2mdn.net.", + "r2---sn-bvvbaxivnuxq5uu-p5ie.googlevideo.com.", + "r2---sn-bvvbaxivnuxq5uu-p5il.googlevideo.com.", + "r2---sn-bvvbaxivnuxq5uu-p5is.googlevideo.com.", + "r2---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", "r2---sn-bvvbaxivnuxq5uu-q4fe.googlevideo.com.", "r2---sn-bvvbaxivnuxq5uu-q4fl.googlevideo.com.", + "r2---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "r2---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "r2---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "r2---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "r2---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "r2---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "r2---sn-n2uxaxjvh-j5xs.gvt1.com.", + "r2---sn-nh5gujvh-h4xe.googlevideo.com.", + "r2---sn-nh5gujvh-h4xe.gvt1.com.", + "r2---sn-nh5gujvh-h4xl.googlevideo.com.", + "r2---sn-nh5gujvh-h4xl.gvt1.com.", + "r2---sn-nx57ynsk.c.2mdn.net.", + "r2---sn-q4fzen7l.c.2mdn.net.", + "r2---sn-q4fzen7s.c.2mdn.net.", "r2---sn-tn.googlevideo.com.", - "r2.cloudflarestorage.com.", + "r2---sn-vgqskn6s.c.2mdn.net.", + "r2---sn-vgqsknld.c.2mdn.net.", + "r2---sn-vgqsknlr.c.2mdn.net.", + "r2---sn-vgqsknse.c.2mdn.net.", + "r2---sn-vgqsknzd.c.2mdn.net.", + "r2---sn-vgqsrn67.c.2mdn.net.", + "r2---sn-vgqsrn6z.c.2mdn.net.", + "r2---sn-vgqsrnll.c.2mdn.net.", + "r2---sn-vgqsrnls.c.2mdn.net.", + "r2---sn-vgqsrnsd.c.2mdn.net.", + "r2---sn-vgqsrnsr.c.2mdn.net.", + "r2---sn-vgqsrnsy.c.2mdn.net.", + "r2---sn-vgqsrnzz.c.2mdn.net.", "r2.sn-tn.googlevideo.com.", + "r2.visualwebsiteoptimizer.com.", + "r3---sn-ab5l6nr6.c.2mdn.net.", + "r3---sn-ab5sznzr.c.2mdn.net.", + "r3---sn-bvvbaxivnuxq5uu-p5ie.googlevideo.com.", + "r3---sn-bvvbaxivnuxq5uu-p5il.googlevideo.com.", + "r3---sn-bvvbaxivnuxq5uu-p5is.googlevideo.com.", + "r3---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", "r3---sn-bvvbaxivnuxq5uu-q4fe.googlevideo.com.", "r3---sn-bvvbaxivnuxq5uu-q4fl.googlevideo.com.", + "r3---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "r3---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "r3---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "r3---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "r3---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "r3---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "r3---sn-jxopj-nh4e.gvt1.com.", + "r3---sn-q4fl6nsd.c.2mdn.net.", + "r3---sn-q4flrnlz.c.2mdn.net.", + "r3---sn-q4flrnss.c.2mdn.net.", + "r3---sn-q4fzene7.c.2mdn.net.", + "r3---sn-q4fzenee.c.2mdn.net.", "r3---sn-tn.googlevideo.com.", + "r3---sn-vgqskn67.c.2mdn.net.", + "r3---sn-vgqskne6.c.2mdn.net.", + "r3---sn-vgqskns7.c.2mdn.net.", + "r3---sn-vgqsknz7.c.2mdn.net.", + "r3---sn-vgqsrn6e.c.2mdn.net.", + "r3---sn-vgqsrnll.c.2mdn.net.", + "r3---sn-vgqsrnzs.c.2mdn.net.", "r3.sn-tn.googlevideo.com.", + "r3.visualwebsiteoptimizer.com.", + "r4---sn-ab5l6nrz.c.2mdn.net.", + "r4---sn-ab5sznzr.c.2mdn.net.", + "r4---sn-bvvbaxivnuxq5uu-p5ie.googlevideo.com.", + "r4---sn-bvvbaxivnuxq5uu-p5il.googlevideo.com.", + "r4---sn-bvvbaxivnuxq5uu-p5is.googlevideo.com.", + "r4---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", "r4---sn-bvvbaxivnuxq5uu-q4fe.googlevideo.com.", "r4---sn-bvvbaxivnuxq5uu-q4fl.googlevideo.com.", - "r4---sn-vgqsknlk.c.2mdn.net.", - "r4.visualwebsiteoptimizer.com.", + "r4---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "r4---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "r4---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "r4---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "r4---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "r4---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "r4---sn-q4fl6n6d.c.2mdn.net.", + "r4---sn-q4flrn7k.c.2mdn.net.", + "r4---sn-q4flrnlz.c.2mdn.net.", + "r4---sn-q4fzen7s.c.2mdn.net.", + "r4---sn-q4fzenee.c.2mdn.net.", + "r4---sn-vgqsknz6.c.2mdn.net.", + "r4---sn-vgqsknz7.c.2mdn.net.", + "r4---sn-vgqsrnll.c.2mdn.net.", + "r4---sn-vgqsrnlz.c.2mdn.net.", + "r4---sn-vgqsrnsy.c.2mdn.net.", + "r4---sn-vgqsrnz6.c.2mdn.net.", + "r4---sn-vgqsrnzr.c.2mdn.net.", + "r5---sn-ab5l6nr6.c.2mdn.net.", + "r5---sn-ab5l6nrl.c.2mdn.net.", + "r5---sn-bvvbaxivnuxq5uu-p5ie.googlevideo.com.", + "r5---sn-bvvbaxivnuxq5uu-p5il.googlevideo.com.", + "r5---sn-bvvbaxivnuxq5uu-p5is.googlevideo.com.", + "r5---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", "r5---sn-bvvbaxivnuxq5uu-q4fe.googlevideo.com.", "r5---sn-bvvbaxivnuxq5uu-q4fl.googlevideo.com.", - "r5---sn-vgqsrnzs.c.2mdn.net.", - "r5.visualwebsiteoptimizer.com.", + "r5---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "r5---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "r5---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "r5---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "r5---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "r5---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "r5---sn-q4fzen7l.c.2mdn.net.", + "r5---sn-vgqskn6d.c.2mdn.net.", + "r5---sn-vgqskne6.c.2mdn.net.", + "r5---sn-vgqsknlk.c.2mdn.net.", + "r5---sn-vgqsknlr.c.2mdn.net.", + "r5---sn-vgqsknls.c.2mdn.net.", + "r5---sn-vgqsknsk.c.2mdn.net.", + "r5---sn-vgqsrn6e.c.2mdn.net.", + "r5---sn-vgqsrnlz.c.2mdn.net.", + "r5---sn-vgqsrnz6.c.2mdn.net.", + "r5---sn-vgqsrnzz.c.2mdn.net.", + "r6---sn-bvvbaxivnuxq5uu-p5ie.googlevideo.com.", + "r6---sn-bvvbaxivnuxq5uu-p5il.googlevideo.com.", + "r6---sn-bvvbaxivnuxq5uu-p5is.googlevideo.com.", + "r6---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", "r6---sn-bvvbaxivnuxq5uu-q4fe.googlevideo.com.", "r6---sn-bvvbaxivnuxq5uu-q4fl.googlevideo.com.", - "r6.visualwebsiteoptimizer.com.", - "rack.avads.live.", + "r6---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "r6---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "r6---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "r6---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "r6---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "r6---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "r7---sn-bvvbaxivnuxq5uu-p5ie.googlevideo.com.", + "r7---sn-bvvbaxivnuxq5uu-p5il.googlevideo.com.", + "r7---sn-bvvbaxivnuxq5uu-p5is.googlevideo.com.", + "r7---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", + "r7---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "r7---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "r7---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "r7---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "r7---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "r7---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "r8---sn-bvvbaxivnuxq5uu-p5ie.googlevideo.com.", + "r8---sn-bvvbaxivnuxq5uu-p5il.googlevideo.com.", + "r8---sn-bvvbaxivnuxq5uu-p5is.googlevideo.com.", + "r8---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", + "r8---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "r8---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "r8---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "r8---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "r8---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "r8---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "r9---sn-bvvbaxivnuxq5uu-p5ie.googlevideo.com.", + "r9---sn-bvvbaxivnuxq5uu-p5il.googlevideo.com.", + "r9---sn-bvvbaxivnuxq5uu-p5is.googlevideo.com.", + "r9---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", + "r9---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "r9---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "r9---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "r9---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "r9---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "r9---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", "radar.cedexis.com.", - "radio-browser.info.", - "raiderlogbook.com.", "raleigh.remotepc.com.", - "raspbian.raspberrypi.org.", + "rba-screen.healthsafe-id.com.", + "rba.onehealthcareid.com.", "rbm-ap.storage.googleapis.com.", "rbm-us.storage.googleapis.com.", "rcs-acs-att-us.jibe.google.com.", "rcs-acs-mcc311.jibe.google.com.", "rcs-acs-mcc510.jibe.google.com.", - "rcs-acs-mcc515.jibe.google.com.", "rcs-acs-tmo-us.jibe.google.com.", "rcs-copper-optimized-us.googleapis.com.", "rcs-copper-us.googleapis.com.", "rcs.telephony.goog.", - "rctiplus.id.", - "readlink.com.", + "rdtxd.mediav.com.", + "readingplus.com.", "readwise.io.", "realtime-data-api.transitapp.com.", - "realtime.dewasyscrm.com.", + "realtime.kabutoservices.com.", "realtime.luckyorange.com.", "realtime.services.box.net.", - "realtotal.de.", "rec-cgi.evgcdn.net.", + "recognizedpotential.com.", "recombee.com.", + "recruiting.ultipro.com.", + "recruiting2.ultipro.com.", + "reg.c.nssvc.net.", "regions.com.", - "registration.prna01.cmdagent.trafficmanager.net.", + "register.com.", "reichelcormier.bid.", "relatoriounico.pt.", - "relay-f0493aa4.net.anydesk.com.", "remote-config.gslb.sgw.shopeemobile.com.", "remote.control4.com.", - "renfei.net.", "repo.zabbix.com.", "report.apkpure.net.", - "req.adx.ws.", - "request-global.czilladx.com.", "resideo.com.", + "resolver.1.geo.ctmail.com.", + "resolver.2.geo.ctmail.com.", "resolver.3.geo.ctmail.com.", "resolver.4.geo.ctmail.com.", "resolver.5.geo.ctmail.com.", + "resolver1.ast.ctmail.com.", + "resolver2.ast.ctmail.com.", "resolver3.ast.ctmail.com.", "resolver4.ast.ctmail.com.", "resolver5.ast.ctmail.com.", - "resources-minvest-prod.cdn-tinkoff.ru.", "restaurantguru.com.", + "restproxy-analytics.ascendlearning.com.", "restrict.youtube.com.", "restrictmoderate.youtube.com.", "retcode-us-west-1.arms.aliyuncs.com.", - "retention.dewasyscrm.com.", - "retirementpartner.com.", - "retool.com.", "revize.com.", + "rhmail.sharepoint.com.", "ri9864.ci.managedwhitelisting.com.", "richrelevance.com.", "rivergame.net.", "riverside.remotepc.com.", + "rl.progressive.com.", "rl.quantummetric.com.", "rlm.haokan.mobi.", "rmm.trustapex.com.", "rmm2.jmark.com.", "rms-dra.platform.dbankcloud.com.", "rn-resource-app.xiaohongshu.com.", + "ro.zoominfo.com.", "roborock.com.", + "rocketsoftwareinc-my.sharepoint.com.", + "rockhillssch.aristotleinsight.com.", "rockylinux.org.", "roistat.com.", "roockmobile.com.", - "roseperl.com.", + "router-01.edge.scw.cloud.", + "router-02.edge.scw.cloud.", "router.teamviewer.com.", "roxy.azurefd.net.", "rpt.cedexis.com.", - "rq.upgrade.cmpc.cmcm.com.", "rq.wh.cmcm.com.", "rr1---sn-0nnpbo5a-bggl.googlevideo.com.", - "rr1---sn-2aqu-hoal6.googlevideo.com.", - "rr1---sn-2aqu-hoalk.googlevideo.com.", "rr1---sn-2aqu-hoaly.googlevideo.com.", "rr1---sn-2aqu-hoas7.googlevideo.com.", - "rr1---sn-2aqu-hoasd.googlevideo.com.", "rr1---sn-2aqu-hoasz.googlevideo.com.", - "rr1---sn-2aqu-jbt6.googlevideo.com.", - "rr1---sn-2aqu-jbtd.googlevideo.com.", - "rr1---sn-2aqu-jxcr.googlevideo.com.", - "rr1---sn-2aqu-jxcy.googlevideo.com.", "rr1---sn-2imern76.googlevideo.com.", "rr1---sn-2imern7d.googlevideo.com.", + "rr1---sn-2imern7d.gvt1.com.", + "rr1---sn-2imern7r.googlevideo.com.", "rr1---sn-2imeyn7k.googlevideo.com.", "rr1---sn-2o5ua5-53.googlevideo.com.", "rr1---sn-2oaig5-55.googlevideo.com.", "rr1---sn-2oq4f5-c4.googlevideo.com.", + "rr1---sn-2oq4f5-c4.gvt1.com.", "rr1---sn-2ovgq5-cw.googlevideo.com.", - "rr1---sn-30a7rne6.googlevideo.com.", "rr1---sn-30a7rned.googlevideo.com.", "rr1---sn-30a7rnek.googlevideo.com.", "rr1---sn-30a7rner.googlevideo.com.", @@ -2775,18 +3205,12 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-30a7yner.googlevideo.com.", "rr1---sn-30a7yney.googlevideo.com.", "rr1---sn-30a7ynl7.googlevideo.com.", - "rr1---sn-3n4pcxg-pju6.googlevideo.com.", - "rr1---sn-3n4pcxg-pjuz.googlevideo.com.", "rr1---sn-42u-nboze.googlevideo.com.", - "rr1---sn-42u-nbozl.googlevideo.com.", - "rr1---sn-42u-nbozs.googlevideo.com.", "rr1---sn-42u-nbozz.googlevideo.com.", "rr1---sn-4g5e6ns6.googlevideo.com.", "rr1---sn-4g5e6ns7.googlevideo.com.", "rr1---sn-4g5e6nsd.googlevideo.com.", "rr1---sn-4g5e6nsk.googlevideo.com.", - "rr1---sn-4g5e6nsr.googlevideo.com.", - "rr1---sn-4g5e6nss.googlevideo.com.", "rr1---sn-4g5e6nsy.googlevideo.com.", "rr1---sn-4g5e6nsz.googlevideo.com.", "rr1---sn-4g5e6nz7.googlevideo.com.", @@ -2803,7 +3227,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-4g5edndk.googlevideo.com.", "rr1---sn-4g5edndl.googlevideo.com.", "rr1---sn-4g5edndr.googlevideo.com.", - "rr1---sn-4g5ednds.googlevideo.com.", "rr1---sn-4g5edndy.googlevideo.com.", "rr1---sn-4g5edndz.googlevideo.com.", "rr1---sn-4g5ednkl.googlevideo.com.", @@ -2815,6 +3238,7 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-4g5ednse.googlevideo.com.", "rr1---sn-4g5ednsk.googlevideo.com.", "rr1---sn-4g5ednsl.googlevideo.com.", + "rr1---sn-4g5ednsr.googlevideo.com.", "rr1---sn-4g5ednss.googlevideo.com.", "rr1---sn-4g5ednsy.googlevideo.com.", "rr1---sn-4g5ednsz.googlevideo.com.", @@ -2826,29 +3250,19 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-4g5lznes.googlevideo.com.", "rr1---sn-4g5lzney.googlevideo.com.", "rr1---sn-4g5lznez.googlevideo.com.", + "rr1---sn-4g5lznl6.googlevideo.com.", "rr1---sn-4g5lznl7.googlevideo.com.", "rr1---sn-4g5lznle.googlevideo.com.", "rr1---sn-4g5lznls.googlevideo.com.", "rr1---sn-4g5lznlz.googlevideo.com.", - "rr1---sn-5abxgpxuxaxjvh-9n4l.googlevideo.com.", "rr1---sn-5abxgpxuxaxjvh-9n4s.googlevideo.com.", "rr1---sn-5abxgpxuxaxjvh-9n4z.googlevideo.com.", - "rr1---sn-5abxgpxuxaxjvh-cawl.googlevideo.com.", - "rr1---sn-5abxgpxuxaxjvh-caws.googlevideo.com.", - "rr1---sn-5abxgpxuxaxjvh-j1ae.googlevideo.com.", - "rr1---sn-5abxgpxuxaxjvh-j1as.googlevideo.com.", - "rr1---sn-5abxgpxuxaxjvh-j1az.googlevideo.com.", - "rr1---sn-5axnug5-hxm6.googlevideo.com.", - "rr1---sn-5go7yner.googlevideo.com.", - "rr1---sn-5go7ynl6.googlevideo.com.", - "rr1---sn-5go7ynld.googlevideo.com.", "rr1---sn-5goeenes.googlevideo.com.", - "rr1---sn-5goeenez.googlevideo.com.", + "rr1---sn-5gxo-in8l.googlevideo.com.", + "rr1---sn-5gxo-in8s.googlevideo.com.", "rr1---sn-5hne6n6e.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-5hne6nsz.googlevideo.com.", "rr1---sn-5hne6nz6.googlevideo.com.", @@ -2856,7 +3270,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-5hne6nzs.googlevideo.com.", "rr1---sn-5hne6nzy.googlevideo.com.", "rr1---sn-5hnednss.googlevideo.com.", - "rr1---sn-5hnednsz.googlevideo.com.", "rr1---sn-5hnekn76.googlevideo.com.", "rr1---sn-5hnekn7d.googlevideo.com.", "rr1---sn-5hnekn7l.googlevideo.com.", @@ -2865,7 +3278,9 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-5hneknee.googlevideo.com.", "rr1---sn-5hneknek.googlevideo.com.", "rr1---sn-5hneknes.googlevideo.com.", - "rr1---sn-5uaezndd.googlevideo.com.", + "rr1---sn-5pgnugx5h-hn26.googlevideo.com.", + "rr1---sn-5pgnugx5h-hn2d.googlevideo.com.", + "rr1---sn-5pgnugx5h-hn2k.googlevideo.com.", "rr1---sn-5uaezne6.googlevideo.com.", "rr1---sn-5uaezned.googlevideo.com.", "rr1---sn-5uaeznel.googlevideo.com.", @@ -2876,6 +3291,7 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-5uaeznls.googlevideo.com.", "rr1---sn-5uaeznly.googlevideo.com.", "rr1---sn-5uaeznlz.googlevideo.com.", + "rr1---sn-5uaeznse.googlevideo.com.", "rr1---sn-5uaeznsl.googlevideo.com.", "rr1---sn-5uaeznss.googlevideo.com.", "rr1---sn-5uaezny6.googlevideo.com.", @@ -2896,39 +3312,29 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-5ualdnsz.googlevideo.com.", "rr1---sn-5ualdnz7.googlevideo.com.", "rr1---sn-5ualdnze.googlevideo.com.", - "rr1---sn-8pxuuxa-nbo6s.googlevideo.com.", - "rr1---sn-8pxuuxa-nboz6.googlevideo.com.", - "rr1---sn-8qj-i2ies.googlevideo.com.", - "rr1---sn-8qj-i5o6k.googlevideo.com.", - "rr1---sn-8qj-i5ody.googlevideo.com.", "rr1---sn-8qj-i5okl.googlevideo.com.", - "rr1---sn-8qj-i5oks.googlevideo.com.", "rr1---sn-8qj-i5ozd.googlevideo.com.", - "rr1---sn-8qj-i5ozr.googlevideo.com.", - "rr1---sn-8qj-i5ozz.googlevideo.com.", "rr1---sn-8qj-nbo66.googlevideo.com.", - "rr1---sn-8qj-nbo67.googlevideo.com.", "rr1---sn-8qj-nbo6y.googlevideo.com.", "rr1---sn-8qj-nbod6.googlevideo.com.", - "rr1---sn-8qj-nbod7.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-nh4e.googlevideo.com.", - "rr1---sn-8xgp1vo-nh4l.googlevideo.com.", + "rr1---sn-8xgp1vo-p5il.googlevideo.com.", + "rr1---sn-8xgp1vo-p5qee.googlevideo.com.", + "rr1---sn-8xgp1vo-p5qel.googlevideo.com.", + "rr1---sn-8xgp1vo-p5qes.googlevideo.com.", "rr1---sn-8xgp1vo-poql.googlevideo.com.", + "rr1---sn-8xgp1vo-vgqe.googlevideo.com.", "rr1---sn-8xgp1vo-xfge.googlevideo.com.", + "rr1---sn-8xgp1vo-xfgl.googlevideo.com.", "rr1---sn-8xgp1vo-xfgs.googlevideo.com.", - "rr1---sn-9gv76n7e.googlevideo.com.", - "rr1---sn-9gv76n7l.googlevideo.com.", "rr1---sn-9gv76n7s.googlevideo.com.", "rr1---sn-9gv76n7z.googlevideo.com.", - "rr1---sn-9gv7ene6.googlevideo.com.", "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-a5m7lnld.googlevideo.com.", @@ -2965,7 +3371,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-a5msenle.googlevideo.com.", "rr1---sn-ab5l6ndr.googlevideo.com.", "rr1---sn-ab5l6ndy.googlevideo.com.", - "rr1---sn-ab5l6nk6.googlevideo.com.", "rr1---sn-ab5l6nkd.googlevideo.com.", "rr1---sn-ab5l6nr6.googlevideo.com.", "rr1---sn-ab5l6nrd.googlevideo.com.", @@ -3016,23 +3421,34 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-aigzrnsz.googlevideo.com.", "rr1---sn-aigzrnz7.googlevideo.com.", "rr1---sn-aigzrnze.googlevideo.com.", + "rr1---sn-aj4g55-5v.googlevideo.com.", "rr1---sn-ajab55-55.googlevideo.com.", "rr1---sn-avbpj-cq5e.googlevideo.com.", - "rr1---sn-b5hh503-h5oe.googlevideo.com.", + "rr1---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", "rr1---sn-bvvbaxivnuxq5uu-q4fe.googlevideo.com.", + "rr1---sn-bvvbaxivnuxq5uu-q4fl.googlevideo.com.", + "rr1---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "rr1---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "rr1---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "rr1---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "rr1---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "rr1---sn-bvvbaxivnuxqjvhj5nu-vgq6.googlevideo.com.", + "rr1---sn-bvvbaxivnuxqjvhj5nu-vgqd.googlevideo.com.", + "rr1---sn-bvvbaxivnuxqjvhj5nu-vgqe.googlevideo.com.", + "rr1---sn-bvvbaxivnuxqjvhj5nu-vgql.googlevideo.com.", + "rr1---sn-bvvbaxivnuxqjvhj5nu-vgqs.googlevideo.com.", + "rr1---sn-bvvbaxivnuxqjvhj5nu-vgqz.googlevideo.com.", + "rr1---sn-bvvbaxivnuxqjvm-2ime.googlevideo.com.", + "rr1---sn-bvvbaxivnuxqjvm-q4fe.googlevideo.com.", + "rr1---sn-bvvbaxivnuxqjvm-q4fl.googlevideo.com.", "rr1---sn-cvb7lne7.googlevideo.com.", "rr1---sn-cvb7lnee.googlevideo.com.", "rr1---sn-cvb7lnlz.googlevideo.com.", "rr1---sn-cvb7sn7k.googlevideo.com.", "rr1---sn-cvb7sn7r.googlevideo.com.", - "rr1---sn-gxuo03g-ig3e.googlevideo.com.", "rr1---sn-gxuo03g-ig3l.googlevideo.com.", - "rr1---sn-h0jeenl6.googlevideo.com.", - "rr1---sn-h0jeenle.googlevideo.com.", - "rr1---sn-h0jelne6.googlevideo.com.", - "rr1---sn-h0jelnes.googlevideo.com.", - "rr1---sn-h0jelnez.googlevideo.com.", - "rr1---sn-hgn7rn7y.googlevideo.com.", + "rr1---sn-hjoj-poul.googlevideo.com.", + "rr1---sn-hjoj-pous.googlevideo.com.", "rr1---sn-hoa7kn76.googlevideo.com.", "rr1---sn-hoa7kn76.gvt1.com.", "rr1---sn-hoa7kn7z.googlevideo.com.", @@ -3042,33 +3458,34 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-hoa7rn7z.googlevideo.com.", "rr1---sn-hoa7rn7z.gvt1.com.", "rr1---sn-hp57kn6r.googlevideo.com.", + "rr1---sn-hp57kn6r.gvt1.com.", "rr1---sn-hp57kn6y.googlevideo.com.", "rr1---sn-hp57knd6.googlevideo.com.", "rr1---sn-hp57kndd.googlevideo.com.", - "rr1---sn-hp57kndd.gvt1.com.", "rr1---sn-hp57kndk.googlevideo.com.", - "rr1---sn-hp57kndk.gvt1.com.", "rr1---sn-hp57kndr.googlevideo.com.", - "rr1---sn-hp57kndr.gvt1.com.", "rr1---sn-hp57knds.googlevideo.com.", "rr1---sn-hp57kndy.googlevideo.com.", + "rr1---sn-hp57kndy.gvt1.com.", "rr1---sn-hp57kndz.googlevideo.com.", "rr1---sn-hp57knk7.googlevideo.com.", "rr1---sn-hp57yn7r.googlevideo.com.", "rr1---sn-hp57yn7y.googlevideo.com.", "rr1---sn-hp57yne7.googlevideo.com.", "rr1---sn-hp57ynee.googlevideo.com.", + "rr1---sn-hp57ynl6.googlevideo.com.", "rr1---sn-hp57ynlr.googlevideo.com.", "rr1---sn-hp57ynly.googlevideo.com.", "rr1---sn-hp57yns7.googlevideo.com.", + "rr1---sn-hp57yns7.gvt1.com.", "rr1---sn-hp57ynse.googlevideo.com.", "rr1---sn-hp57ynsl.googlevideo.com.", + "rr1---sn-hp57ynsl.gvt1.com.", "rr1---sn-hp57ynss.googlevideo.com.", - "rr1---sn-hp57ynss.gvt1.com.", "rr1---sn-hpqfxnu-oaxe.googlevideo.com.", "rr1---sn-hpqfxnu-oaxl.googlevideo.com.", - "rr1---sn-huxaqvv-ubqe.googlevideo.com.", - "rr1---sn-huxaqvv-ubql.googlevideo.com.", + "rr1---sn-hvgxoxu-jv0l.googlevideo.com.", + "rr1---sn-hvgxoxu-jv0s.googlevideo.com.", "rr1---sn-i3b7kn6s.googlevideo.com.", "rr1---sn-i3b7knld.googlevideo.com.", "rr1---sn-i3b7knlk.googlevideo.com.", @@ -3081,15 +3498,20 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-i3belney.googlevideo.com.", "rr1---sn-i3belnl6.googlevideo.com.", "rr1---sn-i3belnl7.googlevideo.com.", - "rr1---sn-i3belnll.googlevideo.com.", "rr1---sn-i3belnls.googlevideo.com.", "rr1---sn-i3bssn7e.googlevideo.com.", + "rr1---sn-jn2pgx4pcxg-w5o6.googlevideo.com.", + "rr1---sn-jn2pgx4pcxg-w5os.googlevideo.com.", + "rr1---sn-jn2pgx4pcxg-w5oz.googlevideo.com.", "rr1---sn-jvhh4pcgx-ajte.googlevideo.com.", "rr1---sn-jvhh4pcgx-ajtl.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-jxopj-n5oe.googlevideo.com.", + "rr1---sn-jxopj-nh4e.googlevideo.com.", + "rr1---sn-jxopj-nh4e.gvt1.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.", @@ -3099,20 +3521,21 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-n4v7snly.googlevideo.com.", "rr1---sn-n4v7sns7.googlevideo.com.", "rr1---sn-n4v7snse.googlevideo.com.", + "rr1---sn-nh5gujvh-h4xe.googlevideo.com.", + "rr1---sn-nh5gujvh-h4xe.gvt1.com.", + "rr1---sn-nh5gujvh-h4xl.googlevideo.com.", + "rr1---sn-nh5gujvh-h4xl.gvt1.com.", "rr1---sn-npoe7ndl.googlevideo.com.", - "rr1---sn-npoe7ndl.gvt1.com.", "rr1---sn-npoe7ne6.googlevideo.com.", "rr1---sn-npoe7ne7.googlevideo.com.", "rr1---sn-npoe7ned.googlevideo.com.", "rr1---sn-npoe7nek.googlevideo.com.", - "rr1---sn-npoe7ner.googlevideo.com.", "rr1---sn-npoe7nes.googlevideo.com.", "rr1---sn-npoe7ney.googlevideo.com.", "rr1---sn-npoe7nez.googlevideo.com.", "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.", @@ -3121,11 +3544,8 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-npoe7nsl.googlevideo.com.", "rr1---sn-npoe7nsr.googlevideo.com.", "rr1---sn-npoe7nsr.gvt1.com.", - "rr1---sn-npoe7nss.googlevideo.com.", - "rr1---sn-npoe7nss.gvt1.com.", "rr1---sn-npoe7nsy.googlevideo.com.", "rr1---sn-npoe7nz7.googlevideo.com.", - "rr1---sn-npoe7nz7.gvt1.com.", "rr1---sn-npoeene6.googlevideo.com.", "rr1---sn-npoeened.googlevideo.com.", "rr1---sn-npoeenee.googlevideo.com.", @@ -3133,42 +3553,33 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-npoeener.googlevideo.com.", "rr1---sn-npoeeney.googlevideo.com.", "rr1---sn-npoeenez.googlevideo.com.", - "rr1---sn-npoeenl7.googlevideo.com.", "rr1---sn-npoeenle.googlevideo.com.", "rr1---sn-npoeenlk.googlevideo.com.", - "rr1---sn-npoeenlk.gvt1.com.", "rr1---sn-npoeenll.googlevideo.com.", "rr1---sn-npoeenly.googlevideo.com.", - "rr1---sn-npoeenly.gvt1.com.", "rr1---sn-npoeens7.googlevideo.com.", - "rr1---sn-npoeens7.gvt1.com.", "rr1---sn-npoldn76.googlevideo.com.", - "rr1---sn-npoldn76.gvt1.com.", "rr1---sn-npoldn7d.googlevideo.com.", "rr1---sn-npoldn7d.gvt1.com.", "rr1---sn-npoldn7e.googlevideo.com.", - "rr1---sn-npoldn7e.gvt1.com.", "rr1---sn-npoldn7l.googlevideo.com.", "rr1---sn-npoldn7l.gvt1.com.", - "rr1---sn-npoldn7s.googlevideo.com.", "rr1---sn-npoldn7y.googlevideo.com.", "rr1---sn-npoldn7y.gvt1.com.", "rr1---sn-npoldn7z.googlevideo.com.", - "rr1---sn-npoldn7z.gvt1.com.", "rr1---sn-npoldne7.googlevideo.com.", - "rr1---sn-npoldne7.gvt1.com.", - "rr1---sn-ntqe6nee.googlevideo.com.", - "rr1---sn-nv47ln6e.googlevideo.com.", - "rr1---sn-nv47zn7y.googlevideo.com.", - "rr1---sn-nv47zne7.googlevideo.com.", - "rr1---sn-nv47znee.googlevideo.com.", - "rr1---sn-nv47znel.googlevideo.com.", + "rr1---sn-nv0uixgo-5ual.googlevideo.com.", + "rr1---sn-nx57ynlk.googlevideo.com.", "rr1---sn-nx57ynsd.googlevideo.com.", "rr1---sn-nx57ynse.googlevideo.com.", "rr1---sn-nx57ynsk.googlevideo.com.", "rr1---sn-nx57ynsl.googlevideo.com.", + "rr1---sn-nx57ynsr.googlevideo.com.", "rr1---sn-nx57ynss.googlevideo.com.", + "rr1---sn-nx57ynsy.googlevideo.com.", "rr1---sn-nx57ynsz.googlevideo.com.", + "rr1---sn-nx57ynz7.googlevideo.com.", + "rr1---sn-nx57ynze.googlevideo.com.", "rr1---sn-o097znsd.googlevideo.com.", "rr1---sn-o097znse.googlevideo.com.", "rr1---sn-o097znsk.googlevideo.com.", @@ -3183,12 +3594,15 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-o097znzr.googlevideo.com.", "rr1---sn-oj5hn5-55.googlevideo.com.", "rr1---sn-oji3bc-5j.googlevideo.com.", + "rr1---sn-ojnpo5-58.googlevideo.com.", "rr1---sn-p5qddn76.googlevideo.com.", "rr1---sn-p5qddn7d.googlevideo.com.", "rr1---sn-p5qddn7k.googlevideo.com.", "rr1---sn-p5qddn7r.googlevideo.com.", "rr1---sn-p5qddn7z.googlevideo.com.", "rr1---sn-p5qlsn6l.googlevideo.com.", + "rr1---sn-p5qlsn6s.googlevideo.com.", + "rr1---sn-p5qlsn6z.googlevideo.com.", "rr1---sn-p5qlsn76.googlevideo.com.", "rr1---sn-p5qlsn7d.googlevideo.com.", "rr1---sn-p5qlsn7l.googlevideo.com.", @@ -3201,25 +3615,40 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-p5qlsnrr.googlevideo.com.", "rr1---sn-p5qlsny6.googlevideo.com.", "rr1---sn-p5qs7n6d.googlevideo.com.", + "rr1---sn-p5qs7n6y.googlevideo.com.", + "rr1---sn-p5qs7nd7.googlevideo.com.", "rr1---sn-p5qs7nsk.googlevideo.com.", - "rr1---sn-p5qs7nsr.googlevideo.com.", "rr1---sn-p5qs7nzk.googlevideo.com.", "rr1---sn-p5qs7nzr.googlevideo.com.", "rr1---sn-p5qs7nzy.googlevideo.com.", + "rr1---sn-paapovpnjxou0gt-nual.googlevideo.com.", + "rr1---sn-paapovpnjxou0gt-nuas.googlevideo.com.", + "rr1---sn-paapovpnjxou0gt-nuaz.googlevideo.com.", "rr1---sn-pjnpu-5hfe.googlevideo.com.", "rr1---sn-pjnpu-5hfl.googlevideo.com.", - "rr1---sn-pouxgoxm5-cvbe.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-q4fl6n6r.googlevideo.com.", + "rr1---sn-q4fl6n6r.gvt1.com.", "rr1---sn-q4fl6n6s.googlevideo.com.", + "rr1---sn-q4fl6n6s.gvt1.com.", "rr1---sn-q4fl6n6y.googlevideo.com.", + "rr1---sn-q4fl6n6y.gvt1.com.", "rr1---sn-q4fl6n6z.googlevideo.com.", + "rr1---sn-q4fl6n6z.gvt1.com.", + "rr1---sn-q4fl6nd6.googlevideo.com.", "rr1---sn-q4fl6nd7.googlevideo.com.", + "rr1---sn-q4fl6nd7.gvt1.com.", "rr1---sn-q4fl6nde.googlevideo.com.", + "rr1---sn-q4fl6nde.gvt1.com.", "rr1---sn-q4fl6ndl.googlevideo.com.", + "rr1---sn-q4fl6ndl.gvt1.com.", "rr1---sn-q4fl6nds.googlevideo.com.", + "rr1---sn-q4fl6nds.gvt1.com.", "rr1---sn-q4fl6ndz.googlevideo.com.", + "rr1---sn-q4fl6ndz.gvt1.com.", "rr1---sn-q4fl6nlz.googlevideo.com.", "rr1---sn-q4fl6ns6.googlevideo.com.", "rr1---sn-q4fl6ns7.googlevideo.com.", @@ -3228,45 +3657,67 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-q4fl6nsl.googlevideo.com.", "rr1---sn-q4fl6nsr.googlevideo.com.", "rr1---sn-q4fl6nss.googlevideo.com.", + "rr1---sn-q4fl6nss.gvt1.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-q4fl6nzy.googlevideo.com.", + "rr1---sn-q4fl6nzy.gvt1.com.", + "rr1---sn-q4flrn7k.googlevideo.com.", "rr1---sn-q4flrn7r.googlevideo.com.", "rr1---sn-q4flrn7y.googlevideo.com.", "rr1---sn-q4flrne6.googlevideo.com.", "rr1---sn-q4flrne7.googlevideo.com.", + "rr1---sn-q4flrne7.gvt1.com.", "rr1---sn-q4flrnee.googlevideo.com.", + "rr1---sn-q4flrnee.gvt1.com.", "rr1---sn-q4flrnek.googlevideo.com.", "rr1---sn-q4flrnel.googlevideo.com.", "rr1---sn-q4flrner.googlevideo.com.", + "rr1---sn-q4flrner.gvt1.com.", "rr1---sn-q4flrnes.googlevideo.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-q4flrnsd.googlevideo.com.", + "rr1---sn-q4flrnsd.gvt1.com.", "rr1---sn-q4flrnsk.googlevideo.com.", - "rr1---sn-q4flrnsl.googlevideo.com.", "rr1---sn-q4flrnss.googlevideo.com.", + "rr1---sn-q4fzen7e.googlevideo.com.", + "rr1---sn-q4fzen7e.gvt1.com.", "rr1---sn-q4fzen7l.googlevideo.com.", + "rr1---sn-q4fzen7l.gvt1.com.", "rr1---sn-q4fzen7s.googlevideo.com.", + "rr1---sn-q4fzen7s.gvt1.com.", "rr1---sn-q4fzen7y.googlevideo.com.", "rr1---sn-q4fzene7.googlevideo.com.", + "rr1---sn-q4fzene7.gvt1.com.", "rr1---sn-q4fzenee.googlevideo.com.", + "rr1---sn-q4fzenee.gvt1.com.", "rr1---sn-qja5mc-5h.googlevideo.com.", "rr1---sn-qjp5q5-55.googlevideo.com.", "rr1---sn-qxo7rn7k.googlevideo.com.", - "rr1---sn-qxo7rn7r.googlevideo.com.", - "rr1---sn-qxo7rn7y.googlevideo.com.", + "rr1---sn-qxo7rne7.googlevideo.com.", + "rr1---sn-qxo7rnee.googlevideo.com.", "rr1---sn-qxoedn7k.googlevideo.com.", "rr1---sn-qxoedne7.googlevideo.com.", "rr1---sn-qxoednee.googlevideo.com.", - "rr1---sn-t0aedn7l.googlevideo.com.", + "rr1---sn-qxoednel.googlevideo.com.", + "rr1---sn-qxoednes.googlevideo.com.", + "rr1---sn-qxuxa-5xme.googlevideo.com.", "rr1---sn-u1hp55-5c.googlevideo.com.", + "rr1---sn-u1hp55-5c.gvt1.com.", + "rr1---sn-v5goxu-jhi6.googlevideo.com.", + "rr1---sn-v5goxu-jhid.googlevideo.com.", + "rr1---sn-v5goxu-jhil.googlevideo.com.", + "rr1---sn-v5goxu-jhiz.googlevideo.com.", "rr1---sn-vgqskn66.googlevideo.com.", "rr1---sn-vgqskn67.googlevideo.com.", "rr1---sn-vgqskn6d.googlevideo.com.", @@ -3280,6 +3731,7 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-vgqsknld.googlevideo.com.", "rr1---sn-vgqsknlk.googlevideo.com.", "rr1---sn-vgqsknll.googlevideo.com.", + "rr1---sn-vgqsknlr.googlevideo.com.", "rr1---sn-vgqsknls.googlevideo.com.", "rr1---sn-vgqsknlz.googlevideo.com.", "rr1---sn-vgqskns7.googlevideo.com.", @@ -3300,6 +3752,7 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-vgqsrn6e.googlevideo.com.", "rr1---sn-vgqsrn6l.googlevideo.com.", "rr1---sn-vgqsrn6z.googlevideo.com.", + "rr1---sn-vgqsrn6z.gvt1.com.", "rr1---sn-vgqsrne6.googlevideo.com.", "rr1---sn-vgqsrned.googlevideo.com.", "rr1---sn-vgqsrnek.googlevideo.com.", @@ -3315,7 +3768,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-vgqsrnsd.googlevideo.com.", "rr1---sn-vgqsrnsr.googlevideo.com.", "rr1---sn-vgqsrnsy.googlevideo.com.", - "rr1---sn-vgqsrnz6.googlevideo.com.", "rr1---sn-vgqsrnz7.googlevideo.com.", "rr1---sn-vgqsrnzd.googlevideo.com.", "rr1---sn-vgqsrnzk.googlevideo.com.", @@ -3325,39 +3777,8 @@ var FakeECSFQDNs = container.NewMapSet( "rr1---sn-vgqsrnzz.googlevideo.com.", "rr1---sn-voxoxu-v3jl.googlevideo.com.", "rr1---sn-voxoxu-v3js.googlevideo.com.", - "rr1.sn-2oq4f5-c4.googlevideo.com.", - "rr1.sn-2ovgq5-cw.googlevideo.com.", - "rr1.sn-4g5e6nze.googlevideo.com.", - "rr1.sn-a5m7lnl6.googlevideo.com.", - "rr1.sn-a5m7lnld.googlevideo.com.", - "rr1.sn-a5mekn6d.googlevideo.com.", - "rr1.sn-a5mekn6k.googlevideo.com.", - "rr1.sn-a5mekn6l.googlevideo.com.", - "rr1.sn-a5mekn6r.googlevideo.com.", - "rr1.sn-a5mekn6s.googlevideo.com.", - "rr1.sn-a5mekn6z.googlevideo.com.", - "rr1.sn-a5meknd6.googlevideo.com.", - "rr1.sn-a5meknde.googlevideo.com.", - "rr1.sn-a5mekndl.googlevideo.com.", - "rr1.sn-a5meknds.googlevideo.com.", - "rr1.sn-a5mekndz.googlevideo.com.", - "rr1.sn-a5meknsd.googlevideo.com.", - "rr1.sn-a5meknsy.googlevideo.com.", - "rr1.sn-a5meknzk.googlevideo.com.", - "rr1.sn-a5meknzr.googlevideo.com.", - "rr1.sn-a5mlrnek.googlevideo.com.", - "rr1.sn-a5mlrnl6.googlevideo.com.", - "rr1.sn-a5mlrnll.googlevideo.com.", - "rr1.sn-a5mlrnls.googlevideo.com.", - "rr1.sn-a5mlrnlz.googlevideo.com.", - "rr1.sn-a5msen76.googlevideo.com.", - "rr1.sn-a5msen7l.googlevideo.com.", - "rr1.sn-a5msenek.googlevideo.com.", - "rr1.sn-a5msener.googlevideo.com.", - "rr1.sn-a5msenes.googlevideo.com.", - "rr1.sn-a5msenl7.googlevideo.com.", - "rr1.sn-a5msenle.googlevideo.com.", - "rr1.sn-hgn7rn7y.googlevideo.com.", + "rr1.sn-5hne6nsz.googlevideo.com.", + "rr1.sn-5hneknek.googlevideo.com.", "rr1.sn-hp57kn6r.googlevideo.com.", "rr1.sn-hp57kn6y.googlevideo.com.", "rr1.sn-hp57knd6.googlevideo.com.", @@ -3368,151 +3789,140 @@ var FakeECSFQDNs = container.NewMapSet( "rr1.sn-hp57kndy.googlevideo.com.", "rr1.sn-hp57kndz.googlevideo.com.", "rr1.sn-hp57knk7.googlevideo.com.", - "rr1.sn-hp57yn7r.googlevideo.com.", - "rr1.sn-hp57yn7y.googlevideo.com.", + "rr1.sn-hp57ynl6.googlevideo.com.", "rr1.sn-hp57ynlr.googlevideo.com.", "rr1.sn-hp57ynly.googlevideo.com.", "rr1.sn-hp57yns7.googlevideo.com.", "rr1.sn-hp57ynse.googlevideo.com.", "rr1.sn-hp57ynsl.googlevideo.com.", "rr1.sn-hp57ynss.googlevideo.com.", - "rr1.sn-ntqe6nee.googlevideo.com.", "rr1.sn-nx57ynsk.googlevideo.com.", "rr1.sn-q4fl6n6d.googlevideo.com.", "rr1.sn-q4fl6n6r.googlevideo.com.", - "rr1.sn-q4fl6n6z.googlevideo.com.", - "rr1.sn-q4fl6ndz.googlevideo.com.", - "rr1.sn-q4fl6ns6.googlevideo.com.", - "rr1.sn-q4flrn7r.googlevideo.com.", - "rr1.sn-q4flrnl6.googlevideo.com.", - "rr1.sn-q4flrnld.googlevideo.com.", - "rr1.sn-q4flrnle.googlevideo.com.", - "rr1.sn-q4flrnlz.googlevideo.com.", - "rr1.sn-q4flrnsd.googlevideo.com.", - "rr1.sn-q4flrnsk.googlevideo.com.", - "rr1.sn-q4flrnsl.googlevideo.com.", - "rr1.sn-q4flrnss.googlevideo.com.", + "rr1.sn-q4fl6nd6.googlevideo.com.", + "rr1.sn-q4fl6nsl.googlevideo.com.", + "rr1.sn-q4flrn7k.googlevideo.com.", + "rr1.sn-q4flrnes.googlevideo.com.", "rr1.sn-q4fzen7l.googlevideo.com.", - "rr1.sn-q4fzen7s.googlevideo.com.", - "rr1.sn-q4fzen7y.googlevideo.com.", - "rr1.sn-q4fzene7.googlevideo.com.", - "rr1.sn-q4fzenee.googlevideo.com.", - "rr1.sn-qja5mc-5h.googlevideo.com.", "rr1.sn-u1hp55-5c.googlevideo.com.", - "rr1.sn-vgqskn66.googlevideo.com.", - "rr1.sn-vgqskn67.googlevideo.com.", - "rr1.sn-vgqskn6s.googlevideo.com.", - "rr1.sn-vgqskn6z.googlevideo.com.", - "rr1.sn-vgqsknld.googlevideo.com.", - "rr1.sn-vgqsknlk.googlevideo.com.", - "rr1.sn-vgqskns7.googlevideo.com.", - "rr1.sn-vgqsknse.googlevideo.com.", - "rr1.sn-vgqsknsk.googlevideo.com.", - "rr1.sn-vgqsknz6.googlevideo.com.", - "rr1.sn-vgqsknz7.googlevideo.com.", - "rr1.sn-vgqsknzd.googlevideo.com.", - "rr1.sn-vgqsknze.googlevideo.com.", - "rr1.sn-vgqsknzk.googlevideo.com.", - "rr1.sn-vgqsknzl.googlevideo.com.", - "rr1.sn-vgqsknzr.googlevideo.com.", - "rr1.sn-vgqsknzs.googlevideo.com.", - "rr1.sn-vgqsknzy.googlevideo.com.", - "rr1.sn-vgqsknzz.googlevideo.com.", - "rr1.sn-vgqsrn67.googlevideo.com.", - "rr1.sn-vgqsrn6e.googlevideo.com.", - "rr1.sn-vgqsrn6l.googlevideo.com.", - "rr1.sn-vgqsrn6z.googlevideo.com.", - "rr1.sn-vgqsrnl6.googlevideo.com.", - "rr1.sn-vgqsrnld.googlevideo.com.", - "rr1.sn-vgqsrnlk.googlevideo.com.", - "rr1.sn-vgqsrnls.googlevideo.com.", - "rr1.sn-vgqsrnlz.googlevideo.com.", - "rr1.sn-vgqsrns6.googlevideo.com.", - "rr1.sn-vgqsrnsd.googlevideo.com.", - "rr1.sn-vgqsrnsr.googlevideo.com.", - "rr1.sn-vgqsrnsy.googlevideo.com.", - "rr1.sn-vgqsrnz6.googlevideo.com.", - "rr1.sn-vgqsrnz7.googlevideo.com.", - "rr1.sn-vgqsrnzd.googlevideo.com.", - "rr1.sn-vgqsrnzk.googlevideo.com.", - "rr1.sn-vgqsrnzr.googlevideo.com.", - "rr1.sn-vgqsrnzs.googlevideo.com.", - "rr1.sn-vgqsrnzy.googlevideo.com.", "rr1.sn-vgqsrnzz.googlevideo.com.", - "rr10---sn-42u-nboze.googlevideo.com.", - "rr10---sn-42u-nbozl.googlevideo.com.", - "rr10---sn-42u-nbozs.googlevideo.com.", - "rr10---sn-8pxuuxa-nbosd.googlevideo.com.", "rr10---sn-8qj-i5ozd.googlevideo.com.", "rr10---sn-8xgp1vo-ab56.googlevideo.com.", "rr10---sn-8xgp1vo-ab5d.googlevideo.com.", - "rr10---sn-jvhj5nu-nh4e.googlevideo.com.", - "rr10---sn-jvhj5nu-nh4l.googlevideo.com.", - "rr10---sn-jvhj5nu-nh4s.googlevideo.com.", - "rr10---sn-jvhj5nu-nh4z.googlevideo.com.", - "rr11---sn-42u-nboze.googlevideo.com.", - "rr11---sn-42u-nbozl.googlevideo.com.", - "rr11---sn-42u-nbozs.googlevideo.com.", - "rr11---sn-8qj-i5ozd.googlevideo.com.", + "rr10---sn-8xgp1vo-p5qee.googlevideo.com.", + "rr10---sn-8xgp1vo-p5qel.googlevideo.com.", + "rr10---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", + "rr10---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "rr10---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "rr10---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "rr10---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "rr10---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "rr10---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "rr10---sn-bvvbaxivnuxqjvhj5nu-vgq6.googlevideo.com.", + "rr10---sn-bvvbaxivnuxqjvhj5nu-vgqd.googlevideo.com.", + "rr10---sn-bvvbaxivnuxqjvhj5nu-vgqe.googlevideo.com.", + "rr10---sn-bvvbaxivnuxqjvhj5nu-vgql.googlevideo.com.", + "rr10---sn-bvvbaxivnuxqjvhj5nu-vgqs.googlevideo.com.", + "rr10---sn-bvvbaxivnuxqjvhj5nu-vgqz.googlevideo.com.", + "rr10---sn-bvvbaxivnuxqjvm-2ime.googlevideo.com.", + "rr10---sn-bvvbaxivnuxqjvm-2ims.googlevideo.com.", + "rr10---sn-bvvbaxivnuxqjvm-q4fe.googlevideo.com.", + "rr10---sn-bvvbaxivnuxqjvm-q4fl.googlevideo.com.", + "rr10---sn-bvvbaxivnuxqjvm-q4fl.gvt1.com.", "rr11---sn-8xgp1vo-ab56.googlevideo.com.", "rr11---sn-8xgp1vo-ab5d.googlevideo.com.", - "rr12---sn-42u-nboze.googlevideo.com.", - "rr12---sn-42u-nbozl.googlevideo.com.", + "rr11---sn-8xgp1vo-p5qee.googlevideo.com.", + "rr11---sn-8xgp1vo-p5qel.googlevideo.com.", + "rr11---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", + "rr11---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "rr11---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "rr11---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "rr11---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "rr11---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "rr11---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "rr11---sn-bvvbaxivnuxqjvhj5nu-n4vl.googlevideo.com.", + "rr11---sn-bvvbaxivnuxqjvhj5nu-vgq6.googlevideo.com.", + "rr11---sn-bvvbaxivnuxqjvhj5nu-vgqd.googlevideo.com.", + "rr11---sn-bvvbaxivnuxqjvhj5nu-vgqe.googlevideo.com.", + "rr11---sn-bvvbaxivnuxqjvhj5nu-vgql.googlevideo.com.", + "rr11---sn-bvvbaxivnuxqjvhj5nu-vgqs.googlevideo.com.", + "rr11---sn-bvvbaxivnuxqjvhj5nu-vgqz.googlevideo.com.", + "rr11---sn-bvvbaxivnuxqjvm-2ime.googlevideo.com.", + "rr11---sn-bvvbaxivnuxqjvm-2ims.googlevideo.com.", + "rr11---sn-bvvbaxivnuxqjvm-q4fe.googlevideo.com.", + "rr11---sn-bvvbaxivnuxqjvm-q4fe.gvt1.com.", + "rr11---sn-bvvbaxivnuxqjvm-q4fl.googlevideo.com.", + "rr11---sn-bvvbaxivnuxqjvm-q4fl.gvt1.com.", "rr12---sn-42u-nbozs.googlevideo.com.", - "rr12---sn-8qj-i5ozd.googlevideo.com.", - "rr13---sn-42u-nboze.googlevideo.com.", - "rr13---sn-42u-nbozl.googlevideo.com.", + "rr12---sn-8xgp1vo-p5qel.googlevideo.com.", + "rr12---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", + "rr12---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "rr12---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "rr12---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "rr12---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "rr12---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "rr12---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "rr12---sn-bvvbaxivnuxqjvhj5nu-vgq6.googlevideo.com.", + "rr12---sn-bvvbaxivnuxqjvhj5nu-vgqd.googlevideo.com.", + "rr12---sn-bvvbaxivnuxqjvhj5nu-vgqe.googlevideo.com.", + "rr12---sn-bvvbaxivnuxqjvhj5nu-vgql.googlevideo.com.", + "rr12---sn-bvvbaxivnuxqjvhj5nu-vgqs.googlevideo.com.", + "rr12---sn-bvvbaxivnuxqjvm-2ime.googlevideo.com.", + "rr12---sn-bvvbaxivnuxqjvm-2ims.googlevideo.com.", + "rr12---sn-bvvbaxivnuxqjvm-q4fe.googlevideo.com.", + "rr12---sn-bvvbaxivnuxqjvm-q4fl.googlevideo.com.", "rr13---sn-42u-nbozs.googlevideo.com.", - "rr14---sn-42u-nboze.googlevideo.com.", - "rr14---sn-42u-nbozl.googlevideo.com.", - "rr14---sn-42u-nbozs.googlevideo.com.", - "rr15---sn-42u-nboze.googlevideo.com.", - "rr15---sn-42u-nbozl.googlevideo.com.", - "rr16---sn-42u-nbozl.googlevideo.com.", + "rr13---sn-8xgp1vo-p5qel.googlevideo.com.", + "rr13---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", + "rr13---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "rr13---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "rr13---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "rr13---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "rr13---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "rr13---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "rr13---sn-bvvbaxivnuxqjvhj5nu-vgq6.googlevideo.com.", + "rr13---sn-bvvbaxivnuxqjvhj5nu-vgqd.googlevideo.com.", + "rr13---sn-bvvbaxivnuxqjvhj5nu-vgqe.googlevideo.com.", + "rr13---sn-bvvbaxivnuxqjvhj5nu-vgql.googlevideo.com.", + "rr13---sn-bvvbaxivnuxqjvhj5nu-vgqs.googlevideo.com.", + "rr13---sn-bvvbaxivnuxqjvhj5nu-vgqz.googlevideo.com.", + "rr13---sn-bvvbaxivnuxqjvm-q4fe.googlevideo.com.", + "rr13---sn-bvvbaxivnuxqjvm-q4fe.gvt1.com.", + "rr13---sn-bvvbaxivnuxqjvm-q4fl.googlevideo.com.", + "rr14---sn-bvvbaxivnuxqjvhj5nu-n4vl.googlevideo.com.", + "rr15---sn-bvvbaxivnuxqjvhj5nu-n4v6.googlevideo.com.", "rr2---sn-0nnpbo5a-bggl.googlevideo.com.", - "rr2---sn-2aqu-hoal6.googlevideo.com.", - "rr2---sn-2aqu-hoalk.googlevideo.com.", "rr2---sn-2aqu-hoaly.googlevideo.com.", "rr2---sn-2aqu-hoas7.googlevideo.com.", - "rr2---sn-2aqu-hoasd.googlevideo.com.", - "rr2---sn-2aqu-hoasz.googlevideo.com.", - "rr2---sn-2aqu-jbt6.googlevideo.com.", - "rr2---sn-2aqu-jbtd.googlevideo.com.", - "rr2---sn-2aqu-jxcr.googlevideo.com.", - "rr2---sn-2aqu-jxcy.googlevideo.com.", "rr2---sn-2imern76.googlevideo.com.", "rr2---sn-2imern7d.googlevideo.com.", + "rr2---sn-2imern7d.gvt1.com.", + "rr2---sn-2imern7r.googlevideo.com.", + "rr2---sn-2imern7r.gvt1.com.", "rr2---sn-2imeyn7k.googlevideo.com.", "rr2---sn-2o5ua5-53.googlevideo.com.", "rr2---sn-2oaig5-55.googlevideo.com.", + "rr2---sn-2op5q5-58.googlevideo.com.", "rr2---sn-2oq4f5-c4.googlevideo.com.", + "rr2---sn-2oq4f5-c4.gvt1.com.", "rr2---sn-2ovgq5-cw.googlevideo.com.", - "rr2---sn-30a7rne6.googlevideo.com.", "rr2---sn-30a7rned.googlevideo.com.", "rr2---sn-30a7rnek.googlevideo.com.", "rr2---sn-30a7ynek.googlevideo.com.", "rr2---sn-30a7yner.googlevideo.com.", "rr2---sn-30a7yney.googlevideo.com.", "rr2---sn-30a7ynl7.googlevideo.com.", - "rr2---sn-3n4pcxg-pju6.googlevideo.com.", - "rr2---sn-3n4pcxg-pjuz.googlevideo.com.", - "rr2---sn-42u-nbosk.googlevideo.com.", - "rr2---sn-42u-nboze.googlevideo.com.", - "rr2---sn-42u-nbozl.googlevideo.com.", - "rr2---sn-42u-nbozs.googlevideo.com.", "rr2---sn-42u-nbozz.googlevideo.com.", "rr2---sn-4g5e6ns6.googlevideo.com.", "rr2---sn-4g5e6ns7.googlevideo.com.", "rr2---sn-4g5e6nsd.googlevideo.com.", "rr2---sn-4g5e6nsk.googlevideo.com.", - "rr2---sn-4g5e6nsr.googlevideo.com.", "rr2---sn-4g5e6nss.googlevideo.com.", "rr2---sn-4g5e6nsy.googlevideo.com.", "rr2---sn-4g5e6nsz.googlevideo.com.", "rr2---sn-4g5e6nz7.googlevideo.com.", "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.", @@ -3523,7 +3933,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-4g5edndk.googlevideo.com.", "rr2---sn-4g5edndl.googlevideo.com.", "rr2---sn-4g5edndr.googlevideo.com.", - "rr2---sn-4g5ednds.googlevideo.com.", "rr2---sn-4g5edndy.googlevideo.com.", "rr2---sn-4g5edndz.googlevideo.com.", "rr2---sn-4g5ednkl.googlevideo.com.", @@ -3539,6 +3948,7 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-4g5ednss.googlevideo.com.", "rr2---sn-4g5ednsy.googlevideo.com.", "rr2---sn-4g5ednsz.googlevideo.com.", + "rr2---sn-4g5ednz7.googlevideo.com.", "rr2---sn-4g5lzne6.googlevideo.com.", "rr2---sn-4g5lzned.googlevideo.com.", "rr2---sn-4g5lznek.googlevideo.com.", @@ -3550,22 +3960,12 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-4g5lznle.googlevideo.com.", "rr2---sn-4g5lznls.googlevideo.com.", "rr2---sn-4g5lznlz.googlevideo.com.", - "rr2---sn-5abxgpxuxaxjvh-9n4l.googlevideo.com.", "rr2---sn-5abxgpxuxaxjvh-9n4s.googlevideo.com.", "rr2---sn-5abxgpxuxaxjvh-9n4z.googlevideo.com.", - "rr2---sn-5abxgpxuxaxjvh-cawe.googlevideo.com.", - "rr2---sn-5abxgpxuxaxjvh-cawl.googlevideo.com.", - "rr2---sn-5abxgpxuxaxjvh-caws.googlevideo.com.", - "rr2---sn-5abxgpxuxaxjvh-j1ae.googlevideo.com.", - "rr2---sn-5abxgpxuxaxjvh-j1as.googlevideo.com.", - "rr2---sn-5abxgpxuxaxjvh-j1az.googlevideo.com.", - "rr2---sn-5axnug5-hxm6.googlevideo.com.", - "rr2---sn-5go7yner.googlevideo.com.", "rr2---sn-5go7ynl6.googlevideo.com.", - "rr2---sn-5go7ynld.googlevideo.com.", - "rr2---sn-5go7ynlk.googlevideo.com.", "rr2---sn-5goeenes.googlevideo.com.", - "rr2---sn-5goeenez.googlevideo.com.", + "rr2---sn-5gxo-in8l.googlevideo.com.", + "rr2---sn-5gxo-in8s.googlevideo.com.", "rr2---sn-5hne6n6e.googlevideo.com.", "rr2---sn-5hne6n6l.googlevideo.com.", "rr2---sn-5hne6ns6.googlevideo.com.", @@ -3576,10 +3976,13 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-5hne6nsz.googlevideo.com.", "rr2---sn-5hne6nz6.googlevideo.com.", "rr2---sn-5hne6nzd.googlevideo.com.", + "rr2---sn-5hne6nzd.gvt1.com.", "rr2---sn-5hne6nzk.googlevideo.com.", - "rr2---sn-5hne6nzs.googlevideo.com.", + "rr2---sn-5hne6nzk.gvt1.com.", + "rr2---sn-5hne6nzy.googlevideo.com.", "rr2---sn-5hnednss.googlevideo.com.", "rr2---sn-5hnednsz.googlevideo.com.", + "rr2---sn-5hnednsz.gvt1.com.", "rr2---sn-5hnekn76.googlevideo.com.", "rr2---sn-5hnekn7d.googlevideo.com.", "rr2---sn-5hnekn7l.googlevideo.com.", @@ -3588,20 +3991,29 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-5hneknee.googlevideo.com.", "rr2---sn-5hneknek.googlevideo.com.", "rr2---sn-5hneknes.googlevideo.com.", + "rr2---sn-5hneknes.gvt1.com.", + "rr2---sn-5pgnugx5h-hn26.googlevideo.com.", + "rr2---sn-5pgnugx5h-hn2d.googlevideo.com.", + "rr2---sn-5pgnugx5h-hn2k.googlevideo.com.", "rr2---sn-5uaezndd.googlevideo.com.", "rr2---sn-5uaezne6.googlevideo.com.", "rr2---sn-5uaezned.googlevideo.com.", "rr2---sn-5uaeznel.googlevideo.com.", + "rr2---sn-5uaezner.googlevideo.com.", + "rr2---sn-5uaezner.gvt1.com.", "rr2---sn-5uaeznes.googlevideo.com.", "rr2---sn-5uaeznez.googlevideo.com.", "rr2---sn-5uaeznl6.googlevideo.com.", + "rr2---sn-5uaeznld.googlevideo.com.", "rr2---sn-5uaeznls.googlevideo.com.", "rr2---sn-5uaeznly.googlevideo.com.", "rr2---sn-5uaeznlz.googlevideo.com.", + "rr2---sn-5uaeznlz.gvt1.com.", "rr2---sn-5uaeznse.googlevideo.com.", "rr2---sn-5uaeznsl.googlevideo.com.", "rr2---sn-5uaeznss.googlevideo.com.", "rr2---sn-5uaezny6.googlevideo.com.", + "rr2---sn-5uaeznyz.googlevideo.com.", "rr2---sn-5uaeznze.googlevideo.com.", "rr2---sn-5ualdnle.googlevideo.com.", "rr2---sn-5ualdnll.googlevideo.com.", @@ -3609,6 +4021,7 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-5ualdnls.googlevideo.com.", "rr2---sn-5ualdns6.googlevideo.com.", "rr2---sn-5ualdns7.googlevideo.com.", + "rr2---sn-5ualdns7.gvt1.com.", "rr2---sn-5ualdnsd.googlevideo.com.", "rr2---sn-5ualdnse.googlevideo.com.", "rr2---sn-5ualdnsk.googlevideo.com.", @@ -3619,39 +4032,31 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-5ualdnsz.googlevideo.com.", "rr2---sn-5ualdnz7.googlevideo.com.", "rr2---sn-5ualdnze.googlevideo.com.", - "rr2---sn-8pxuuxa-nbo6l.googlevideo.com.", - "rr2---sn-8pxuuxa-nbo6s.googlevideo.com.", "rr2---sn-8qj-i5o6k.googlevideo.com.", - "rr2---sn-8qj-i5ody.googlevideo.com.", "rr2---sn-8qj-i5okl.googlevideo.com.", - "rr2---sn-8qj-i5oks.googlevideo.com.", - "rr2---sn-8qj-i5ozd.googlevideo.com.", - "rr2---sn-8qj-i5ozr.googlevideo.com.", - "rr2---sn-8qj-i5ozz.googlevideo.com.", "rr2---sn-8qj-nbo66.googlevideo.com.", - "rr2---sn-8qj-nbo67.googlevideo.com.", - "rr2---sn-8qj-nbo6r.googlevideo.com.", "rr2---sn-8qj-nbo6y.googlevideo.com.", "rr2---sn-8qj-nbod6.googlevideo.com.", + "rr2---sn-8qj-nbosd.googlevideo.com.", "rr2---sn-8xgp1vo-ab56.googlevideo.com.", "rr2---sn-8xgp1vo-ab5d.googlevideo.com.", + "rr2---sn-8xgp1vo-ab5e.googlevideo.com.", "rr2---sn-8xgp1vo-ab5l.googlevideo.com.", "rr2---sn-8xgp1vo-ab5s.googlevideo.com.", "rr2---sn-8xgp1vo-ab5z.googlevideo.com.", - "rr2---sn-8xgp1vo-nh4l.googlevideo.com.", - "rr2---sn-8xgp1vo-poql.googlevideo.com.", + "rr2---sn-8xgp1vo-p5il.googlevideo.com.", + "rr2---sn-8xgp1vo-p5qee.googlevideo.com.", + "rr2---sn-8xgp1vo-p5qel.googlevideo.com.", + "rr2---sn-8xgp1vo-p5qes.googlevideo.com.", + "rr2---sn-8xgp1vo-vgqe.googlevideo.com.", "rr2---sn-8xgp1vo-xfge.googlevideo.com.", "rr2---sn-8xgp1vo-xfgl.googlevideo.com.", "rr2---sn-8xgp1vo-xfgs.googlevideo.com.", - "rr2---sn-9gv76n7e.googlevideo.com.", - "rr2---sn-9gv76n7l.googlevideo.com.", "rr2---sn-9gv76n7s.googlevideo.com.", "rr2---sn-9gv76n7z.googlevideo.com.", - "rr2---sn-9gv7ene6.googlevideo.com.", "rr2---sn-9gv7ened.googlevideo.com.", "rr2---sn-9gv7zn76.googlevideo.com.", "rr2---sn-9gv7zn7e.googlevideo.com.", - "rr2---sn-9gv7zn7r.googlevideo.com.", "rr2---sn-9gv7zn7y.googlevideo.com.", "rr2---sn-a5m7lnl6.googlevideo.com.", "rr2---sn-a5m7lnld.googlevideo.com.", @@ -3690,7 +4095,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-ab5l6ndy.googlevideo.com.", "rr2---sn-ab5l6nk6.googlevideo.com.", "rr2---sn-ab5l6nkd.googlevideo.com.", - "rr2---sn-ab5l6nr6.googlevideo.com.", "rr2---sn-ab5l6nrd.googlevideo.com.", "rr2---sn-ab5l6nrk.googlevideo.com.", "rr2---sn-ab5l6nrl.googlevideo.com.", @@ -3721,6 +4125,7 @@ var FakeECSFQDNs = container.NewMapSet( "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-aigzrn76.googlevideo.com.", @@ -3738,24 +4143,36 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-aigzrnsz.googlevideo.com.", "rr2---sn-aigzrnz7.googlevideo.com.", "rr2---sn-aigzrnze.googlevideo.com.", + "rr2---sn-aj4g55-5v.googlevideo.com.", "rr2---sn-aj5ua5-5c.googlevideo.com.", "rr2---sn-ajab55-55.googlevideo.com.", "rr2---sn-avbpj-cq5e.googlevideo.com.", - "rr2---sn-b5hh503-h5oe.googlevideo.com.", "rr2---sn-bvvbaxivnuxq5uu-q4fe.googlevideo.com.", - "rr2---sn-c0q7lnz7.googlevideo.com.", + "rr2---sn-bvvbaxivnuxq5uu-q4fl.googlevideo.com.", + "rr2---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "rr2---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "rr2---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "rr2---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "rr2---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "rr2---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "rr2---sn-bvvbaxivnuxqjvhj5nu-vgq6.googlevideo.com.", + "rr2---sn-bvvbaxivnuxqjvhj5nu-vgqd.googlevideo.com.", + "rr2---sn-bvvbaxivnuxqjvhj5nu-vgqe.googlevideo.com.", + "rr2---sn-bvvbaxivnuxqjvhj5nu-vgql.googlevideo.com.", + "rr2---sn-bvvbaxivnuxqjvhj5nu-vgqs.googlevideo.com.", + "rr2---sn-bvvbaxivnuxqjvhj5nu-vgqz.googlevideo.com.", + "rr2---sn-bvvbaxivnuxqjvm-2ime.googlevideo.com.", + "rr2---sn-bvvbaxivnuxqjvm-2ims.googlevideo.com.", + "rr2---sn-bvvbaxivnuxqjvm-q4fe.googlevideo.com.", "rr2---sn-cvb7lne7.googlevideo.com.", "rr2---sn-cvb7lnee.googlevideo.com.", + "rr2---sn-cvb7lnlz.googlevideo.com.", "rr2---sn-cvb7sn7k.googlevideo.com.", "rr2---sn-cvb7sn7r.googlevideo.com.", - "rr2---sn-gxuo03g-3c2l.googlevideo.com.", "rr2---sn-gxuo03g-ig3l.googlevideo.com.", - "rr2---sn-h0jeenl6.googlevideo.com.", - "rr2---sn-h0jeenld.googlevideo.com.", - "rr2---sn-h0jeenle.googlevideo.com.", - "rr2---sn-h0jelnes.googlevideo.com.", - "rr2---sn-h0jelnez.googlevideo.com.", - "rr2---sn-hgn7rnll.googlevideo.com.", + "rr2---sn-hjoj-gq0l.googlevideo.com.", + "rr2---sn-hjoj-poul.googlevideo.com.", + "rr2---sn-hjoj-pous.googlevideo.com.", "rr2---sn-hoa7kn76.googlevideo.com.", "rr2---sn-hoa7kn76.gvt1.com.", "rr2---sn-hoa7rn76.googlevideo.com.", @@ -3763,10 +4180,11 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-hoa7rn7z.googlevideo.com.", "rr2---sn-hoa7rn7z.gvt1.com.", "rr2---sn-hp57kn6r.googlevideo.com.", + "rr2---sn-hp57kn6r.gvt1.com.", "rr2---sn-hp57kn6y.googlevideo.com.", "rr2---sn-hp57knd6.googlevideo.com.", + "rr2---sn-hp57knd6.gvt1.com.", "rr2---sn-hp57kndd.googlevideo.com.", - "rr2---sn-hp57kndd.gvt1.com.", "rr2---sn-hp57kndk.googlevideo.com.", "rr2---sn-hp57kndr.googlevideo.com.", "rr2---sn-hp57kndr.gvt1.com.", @@ -3775,20 +4193,22 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-hp57kndz.googlevideo.com.", "rr2---sn-hp57kndz.gvt1.com.", "rr2---sn-hp57knk7.googlevideo.com.", + "rr2---sn-hp57knk7.gvt1.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-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-hpqfxnu-oaxe.googlevideo.com.", "rr2---sn-hpqfxnu-oaxl.googlevideo.com.", - "rr2---sn-huxaqvv-ubqe.googlevideo.com.", - "rr2---sn-huxaqvv-ubql.googlevideo.com.", + "rr2---sn-hvgxoxu-jv0l.googlevideo.com.", + "rr2---sn-hvgxoxu-jv0s.googlevideo.com.", "rr2---sn-i3b7kn6s.googlevideo.com.", "rr2---sn-i3b7knld.googlevideo.com.", "rr2---sn-i3b7knlk.googlevideo.com.", @@ -3798,25 +4218,37 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-i3b7knsl.googlevideo.com.", "rr2---sn-i3b7knzl.googlevideo.com.", "rr2---sn-i3b7knzs.googlevideo.com.", - "rr2---sn-i3belne6.googlevideo.com.", + "rr2---sn-i3belney.googlevideo.com.", "rr2---sn-i3belnl6.googlevideo.com.", "rr2---sn-i3belnl7.googlevideo.com.", "rr2---sn-i3belnll.googlevideo.com.", + "rr2---sn-i3belnls.googlevideo.com.", "rr2---sn-i3bssn7e.googlevideo.com.", + "rr2---sn-jn2pgx4pcxg-w5o6.googlevideo.com.", + "rr2---sn-jn2pgx4pcxg-w5os.googlevideo.com.", + "rr2---sn-jn2pgx4pcxg-w5oz.googlevideo.com.", "rr2---sn-jvhh4pcgx-ajte.googlevideo.com.", "rr2---sn-jvhh4pcgx-ajtl.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-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-n4v7snly.googlevideo.com.", "rr2---sn-n4v7sns7.googlevideo.com.", "rr2---sn-n4v7snse.googlevideo.com.", + "rr2---sn-nh5gujvh-h4xe.googlevideo.com.", + "rr2---sn-nh5gujvh-h4xe.gvt1.com.", + "rr2---sn-nh5gujvh-h4xl.googlevideo.com.", + "rr2---sn-nh5gujvh-h4xl.gvt1.com.", "rr2---sn-npoe7ndl.googlevideo.com.", "rr2---sn-npoe7ndl.gvt1.com.", "rr2---sn-npoe7ne6.googlevideo.com.", @@ -3834,18 +4266,12 @@ var FakeECSFQDNs = container.NewMapSet( "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-npoe7nsk.gvt1.com.", "rr2---sn-npoe7nsr.googlevideo.com.", - "rr2---sn-npoe7nss.googlevideo.com.", - "rr2---sn-npoe7nss.gvt1.com.", + "rr2---sn-npoe7nsr.gvt1.com.", "rr2---sn-npoe7nsy.googlevideo.com.", - "rr2---sn-npoe7nsy.gvt1.com.", "rr2---sn-npoe7nz7.googlevideo.com.", - "rr2---sn-npoe7nz7.gvt1.com.", "rr2---sn-npoeene6.googlevideo.com.", - "rr2---sn-npoeene6.gvt1.com.", "rr2---sn-npoeened.googlevideo.com.", "rr2---sn-npoeenee.googlevideo.com.", "rr2---sn-npoeenek.googlevideo.com.", @@ -3855,38 +4281,30 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-npoeenl7.googlevideo.com.", "rr2---sn-npoeenle.googlevideo.com.", "rr2---sn-npoeenlk.googlevideo.com.", - "rr2---sn-npoeenlk.gvt1.com.", "rr2---sn-npoeenll.googlevideo.com.", "rr2---sn-npoeenly.googlevideo.com.", - "rr2---sn-npoeenly.gvt1.com.", "rr2---sn-npoeens7.googlevideo.com.", "rr2---sn-npoeens7.gvt1.com.", "rr2---sn-npoldn76.googlevideo.com.", - "rr2---sn-npoldn76.gvt1.com.", "rr2---sn-npoldn7d.googlevideo.com.", "rr2---sn-npoldn7d.gvt1.com.", "rr2---sn-npoldn7e.googlevideo.com.", - "rr2---sn-npoldn7e.gvt1.com.", "rr2---sn-npoldn7l.googlevideo.com.", - "rr2---sn-npoldn7l.gvt1.com.", - "rr2---sn-npoldn7s.googlevideo.com.", "rr2---sn-npoldn7y.googlevideo.com.", - "rr2---sn-npoldn7y.gvt1.com.", "rr2---sn-npoldn7z.googlevideo.com.", - "rr2---sn-npoldn7z.gvt1.com.", "rr2---sn-npoldne7.googlevideo.com.", - "rr2---sn-npoldne7.gvt1.com.", - "rr2---sn-nv47ln6e.googlevideo.com.", - "rr2---sn-nv47zn7r.googlevideo.com.", - "rr2---sn-nv47znee.googlevideo.com.", - "rr2---sn-nv47znel.googlevideo.com.", + "rr2---sn-ntq7yned.googlevideo.com.", + "rr2---sn-nv0uixgo-5ual.googlevideo.com.", "rr2---sn-nx57ynlk.googlevideo.com.", "rr2---sn-nx57ynsd.googlevideo.com.", "rr2---sn-nx57ynse.googlevideo.com.", "rr2---sn-nx57ynsk.googlevideo.com.", "rr2---sn-nx57ynsl.googlevideo.com.", - "rr2---sn-nx57ynss.googlevideo.com.", + "rr2---sn-nx57ynsr.googlevideo.com.", + "rr2---sn-nx57ynsy.googlevideo.com.", "rr2---sn-nx57ynsz.googlevideo.com.", + "rr2---sn-nx57ynz7.googlevideo.com.", + "rr2---sn-nx57ynze.googlevideo.com.", "rr2---sn-o097znsd.googlevideo.com.", "rr2---sn-o097znse.googlevideo.com.", "rr2---sn-o097znsk.googlevideo.com.", @@ -3895,18 +4313,20 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-o097znss.googlevideo.com.", "rr2---sn-o097znsz.googlevideo.com.", "rr2---sn-o097znz7.googlevideo.com.", - "rr2---sn-o097znzd.googlevideo.com.", "rr2---sn-o097znze.googlevideo.com.", "rr2---sn-o097znzk.googlevideo.com.", "rr2---sn-o097znzr.googlevideo.com.", "rr2---sn-oj5hn5-55.googlevideo.com.", "rr2---sn-oji3bc-5j.googlevideo.com.", + "rr2---sn-ojnpo5-58.googlevideo.com.", "rr2---sn-p5qddn76.googlevideo.com.", "rr2---sn-p5qddn7d.googlevideo.com.", "rr2---sn-p5qddn7k.googlevideo.com.", "rr2---sn-p5qddn7r.googlevideo.com.", "rr2---sn-p5qddn7z.googlevideo.com.", "rr2---sn-p5qlsn6l.googlevideo.com.", + "rr2---sn-p5qlsn6s.googlevideo.com.", + "rr2---sn-p5qlsn6z.googlevideo.com.", "rr2---sn-p5qlsn76.googlevideo.com.", "rr2---sn-p5qlsn7d.googlevideo.com.", "rr2---sn-p5qlsn7l.googlevideo.com.", @@ -3919,29 +4339,36 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-p5qlsnrr.googlevideo.com.", "rr2---sn-p5qlsny6.googlevideo.com.", "rr2---sn-p5qs7n6d.googlevideo.com.", + "rr2---sn-p5qs7n6y.googlevideo.com.", + "rr2---sn-p5qs7nd7.googlevideo.com.", "rr2---sn-p5qs7nsk.googlevideo.com.", - "rr2---sn-p5qs7nsr.googlevideo.com.", "rr2---sn-p5qs7nzk.googlevideo.com.", "rr2---sn-p5qs7nzr.googlevideo.com.", "rr2---sn-p5qs7nzy.googlevideo.com.", + "rr2---sn-paapovpnjxou0gt-nual.googlevideo.com.", + "rr2---sn-paapovpnjxou0gt-nuas.googlevideo.com.", + "rr2---sn-paapovpnjxou0gt-nuaz.googlevideo.com.", "rr2---sn-pjnpu-5hfe.googlevideo.com.", "rr2---sn-pjnpu-5hfl.googlevideo.com.", - "rr2---sn-pjx-nwv6.googlevideo.com.", - "rr2---sn-pouxgoxm5-cvbe.googlevideo.com.", + "rr2---sn-pobpb-poql.googlevideo.com.", "rr2---sn-q4fl6n66.googlevideo.com.", + "rr2---sn-q4fl6n66.gvt1.com.", "rr2---sn-q4fl6n6d.googlevideo.com.", "rr2---sn-q4fl6n6r.googlevideo.com.", "rr2---sn-q4fl6n6s.googlevideo.com.", + "rr2---sn-q4fl6n6s.gvt1.com.", "rr2---sn-q4fl6n6y.googlevideo.com.", + "rr2---sn-q4fl6n6y.gvt1.com.", "rr2---sn-q4fl6n6z.googlevideo.com.", - "rr2---sn-q4fl6nd6.googlevideo.com.", + "rr2---sn-q4fl6n6z.gvt1.com.", "rr2---sn-q4fl6nd7.googlevideo.com.", "rr2---sn-q4fl6nde.googlevideo.com.", "rr2---sn-q4fl6ndl.googlevideo.com.", "rr2---sn-q4fl6nds.googlevideo.com.", "rr2---sn-q4fl6ndz.googlevideo.com.", - "rr2---sn-q4fl6nlz.googlevideo.com.", + "rr2---sn-q4fl6ndz.gvt1.com.", "rr2---sn-q4fl6ns6.googlevideo.com.", + "rr2---sn-q4fl6ns6.gvt1.com.", "rr2---sn-q4fl6ns7.googlevideo.com.", "rr2---sn-q4fl6nsd.googlevideo.com.", "rr2---sn-q4fl6nsk.googlevideo.com.", @@ -3951,40 +4378,59 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-q4fl6nsy.googlevideo.com.", "rr2---sn-q4fl6nz6.googlevideo.com.", "rr2---sn-q4fl6nz7.googlevideo.com.", + "rr2---sn-q4fl6nz7.gvt1.com.", "rr2---sn-q4fl6nzy.googlevideo.com.", "rr2---sn-q4flrn7k.googlevideo.com.", "rr2---sn-q4flrn7r.googlevideo.com.", + "rr2---sn-q4flrn7r.gvt1.com.", "rr2---sn-q4flrn7y.googlevideo.com.", "rr2---sn-q4flrne6.googlevideo.com.", "rr2---sn-q4flrne7.googlevideo.com.", + "rr2---sn-q4flrne7.gvt1.com.", "rr2---sn-q4flrnee.googlevideo.com.", + "rr2---sn-q4flrnee.gvt1.com.", "rr2---sn-q4flrnek.googlevideo.com.", "rr2---sn-q4flrnel.googlevideo.com.", + "rr2---sn-q4flrnel.gvt1.com.", "rr2---sn-q4flrner.googlevideo.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-q4flrnl7.gvt1.com.", "rr2---sn-q4flrnle.googlevideo.com.", + "rr2---sn-q4flrnle.gvt1.com.", "rr2---sn-q4flrnlz.googlevideo.com.", - "rr2---sn-q4flrnsd.googlevideo.com.", - "rr2---sn-q4flrnsk.googlevideo.com.", "rr2---sn-q4flrnsl.googlevideo.com.", + "rr2---sn-q4flrnsl.gvt1.com.", "rr2---sn-q4flrnss.googlevideo.com.", + "rr2---sn-q4flrnss.gvt1.com.", "rr2---sn-q4fzen7e.googlevideo.com.", "rr2---sn-q4fzen7l.googlevideo.com.", + "rr2---sn-q4fzen7l.gvt1.com.", "rr2---sn-q4fzen7s.googlevideo.com.", + "rr2---sn-q4fzen7s.gvt1.com.", "rr2---sn-q4fzen7y.googlevideo.com.", "rr2---sn-q4fzene7.googlevideo.com.", + "rr2---sn-q4fzene7.gvt1.com.", "rr2---sn-q4fzenee.googlevideo.com.", "rr2---sn-qjp5q5-55.googlevideo.com.", "rr2---sn-qxo7rn7k.googlevideo.com.", - "rr2---sn-qxo7rn7r.googlevideo.com.", + "rr2---sn-qxo7rne7.googlevideo.com.", + "rr2---sn-qxo7rnee.googlevideo.com.", "rr2---sn-qxoedn7k.googlevideo.com.", "rr2---sn-qxoednee.googlevideo.com.", - "rr2---sn-t0aedn7l.googlevideo.com.", + "rr2---sn-qxoednel.googlevideo.com.", + "rr2---sn-qxoednes.googlevideo.com.", + "rr2---sn-qxuxa-5xme.googlevideo.com.", + "rr2---sn-v5goxu-jhi6.googlevideo.com.", + "rr2---sn-v5goxu-jhid.googlevideo.com.", + "rr2---sn-v5goxu-jhil.googlevideo.com.", + "rr2---sn-v5goxu-jhiz.googlevideo.com.", "rr2---sn-vgqskn66.googlevideo.com.", "rr2---sn-vgqskn67.googlevideo.com.", "rr2---sn-vgqskn6d.googlevideo.com.", @@ -4017,6 +4463,7 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-vgqsrn66.googlevideo.com.", "rr2---sn-vgqsrn67.googlevideo.com.", "rr2---sn-vgqsrn6e.googlevideo.com.", + "rr2---sn-vgqsrn6l.googlevideo.com.", "rr2---sn-vgqsrn6z.googlevideo.com.", "rr2---sn-vgqsrne6.googlevideo.com.", "rr2---sn-vgqsrned.googlevideo.com.", @@ -4028,52 +4475,20 @@ var FakeECSFQDNs = container.NewMapSet( "rr2---sn-vgqsrnlk.googlevideo.com.", "rr2---sn-vgqsrnll.googlevideo.com.", "rr2---sn-vgqsrnls.googlevideo.com.", - "rr2---sn-vgqsrnlz.googlevideo.com.", "rr2---sn-vgqsrns6.googlevideo.com.", "rr2---sn-vgqsrnsd.googlevideo.com.", + "rr2---sn-vgqsrnsr.googlevideo.com.", "rr2---sn-vgqsrnsy.googlevideo.com.", "rr2---sn-vgqsrnz6.googlevideo.com.", "rr2---sn-vgqsrnz7.googlevideo.com.", - "rr2---sn-vgqsrnzd.googlevideo.com.", "rr2---sn-vgqsrnzk.googlevideo.com.", + "rr2---sn-vgqsrnzr.googlevideo.com.", "rr2---sn-vgqsrnzs.googlevideo.com.", "rr2---sn-vgqsrnzy.googlevideo.com.", "rr2---sn-vgqsrnzz.googlevideo.com.", "rr2---sn-voxoxu-v3jl.googlevideo.com.", "rr2---sn-voxoxu-v3js.googlevideo.com.", - "rr2.sn-2oq4f5-c4.googlevideo.com.", - "rr2.sn-a5m7lnl6.googlevideo.com.", - "rr2.sn-a5m7lnld.googlevideo.com.", - "rr2.sn-a5mekn6d.googlevideo.com.", - "rr2.sn-a5mekn6k.googlevideo.com.", - "rr2.sn-a5mekn6l.googlevideo.com.", - "rr2.sn-a5mekn6r.googlevideo.com.", - "rr2.sn-a5mekn6s.googlevideo.com.", - "rr2.sn-a5mekn6z.googlevideo.com.", - "rr2.sn-a5meknd6.googlevideo.com.", - "rr2.sn-a5meknde.googlevideo.com.", - "rr2.sn-a5mekndl.googlevideo.com.", - "rr2.sn-a5meknds.googlevideo.com.", - "rr2.sn-a5mekndz.googlevideo.com.", - "rr2.sn-a5meknsd.googlevideo.com.", - "rr2.sn-a5meknsy.googlevideo.com.", - "rr2.sn-a5meknzk.googlevideo.com.", - "rr2.sn-a5meknzr.googlevideo.com.", - "rr2.sn-a5meknzs.googlevideo.com.", - "rr2.sn-a5mlrnek.googlevideo.com.", - "rr2.sn-a5mlrnl6.googlevideo.com.", - "rr2.sn-a5mlrnll.googlevideo.com.", - "rr2.sn-a5mlrnls.googlevideo.com.", - "rr2.sn-a5mlrnlz.googlevideo.com.", - "rr2.sn-a5msen76.googlevideo.com.", - "rr2.sn-a5msen7l.googlevideo.com.", - "rr2.sn-a5msen7s.googlevideo.com.", - "rr2.sn-a5msen7z.googlevideo.com.", - "rr2.sn-a5msenek.googlevideo.com.", - "rr2.sn-a5msener.googlevideo.com.", - "rr2.sn-a5msenes.googlevideo.com.", - "rr2.sn-a5msenl7.googlevideo.com.", - "rr2.sn-a5msenle.googlevideo.com.", + "rr2.sn-5hnekn76.googlevideo.com.", "rr2.sn-hp57kn6r.googlevideo.com.", "rr2.sn-hp57kn6y.googlevideo.com.", "rr2.sn-hp57knd6.googlevideo.com.", @@ -4085,109 +4500,48 @@ var FakeECSFQDNs = container.NewMapSet( "rr2.sn-hp57kndz.googlevideo.com.", "rr2.sn-hp57knk7.googlevideo.com.", "rr2.sn-hp57yn7y.googlevideo.com.", - "rr2.sn-hp57ynl6.googlevideo.com.", "rr2.sn-hp57ynlr.googlevideo.com.", "rr2.sn-hp57ynly.googlevideo.com.", "rr2.sn-hp57yns7.googlevideo.com.", "rr2.sn-hp57ynse.googlevideo.com.", "rr2.sn-hp57ynsl.googlevideo.com.", - "rr2.sn-hpqfxnu-oaxl.googlevideo.com.", "rr2.sn-nx57ynsk.googlevideo.com.", - "rr2.sn-q4fl6n6r.googlevideo.com.", - "rr2.sn-q4fl6n6s.googlevideo.com.", - "rr2.sn-q4fl6nd6.googlevideo.com.", - "rr2.sn-q4fl6nsy.googlevideo.com.", - "rr2.sn-q4flrnl6.googlevideo.com.", - "rr2.sn-q4flrnld.googlevideo.com.", - "rr2.sn-q4flrnle.googlevideo.com.", - "rr2.sn-q4flrnlz.googlevideo.com.", - "rr2.sn-q4flrnsd.googlevideo.com.", - "rr2.sn-q4flrnsk.googlevideo.com.", - "rr2.sn-q4flrnsl.googlevideo.com.", - "rr2.sn-q4flrnss.googlevideo.com.", - "rr2.sn-q4fzen7e.googlevideo.com.", + "rr2.sn-q4fl6n6y.googlevideo.com.", + "rr2.sn-q4fl6ns7.googlevideo.com.", + "rr2.sn-q4flrnee.googlevideo.com.", + "rr2.sn-q4flrnez.googlevideo.com.", "rr2.sn-q4fzen7l.googlevideo.com.", - "rr2.sn-q4fzen7s.googlevideo.com.", - "rr2.sn-q4fzen7y.googlevideo.com.", - "rr2.sn-q4fzene7.googlevideo.com.", - "rr2.sn-q4fzenee.googlevideo.com.", - "rr2.sn-vgqskn66.googlevideo.com.", - "rr2.sn-vgqskn67.googlevideo.com.", - "rr2.sn-vgqskn6d.googlevideo.com.", - "rr2.sn-vgqskn6s.googlevideo.com.", - "rr2.sn-vgqskn6z.googlevideo.com.", - "rr2.sn-vgqsknld.googlevideo.com.", - "rr2.sn-vgqsknlk.googlevideo.com.", - "rr2.sn-vgqsknlr.googlevideo.com.", - "rr2.sn-vgqskns7.googlevideo.com.", - "rr2.sn-vgqsknse.googlevideo.com.", - "rr2.sn-vgqsknsk.googlevideo.com.", - "rr2.sn-vgqsknz6.googlevideo.com.", - "rr2.sn-vgqsknz7.googlevideo.com.", - "rr2.sn-vgqsknzd.googlevideo.com.", - "rr2.sn-vgqsknze.googlevideo.com.", - "rr2.sn-vgqsknzk.googlevideo.com.", - "rr2.sn-vgqsknzl.googlevideo.com.", - "rr2.sn-vgqsknzr.googlevideo.com.", - "rr2.sn-vgqsknzs.googlevideo.com.", - "rr2.sn-vgqsknzy.googlevideo.com.", - "rr2.sn-vgqsknzz.googlevideo.com.", - "rr2.sn-vgqsrn66.googlevideo.com.", - "rr2.sn-vgqsrn67.googlevideo.com.", - "rr2.sn-vgqsrn6e.googlevideo.com.", - "rr2.sn-vgqsrn6z.googlevideo.com.", - "rr2.sn-vgqsrnes.googlevideo.com.", - "rr2.sn-vgqsrnl6.googlevideo.com.", - "rr2.sn-vgqsrnld.googlevideo.com.", - "rr2.sn-vgqsrnlk.googlevideo.com.", - "rr2.sn-vgqsrnls.googlevideo.com.", - "rr2.sn-vgqsrnlz.googlevideo.com.", - "rr2.sn-vgqsrns6.googlevideo.com.", - "rr2.sn-vgqsrnsd.googlevideo.com.", - "rr2.sn-vgqsrnsy.googlevideo.com.", - "rr2.sn-vgqsrnz6.googlevideo.com.", - "rr2.sn-vgqsrnz7.googlevideo.com.", - "rr2.sn-vgqsrnzd.googlevideo.com.", - "rr2.sn-vgqsrnzk.googlevideo.com.", - "rr2.sn-vgqsrnzs.googlevideo.com.", - "rr2.sn-vgqsrnzy.googlevideo.com.", "rr2.sn-vgqsrnzz.googlevideo.com.", "rr3---sn-0nnpbo5a-bggl.googlevideo.com.", - "rr3---sn-2aqu-hoal6.googlevideo.com.", "rr3---sn-2aqu-hoas7.googlevideo.com.", - "rr3---sn-2aqu-hoasd.googlevideo.com.", "rr3---sn-2aqu-hoasz.googlevideo.com.", - "rr3---sn-2aqu-jbt6.googlevideo.com.", - "rr3---sn-2aqu-jbtd.googlevideo.com.", - "rr3---sn-2aqu-jxcr.googlevideo.com.", - "rr3---sn-2aqu-jxcy.googlevideo.com.", "rr3---sn-2imern76.googlevideo.com.", "rr3---sn-2imern7d.googlevideo.com.", + "rr3---sn-2imern7d.gvt1.com.", + "rr3---sn-2imern7r.googlevideo.com.", + "rr3---sn-2imern7r.gvt1.com.", "rr3---sn-2imeyn7k.googlevideo.com.", "rr3---sn-2o5ua5-53.googlevideo.com.", "rr3---sn-2oaig5-55.googlevideo.com.", + "rr3---sn-2op5q5-58.googlevideo.com.", "rr3---sn-2oq4f5-c4.googlevideo.com.", - "rr3---sn-2ovgq5-cw.googlevideo.com.", + "rr3---sn-2oq4f5-c4.gvt1.com.", "rr3---sn-30a7rned.googlevideo.com.", "rr3---sn-30a7rnek.googlevideo.com.", "rr3---sn-30a7ynek.googlevideo.com.", "rr3---sn-30a7yner.googlevideo.com.", "rr3---sn-30a7yney.googlevideo.com.", "rr3---sn-30a7ynl7.googlevideo.com.", - "rr3---sn-42u-nboze.googlevideo.com.", - "rr3---sn-42u-nbozl.googlevideo.com.", - "rr3---sn-42u-nbozs.googlevideo.com.", - "rr3---sn-42u-nbozz.googlevideo.com.", "rr3---sn-4g5e6ns6.googlevideo.com.", "rr3---sn-4g5e6ns7.googlevideo.com.", "rr3---sn-4g5e6nsd.googlevideo.com.", "rr3---sn-4g5e6nsk.googlevideo.com.", - "rr3---sn-4g5e6nsr.googlevideo.com.", "rr3---sn-4g5e6nss.googlevideo.com.", "rr3---sn-4g5e6nsy.googlevideo.com.", "rr3---sn-4g5e6nsz.googlevideo.com.", "rr3---sn-4g5e6nz7.googlevideo.com.", "rr3---sn-4g5e6nze.googlevideo.com.", + "rr3---sn-4g5e6nzl.googlevideo.com.", "rr3---sn-4g5e6nzs.googlevideo.com.", "rr3---sn-4g5e6nzz.googlevideo.com.", "rr3---sn-4g5edn6k.googlevideo.com.", @@ -4199,13 +4553,11 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-4g5edndk.googlevideo.com.", "rr3---sn-4g5edndl.googlevideo.com.", "rr3---sn-4g5edndr.googlevideo.com.", - "rr3---sn-4g5ednds.googlevideo.com.", "rr3---sn-4g5edndy.googlevideo.com.", "rr3---sn-4g5edndz.googlevideo.com.", "rr3---sn-4g5ednkl.googlevideo.com.", "rr3---sn-4g5ednld.googlevideo.com.", "rr3---sn-4g5ednly.googlevideo.com.", - "rr3---sn-4g5edns6.googlevideo.com.", "rr3---sn-4g5edns7.googlevideo.com.", "rr3---sn-4g5ednsd.googlevideo.com.", "rr3---sn-4g5ednse.googlevideo.com.", @@ -4228,17 +4580,8 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-4g5lznle.googlevideo.com.", "rr3---sn-4g5lznls.googlevideo.com.", "rr3---sn-4g5lznlz.googlevideo.com.", - "rr3---sn-5abxgpxuxaxjvh-9n4e.googlevideo.com.", - "rr3---sn-5abxgpxuxaxjvh-9n4l.googlevideo.com.", "rr3---sn-5abxgpxuxaxjvh-9n4z.googlevideo.com.", - "rr3---sn-5abxgpxuxaxjvh-cawe.googlevideo.com.", - "rr3---sn-5abxgpxuxaxjvh-caws.googlevideo.com.", - "rr3---sn-5abxgpxuxaxjvh-j1ae.googlevideo.com.", - "rr3---sn-5abxgpxuxaxjvh-j1as.googlevideo.com.", - "rr3---sn-5abxgpxuxaxjvh-j1az.googlevideo.com.", - "rr3---sn-5go7yner.googlevideo.com.", "rr3---sn-5go7ynl6.googlevideo.com.", - "rr3---sn-5go7ynld.googlevideo.com.", "rr3---sn-5goeenes.googlevideo.com.", "rr3---sn-5goeenez.googlevideo.com.", "rr3---sn-5hne6n6e.googlevideo.com.", @@ -4262,10 +4605,10 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-5hnekn7s.googlevideo.com.", "rr3---sn-5hnekn7z.googlevideo.com.", "rr3---sn-5hneknee.googlevideo.com.", - "rr3---sn-5hneknee.gvt1.com.", "rr3---sn-5hneknek.googlevideo.com.", "rr3---sn-5hneknes.googlevideo.com.", "rr3---sn-5uaezndd.googlevideo.com.", + "rr3---sn-5uaezndd.gvt1.com.", "rr3---sn-5uaezne6.googlevideo.com.", "rr3---sn-5uaezned.googlevideo.com.", "rr3---sn-5uaeznel.googlevideo.com.", @@ -4274,7 +4617,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-5uaeznl6.googlevideo.com.", "rr3---sn-5uaeznls.googlevideo.com.", "rr3---sn-5uaeznly.googlevideo.com.", - "rr3---sn-5uaeznlz.googlevideo.com.", "rr3---sn-5uaeznse.googlevideo.com.", "rr3---sn-5uaeznsl.googlevideo.com.", "rr3---sn-5uaeznss.googlevideo.com.", @@ -4292,58 +4634,45 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-5ualdnsk.googlevideo.com.", "rr3---sn-5ualdnsl.googlevideo.com.", "rr3---sn-5ualdnsr.googlevideo.com.", - "rr3---sn-5ualdnss.googlevideo.com.", "rr3---sn-5ualdnsy.googlevideo.com.", + "rr3---sn-5ualdnsy.gvt1.com.", "rr3---sn-5ualdnsz.googlevideo.com.", "rr3---sn-5ualdnz7.googlevideo.com.", "rr3---sn-5ualdnze.googlevideo.com.", - "rr3---sn-8pxuuxa-nbo6l.googlevideo.com.", - "rr3---sn-8pxuuxa-nbo6s.googlevideo.com.", - "rr3---sn-8pxuuxa-nboz6.googlevideo.com.", - "rr3---sn-8qj-i2iez.googlevideo.com.", - "rr3---sn-8qj-i5o66.googlevideo.com.", - "rr3---sn-8qj-i5o6k.googlevideo.com.", - "rr3---sn-8qj-i5ody.googlevideo.com.", - "rr3---sn-8qj-i5oks.googlevideo.com.", - "rr3---sn-8qj-i5ozd.googlevideo.com.", - "rr3---sn-8qj-i5ozr.googlevideo.com.", - "rr3---sn-8qj-i5ozz.googlevideo.com.", "rr3---sn-8qj-nbo66.googlevideo.com.", - "rr3---sn-8qj-nbo67.googlevideo.com.", "rr3---sn-8qj-nbod6.googlevideo.com.", + "rr3---sn-8qj-nbosd.googlevideo.com.", + "rr3---sn-8xgp1vo-a5ml.googlevideo.com.", "rr3---sn-8xgp1vo-ab56.googlevideo.com.", "rr3---sn-8xgp1vo-ab5d.googlevideo.com.", "rr3---sn-8xgp1vo-ab5e.googlevideo.com.", "rr3---sn-8xgp1vo-ab5l.googlevideo.com.", "rr3---sn-8xgp1vo-ab5s.googlevideo.com.", "rr3---sn-8xgp1vo-ab5z.googlevideo.com.", - "rr3---sn-8xgp1vo-nh4e.googlevideo.com.", - "rr3---sn-8xgp1vo-poql.googlevideo.com.", + "rr3---sn-8xgp1vo-p5qee.googlevideo.com.", + "rr3---sn-8xgp1vo-p5qel.googlevideo.com.", + "rr3---sn-8xgp1vo-p5qes.googlevideo.com.", "rr3---sn-8xgp1vo-vgqe.googlevideo.com.", "rr3---sn-8xgp1vo-xfge.googlevideo.com.", "rr3---sn-8xgp1vo-xfgl.googlevideo.com.", "rr3---sn-8xgp1vo-xfgs.googlevideo.com.", - "rr3---sn-9gv76n7e.googlevideo.com.", - "rr3---sn-9gv76n7l.googlevideo.com.", "rr3---sn-9gv76n7s.googlevideo.com.", "rr3---sn-9gv76n7z.googlevideo.com.", - "rr3---sn-9gv7ene6.googlevideo.com.", - "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-a5mekn6d.googlevideo.com.", "rr3---sn-a5mekn6k.googlevideo.com.", + "rr3---sn-a5mekn6k.gvt1.com.", "rr3---sn-a5mekn6l.googlevideo.com.", "rr3---sn-a5mekn6r.googlevideo.com.", "rr3---sn-a5mekn6s.googlevideo.com.", "rr3---sn-a5mekn6z.googlevideo.com.", "rr3---sn-a5meknd6.googlevideo.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-a5meknsd.googlevideo.com.", @@ -4376,11 +4705,14 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-ab5l6nrl.googlevideo.com.", "rr3---sn-ab5l6nrr.googlevideo.com.", "rr3---sn-ab5l6nrs.googlevideo.com.", + "rr3---sn-ab5l6nrz.googlevideo.com.", + "rr3---sn-ab5sznld.googlevideo.com.", "rr3---sn-ab5sznly.googlevideo.com.", "rr3---sn-ab5sznz6.googlevideo.com.", "rr3---sn-ab5sznzd.googlevideo.com.", "rr3---sn-ab5sznze.googlevideo.com.", "rr3---sn-ab5sznzk.googlevideo.com.", + "rr3---sn-ab5sznzl.googlevideo.com.", "rr3---sn-ab5sznzr.googlevideo.com.", "rr3---sn-ab5sznzs.googlevideo.com.", "rr3---sn-ab5sznzy.googlevideo.com.", @@ -4397,6 +4729,7 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-aigl6nsr.googlevideo.com.", "rr3---sn-aigl6nz7.googlevideo.com.", "rr3---sn-aigl6nze.googlevideo.com.", + "rr3---sn-aigl6nzk.googlevideo.com.", "rr3---sn-aigl6nzl.googlevideo.com.", "rr3---sn-aigl6nzr.googlevideo.com.", "rr3---sn-aigl6nzs.googlevideo.com.", @@ -4411,29 +4744,40 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-aigzrnse.googlevideo.com.", "rr3---sn-aigzrnsl.googlevideo.com.", "rr3---sn-aigzrnsr.googlevideo.com.", - "rr3---sn-aigzrnss.googlevideo.com.", "rr3---sn-aigzrnsz.googlevideo.com.", "rr3---sn-aigzrnz7.googlevideo.com.", "rr3---sn-aigzrnze.googlevideo.com.", + "rr3---sn-aj4g55-5v.googlevideo.com.", + "rr3---sn-aj5ua5-5c.googlevideo.com.", "rr3---sn-ajab55-55.googlevideo.com.", + "rr3---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", "rr3---sn-bvvbaxivnuxq5uu-q4fe.googlevideo.com.", "rr3---sn-bvvbaxivnuxq5uu-q4fl.googlevideo.com.", - "rr3---sn-c0q7lnz7.googlevideo.com.", + "rr3---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "rr3---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "rr3---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "rr3---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "rr3---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "rr3---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "rr3---sn-bvvbaxivnuxqjvhj5nu-n4vl.googlevideo.com.", + "rr3---sn-bvvbaxivnuxqjvhj5nu-vgq6.googlevideo.com.", + "rr3---sn-bvvbaxivnuxqjvhj5nu-vgqd.googlevideo.com.", + "rr3---sn-bvvbaxivnuxqjvhj5nu-vgqe.googlevideo.com.", + "rr3---sn-bvvbaxivnuxqjvhj5nu-vgql.googlevideo.com.", + "rr3---sn-bvvbaxivnuxqjvhj5nu-vgqs.googlevideo.com.", + "rr3---sn-bvvbaxivnuxqjvhj5nu-vgqz.googlevideo.com.", + "rr3---sn-bvvbaxivnuxqjvm-2ims.googlevideo.com.", + "rr3---sn-bvvbaxivnuxqjvm-q4fl.googlevideo.com.", "rr3---sn-cvb7lne7.googlevideo.com.", "rr3---sn-cvb7lnee.googlevideo.com.", "rr3---sn-cvb7lnlz.googlevideo.com.", "rr3---sn-cvb7sn7k.googlevideo.com.", "rr3---sn-cvb7sn7r.googlevideo.com.", - "rr3---sn-gxuo03g-3c2l.googlevideo.com.", - "rr3---sn-gxuo03g-ig3e.googlevideo.com.", "rr3---sn-gxuo03g-ig3l.googlevideo.com.", - "rr3---sn-h0jeenl6.googlevideo.com.", - "rr3---sn-h0jeenld.googlevideo.com.", - "rr3---sn-h0jeenle.googlevideo.com.", - "rr3---sn-h0jelne6.googlevideo.com.", - "rr3---sn-h0jelnes.googlevideo.com.", - "rr3---sn-h0jelnez.googlevideo.com.", + "rr3---sn-hgn7rn7k.googlevideo.com.", "rr3---sn-hgn7rnls.googlevideo.com.", + "rr3---sn-hjoj-gq0l.googlevideo.com.", + "rr3---sn-hjoj-poul.googlevideo.com.", "rr3---sn-hoa7kn76.googlevideo.com.", "rr3---sn-hoa7kn76.gvt1.com.", "rr3---sn-hoa7kn7z.googlevideo.com.", @@ -4443,31 +4787,35 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-hoa7rn7z.googlevideo.com.", "rr3---sn-hoa7rn7z.gvt1.com.", "rr3---sn-hp57kn6r.googlevideo.com.", + "rr3---sn-hp57kn6r.gvt1.com.", "rr3---sn-hp57kn6y.googlevideo.com.", "rr3---sn-hp57kn6y.gvt1.com.", "rr3---sn-hp57knd6.googlevideo.com.", + "rr3---sn-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-hp57kndy.googlevideo.com.", + "rr3---sn-hp57kndy.gvt1.com.", "rr3---sn-hp57kndz.googlevideo.com.", "rr3---sn-hp57knk7.googlevideo.com.", - "rr3---sn-hp57yn7r.googlevideo.com.", "rr3---sn-hp57yn7y.googlevideo.com.", "rr3---sn-hp57yne7.googlevideo.com.", "rr3---sn-hp57ynee.googlevideo.com.", "rr3---sn-hp57ynl6.googlevideo.com.", "rr3---sn-hp57ynlr.googlevideo.com.", + "rr3---sn-hp57ynlr.gvt1.com.", "rr3---sn-hp57ynly.googlevideo.com.", "rr3---sn-hp57yns7.googlevideo.com.", - "rr3---sn-hp57yns7.gvt1.com.", "rr3---sn-hp57ynse.googlevideo.com.", + "rr3---sn-hp57ynse.gvt1.com.", "rr3---sn-hp57ynsl.googlevideo.com.", "rr3---sn-hp57ynss.googlevideo.com.", - "rr3---sn-huxaqvv-ubqe.googlevideo.com.", + "rr3---sn-hp57ynss.gvt1.com.", "rr3---sn-i3b7kn6s.googlevideo.com.", - "rr3---sn-i3b7knld.googlevideo.com.", "rr3---sn-i3b7knlk.googlevideo.com.", "rr3---sn-i3b7kns6.googlevideo.com.", "rr3---sn-i3b7knsd.googlevideo.com.", @@ -4476,15 +4824,16 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-i3b7knzl.googlevideo.com.", "rr3---sn-i3b7knzs.googlevideo.com.", "rr3---sn-i3belne6.googlevideo.com.", - "rr3---sn-i3belney.googlevideo.com.", - "rr3---sn-i3belnl6.googlevideo.com.", "rr3---sn-i3belnl7.googlevideo.com.", "rr3---sn-i3belnll.googlevideo.com.", + "rr3---sn-i3belnls.googlevideo.com.", "rr3---sn-i3bssn7e.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-jn2pgx4pcxg-w5o6.googlevideo.com.", + "rr3---sn-jn2pgx4pcxg-w5os.googlevideo.com.", + "rr3---sn-jn2pgx4pcxg-w5oz.googlevideo.com.", + "rr3---sn-jxopj-n5oe.googlevideo.com.", + "rr3---sn-jxopj-nh4e.googlevideo.com.", + "rr3---sn-jxopj-nh4e.gvt1.com.", "rr3---sn-n4v7snee.googlevideo.com.", "rr3---sn-n4v7sney.googlevideo.com.", "rr3---sn-n4v7snl7.googlevideo.com.", @@ -4495,7 +4844,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-n4v7sns7.googlevideo.com.", "rr3---sn-n4v7snse.googlevideo.com.", "rr3---sn-npoe7ndl.googlevideo.com.", - "rr3---sn-npoe7ndl.gvt1.com.", "rr3---sn-npoe7ne6.googlevideo.com.", "rr3---sn-npoe7ne7.googlevideo.com.", "rr3---sn-npoe7ned.googlevideo.com.", @@ -4514,14 +4862,9 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-npoe7nsk.googlevideo.com.", "rr3---sn-npoe7nsk.gvt1.com.", "rr3---sn-npoe7nsl.googlevideo.com.", - "rr3---sn-npoe7nsl.gvt1.com.", "rr3---sn-npoe7nsr.googlevideo.com.", - "rr3---sn-npoe7nss.googlevideo.com.", - "rr3---sn-npoe7nss.gvt1.com.", "rr3---sn-npoe7nsy.googlevideo.com.", - "rr3---sn-npoe7nsy.gvt1.com.", "rr3---sn-npoe7nz7.googlevideo.com.", - "rr3---sn-npoe7nz7.gvt1.com.", "rr3---sn-npoeene6.googlevideo.com.", "rr3---sn-npoeened.googlevideo.com.", "rr3---sn-npoeenee.googlevideo.com.", @@ -4530,16 +4873,11 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-npoeeney.googlevideo.com.", "rr3---sn-npoeenez.googlevideo.com.", "rr3---sn-npoeenl7.googlevideo.com.", - "rr3---sn-npoeenl7.gvt1.com.", "rr3---sn-npoeenle.googlevideo.com.", "rr3---sn-npoeenlk.googlevideo.com.", - "rr3---sn-npoeenlk.gvt1.com.", "rr3---sn-npoeenll.googlevideo.com.", - "rr3---sn-npoeenll.gvt1.com.", "rr3---sn-npoeenly.googlevideo.com.", - "rr3---sn-npoeenly.gvt1.com.", "rr3---sn-npoeens7.googlevideo.com.", - "rr3---sn-npoeens7.gvt1.com.", "rr3---sn-npoldn76.googlevideo.com.", "rr3---sn-npoldn76.gvt1.com.", "rr3---sn-npoldn7d.googlevideo.com.", @@ -4547,25 +4885,20 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-npoldn7e.googlevideo.com.", "rr3---sn-npoldn7e.gvt1.com.", "rr3---sn-npoldn7l.googlevideo.com.", - "rr3---sn-npoldn7l.gvt1.com.", - "rr3---sn-npoldn7s.googlevideo.com.", "rr3---sn-npoldn7y.googlevideo.com.", - "rr3---sn-npoldn7y.gvt1.com.", "rr3---sn-npoldn7z.googlevideo.com.", - "rr3---sn-npoldn7z.gvt1.com.", "rr3---sn-npoldne7.googlevideo.com.", "rr3---sn-npoldne7.gvt1.com.", - "rr3---sn-nv47ln6e.googlevideo.com.", - "rr3---sn-nv47zn7r.googlevideo.com.", - "rr3---sn-nv47zn7y.googlevideo.com.", - "rr3---sn-nv47zne7.googlevideo.com.", - "rr3---sn-nv47znee.googlevideo.com.", + "rr3---sn-nx57ynlk.googlevideo.com.", "rr3---sn-nx57ynsd.googlevideo.com.", "rr3---sn-nx57ynse.googlevideo.com.", "rr3---sn-nx57ynsk.googlevideo.com.", - "rr3---sn-nx57ynsl.googlevideo.com.", + "rr3---sn-nx57ynsr.googlevideo.com.", "rr3---sn-nx57ynss.googlevideo.com.", + "rr3---sn-nx57ynsy.googlevideo.com.", "rr3---sn-nx57ynsz.googlevideo.com.", + "rr3---sn-nx57ynz7.googlevideo.com.", + "rr3---sn-nx57ynze.googlevideo.com.", "rr3---sn-o097znsd.googlevideo.com.", "rr3---sn-o097znse.googlevideo.com.", "rr3---sn-o097znsk.googlevideo.com.", @@ -4574,18 +4907,18 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-o097znss.googlevideo.com.", "rr3---sn-o097znsz.googlevideo.com.", "rr3---sn-o097znz7.googlevideo.com.", - "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-oji3bc-5j.googlevideo.com.", + "rr3---sn-ojnpo5-58.googlevideo.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-p5qlsn6s.googlevideo.com.", + "rr3---sn-p5qlsn6z.googlevideo.com.", "rr3---sn-p5qlsn76.googlevideo.com.", "rr3---sn-p5qlsn7d.googlevideo.com.", "rr3---sn-p5qlsn7l.googlevideo.com.", @@ -4595,19 +4928,24 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-p5qlsndr.googlevideo.com.", "rr3---sn-p5qlsndz.googlevideo.com.", "rr3---sn-p5qlsnrl.googlevideo.com.", - "rr3---sn-p5qlsny6.googlevideo.com.", + "rr3---sn-p5qlsnrr.googlevideo.com.", "rr3---sn-p5qs7n6d.googlevideo.com.", + "rr3---sn-p5qs7n6y.googlevideo.com.", + "rr3---sn-p5qs7nd7.googlevideo.com.", "rr3---sn-p5qs7nsk.googlevideo.com.", - "rr3---sn-p5qs7nsr.googlevideo.com.", "rr3---sn-p5qs7nzk.googlevideo.com.", "rr3---sn-p5qs7nzr.googlevideo.com.", "rr3---sn-p5qs7nzy.googlevideo.com.", + "rr3---sn-pobpb-poql.googlevideo.com.", "rr3---sn-q4fl6n66.googlevideo.com.", "rr3---sn-q4fl6n6d.googlevideo.com.", "rr3---sn-q4fl6n6r.googlevideo.com.", "rr3---sn-q4fl6n6s.googlevideo.com.", + "rr3---sn-q4fl6n6s.gvt1.com.", "rr3---sn-q4fl6n6y.googlevideo.com.", + "rr3---sn-q4fl6n6y.gvt1.com.", "rr3---sn-q4fl6n6z.googlevideo.com.", + "rr3---sn-q4fl6n6z.gvt1.com.", "rr3---sn-q4fl6nd7.googlevideo.com.", "rr3---sn-q4fl6nde.googlevideo.com.", "rr3---sn-q4fl6ndl.googlevideo.com.", @@ -4618,55 +4956,71 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-q4fl6ns7.googlevideo.com.", "rr3---sn-q4fl6nsd.googlevideo.com.", "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-q4fl6nss.googlevideo.com.", "rr3---sn-q4fl6nsy.googlevideo.com.", "rr3---sn-q4fl6nz6.googlevideo.com.", + "rr3---sn-q4fl6nz6.gvt1.com.", "rr3---sn-q4fl6nz7.googlevideo.com.", + "rr3---sn-q4fl6nz7.gvt1.com.", "rr3---sn-q4fl6nzy.googlevideo.com.", "rr3---sn-q4flrn7k.googlevideo.com.", "rr3---sn-q4flrn7r.googlevideo.com.", "rr3---sn-q4flrn7y.googlevideo.com.", "rr3---sn-q4flrne6.googlevideo.com.", + "rr3---sn-q4flrne6.gvt1.com.", "rr3---sn-q4flrne7.googlevideo.com.", + "rr3---sn-q4flrne7.gvt1.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-q4flrnes.googlevideo.com.", "rr3---sn-q4flrney.googlevideo.com.", "rr3---sn-q4flrnez.googlevideo.com.", "rr3---sn-q4flrnl6.googlevideo.com.", + "rr3---sn-q4flrnl6.gvt1.com.", "rr3---sn-q4flrnl7.googlevideo.com.", "rr3---sn-q4flrnld.googlevideo.com.", "rr3---sn-q4flrnle.googlevideo.com.", "rr3---sn-q4flrnlz.googlevideo.com.", - "rr3---sn-q4flrnsd.googlevideo.com.", + "rr3---sn-q4flrnlz.gvt1.com.", "rr3---sn-q4flrnsk.googlevideo.com.", + "rr3---sn-q4flrnsk.gvt1.com.", "rr3---sn-q4flrnss.googlevideo.com.", "rr3---sn-q4fzen7e.googlevideo.com.", + "rr3---sn-q4fzen7e.gvt1.com.", "rr3---sn-q4fzen7l.googlevideo.com.", + "rr3---sn-q4fzen7l.gvt1.com.", "rr3---sn-q4fzen7s.googlevideo.com.", "rr3---sn-q4fzen7y.googlevideo.com.", "rr3---sn-q4fzene7.googlevideo.com.", "rr3---sn-q4fzenee.googlevideo.com.", + "rr3---sn-q4fzenee.gvt1.com.", "rr3---sn-qja5mc-5h.googlevideo.com.", "rr3---sn-qjp5q5-55.googlevideo.com.", "rr3---sn-qxo7rn7k.googlevideo.com.", - "rr3---sn-qxo7rn7r.googlevideo.com.", - "rr3---sn-qxo7rn7y.googlevideo.com.", + "rr3---sn-qxo7rne7.googlevideo.com.", + "rr3---sn-qxo7rnee.googlevideo.com.", "rr3---sn-qxoedn7k.googlevideo.com.", "rr3---sn-qxoedne7.googlevideo.com.", "rr3---sn-qxoednee.googlevideo.com.", - "rr3---sn-t0aedn7l.googlevideo.com.", + "rr3---sn-qxoednel.googlevideo.com.", + "rr3---sn-qxoednes.googlevideo.com.", + "rr3---sn-u1hp55-5c.googlevideo.com.", + "rr3---sn-u1hp55-5c.gvt1.com.", + "rr3---sn-v5goxu-jhil.googlevideo.com.", "rr3---sn-vgqskn66.googlevideo.com.", "rr3---sn-vgqskn67.googlevideo.com.", "rr3---sn-vgqskn6d.googlevideo.com.", "rr3---sn-vgqskn6s.googlevideo.com.", "rr3---sn-vgqskn6z.googlevideo.com.", "rr3---sn-vgqskne6.googlevideo.com.", - "rr3---sn-vgqskned.googlevideo.com.", "rr3---sn-vgqsknek.googlevideo.com.", "rr3---sn-vgqsknes.googlevideo.com.", "rr3---sn-vgqsknez.googlevideo.com.", @@ -4683,7 +5037,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-vgqsknz7.googlevideo.com.", "rr3---sn-vgqsknzd.googlevideo.com.", "rr3---sn-vgqsknze.googlevideo.com.", - "rr3---sn-vgqsknzk.googlevideo.com.", "rr3---sn-vgqsknzl.googlevideo.com.", "rr3---sn-vgqsknzr.googlevideo.com.", "rr3---sn-vgqsknzs.googlevideo.com.", @@ -4703,12 +5056,10 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-vgqsrnld.googlevideo.com.", "rr3---sn-vgqsrnlk.googlevideo.com.", "rr3---sn-vgqsrnll.googlevideo.com.", - "rr3---sn-vgqsrnls.googlevideo.com.", "rr3---sn-vgqsrnlz.googlevideo.com.", "rr3---sn-vgqsrns6.googlevideo.com.", "rr3---sn-vgqsrnsd.googlevideo.com.", "rr3---sn-vgqsrnsr.googlevideo.com.", - "rr3---sn-vgqsrnsy.googlevideo.com.", "rr3---sn-vgqsrnz6.googlevideo.com.", "rr3---sn-vgqsrnz7.googlevideo.com.", "rr3---sn-vgqsrnzd.googlevideo.com.", @@ -4717,40 +5068,7 @@ var FakeECSFQDNs = container.NewMapSet( "rr3---sn-vgqsrnzs.googlevideo.com.", "rr3---sn-vgqsrnzy.googlevideo.com.", "rr3---sn-vgqsrnzz.googlevideo.com.", - "rr3.sn-2oq4f5-c4.googlevideo.com.", - "rr3.sn-2ovgq5-cw.googlevideo.com.", - "rr3.sn-a5m7lnl6.googlevideo.com.", - "rr3.sn-a5m7lnld.googlevideo.com.", - "rr3.sn-a5mekn6d.googlevideo.com.", - "rr3.sn-a5mekn6k.googlevideo.com.", - "rr3.sn-a5mekn6l.googlevideo.com.", - "rr3.sn-a5mekn6r.googlevideo.com.", - "rr3.sn-a5mekn6s.googlevideo.com.", - "rr3.sn-a5mekn6z.googlevideo.com.", - "rr3.sn-a5meknd6.googlevideo.com.", - "rr3.sn-a5meknde.googlevideo.com.", - "rr3.sn-a5mekndl.googlevideo.com.", - "rr3.sn-a5meknds.googlevideo.com.", - "rr3.sn-a5mekndz.googlevideo.com.", - "rr3.sn-a5meknsd.googlevideo.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-a5mlrnl6.googlevideo.com.", - "rr3.sn-a5mlrnll.googlevideo.com.", - "rr3.sn-a5mlrnls.googlevideo.com.", - "rr3.sn-a5mlrnlz.googlevideo.com.", - "rr3.sn-a5msen76.googlevideo.com.", - "rr3.sn-a5msen7s.googlevideo.com.", - "rr3.sn-a5msen7z.googlevideo.com.", - "rr3.sn-a5msenek.googlevideo.com.", - "rr3.sn-a5msener.googlevideo.com.", - "rr3.sn-a5msenes.googlevideo.com.", - "rr3.sn-a5msenl7.googlevideo.com.", - "rr3.sn-a5msenle.googlevideo.com.", + "rr3.sn-hgn7rn7k.googlevideo.com.", "rr3.sn-hp57kn6r.googlevideo.com.", "rr3.sn-hp57kn6y.googlevideo.com.", "rr3.sn-hp57knd6.googlevideo.com.", @@ -4761,7 +5079,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr3.sn-hp57kndy.googlevideo.com.", "rr3.sn-hp57kndz.googlevideo.com.", "rr3.sn-hp57knk7.googlevideo.com.", - "rr3.sn-hp57yn7y.googlevideo.com.", "rr3.sn-hp57ynl6.googlevideo.com.", "rr3.sn-hp57ynlr.googlevideo.com.", "rr3.sn-hp57ynly.googlevideo.com.", @@ -4770,96 +5087,33 @@ var FakeECSFQDNs = container.NewMapSet( "rr3.sn-hp57ynsl.googlevideo.com.", "rr3.sn-hp57ynss.googlevideo.com.", "rr3.sn-nx57ynsk.googlevideo.com.", - "rr3.sn-q4fl6n66.googlevideo.com.", - "rr3.sn-q4fl6n6r.googlevideo.com.", - "rr3.sn-q4fl6nd7.googlevideo.com.", - "rr3.sn-q4fl6nz7.googlevideo.com.", - "rr3.sn-q4fl6nzy.googlevideo.com.", "rr3.sn-q4flrnl6.googlevideo.com.", - "rr3.sn-q4flrnld.googlevideo.com.", - "rr3.sn-q4flrnle.googlevideo.com.", - "rr3.sn-q4flrnlz.googlevideo.com.", - "rr3.sn-q4flrnsk.googlevideo.com.", - "rr3.sn-q4flrnss.googlevideo.com.", - "rr3.sn-q4fzen7e.googlevideo.com.", "rr3.sn-q4fzen7l.googlevideo.com.", - "rr3.sn-q4fzen7s.googlevideo.com.", - "rr3.sn-q4fzen7y.googlevideo.com.", - "rr3.sn-q4fzene7.googlevideo.com.", - "rr3.sn-qja5mc-5h.googlevideo.com.", - "rr3.sn-vgqskn66.googlevideo.com.", - "rr3.sn-vgqskn67.googlevideo.com.", - "rr3.sn-vgqskn6d.googlevideo.com.", - "rr3.sn-vgqskn6s.googlevideo.com.", - "rr3.sn-vgqskn6z.googlevideo.com.", - "rr3.sn-vgqsknld.googlevideo.com.", - "rr3.sn-vgqsknlk.googlevideo.com.", - "rr3.sn-vgqsknlr.googlevideo.com.", - "rr3.sn-vgqskns7.googlevideo.com.", - "rr3.sn-vgqsknse.googlevideo.com.", - "rr3.sn-vgqsknsk.googlevideo.com.", - "rr3.sn-vgqsknz6.googlevideo.com.", - "rr3.sn-vgqsknz7.googlevideo.com.", - "rr3.sn-vgqsknzd.googlevideo.com.", - "rr3.sn-vgqsknze.googlevideo.com.", - "rr3.sn-vgqsknzk.googlevideo.com.", - "rr3.sn-vgqsknzl.googlevideo.com.", - "rr3.sn-vgqsknzr.googlevideo.com.", - "rr3.sn-vgqsknzs.googlevideo.com.", - "rr3.sn-vgqsknzy.googlevideo.com.", - "rr3.sn-vgqsknzz.googlevideo.com.", - "rr3.sn-vgqsrn66.googlevideo.com.", - "rr3.sn-vgqsrn67.googlevideo.com.", - "rr3.sn-vgqsrn6e.googlevideo.com.", - "rr3.sn-vgqsrn6l.googlevideo.com.", - "rr3.sn-vgqsrn6z.googlevideo.com.", - "rr3.sn-vgqsrnl6.googlevideo.com.", - "rr3.sn-vgqsrnld.googlevideo.com.", - "rr3.sn-vgqsrnlk.googlevideo.com.", - "rr3.sn-vgqsrnls.googlevideo.com.", - "rr3.sn-vgqsrnlz.googlevideo.com.", - "rr3.sn-vgqsrns6.googlevideo.com.", - "rr3.sn-vgqsrnsd.googlevideo.com.", - "rr3.sn-vgqsrnsr.googlevideo.com.", - "rr3.sn-vgqsrnsy.googlevideo.com.", - "rr3.sn-vgqsrnz6.googlevideo.com.", - "rr3.sn-vgqsrnz7.googlevideo.com.", - "rr3.sn-vgqsrnzd.googlevideo.com.", - "rr3.sn-vgqsrnzk.googlevideo.com.", - "rr3.sn-vgqsrnzr.googlevideo.com.", - "rr3.sn-vgqsrnzs.googlevideo.com.", - "rr3.sn-vgqsrnzy.googlevideo.com.", + "rr3.sn-u1hp55-5c.googlevideo.com.", "rr3.sn-vgqsrnzz.googlevideo.com.", "rr4---sn-0nnpbo5a-bggl.googlevideo.com.", "rr4---sn-2aqu-hoas7.googlevideo.com.", - "rr4---sn-2aqu-hoasd.googlevideo.com.", - "rr4---sn-2aqu-hoasz.googlevideo.com.", - "rr4---sn-2aqu-jbt6.googlevideo.com.", - "rr4---sn-2aqu-jbtd.googlevideo.com.", - "rr4---sn-2aqu-jxcr.googlevideo.com.", - "rr4---sn-2aqu-jxcy.googlevideo.com.", "rr4---sn-2imern76.googlevideo.com.", + "rr4---sn-2imern76.gvt1.com.", "rr4---sn-2imern7d.googlevideo.com.", + "rr4---sn-2imern7r.googlevideo.com.", "rr4---sn-2imeyn7k.googlevideo.com.", + "rr4---sn-2imeyn7k.gvt1.com.", "rr4---sn-2o5ua5-53.googlevideo.com.", "rr4---sn-2oaig5-55.googlevideo.com.", + "rr4---sn-2op5q5-58.googlevideo.com.", "rr4---sn-2oq4f5-c4.googlevideo.com.", + "rr4---sn-2oq4f5-c4.gvt1.com.", "rr4---sn-2ovgq5-cw.googlevideo.com.", "rr4---sn-30a7rned.googlevideo.com.", "rr4---sn-30a7rnek.googlevideo.com.", "rr4---sn-30a7rner.googlevideo.com.", "rr4---sn-30a7ynek.googlevideo.com.", + "rr4---sn-30a7yney.googlevideo.com.", "rr4---sn-30a7ynl7.googlevideo.com.", - "rr4---sn-42u-nbosr.googlevideo.com.", - "rr4---sn-42u-nboze.googlevideo.com.", - "rr4---sn-42u-nbozl.googlevideo.com.", - "rr4---sn-42u-nbozs.googlevideo.com.", - "rr4---sn-42u-nbozz.googlevideo.com.", "rr4---sn-4g5e6ns6.googlevideo.com.", "rr4---sn-4g5e6ns7.googlevideo.com.", - "rr4---sn-4g5e6nsd.googlevideo.com.", "rr4---sn-4g5e6nsk.googlevideo.com.", - "rr4---sn-4g5e6nsr.googlevideo.com.", "rr4---sn-4g5e6nss.googlevideo.com.", "rr4---sn-4g5e6nsy.googlevideo.com.", "rr4---sn-4g5e6nz7.googlevideo.com.", @@ -4876,7 +5130,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-4g5edndk.googlevideo.com.", "rr4---sn-4g5edndl.googlevideo.com.", "rr4---sn-4g5edndr.googlevideo.com.", - "rr4---sn-4g5ednds.googlevideo.com.", "rr4---sn-4g5edndy.googlevideo.com.", "rr4---sn-4g5edndz.googlevideo.com.", "rr4---sn-4g5ednkl.googlevideo.com.", @@ -4905,16 +5158,8 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-4g5lznle.googlevideo.com.", "rr4---sn-4g5lznls.googlevideo.com.", "rr4---sn-4g5lznlz.googlevideo.com.", - "rr4---sn-5abxgpxuxaxjvh-9n4e.googlevideo.com.", - "rr4---sn-5abxgpxuxaxjvh-9n4l.googlevideo.com.", "rr4---sn-5abxgpxuxaxjvh-9n4z.googlevideo.com.", - "rr4---sn-5abxgpxuxaxjvh-cawe.googlevideo.com.", - "rr4---sn-5abxgpxuxaxjvh-caws.googlevideo.com.", - "rr4---sn-5abxgpxuxaxjvh-j1ae.googlevideo.com.", - "rr4---sn-5abxgpxuxaxjvh-j1az.googlevideo.com.", - "rr4---sn-5go7yner.googlevideo.com.", "rr4---sn-5go7ynl6.googlevideo.com.", - "rr4---sn-5go7ynld.googlevideo.com.", "rr4---sn-5goeenes.googlevideo.com.", "rr4---sn-5goeenez.googlevideo.com.", "rr4---sn-5hne6n6e.googlevideo.com.", @@ -4928,7 +5173,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-5hne6nz6.googlevideo.com.", "rr4---sn-5hne6nzd.googlevideo.com.", "rr4---sn-5hne6nzk.googlevideo.com.", - "rr4---sn-5hne6nzs.googlevideo.com.", "rr4---sn-5hne6nzy.googlevideo.com.", "rr4---sn-5hnednss.googlevideo.com.", "rr4---sn-5hnednsz.googlevideo.com.", @@ -4936,7 +5180,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-5hnekn7d.googlevideo.com.", "rr4---sn-5hnekn7l.googlevideo.com.", "rr4---sn-5hnekn7s.googlevideo.com.", - "rr4---sn-5hnekn7z.googlevideo.com.", "rr4---sn-5hneknee.googlevideo.com.", "rr4---sn-5hneknek.googlevideo.com.", "rr4---sn-5hneknes.googlevideo.com.", @@ -4947,10 +5190,10 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-5uaeznes.googlevideo.com.", "rr4---sn-5uaeznez.googlevideo.com.", "rr4---sn-5uaeznl6.googlevideo.com.", + "rr4---sn-5uaeznl6.gvt1.com.", "rr4---sn-5uaeznld.googlevideo.com.", "rr4---sn-5uaeznls.googlevideo.com.", "rr4---sn-5uaeznly.googlevideo.com.", - "rr4---sn-5uaeznlz.googlevideo.com.", "rr4---sn-5uaeznse.googlevideo.com.", "rr4---sn-5uaeznsl.googlevideo.com.", "rr4---sn-5uaeznss.googlevideo.com.", @@ -4961,49 +5204,40 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-5ualdnll.googlevideo.com.", "rr4---sn-5ualdnlr.googlevideo.com.", "rr4---sn-5ualdnls.googlevideo.com.", + "rr4---sn-5ualdns6.googlevideo.com.", "rr4---sn-5ualdns7.googlevideo.com.", "rr4---sn-5ualdnsd.googlevideo.com.", "rr4---sn-5ualdnse.googlevideo.com.", "rr4---sn-5ualdnsk.googlevideo.com.", - "rr4---sn-5ualdnsl.googlevideo.com.", "rr4---sn-5ualdnsr.googlevideo.com.", "rr4---sn-5ualdnss.googlevideo.com.", + "rr4---sn-5ualdnss.gvt1.com.", "rr4---sn-5ualdnsy.googlevideo.com.", "rr4---sn-5ualdnsz.googlevideo.com.", "rr4---sn-5ualdnz7.googlevideo.com.", "rr4---sn-5ualdnze.googlevideo.com.", - "rr4---sn-8pxuuxa-nbo6l.googlevideo.com.", - "rr4---sn-8pxuuxa-nbo6s.googlevideo.com.", - "rr4---sn-8pxuuxa-nboz6.googlevideo.com.", - "rr4---sn-8qj-i2iez.googlevideo.com.", - "rr4---sn-8qj-i5o6d.googlevideo.com.", "rr4---sn-8qj-i5o6k.googlevideo.com.", - "rr4---sn-8qj-i5ody.googlevideo.com.", - "rr4---sn-8qj-i5oks.googlevideo.com.", - "rr4---sn-8qj-i5ozd.googlevideo.com.", - "rr4---sn-8qj-i5ozr.googlevideo.com.", - "rr4---sn-8qj-i5ozz.googlevideo.com.", "rr4---sn-8qj-nbo66.googlevideo.com.", - "rr4---sn-8qj-nbo67.googlevideo.com.", + "rr4---sn-8qj-nbosd.googlevideo.com.", "rr4---sn-8xgp1vo-ab56.googlevideo.com.", "rr4---sn-8xgp1vo-ab5d.googlevideo.com.", "rr4---sn-8xgp1vo-ab5e.googlevideo.com.", "rr4---sn-8xgp1vo-ab5l.googlevideo.com.", "rr4---sn-8xgp1vo-ab5s.googlevideo.com.", "rr4---sn-8xgp1vo-ab5z.googlevideo.com.", - "rr4---sn-8xgp1vo-nh4e.googlevideo.com.", + "rr4---sn-8xgp1vo-p5qe.googlevideo.com.", + "rr4---sn-8xgp1vo-p5qe7.googlevideo.com.", + "rr4---sn-8xgp1vo-p5qee.googlevideo.com.", + "rr4---sn-8xgp1vo-p5qel.googlevideo.com.", "rr4---sn-8xgp1vo-poql.googlevideo.com.", + "rr4---sn-8xgp1vo-vgqe.googlevideo.com.", "rr4---sn-8xgp1vo-xfge.googlevideo.com.", "rr4---sn-8xgp1vo-xfgl.googlevideo.com.", "rr4---sn-8xgp1vo-xfgs.googlevideo.com.", - "rr4---sn-9gv76n7e.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-a5m7lnl6.googlevideo.com.", "rr4---sn-a5m7lnld.googlevideo.com.", "rr4---sn-a5mekn6d.googlevideo.com.", @@ -5089,18 +5323,32 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-aigzrnsz.googlevideo.com.", "rr4---sn-aigzrnz7.googlevideo.com.", "rr4---sn-aigzrnze.googlevideo.com.", + "rr4---sn-aj4g55-5v.googlevideo.com.", + "rr4---sn-aj5ua5-5c.googlevideo.com.", + "rr4---sn-ajab55-55.googlevideo.com.", + "rr4---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", "rr4---sn-bvvbaxivnuxq5uu-q4fe.googlevideo.com.", - "rr4---sn-c0q7lnz7.googlevideo.com.", + "rr4---sn-bvvbaxivnuxq5uu-q4fl.googlevideo.com.", + "rr4---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "rr4---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "rr4---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "rr4---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "rr4---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "rr4---sn-bvvbaxivnuxqjvhj5nu-vgq6.googlevideo.com.", + "rr4---sn-bvvbaxivnuxqjvhj5nu-vgqd.googlevideo.com.", + "rr4---sn-bvvbaxivnuxqjvhj5nu-vgqe.googlevideo.com.", + "rr4---sn-bvvbaxivnuxqjvhj5nu-vgql.googlevideo.com.", + "rr4---sn-bvvbaxivnuxqjvhj5nu-vgqs.googlevideo.com.", + "rr4---sn-bvvbaxivnuxqjvhj5nu-vgqz.googlevideo.com.", + "rr4---sn-bvvbaxivnuxqjvm-2ime.googlevideo.com.", + "rr4---sn-bvvbaxivnuxqjvm-2ims.googlevideo.com.", + "rr4---sn-bvvbaxivnuxqjvm-q4fe.googlevideo.com.", + "rr4---sn-bvvbaxivnuxqjvm-q4fl.googlevideo.com.", "rr4---sn-cvb7lne7.googlevideo.com.", "rr4---sn-cvb7lnee.googlevideo.com.", "rr4---sn-cvb7lnlz.googlevideo.com.", "rr4---sn-cvb7sn7k.googlevideo.com.", "rr4---sn-cvb7sn7r.googlevideo.com.", - "rr4---sn-gxuo03g-ig3e.googlevideo.com.", - "rr4---sn-h0jeenl6.googlevideo.com.", - "rr4---sn-h0jeenld.googlevideo.com.", - "rr4---sn-h0jelne6.googlevideo.com.", - "rr4---sn-h0jelnes.googlevideo.com.", "rr4---sn-hoa7kn7z.googlevideo.com.", "rr4---sn-hoa7kn7z.gvt1.com.", "rr4---sn-hoa7rn76.googlevideo.com.", @@ -5110,12 +5358,11 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-hp57kn6r.googlevideo.com.", "rr4---sn-hp57kn6r.gvt1.com.", "rr4---sn-hp57kn6y.googlevideo.com.", + "rr4---sn-hp57kn6y.gvt1.com.", "rr4---sn-hp57knd6.googlevideo.com.", "rr4---sn-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-hp57kndy.googlevideo.com.", "rr4---sn-hp57kndz.googlevideo.com.", @@ -5126,13 +5373,16 @@ var FakeECSFQDNs = container.NewMapSet( "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-hp57ynly.googlevideo.com.", "rr4---sn-hp57yns7.googlevideo.com.", + "rr4---sn-hp57yns7.gvt1.com.", "rr4---sn-hp57ynse.googlevideo.com.", "rr4---sn-hp57ynsl.googlevideo.com.", + "rr4---sn-hp57ynsl.gvt1.com.", "rr4---sn-hp57ynss.googlevideo.com.", - "rr4---sn-huxaqvv-ubqe.googlevideo.com.", + "rr4---sn-hp57ynss.gvt1.com.", "rr4---sn-i3b7kn6s.googlevideo.com.", "rr4---sn-i3b7knld.googlevideo.com.", "rr4---sn-i3b7knlk.googlevideo.com.", @@ -5145,12 +5395,14 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-i3belne6.googlevideo.com.", "rr4---sn-i3belney.googlevideo.com.", "rr4---sn-i3belnl7.googlevideo.com.", + "rr4---sn-i3belnll.googlevideo.com.", "rr4---sn-i3belnls.googlevideo.com.", "rr4---sn-i3bssn7e.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-jn2pgx4pcxg-w5o6.googlevideo.com.", + "rr4---sn-jn2pgx4pcxg-w5os.googlevideo.com.", + "rr4---sn-jxopj-n5oe.googlevideo.com.", + "rr4---sn-jxopj-nh4e.googlevideo.com.", + "rr4---sn-jxopj-nh4e.gvt1.com.", "rr4---sn-n4v7snee.googlevideo.com.", "rr4---sn-n4v7sney.googlevideo.com.", "rr4---sn-n4v7snl7.googlevideo.com.", @@ -5160,7 +5412,7 @@ 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-npoe7ne6.googlevideo.com.", "rr4---sn-npoe7ne7.googlevideo.com.", "rr4---sn-npoe7ned.googlevideo.com.", "rr4---sn-npoe7nek.googlevideo.com.", @@ -5171,64 +5423,46 @@ 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-npoe7nsk.gvt1.com.", "rr4---sn-npoe7nsl.googlevideo.com.", "rr4---sn-npoe7nsr.googlevideo.com.", - "rr4---sn-npoe7nss.googlevideo.com.", - "rr4---sn-npoe7nss.gvt1.com.", "rr4---sn-npoe7nsy.googlevideo.com.", - "rr4---sn-npoe7nsy.gvt1.com.", "rr4---sn-npoe7nz7.googlevideo.com.", - "rr4---sn-npoe7nz7.gvt1.com.", "rr4---sn-npoeene6.googlevideo.com.", "rr4---sn-npoeened.googlevideo.com.", "rr4---sn-npoeenee.googlevideo.com.", "rr4---sn-npoeenek.googlevideo.com.", "rr4---sn-npoeener.googlevideo.com.", - "rr4---sn-npoeener.gvt1.com.", "rr4---sn-npoeeney.googlevideo.com.", + "rr4---sn-npoeenez.googlevideo.com.", "rr4---sn-npoeenl7.googlevideo.com.", - "rr4---sn-npoeenl7.gvt1.com.", "rr4---sn-npoeenle.googlevideo.com.", "rr4---sn-npoeenlk.googlevideo.com.", "rr4---sn-npoeenlk.gvt1.com.", "rr4---sn-npoeenll.googlevideo.com.", + "rr4---sn-npoeenll.gvt1.com.", "rr4---sn-npoeenly.googlevideo.com.", - "rr4---sn-npoeenly.gvt1.com.", "rr4---sn-npoeens7.googlevideo.com.", - "rr4---sn-npoeens7.gvt1.com.", "rr4---sn-npoldn76.googlevideo.com.", - "rr4---sn-npoldn76.gvt1.com.", "rr4---sn-npoldn7d.googlevideo.com.", - "rr4---sn-npoldn7d.gvt1.com.", "rr4---sn-npoldn7e.googlevideo.com.", - "rr4---sn-npoldn7e.gvt1.com.", "rr4---sn-npoldn7l.googlevideo.com.", "rr4---sn-npoldn7l.gvt1.com.", - "rr4---sn-npoldn7s.googlevideo.com.", "rr4---sn-npoldn7y.googlevideo.com.", - "rr4---sn-npoldn7y.gvt1.com.", "rr4---sn-npoldn7z.googlevideo.com.", - "rr4---sn-npoldn7z.gvt1.com.", - "rr4---sn-npoldne7.googlevideo.com.", - "rr4---sn-npoldne7.gvt1.com.", - "rr4---sn-ntqe6n7r.googlevideo.com.", - "rr4---sn-nv47ln6e.googlevideo.com.", - "rr4---sn-nv47zn7y.googlevideo.com.", - "rr4---sn-nv47zne7.googlevideo.com.", - "rr4---sn-nv47znee.googlevideo.com.", - "rr4---sn-nv47znel.googlevideo.com.", - "rr4---sn-nx57ynlk.googlevideo.com.", + "rr4---sn-ntq7yned.googlevideo.com.", "rr4---sn-nx57ynsd.googlevideo.com.", "rr4---sn-nx57ynse.googlevideo.com.", "rr4---sn-nx57ynsk.googlevideo.com.", - "rr4---sn-nx57ynss.googlevideo.com.", + "rr4---sn-nx57ynsl.googlevideo.com.", + "rr4---sn-nx57ynsr.googlevideo.com.", + "rr4---sn-nx57ynsy.googlevideo.com.", "rr4---sn-nx57ynsz.googlevideo.com.", + "rr4---sn-nx57ynz7.googlevideo.com.", + "rr4---sn-nx57ynze.googlevideo.com.", "rr4---sn-o097znsd.googlevideo.com.", "rr4---sn-o097znse.googlevideo.com.", "rr4---sn-o097znsk.googlevideo.com.", @@ -5239,7 +5473,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-o097znz7.googlevideo.com.", "rr4---sn-o097znzd.googlevideo.com.", "rr4---sn-o097znze.googlevideo.com.", - "rr4---sn-o097znzk.googlevideo.com.", "rr4---sn-o097znzr.googlevideo.com.", "rr4---sn-oj5hn5-55.googlevideo.com.", "rr4---sn-oji3bc-5j.googlevideo.com.", @@ -5249,6 +5482,8 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-p5qddn7r.googlevideo.com.", "rr4---sn-p5qddn7z.googlevideo.com.", "rr4---sn-p5qlsn6l.googlevideo.com.", + "rr4---sn-p5qlsn6s.googlevideo.com.", + "rr4---sn-p5qlsn6z.googlevideo.com.", "rr4---sn-p5qlsn76.googlevideo.com.", "rr4---sn-p5qlsn7d.googlevideo.com.", "rr4---sn-p5qlsn7l.googlevideo.com.", @@ -5256,13 +5491,12 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-p5qlsnd6.googlevideo.com.", "rr4---sn-p5qlsndk.googlevideo.com.", "rr4---sn-p5qlsndr.googlevideo.com.", - "rr4---sn-p5qlsndz.googlevideo.com.", "rr4---sn-p5qlsnrl.googlevideo.com.", "rr4---sn-p5qlsnrr.googlevideo.com.", "rr4---sn-p5qlsny6.googlevideo.com.", "rr4---sn-p5qs7n6d.googlevideo.com.", + "rr4---sn-p5qs7nd7.googlevideo.com.", "rr4---sn-p5qs7nsk.googlevideo.com.", - "rr4---sn-p5qs7nsr.googlevideo.com.", "rr4---sn-p5qs7nzk.googlevideo.com.", "rr4---sn-p5qs7nzr.googlevideo.com.", "rr4---sn-p5qs7nzy.googlevideo.com.", @@ -5270,26 +5504,37 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-q4fl6n6d.googlevideo.com.", "rr4---sn-q4fl6n6s.googlevideo.com.", "rr4---sn-q4fl6n6y.googlevideo.com.", + "rr4---sn-q4fl6n6y.gvt1.com.", "rr4---sn-q4fl6n6z.googlevideo.com.", "rr4---sn-q4fl6nd7.googlevideo.com.", "rr4---sn-q4fl6nde.googlevideo.com.", + "rr4---sn-q4fl6nde.gvt1.com.", "rr4---sn-q4fl6ndl.googlevideo.com.", "rr4---sn-q4fl6nds.googlevideo.com.", "rr4---sn-q4fl6ndz.googlevideo.com.", "rr4---sn-q4fl6nlz.googlevideo.com.", "rr4---sn-q4fl6ns6.googlevideo.com.", + "rr4---sn-q4fl6ns6.gvt1.com.", "rr4---sn-q4fl6ns7.googlevideo.com.", "rr4---sn-q4fl6nsd.googlevideo.com.", + "rr4---sn-q4fl6nsd.gvt1.com.", "rr4---sn-q4fl6nsk.googlevideo.com.", "rr4---sn-q4fl6nsl.googlevideo.com.", "rr4---sn-q4fl6nsr.googlevideo.com.", "rr4---sn-q4fl6nss.googlevideo.com.", "rr4---sn-q4fl6nsy.googlevideo.com.", + "rr4---sn-q4fl6nsy.gvt1.com.", "rr4---sn-q4fl6nz6.googlevideo.com.", + "rr4---sn-q4fl6nz6.gvt1.com.", "rr4---sn-q4fl6nz7.googlevideo.com.", + "rr4---sn-q4fl6nz7.gvt1.com.", "rr4---sn-q4fl6nzy.googlevideo.com.", + "rr4---sn-q4fl6nzy.gvt1.com.", "rr4---sn-q4flrn7k.googlevideo.com.", "rr4---sn-q4flrn7r.googlevideo.com.", + "rr4---sn-q4flrn7r.gvt1.com.", + "rr4---sn-q4flrn7y.googlevideo.com.", + "rr4---sn-q4flrn7y.gvt1.com.", "rr4---sn-q4flrne6.googlevideo.com.", "rr4---sn-q4flrne7.googlevideo.com.", "rr4---sn-q4flrnee.googlevideo.com.", @@ -5301,34 +5546,41 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-q4flrnez.googlevideo.com.", "rr4---sn-q4flrnl6.googlevideo.com.", "rr4---sn-q4flrnl7.googlevideo.com.", + "rr4---sn-q4flrnl7.gvt1.com.", "rr4---sn-q4flrnld.googlevideo.com.", "rr4---sn-q4flrnle.googlevideo.com.", "rr4---sn-q4flrnlz.googlevideo.com.", "rr4---sn-q4flrnsd.googlevideo.com.", - "rr4---sn-q4flrnsk.googlevideo.com.", + "rr4---sn-q4flrnsd.gvt1.com.", "rr4---sn-q4flrnsl.googlevideo.com.", + "rr4---sn-q4flrnsl.gvt1.com.", "rr4---sn-q4flrnss.googlevideo.com.", "rr4---sn-q4fzen7e.googlevideo.com.", "rr4---sn-q4fzen7l.googlevideo.com.", + "rr4---sn-q4fzen7l.gvt1.com.", "rr4---sn-q4fzen7s.googlevideo.com.", + "rr4---sn-q4fzen7s.gvt1.com.", "rr4---sn-q4fzen7y.googlevideo.com.", "rr4---sn-q4fzene7.googlevideo.com.", + "rr4---sn-q4fzene7.gvt1.com.", "rr4---sn-q4fzenee.googlevideo.com.", "rr4---sn-qja5mc-5h.googlevideo.com.", "rr4---sn-qjp5q5-55.googlevideo.com.", "rr4---sn-qxo7rn7k.googlevideo.com.", - "rr4---sn-qxo7rn7r.googlevideo.com.", - "rr4---sn-qxo7rn7y.googlevideo.com.", + "rr4---sn-qxo7rne7.googlevideo.com.", + "rr4---sn-qxo7rnee.googlevideo.com.", "rr4---sn-qxoedn7k.googlevideo.com.", "rr4---sn-qxoedne7.googlevideo.com.", - "rr4---sn-t0aedn7l.googlevideo.com.", + "rr4---sn-qxoednee.googlevideo.com.", + "rr4---sn-qxoednel.googlevideo.com.", + "rr4---sn-qxoednes.googlevideo.com.", "rr4---sn-u1hp55-5c.googlevideo.com.", + "rr4---sn-v5goxu-jhil.googlevideo.com.", "rr4---sn-vgqskn66.googlevideo.com.", "rr4---sn-vgqskn67.googlevideo.com.", "rr4---sn-vgqskn6d.googlevideo.com.", "rr4---sn-vgqskn6s.googlevideo.com.", "rr4---sn-vgqskn6z.googlevideo.com.", - "rr4---sn-vgqskne6.googlevideo.com.", "rr4---sn-vgqskned.googlevideo.com.", "rr4---sn-vgqsknek.googlevideo.com.", "rr4---sn-vgqsknes.googlevideo.com.", @@ -5336,6 +5588,7 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-vgqsknld.googlevideo.com.", "rr4---sn-vgqsknlk.googlevideo.com.", "rr4---sn-vgqsknll.googlevideo.com.", + "rr4---sn-vgqsknlr.googlevideo.com.", "rr4---sn-vgqsknls.googlevideo.com.", "rr4---sn-vgqsknlz.googlevideo.com.", "rr4---sn-vgqskns7.googlevideo.com.", @@ -5378,40 +5631,9 @@ var FakeECSFQDNs = container.NewMapSet( "rr4---sn-vgqsrnzr.googlevideo.com.", "rr4---sn-vgqsrnzs.googlevideo.com.", "rr4---sn-vgqsrnzy.googlevideo.com.", + "rr4---sn-vgqsrnzy.gvt1.com.", "rr4---sn-vgqsrnzz.googlevideo.com.", - "rr4.sn-2oq4f5-c4.googlevideo.com.", - "rr4.sn-2ovgq5-cw.googlevideo.com.", - "rr4.sn-a5m7lnl6.googlevideo.com.", - "rr4.sn-a5m7lnld.googlevideo.com.", - "rr4.sn-a5mekn6d.googlevideo.com.", - "rr4.sn-a5mekn6k.googlevideo.com.", - "rr4.sn-a5mekn6l.googlevideo.com.", - "rr4.sn-a5mekn6r.googlevideo.com.", - "rr4.sn-a5mekn6s.googlevideo.com.", - "rr4.sn-a5mekn6z.googlevideo.com.", - "rr4.sn-a5meknd6.googlevideo.com.", - "rr4.sn-a5meknde.googlevideo.com.", - "rr4.sn-a5mekndl.googlevideo.com.", - "rr4.sn-a5meknds.googlevideo.com.", - "rr4.sn-a5meknsd.googlevideo.com.", - "rr4.sn-a5meknsy.googlevideo.com.", - "rr4.sn-a5meknzk.googlevideo.com.", - "rr4.sn-a5meknzr.googlevideo.com.", - "rr4.sn-a5meknzs.googlevideo.com.", - "rr4.sn-a5mlrnek.googlevideo.com.", - "rr4.sn-a5mlrnl6.googlevideo.com.", - "rr4.sn-a5mlrnll.googlevideo.com.", - "rr4.sn-a5mlrnls.googlevideo.com.", - "rr4.sn-a5mlrnlz.googlevideo.com.", - "rr4.sn-a5msen76.googlevideo.com.", - "rr4.sn-a5msen7l.googlevideo.com.", - "rr4.sn-a5msen7s.googlevideo.com.", - "rr4.sn-a5msen7z.googlevideo.com.", - "rr4.sn-a5msenek.googlevideo.com.", - "rr4.sn-a5msener.googlevideo.com.", - "rr4.sn-a5msenes.googlevideo.com.", - "rr4.sn-a5msenl7.googlevideo.com.", - "rr4.sn-a5msenle.googlevideo.com.", + "rr4---sn-vgqsrnzz.gvt1.com.", "rr4.sn-hp57kn6r.googlevideo.com.", "rr4.sn-hp57kn6y.googlevideo.com.", "rr4.sn-hp57knd6.googlevideo.com.", @@ -5422,8 +5644,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr4.sn-hp57kndy.googlevideo.com.", "rr4.sn-hp57kndz.googlevideo.com.", "rr4.sn-hp57knk7.googlevideo.com.", - "rr4.sn-hp57yn7r.googlevideo.com.", - "rr4.sn-hp57yn7y.googlevideo.com.", "rr4.sn-hp57ynl6.googlevideo.com.", "rr4.sn-hp57ynlr.googlevideo.com.", "rr4.sn-hp57ynly.googlevideo.com.", @@ -5431,102 +5651,37 @@ var FakeECSFQDNs = container.NewMapSet( "rr4.sn-hp57ynse.googlevideo.com.", "rr4.sn-hp57ynsl.googlevideo.com.", "rr4.sn-hp57ynss.googlevideo.com.", - "rr4.sn-ntqe6n7r.googlevideo.com.", "rr4.sn-nx57ynsk.googlevideo.com.", - "rr4.sn-q4fl6n6y.googlevideo.com.", - "rr4.sn-q4fl6ndz.googlevideo.com.", - "rr4.sn-q4fl6nsd.googlevideo.com.", - "rr4.sn-q4flrn7k.googlevideo.com.", - "rr4.sn-q4flrn7r.googlevideo.com.", - "rr4.sn-q4flrnee.googlevideo.com.", - "rr4.sn-q4flrnes.googlevideo.com.", - "rr4.sn-q4flrnl6.googlevideo.com.", - "rr4.sn-q4flrnld.googlevideo.com.", - "rr4.sn-q4flrnle.googlevideo.com.", - "rr4.sn-q4flrnlz.googlevideo.com.", - "rr4.sn-q4flrnsd.googlevideo.com.", - "rr4.sn-q4flrnsl.googlevideo.com.", - "rr4.sn-q4flrnss.googlevideo.com.", - "rr4.sn-q4fzen7e.googlevideo.com.", + "rr4.sn-q4fl6ndl.googlevideo.com.", + "rr4.sn-q4flrney.googlevideo.com.", + "rr4.sn-q4flrnl7.googlevideo.com.", "rr4.sn-q4fzen7l.googlevideo.com.", - "rr4.sn-q4fzen7s.googlevideo.com.", - "rr4.sn-q4fzen7y.googlevideo.com.", - "rr4.sn-q4fzene7.googlevideo.com.", - "rr4.sn-q4fzenee.googlevideo.com.", - "rr4.sn-qja5mc-5h.googlevideo.com.", "rr4.sn-u1hp55-5c.googlevideo.com.", - "rr4.sn-vgqskn66.googlevideo.com.", - "rr4.sn-vgqskn67.googlevideo.com.", - "rr4.sn-vgqskn6d.googlevideo.com.", - "rr4.sn-vgqskn6s.googlevideo.com.", - "rr4.sn-vgqskn6z.googlevideo.com.", - "rr4.sn-vgqsknld.googlevideo.com.", - "rr4.sn-vgqsknlk.googlevideo.com.", - "rr4.sn-vgqskns7.googlevideo.com.", - "rr4.sn-vgqsknse.googlevideo.com.", - "rr4.sn-vgqsknsk.googlevideo.com.", - "rr4.sn-vgqsknz6.googlevideo.com.", - "rr4.sn-vgqsknz7.googlevideo.com.", - "rr4.sn-vgqsknzd.googlevideo.com.", - "rr4.sn-vgqsknze.googlevideo.com.", - "rr4.sn-vgqsknzk.googlevideo.com.", - "rr4.sn-vgqsknzl.googlevideo.com.", - "rr4.sn-vgqsknzr.googlevideo.com.", - "rr4.sn-vgqsknzs.googlevideo.com.", - "rr4.sn-vgqsknzy.googlevideo.com.", - "rr4.sn-vgqsknzz.googlevideo.com.", - "rr4.sn-vgqsrn66.googlevideo.com.", - "rr4.sn-vgqsrn67.googlevideo.com.", - "rr4.sn-vgqsrn6e.googlevideo.com.", - "rr4.sn-vgqsrn6l.googlevideo.com.", - "rr4.sn-vgqsrn6z.googlevideo.com.", - "rr4.sn-vgqsrnl6.googlevideo.com.", - "rr4.sn-vgqsrnld.googlevideo.com.", - "rr4.sn-vgqsrnlk.googlevideo.com.", - "rr4.sn-vgqsrnls.googlevideo.com.", - "rr4.sn-vgqsrnlz.googlevideo.com.", - "rr4.sn-vgqsrns6.googlevideo.com.", - "rr4.sn-vgqsrnsd.googlevideo.com.", - "rr4.sn-vgqsrnsr.googlevideo.com.", - "rr4.sn-vgqsrnsy.googlevideo.com.", - "rr4.sn-vgqsrnz6.googlevideo.com.", - "rr4.sn-vgqsrnz7.googlevideo.com.", - "rr4.sn-vgqsrnzd.googlevideo.com.", - "rr4.sn-vgqsrnzk.googlevideo.com.", - "rr4.sn-vgqsrnzr.googlevideo.com.", - "rr4.sn-vgqsrnzs.googlevideo.com.", - "rr4.sn-vgqsrnzy.googlevideo.com.", "rr4.sn-vgqsrnzz.googlevideo.com.", "rr5---sn-0nnpbo5a-bggl.googlevideo.com.", "rr5---sn-2aqu-hoas7.googlevideo.com.", - "rr5---sn-2aqu-hoasd.googlevideo.com.", - "rr5---sn-2aqu-hoasz.googlevideo.com.", - "rr5---sn-2aqu-jbt6.googlevideo.com.", - "rr5---sn-2aqu-jxcy.googlevideo.com.", "rr5---sn-2imern76.googlevideo.com.", + "rr5---sn-2imern76.gvt1.com.", "rr5---sn-2imern7d.googlevideo.com.", + "rr5---sn-2imern7r.googlevideo.com.", "rr5---sn-2imeyn7k.googlevideo.com.", + "rr5---sn-2imeyn7k.gvt1.com.", "rr5---sn-2o5ua5-53.googlevideo.com.", "rr5---sn-2oaig5-55.googlevideo.com.", + "rr5---sn-2op5q5-58.googlevideo.com.", "rr5---sn-2oq4f5-c4.googlevideo.com.", + "rr5---sn-2oq4f5-c4.gvt1.com.", "rr5---sn-2ovgq5-cw.googlevideo.com.", - "rr5---sn-30a7rned.googlevideo.com.", - "rr5---sn-30a7rnek.googlevideo.com.", "rr5---sn-30a7rner.googlevideo.com.", "rr5---sn-30a7ynek.googlevideo.com.", "rr5---sn-30a7yner.googlevideo.com.", "rr5---sn-30a7yney.googlevideo.com.", "rr5---sn-30a7ynl7.googlevideo.com.", - "rr5---sn-42u-nbosd.googlevideo.com.", - "rr5---sn-42u-nbosk.googlevideo.com.", "rr5---sn-42u-nboze.googlevideo.com.", - "rr5---sn-42u-nbozl.googlevideo.com.", - "rr5---sn-42u-nbozs.googlevideo.com.", "rr5---sn-4g5e6ns6.googlevideo.com.", "rr5---sn-4g5e6ns7.googlevideo.com.", "rr5---sn-4g5e6nsd.googlevideo.com.", "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.", @@ -5544,7 +5699,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-4g5edndk.googlevideo.com.", "rr5---sn-4g5edndl.googlevideo.com.", "rr5---sn-4g5edndr.googlevideo.com.", - "rr5---sn-4g5ednds.googlevideo.com.", "rr5---sn-4g5edndy.googlevideo.com.", "rr5---sn-4g5edndz.googlevideo.com.", "rr5---sn-4g5ednkl.googlevideo.com.", @@ -5573,19 +5727,10 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-4g5lznle.googlevideo.com.", "rr5---sn-4g5lznls.googlevideo.com.", "rr5---sn-4g5lznlz.googlevideo.com.", - "rr5---sn-5abxgpxuxaxjvh-9n4e.googlevideo.com.", - "rr5---sn-5abxgpxuxaxjvh-9n4l.googlevideo.com.", - "rr5---sn-5abxgpxuxaxjvh-cawe.googlevideo.com.", - "rr5---sn-5abxgpxuxaxjvh-caws.googlevideo.com.", - "rr5---sn-5abxgpxuxaxjvh-j1ae.googlevideo.com.", - "rr5---sn-5abxgpxuxaxjvh-j1az.googlevideo.com.", - "rr5---sn-5go7yner.googlevideo.com.", "rr5---sn-5go7ynl6.googlevideo.com.", - "rr5---sn-5go7ynld.googlevideo.com.", - "rr5---sn-5go7ynlk.googlevideo.com.", - "rr5---sn-5goeenez.googlevideo.com.", "rr5---sn-5hne6n6e.googlevideo.com.", "rr5---sn-5hne6n6l.googlevideo.com.", + "rr5---sn-5hne6n6l.gvt1.com.", "rr5---sn-5hne6ns6.googlevideo.com.", "rr5---sn-5hne6nsd.googlevideo.com.", "rr5---sn-5hne6nsk.googlevideo.com.", @@ -5595,7 +5740,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-5hne6nz6.googlevideo.com.", "rr5---sn-5hne6nzd.googlevideo.com.", "rr5---sn-5hne6nzk.googlevideo.com.", - "rr5---sn-5hne6nzk.gvt1.com.", "rr5---sn-5hne6nzs.googlevideo.com.", "rr5---sn-5hne6nzy.googlevideo.com.", "rr5---sn-5hnednss.googlevideo.com.", @@ -5612,6 +5756,7 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-5uaezne6.googlevideo.com.", "rr5---sn-5uaezned.googlevideo.com.", "rr5---sn-5uaeznel.googlevideo.com.", + "rr5---sn-5uaezner.googlevideo.com.", "rr5---sn-5uaeznes.googlevideo.com.", "rr5---sn-5uaeznez.googlevideo.com.", "rr5---sn-5uaeznl6.googlevideo.com.", @@ -5632,7 +5777,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-5ualdns7.googlevideo.com.", "rr5---sn-5ualdnsd.googlevideo.com.", "rr5---sn-5ualdnse.googlevideo.com.", - "rr5---sn-5ualdnsk.googlevideo.com.", "rr5---sn-5ualdnsl.googlevideo.com.", "rr5---sn-5ualdnsr.googlevideo.com.", "rr5---sn-5ualdnss.googlevideo.com.", @@ -5640,35 +5784,22 @@ 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-8pxuuxa-nbosd.googlevideo.com.", - "rr5---sn-8qj-i5o6d.googlevideo.com.", - "rr5---sn-8qj-i5o6k.googlevideo.com.", - "rr5---sn-8qj-i5ody.googlevideo.com.", - "rr5---sn-8qj-i5ozd.googlevideo.com.", - "rr5---sn-8qj-i5ozr.googlevideo.com.", - "rr5---sn-8qj-i5ozz.googlevideo.com.", "rr5---sn-8qj-nbo66.googlevideo.com.", - "rr5---sn-8qj-nbo67.googlevideo.com.", + "rr5---sn-8xgp1vo-a5ml.googlevideo.com.", "rr5---sn-8xgp1vo-ab56.googlevideo.com.", "rr5---sn-8xgp1vo-ab5d.googlevideo.com.", "rr5---sn-8xgp1vo-ab5e.googlevideo.com.", "rr5---sn-8xgp1vo-ab5l.googlevideo.com.", "rr5---sn-8xgp1vo-ab5s.googlevideo.com.", "rr5---sn-8xgp1vo-ab5z.googlevideo.com.", - "rr5---sn-8xgp1vo-nh4e.googlevideo.com.", + "rr5---sn-8xgp1vo-p5qee.googlevideo.com.", + "rr5---sn-8xgp1vo-p5qel.googlevideo.com.", + "rr5---sn-8xgp1vo-vgqe.googlevideo.com.", "rr5---sn-8xgp1vo-xfge.googlevideo.com.", - "rr5---sn-8xgp1vo-xfgl.googlevideo.com.", "rr5---sn-8xgp1vo-xfgs.googlevideo.com.", - "rr5---sn-9gv76n7e.googlevideo.com.", - "rr5---sn-9gv76n7l.googlevideo.com.", "rr5---sn-9gv76n7s.googlevideo.com.", - "rr5---sn-9gv76n7z.googlevideo.com.", - "rr5---sn-9gv7ene6.googlevideo.com.", - "rr5---sn-9gv7ened.googlevideo.com.", "rr5---sn-9gv7zn76.googlevideo.com.", "rr5---sn-9gv7zn7e.googlevideo.com.", - "rr5---sn-9gv7zn7r.googlevideo.com.", "rr5---sn-9gv7zn7y.googlevideo.com.", "rr5---sn-a5m7lnl6.googlevideo.com.", "rr5---sn-a5m7lnld.googlevideo.com.", @@ -5694,6 +5825,7 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-a5mlrnll.googlevideo.com.", "rr5---sn-a5mlrnls.googlevideo.com.", "rr5---sn-a5mlrnlz.googlevideo.com.", + "rr5---sn-a5msen76.googlevideo.com.", "rr5---sn-a5msen7l.googlevideo.com.", "rr5---sn-a5msen7s.googlevideo.com.", "rr5---sn-a5msen7z.googlevideo.com.", @@ -5711,7 +5843,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-ab5l6nrk.googlevideo.com.", "rr5---sn-ab5l6nrl.googlevideo.com.", "rr5---sn-ab5l6nrr.googlevideo.com.", - "rr5---sn-ab5l6nrs.googlevideo.com.", "rr5---sn-ab5l6nrz.googlevideo.com.", "rr5---sn-ab5sznld.googlevideo.com.", "rr5---sn-ab5sznly.googlevideo.com.", @@ -5731,6 +5862,7 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-aigl6ney.googlevideo.com.", "rr5---sn-aigl6nl7.googlevideo.com.", "rr5---sn-aigl6ns6.googlevideo.com.", + "rr5---sn-aigl6nsd.googlevideo.com.", "rr5---sn-aigl6nsk.googlevideo.com.", "rr5---sn-aigl6nsr.googlevideo.com.", "rr5---sn-aigl6nz7.googlevideo.com.", @@ -5738,7 +5870,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-aigl6nzk.googlevideo.com.", "rr5---sn-aigl6nzl.googlevideo.com.", "rr5---sn-aigl6nzr.googlevideo.com.", - "rr5---sn-aigl6nzr.gvt1.com.", "rr5---sn-aigl6nzs.googlevideo.com.", "rr5---sn-aigzrn76.googlevideo.com.", "rr5---sn-aigzrn7d.googlevideo.com.", @@ -5755,23 +5886,32 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-aigzrnsz.googlevideo.com.", "rr5---sn-aigzrnz7.googlevideo.com.", "rr5---sn-aigzrnze.googlevideo.com.", + "rr5---sn-aj4g55-5v.googlevideo.com.", "rr5---sn-aj5ua5-5c.googlevideo.com.", - "rr5---sn-ajab55-55.googlevideo.com.", + "rr5---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", "rr5---sn-bvvbaxivnuxq5uu-q4fe.googlevideo.com.", "rr5---sn-bvvbaxivnuxq5uu-q4fl.googlevideo.com.", - "rr5---sn-c0q7lnz7.googlevideo.com.", + "rr5---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "rr5---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "rr5---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "rr5---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "rr5---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "rr5---sn-bvvbaxivnuxqjvhj5nu-vgq6.googlevideo.com.", + "rr5---sn-bvvbaxivnuxqjvhj5nu-vgqd.googlevideo.com.", + "rr5---sn-bvvbaxivnuxqjvhj5nu-vgqe.googlevideo.com.", + "rr5---sn-bvvbaxivnuxqjvhj5nu-vgql.googlevideo.com.", + "rr5---sn-bvvbaxivnuxqjvhj5nu-vgqs.googlevideo.com.", + "rr5---sn-bvvbaxivnuxqjvhj5nu-vgqz.googlevideo.com.", + "rr5---sn-bvvbaxivnuxqjvm-2ime.googlevideo.com.", + "rr5---sn-bvvbaxivnuxqjvm-2ims.googlevideo.com.", + "rr5---sn-bvvbaxivnuxqjvm-2ims.gvt1.com.", + "rr5---sn-bvvbaxivnuxqjvm-q4fe.googlevideo.com.", + "rr5---sn-bvvbaxivnuxqjvm-q4fl.googlevideo.com.", "rr5---sn-cvb7lne7.googlevideo.com.", "rr5---sn-cvb7lnee.googlevideo.com.", "rr5---sn-cvb7lnlz.googlevideo.com.", "rr5---sn-cvb7sn7k.googlevideo.com.", "rr5---sn-cvb7sn7r.googlevideo.com.", - "rr5---sn-h0jeenl6.googlevideo.com.", - "rr5---sn-h0jeenld.googlevideo.com.", - "rr5---sn-h0jeenle.googlevideo.com.", - "rr5---sn-h0jelne6.googlevideo.com.", - "rr5---sn-h0jelnes.googlevideo.com.", - "rr5---sn-h0jelnez.googlevideo.com.", - "rr5---sn-hgn7rnll.googlevideo.com.", "rr5---sn-hoa7kn76.googlevideo.com.", "rr5---sn-hoa7kn76.gvt1.com.", "rr5---sn-hoa7rn76.googlevideo.com.", @@ -5779,16 +5919,17 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-hoa7rn7z.googlevideo.com.", "rr5---sn-hoa7rn7z.gvt1.com.", "rr5---sn-hp57kn6r.googlevideo.com.", + "rr5---sn-hp57kn6r.gvt1.com.", "rr5---sn-hp57kn6y.googlevideo.com.", "rr5---sn-hp57knd6.googlevideo.com.", - "rr5---sn-hp57knd6.gvt1.com.", "rr5---sn-hp57kndd.googlevideo.com.", "rr5---sn-hp57kndk.googlevideo.com.", - "rr5---sn-hp57kndk.gvt1.com.", "rr5---sn-hp57kndr.googlevideo.com.", + "rr5---sn-hp57kndr.gvt1.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-hp57knk7.googlevideo.com.", "rr5---sn-hp57yn7r.googlevideo.com.", @@ -5796,15 +5937,17 @@ var FakeECSFQDNs = container.NewMapSet( "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-hp57ynlr.gvt1.com.", "rr5---sn-hp57ynly.googlevideo.com.", "rr5---sn-hp57ynly.gvt1.com.", "rr5---sn-hp57yns7.googlevideo.com.", "rr5---sn-hp57yns7.gvt1.com.", "rr5---sn-hp57ynse.googlevideo.com.", "rr5---sn-hp57ynsl.googlevideo.com.", + "rr5---sn-hp57ynsl.gvt1.com.", "rr5---sn-hp57ynss.googlevideo.com.", + "rr5---sn-hp57ynss.gvt1.com.", "rr5---sn-i3b7kn6s.googlevideo.com.", "rr5---sn-i3b7knld.googlevideo.com.", "rr5---sn-i3b7knlk.googlevideo.com.", @@ -5820,10 +5963,10 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-i3belnll.googlevideo.com.", "rr5---sn-i3belnls.googlevideo.com.", "rr5---sn-i3bssn7e.googlevideo.com.", - "rr5---sn-jvhj5nu-nh4e.googlevideo.com.", - "rr5---sn-jvhj5nu-nh4l.googlevideo.com.", - "rr5---sn-jvhj5nu-nh4s.googlevideo.com.", - "rr5---sn-jvhj5nu-nh4z.googlevideo.com.", + "rr5---sn-jn2pgx4pcxg-w5os.googlevideo.com.", + "rr5---sn-jxopj-n5oe.googlevideo.com.", + "rr5---sn-jxopj-nh4e.googlevideo.com.", + "rr5---sn-jxopj-nh4e.gvt1.com.", "rr5---sn-n4v7snee.googlevideo.com.", "rr5---sn-n4v7sney.googlevideo.com.", "rr5---sn-n4v7snl7.googlevideo.com.", @@ -5839,64 +5982,52 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-npoe7ned.googlevideo.com.", "rr5---sn-npoe7nek.googlevideo.com.", "rr5---sn-npoe7ner.googlevideo.com.", - "rr5---sn-npoe7ner.gvt1.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-npoe7ns6.gvt1.com.", "rr5---sn-npoe7ns7.googlevideo.com.", "rr5---sn-npoe7ns7.gvt1.com.", "rr5---sn-npoe7nsd.googlevideo.com.", "rr5---sn-npoe7nsk.googlevideo.com.", - "rr5---sn-npoe7nsk.gvt1.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-npoe7nz7.googlevideo.com.", - "rr5---sn-npoe7nz7.gvt1.com.", "rr5---sn-npoeene6.googlevideo.com.", "rr5---sn-npoeened.googlevideo.com.", "rr5---sn-npoeenee.googlevideo.com.", "rr5---sn-npoeenek.googlevideo.com.", + "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-npoeenlk.gvt1.com.", - "rr5---sn-npoeenly.googlevideo.com.", + "rr5---sn-npoeenll.googlevideo.com.", "rr5---sn-npoeens7.googlevideo.com.", - "rr5---sn-npoeens7.gvt1.com.", "rr5---sn-npoldn76.googlevideo.com.", "rr5---sn-npoldn7d.googlevideo.com.", - "rr5---sn-npoldn7d.gvt1.com.", "rr5---sn-npoldn7e.googlevideo.com.", - "rr5---sn-npoldn7e.gvt1.com.", "rr5---sn-npoldn7l.googlevideo.com.", "rr5---sn-npoldn7l.gvt1.com.", - "rr5---sn-npoldn7s.googlevideo.com.", "rr5---sn-npoldn7y.googlevideo.com.", - "rr5---sn-npoldn7y.gvt1.com.", "rr5---sn-npoldn7z.googlevideo.com.", "rr5---sn-npoldn7z.gvt1.com.", "rr5---sn-npoldne7.googlevideo.com.", "rr5---sn-npoldne7.gvt1.com.", - "rr5---sn-nv47ln6e.googlevideo.com.", - "rr5---sn-nv47zn7y.googlevideo.com.", - "rr5---sn-nv47znee.googlevideo.com.", - "rr5---sn-nv47znel.googlevideo.com.", + "rr5---sn-ntq7yns7.googlevideo.com.", "rr5---sn-nx57ynlk.googlevideo.com.", "rr5---sn-nx57ynsd.googlevideo.com.", - "rr5---sn-nx57ynse.googlevideo.com.", "rr5---sn-nx57ynsk.googlevideo.com.", "rr5---sn-nx57ynsl.googlevideo.com.", + "rr5---sn-nx57ynsr.googlevideo.com.", "rr5---sn-nx57ynss.googlevideo.com.", + "rr5---sn-nx57ynsy.googlevideo.com.", "rr5---sn-nx57ynsz.googlevideo.com.", + "rr5---sn-nx57ynz7.googlevideo.com.", + "rr5---sn-nx57ynze.googlevideo.com.", "rr5---sn-o097znsd.googlevideo.com.", "rr5---sn-o097znse.googlevideo.com.", "rr5---sn-o097znsk.googlevideo.com.", @@ -5910,13 +6041,16 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-o097znzr.googlevideo.com.", "rr5---sn-oj5hn5-55.googlevideo.com.", "rr5---sn-oji3bc-5j.googlevideo.com.", + "rr5---sn-ojnpo5-58.googlevideo.com.", + "rr5---sn-ojnpo5-58.gvt1.com.", "rr5---sn-p5qddn76.googlevideo.com.", "rr5---sn-p5qddn7d.googlevideo.com.", "rr5---sn-p5qddn7k.googlevideo.com.", "rr5---sn-p5qddn7r.googlevideo.com.", "rr5---sn-p5qddn7z.googlevideo.com.", "rr5---sn-p5qlsn6l.googlevideo.com.", - "rr5---sn-p5qlsn76.googlevideo.com.", + "rr5---sn-p5qlsn6s.googlevideo.com.", + "rr5---sn-p5qlsn6z.googlevideo.com.", "rr5---sn-p5qlsn7d.googlevideo.com.", "rr5---sn-p5qlsn7l.googlevideo.com.", "rr5---sn-p5qlsn7s.googlevideo.com.", @@ -5928,69 +6062,94 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-p5qlsnrr.googlevideo.com.", "rr5---sn-p5qlsny6.googlevideo.com.", "rr5---sn-p5qs7n6d.googlevideo.com.", + "rr5---sn-p5qs7n6y.googlevideo.com.", + "rr5---sn-p5qs7nd7.googlevideo.com.", "rr5---sn-p5qs7nsk.googlevideo.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-q4fl6nde.googlevideo.com.", "rr5---sn-q4fl6ndl.googlevideo.com.", "rr5---sn-q4fl6nds.googlevideo.com.", + "rr5---sn-q4fl6nds.gvt1.com.", "rr5---sn-q4fl6ndz.googlevideo.com.", + "rr5---sn-q4fl6ndz.gvt1.com.", "rr5---sn-q4fl6nlz.googlevideo.com.", "rr5---sn-q4fl6ns6.googlevideo.com.", "rr5---sn-q4fl6ns7.googlevideo.com.", "rr5---sn-q4fl6nsd.googlevideo.com.", + "rr5---sn-q4fl6nsd.gvt1.com.", "rr5---sn-q4fl6nsk.googlevideo.com.", "rr5---sn-q4fl6nsl.googlevideo.com.", "rr5---sn-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-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-q4flrn7r.googlevideo.com.", "rr5---sn-q4flrn7y.googlevideo.com.", "rr5---sn-q4flrne6.googlevideo.com.", "rr5---sn-q4flrne7.googlevideo.com.", + "rr5---sn-q4flrne7.gvt1.com.", "rr5---sn-q4flrnee.googlevideo.com.", "rr5---sn-q4flrnek.googlevideo.com.", "rr5---sn-q4flrnel.googlevideo.com.", "rr5---sn-q4flrner.googlevideo.com.", "rr5---sn-q4flrnes.googlevideo.com.", + "rr5---sn-q4flrnes.gvt1.com.", "rr5---sn-q4flrney.googlevideo.com.", "rr5---sn-q4flrnez.googlevideo.com.", "rr5---sn-q4flrnl6.googlevideo.com.", "rr5---sn-q4flrnl7.googlevideo.com.", "rr5---sn-q4flrnld.googlevideo.com.", "rr5---sn-q4flrnle.googlevideo.com.", + "rr5---sn-q4flrnle.gvt1.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-q4flrnsl.googlevideo.com.", + "rr5---sn-q4flrnsl.gvt1.com.", "rr5---sn-q4flrnss.googlevideo.com.", "rr5---sn-q4fzen7e.googlevideo.com.", + "rr5---sn-q4fzen7e.gvt1.com.", "rr5---sn-q4fzen7l.googlevideo.com.", + "rr5---sn-q4fzen7l.gvt1.com.", "rr5---sn-q4fzen7s.googlevideo.com.", "rr5---sn-q4fzen7y.googlevideo.com.", + "rr5---sn-q4fzen7y.gvt1.com.", "rr5---sn-q4fzene7.googlevideo.com.", "rr5---sn-q4fzenee.googlevideo.com.", "rr5---sn-qja5mc-5h.googlevideo.com.", "rr5---sn-qjp5q5-55.googlevideo.com.", "rr5---sn-qxo7rn7k.googlevideo.com.", - "rr5---sn-qxo7rn7r.googlevideo.com.", - "rr5---sn-qxo7rn7y.googlevideo.com.", + "rr5---sn-qxo7rne7.googlevideo.com.", + "rr5---sn-qxo7rnee.googlevideo.com.", "rr5---sn-qxoedn7k.googlevideo.com.", "rr5---sn-qxoedne7.googlevideo.com.", - "rr5---sn-t0aedn7l.googlevideo.com.", + "rr5---sn-qxoednee.googlevideo.com.", + "rr5---sn-qxoednel.googlevideo.com.", + "rr5---sn-qxoednes.googlevideo.com.", "rr5---sn-vgqskn66.googlevideo.com.", "rr5---sn-vgqskn67.googlevideo.com.", "rr5---sn-vgqskn6d.googlevideo.com.", @@ -6011,7 +6170,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-vgqsknse.googlevideo.com.", "rr5---sn-vgqsknsk.googlevideo.com.", "rr5---sn-vgqsknz6.googlevideo.com.", - "rr5---sn-vgqsknz7.googlevideo.com.", "rr5---sn-vgqsknzd.googlevideo.com.", "rr5---sn-vgqsknzk.googlevideo.com.", "rr5---sn-vgqsknzl.googlevideo.com.", @@ -6035,7 +6193,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-vgqsrnll.googlevideo.com.", "rr5---sn-vgqsrnls.googlevideo.com.", "rr5---sn-vgqsrnlz.googlevideo.com.", - "rr5---sn-vgqsrns6.googlevideo.com.", "rr5---sn-vgqsrnsd.googlevideo.com.", "rr5---sn-vgqsrnsy.googlevideo.com.", "rr5---sn-vgqsrnz6.googlevideo.com.", @@ -6046,38 +6203,7 @@ var FakeECSFQDNs = container.NewMapSet( "rr5---sn-vgqsrnzs.googlevideo.com.", "rr5---sn-vgqsrnzy.googlevideo.com.", "rr5---sn-vgqsrnzz.googlevideo.com.", - "rr5.sn-2oq4f5-c4.googlevideo.com.", - "rr5.sn-2ovgq5-cw.googlevideo.com.", - "rr5.sn-a5m7lnl6.googlevideo.com.", - "rr5.sn-a5m7lnld.googlevideo.com.", - "rr5.sn-a5mekn6d.googlevideo.com.", - "rr5.sn-a5mekn6k.googlevideo.com.", - "rr5.sn-a5mekn6l.googlevideo.com.", - "rr5.sn-a5mekn6r.googlevideo.com.", - "rr5.sn-a5mekn6s.googlevideo.com.", - "rr5.sn-a5mekn6z.googlevideo.com.", - "rr5.sn-a5meknd6.googlevideo.com.", - "rr5.sn-a5meknde.googlevideo.com.", - "rr5.sn-a5mekndl.googlevideo.com.", - "rr5.sn-a5meknds.googlevideo.com.", - "rr5.sn-a5mekndz.googlevideo.com.", - "rr5.sn-a5meknsd.googlevideo.com.", - "rr5.sn-a5meknsy.googlevideo.com.", - "rr5.sn-a5meknzk.googlevideo.com.", - "rr5.sn-a5meknzl.googlevideo.com.", - "rr5.sn-a5meknzr.googlevideo.com.", - "rr5.sn-a5mlrnek.googlevideo.com.", - "rr5.sn-a5mlrnl6.googlevideo.com.", - "rr5.sn-a5mlrnll.googlevideo.com.", - "rr5.sn-a5mlrnls.googlevideo.com.", - "rr5.sn-a5mlrnlz.googlevideo.com.", - "rr5.sn-a5msen7l.googlevideo.com.", - "rr5.sn-a5msen7z.googlevideo.com.", - "rr5.sn-a5msenek.googlevideo.com.", - "rr5.sn-a5msener.googlevideo.com.", - "rr5.sn-a5msenes.googlevideo.com.", - "rr5.sn-a5msenl7.googlevideo.com.", - "rr5.sn-a5msenle.googlevideo.com.", + "rr5.sn-aigl6nsd.googlevideo.com.", "rr5.sn-hp57kn6r.googlevideo.com.", "rr5.sn-hp57kn6y.googlevideo.com.", "rr5.sn-hp57knd6.googlevideo.com.", @@ -6088,8 +6214,6 @@ var FakeECSFQDNs = container.NewMapSet( "rr5.sn-hp57kndy.googlevideo.com.", "rr5.sn-hp57kndz.googlevideo.com.", "rr5.sn-hp57knk7.googlevideo.com.", - "rr5.sn-hp57yn7r.googlevideo.com.", - "rr5.sn-hp57yn7y.googlevideo.com.", "rr5.sn-hp57ynl6.googlevideo.com.", "rr5.sn-hp57ynlr.googlevideo.com.", "rr5.sn-hp57ynly.googlevideo.com.", @@ -6097,200 +6221,164 @@ var FakeECSFQDNs = container.NewMapSet( "rr5.sn-hp57ynse.googlevideo.com.", "rr5.sn-hp57ynsl.googlevideo.com.", "rr5.sn-hp57ynss.googlevideo.com.", + "rr5.sn-ntq7yns7.googlevideo.com.", "rr5.sn-nx57ynsk.googlevideo.com.", - "rr5.sn-q4fl6n6r.googlevideo.com.", + "rr5.sn-q4fl6n6s.googlevideo.com.", + "rr5.sn-q4fl6nd7.googlevideo.com.", "rr5.sn-q4fl6ndl.googlevideo.com.", - "rr5.sn-q4fl6ndz.googlevideo.com.", - "rr5.sn-q4flrn7y.googlevideo.com.", - "rr5.sn-q4flrner.googlevideo.com.", - "rr5.sn-q4flrnes.googlevideo.com.", - "rr5.sn-q4flrnl6.googlevideo.com.", - "rr5.sn-q4flrnld.googlevideo.com.", - "rr5.sn-q4flrnlz.googlevideo.com.", - "rr5.sn-q4flrnsd.googlevideo.com.", - "rr5.sn-q4flrnsk.googlevideo.com.", - "rr5.sn-q4flrnsl.googlevideo.com.", + "rr5.sn-q4flrnl7.googlevideo.com.", "rr5.sn-q4flrnss.googlevideo.com.", - "rr5.sn-q4fzen7e.googlevideo.com.", "rr5.sn-q4fzen7l.googlevideo.com.", - "rr5.sn-q4fzen7s.googlevideo.com.", "rr5.sn-q4fzen7y.googlevideo.com.", - "rr5.sn-q4fzene7.googlevideo.com.", - "rr5.sn-q4fzenee.googlevideo.com.", - "rr5.sn-qja5mc-5h.googlevideo.com.", - "rr5.sn-vgqskn66.googlevideo.com.", - "rr5.sn-vgqskn67.googlevideo.com.", - "rr5.sn-vgqskn6d.googlevideo.com.", - "rr5.sn-vgqskn6s.googlevideo.com.", - "rr5.sn-vgqskn6z.googlevideo.com.", - "rr5.sn-vgqsknez.googlevideo.com.", - "rr5.sn-vgqsknld.googlevideo.com.", - "rr5.sn-vgqsknlr.googlevideo.com.", - "rr5.sn-vgqskns7.googlevideo.com.", - "rr5.sn-vgqsknse.googlevideo.com.", - "rr5.sn-vgqsknsk.googlevideo.com.", - "rr5.sn-vgqsknz6.googlevideo.com.", - "rr5.sn-vgqsknz7.googlevideo.com.", - "rr5.sn-vgqsknzd.googlevideo.com.", - "rr5.sn-vgqsknzk.googlevideo.com.", - "rr5.sn-vgqsknzl.googlevideo.com.", - "rr5.sn-vgqsknzr.googlevideo.com.", - "rr5.sn-vgqsknzs.googlevideo.com.", - "rr5.sn-vgqsknzy.googlevideo.com.", - "rr5.sn-vgqsknzz.googlevideo.com.", - "rr5.sn-vgqsrn66.googlevideo.com.", - "rr5.sn-vgqsrn67.googlevideo.com.", - "rr5.sn-vgqsrn6e.googlevideo.com.", - "rr5.sn-vgqsrn6l.googlevideo.com.", - "rr5.sn-vgqsrn6z.googlevideo.com.", - "rr5.sn-vgqsrnl6.googlevideo.com.", - "rr5.sn-vgqsrnld.googlevideo.com.", - "rr5.sn-vgqsrnlk.googlevideo.com.", - "rr5.sn-vgqsrnls.googlevideo.com.", - "rr5.sn-vgqsrnlz.googlevideo.com.", - "rr5.sn-vgqsrns6.googlevideo.com.", - "rr5.sn-vgqsrnsd.googlevideo.com.", - "rr5.sn-vgqsrnsy.googlevideo.com.", - "rr5.sn-vgqsrnz6.googlevideo.com.", - "rr5.sn-vgqsrnz7.googlevideo.com.", - "rr5.sn-vgqsrnzd.googlevideo.com.", - "rr5.sn-vgqsrnzk.googlevideo.com.", - "rr5.sn-vgqsrnzr.googlevideo.com.", - "rr5.sn-vgqsrnzs.googlevideo.com.", - "rr5.sn-vgqsrnzy.googlevideo.com.", "rr5.sn-vgqsrnzz.googlevideo.com.", "rr6---sn-2aqu-hoas7.googlevideo.com.", - "rr6---sn-2aqu-hoasd.googlevideo.com.", - "rr6---sn-2aqu-hoasz.googlevideo.com.", - "rr6---sn-2aqu-jbt6.googlevideo.com.", "rr6---sn-42u-nboze.googlevideo.com.", - "rr6---sn-42u-nbozl.googlevideo.com.", - "rr6---sn-42u-nbozs.googlevideo.com.", - "rr6---sn-42u-nbozz.googlevideo.com.", - "rr6---sn-5abxgpxuxaxjvh-9n4e.googlevideo.com.", - "rr6---sn-5abxgpxuxaxjvh-9n4l.googlevideo.com.", - "rr6---sn-5abxgpxuxaxjvh-j1ae.googlevideo.com.", - "rr6---sn-5abxgpxuxaxjvh-j1az.googlevideo.com.", - "rr6---sn-8pxuuxa-nbo6l.googlevideo.com.", - "rr6---sn-8pxuuxa-nbosd.googlevideo.com.", - "rr6---sn-8qj-i5o66.googlevideo.com.", - "rr6---sn-8qj-i5o6d.googlevideo.com.", - "rr6---sn-8qj-i5o6k.googlevideo.com.", - "rr6---sn-8qj-i5ody.googlevideo.com.", - "rr6---sn-8qj-i5ozd.googlevideo.com.", - "rr6---sn-8qj-i5ozr.googlevideo.com.", "rr6---sn-8qj-nbo66.googlevideo.com.", - "rr6---sn-8qj-nbo67.googlevideo.com.", - "rr6---sn-8xgp1vo-a5me.googlevideo.com.", "rr6---sn-8xgp1vo-ab56.googlevideo.com.", "rr6---sn-8xgp1vo-ab5d.googlevideo.com.", "rr6---sn-8xgp1vo-ab5e.googlevideo.com.", "rr6---sn-8xgp1vo-ab5l.googlevideo.com.", - "rr6---sn-8xgp1vo-p5ie.googlevideo.com.", + "rr6---sn-8xgp1vo-p5qee.googlevideo.com.", + "rr6---sn-8xgp1vo-p5qel.googlevideo.com.", + "rr6---sn-8xgp1vo-vgqe.googlevideo.com.", "rr6---sn-8xgp1vo-xfge.googlevideo.com.", "rr6---sn-8xgp1vo-xfgl.googlevideo.com.", "rr6---sn-8xgp1vo-xfgs.googlevideo.com.", + "rr6---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", "rr6---sn-bvvbaxivnuxq5uu-q4fe.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-bvvbaxivnuxq5uu-q4fl.googlevideo.com.", + "rr6---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "rr6---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "rr6---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "rr6---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "rr6---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "rr6---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "rr6---sn-bvvbaxivnuxqjvhj5nu-n4v6.googlevideo.com.", + "rr6---sn-bvvbaxivnuxqjvhj5nu-vgq6.googlevideo.com.", + "rr6---sn-bvvbaxivnuxqjvhj5nu-vgqd.googlevideo.com.", + "rr6---sn-bvvbaxivnuxqjvhj5nu-vgqe.googlevideo.com.", + "rr6---sn-bvvbaxivnuxqjvhj5nu-vgql.googlevideo.com.", + "rr6---sn-bvvbaxivnuxqjvhj5nu-vgqs.googlevideo.com.", + "rr6---sn-bvvbaxivnuxqjvhj5nu-vgqz.googlevideo.com.", + "rr6---sn-bvvbaxivnuxqjvm-2ime.googlevideo.com.", + "rr6---sn-bvvbaxivnuxqjvm-2ims.googlevideo.com.", + "rr6---sn-bvvbaxivnuxqjvm-2ims.gvt1.com.", + "rr6---sn-bvvbaxivnuxqjvm-q4fe.googlevideo.com.", + "rr6---sn-bvvbaxivnuxqjvm-q4fe.gvt1.com.", + "rr6---sn-bvvbaxivnuxqjvm-q4fl.googlevideo.com.", + "rr6---sn-bvvbaxivnuxqjvm-q4fl.gvt1.com.", + "rr6---sn-jn2pgx4pcxg-w5os.googlevideo.com.", + "rr6---sn-jxopj-n5oe.googlevideo.com.", + "rr6---sn-jxopj-nh4e.googlevideo.com.", + "rr6---sn-jxopj-nh4e.gvt1.com.", "rr7---sn-2aqu-hoas7.googlevideo.com.", - "rr7---sn-2aqu-hoasd.googlevideo.com.", "rr7---sn-2aqu-hoasz.googlevideo.com.", - "rr7---sn-2aqu-jxcy.googlevideo.com.", - "rr7---sn-42u-nboze.googlevideo.com.", - "rr7---sn-42u-nbozl.googlevideo.com.", - "rr7---sn-42u-nbozs.googlevideo.com.", - "rr7---sn-42u-nbozz.googlevideo.com.", - "rr7---sn-5abxgpxuxaxjvh-9n4e.googlevideo.com.", - "rr7---sn-5abxgpxuxaxjvh-9n4l.googlevideo.com.", - "rr7---sn-5abxgpxuxaxjvh-cawe.googlevideo.com.", - "rr7---sn-5abxgpxuxaxjvh-j1ae.googlevideo.com.", - "rr7---sn-8pxuuxa-nbo6l.googlevideo.com.", - "rr7---sn-8pxuuxa-nbosd.googlevideo.com.", - "rr7---sn-8qj-i5o66.googlevideo.com.", - "rr7---sn-8qj-i5o6d.googlevideo.com.", - "rr7---sn-8qj-i5o6k.googlevideo.com.", - "rr7---sn-8qj-i5ody.googlevideo.com.", "rr7---sn-8qj-i5ozd.googlevideo.com.", - "rr7---sn-8qj-i5ozr.googlevideo.com.", "rr7---sn-8qj-nbo66.googlevideo.com.", "rr7---sn-8xgp1vo-ab56.googlevideo.com.", "rr7---sn-8xgp1vo-ab5d.googlevideo.com.", "rr7---sn-8xgp1vo-ab5e.googlevideo.com.", "rr7---sn-8xgp1vo-ab5l.googlevideo.com.", + "rr7---sn-8xgp1vo-p5qee.googlevideo.com.", + "rr7---sn-8xgp1vo-p5qel.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.", - "rr8---sn-2aqu-hoale.googlevideo.com.", + "rr7---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", + "rr7---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "rr7---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "rr7---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "rr7---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "rr7---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "rr7---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "rr7---sn-bvvbaxivnuxqjvhj5nu-vgq6.googlevideo.com.", + "rr7---sn-bvvbaxivnuxqjvhj5nu-vgqd.googlevideo.com.", + "rr7---sn-bvvbaxivnuxqjvhj5nu-vgqe.googlevideo.com.", + "rr7---sn-bvvbaxivnuxqjvhj5nu-vgql.googlevideo.com.", + "rr7---sn-bvvbaxivnuxqjvhj5nu-vgqs.googlevideo.com.", + "rr7---sn-bvvbaxivnuxqjvhj5nu-vgqz.googlevideo.com.", + "rr7---sn-bvvbaxivnuxqjvm-2ime.googlevideo.com.", + "rr7---sn-bvvbaxivnuxqjvm-2ims.googlevideo.com.", + "rr7---sn-bvvbaxivnuxqjvm-q4fe.googlevideo.com.", + "rr7---sn-bvvbaxivnuxqjvm-q4fe.gvt1.com.", + "rr7---sn-bvvbaxivnuxqjvm-q4fl.googlevideo.com.", + "rr7---sn-jxopj-n5oe.googlevideo.com.", "rr8---sn-2aqu-hoas7.googlevideo.com.", - "rr8---sn-2aqu-hoasd.googlevideo.com.", - "rr8---sn-2aqu-hoasz.googlevideo.com.", - "rr8---sn-2aqu-jxcy.googlevideo.com.", - "rr8---sn-42u-nboze.googlevideo.com.", "rr8---sn-42u-nbozl.googlevideo.com.", - "rr8---sn-42u-nbozs.googlevideo.com.", - "rr8---sn-42u-nbozz.googlevideo.com.", - "rr8---sn-5abxgpxuxaxjvh-9n4e.googlevideo.com.", - "rr8---sn-5abxgpxuxaxjvh-j1ae.googlevideo.com.", - "rr8---sn-8pxuuxa-nbo6l.googlevideo.com.", - "rr8---sn-8qj-i5o66.googlevideo.com.", - "rr8---sn-8qj-i5o6d.googlevideo.com.", - "rr8---sn-8qj-i5o6k.googlevideo.com.", - "rr8---sn-8qj-i5ody.googlevideo.com.", "rr8---sn-8qj-i5ozd.googlevideo.com.", - "rr8---sn-8qj-i5ozr.googlevideo.com.", "rr8---sn-8xgp1vo-ab56.googlevideo.com.", "rr8---sn-8xgp1vo-ab5d.googlevideo.com.", "rr8---sn-8xgp1vo-ab5e.googlevideo.com.", "rr8---sn-8xgp1vo-ab5l.googlevideo.com.", "rr8---sn-8xgp1vo-ab5s.googlevideo.com.", "rr8---sn-8xgp1vo-ab5z.googlevideo.com.", + "rr8---sn-8xgp1vo-p5qee.googlevideo.com.", + "rr8---sn-8xgp1vo-p5qel.googlevideo.com.", "rr8---sn-8xgp1vo-xfge.googlevideo.com.", "rr8---sn-8xgp1vo-xfgl.googlevideo.com.", - "rr8---sn-jvhj5nu-nh4e.googlevideo.com.", - "rr8---sn-jvhj5nu-nh4l.googlevideo.com.", - "rr8---sn-jvhj5nu-nh4s.googlevideo.com.", - "rr8---sn-jvhj5nu-nh4z.googlevideo.com.", - "rr9---sn-42u-nboze.googlevideo.com.", - "rr9---sn-42u-nbozl.googlevideo.com.", - "rr9---sn-42u-nbozs.googlevideo.com.", - "rr9---sn-8pxuuxa-nbosd.googlevideo.com.", + "rr8---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", + "rr8---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "rr8---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "rr8---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "rr8---sn-bvvbaxivnuxq5uu-vgql.googlevideo.com.", + "rr8---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "rr8---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "rr8---sn-bvvbaxivnuxqjvhj5nu-vgq6.googlevideo.com.", + "rr8---sn-bvvbaxivnuxqjvhj5nu-vgqd.googlevideo.com.", + "rr8---sn-bvvbaxivnuxqjvhj5nu-vgqe.googlevideo.com.", + "rr8---sn-bvvbaxivnuxqjvhj5nu-vgql.googlevideo.com.", + "rr8---sn-bvvbaxivnuxqjvhj5nu-vgqs.googlevideo.com.", + "rr8---sn-bvvbaxivnuxqjvm-2ime.googlevideo.com.", + "rr8---sn-bvvbaxivnuxqjvm-2ims.googlevideo.com.", + "rr8---sn-bvvbaxivnuxqjvm-q4fe.googlevideo.com.", + "rr8---sn-bvvbaxivnuxqjvm-q4fl.googlevideo.com.", "rr9---sn-8xgp1vo-ab56.googlevideo.com.", "rr9---sn-8xgp1vo-ab5d.googlevideo.com.", - "rr9---sn-jvhj5nu-nh4e.googlevideo.com.", - "rr9---sn-jvhj5nu-nh4l.googlevideo.com.", - "rr9---sn-jvhj5nu-nh4s.googlevideo.com.", - "rr9---sn-jvhj5nu-nh4z.googlevideo.com.", + "rr9---sn-8xgp1vo-p5qee.googlevideo.com.", + "rr9---sn-8xgp1vo-p5qel.googlevideo.com.", + "rr9---sn-bvvbaxivnuxq5uu-q4f6.googlevideo.com.", + "rr9---sn-bvvbaxivnuxq5uu-q4fs.googlevideo.com.", + "rr9---sn-bvvbaxivnuxq5uu-q4fz.googlevideo.com.", + "rr9---sn-bvvbaxivnuxq5uu-vgqe.googlevideo.com.", + "rr9---sn-bvvbaxivnuxq5uu-vgqs.googlevideo.com.", + "rr9---sn-bvvbaxivnuxq5uu-vgqz.googlevideo.com.", + "rr9---sn-bvvbaxivnuxqjvhj5nu-vgqd.googlevideo.com.", + "rr9---sn-bvvbaxivnuxqjvhj5nu-vgqe.googlevideo.com.", + "rr9---sn-bvvbaxivnuxqjvhj5nu-vgql.googlevideo.com.", + "rr9---sn-bvvbaxivnuxqjvhj5nu-vgqs.googlevideo.com.", + "rr9---sn-bvvbaxivnuxqjvhj5nu-vgqz.googlevideo.com.", + "rr9---sn-bvvbaxivnuxqjvm-2ime.googlevideo.com.", + "rr9---sn-bvvbaxivnuxqjvm-2ims.googlevideo.com.", + "rr9---sn-bvvbaxivnuxqjvm-q4fe.googlevideo.com.", + "rr9---sn-bvvbaxivnuxqjvm-q4fe.gvt1.com.", + "rr9---sn-bvvbaxivnuxqjvm-q4fl.googlevideo.com.", + "rs-stripe.alm.com.", "rsapi.haokan.mobi.", "rsx.afterpay.com.", "rt.teramind.co.", - "rtb-eu-v4.prertbdir.com.", + "rtb-use.hellogenie.com.", + "rtb-use.mfadsrvr.com.", "rtb-useast.creativedot.net.", - "rtb2-useast.applabs.live.", + "rtb-usw.mfadsrvr.com.", + "rtb-uswest.creativedot.net.", + "rtbasia.com.", "rtbmax.com.", - "rtbsuperhub.com.", "rtbwave.com.", "rttf.citrix.com.", "ru-comonrt-stsdk.vivoglobal.com.", "ru-comort-stsdk.vivoglobal.com.", - "ru-err-up.vivoglobal.com.", "ru-exappupgrade.vivoglobal.com.", "ru-fcm.sms.intl.xiaomi.com.", - "ru-main-appstore.vivoglobal.com.", "ru-st-exappupgrade.vivoglobal.com.", "ru-st-sysupgrade.vivoglobal.com.", - "ru-vcode-api.vivoglobal.com.", "ru-vcode-od.vivoglobal.com.", "ru.cdn.dnsv1.com.", "ru.global.market.xiaomi.com.", + "rubiconmd.zoom.us.", "rubyfish.cn.", - "rugbyfootballsport.com.", + "rudderstack.beeper-tools.com.", "ruijienetworks.com.", "rule34video.com.", + "rumt-zh.com.", "rus-account.palm.tech.", "rus-api.nebulalive.com.", "rus-configuration.transsion-os.com.", @@ -6300,64 +6388,52 @@ var FakeECSFQDNs = container.NewMapSet( "rutubelist.ru.", "s-cdn.anthropic.com.", "s-cs.send.microad.jp.", - "s-light.tiket.photos.", - "s.alfasrv.com.", - "s.dblks.net.", + "s-pinimg-com.gslb.pinterest.com.", "s.seedtag.com.", "s1.kqxs.tube.", - "s1.welib-premium.org.", - "s10.histats.com.", + "s1002-f107.mp.lura.live.", "s2-a.time.mci1.us.rozint.net.", "s2-b.time.mci1.us.rozint.net.", "s2.kqxs.tube.", - "s2.welib-premium.org.", - "s3-advertising.zalopay.vn.", + "s3-us01.didiglobal.com.", "s3.kqxs.tube.", "s5.kqxs.tube.", - "s5.tuoitre.vn.", "s6.kqxs.tube.", + "s7.kqxs.tube.", "sa1.chat.si.riotgames.com.", "sa2.chat.si.riotgames.com.", "sa3.chat.si.riotgames.com.", "saas.sensorsdata.com.", "saintasaph.remotepc.com.", + "sales.ai.dynamics.com.", + "salesmanago.com.", "saltlakecity.remotepc.com.", - "samhealthanon.genetec.com.", - "samip.genetec.com.", - "sampkac.genetec.com.", - "samsclub.quantummetric.com.", + "samsclubglass.quantummetric.com.", "sanantonio.remotepc.com.", "sandiego.remotepc.com.", "sandiegodc.remotepc.com.", "sanjose.remotepc.com.", "santiago.remotepc.com.", - "saoniuhuo.com.", "saopaulo.remotepc.com.", "saopaulo1.remotepc.com.", - "sav.cynet.com.", - "sblinks.net.", - "sbm.pw.", - "sc-sa.dzfread.cn.", - "sca-vcode-od.vivoglobal.com.", + "satellite-cdn.salesloft.com.", + "sb.adtidy.org.", "scanservice1.qg3.apps.qualys.com.", + "scapi.vmware.com.", "schneidercorp.com.", - "scielo.isciii.es.", "sciener.cn.", - "scirp.org.", "scm.haplat.net.", "scontent-ams2-1.cdninstagram.com.", "scontent-ams2-1.xx.fbcdn.net.", "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-atl3-3.cdninstagram.com.", "scontent-atl3-3.xx.fbcdn.net.", - "scontent-ber1-1.cdninstagram.com.", "scontent-bkk1-1.xx.fbcdn.net.", "scontent-bkk1-2.xx.fbcdn.net.", "scontent-bog2-1.cdninstagram.com.", @@ -6394,6 +6470,7 @@ var FakeECSFQDNs = container.NewMapSet( "scontent-dfw5-3.xx.fbcdn.net.", "scontent-dus1-1.cdninstagram.com.", "scontent-dus1-1.xx.fbcdn.net.", + "scontent-fml1-1.cdninstagram.com.", "scontent-fra3-1.cdninstagram.com.", "scontent-fra3-1.xx.fbcdn.net.", "scontent-fra3-2.cdninstagram.com.", @@ -6402,9 +6479,7 @@ var FakeECSFQDNs = container.NewMapSet( "scontent-fra5-1.xx.fbcdn.net.", "scontent-fra5-2.cdninstagram.com.", "scontent-fra5-2.xx.fbcdn.net.", - "scontent-gua1-1.xx.fbcdn.net.", "scontent-ham3-1.cdninstagram.com.", - "scontent-ham3-1.xx.fbcdn.net.", "scontent-hel3-1.cdninstagram.com.", "scontent-hkg1-1.cdninstagram.com.", "scontent-hkg1-1.xx.fbcdn.net.", @@ -6456,21 +6531,14 @@ var FakeECSFQDNs = container.NewMapSet( "scontent-mnl1-1.xx.fbcdn.net.", "scontent-mnl1-2.cdninstagram.com.", "scontent-mnl1-2.xx.fbcdn.net.", - "scontent-mnl3-1.cdninstagram.com.", "scontent-mnl3-1.xx.fbcdn.net.", - "scontent-mnl3-2.cdninstagram.com.", "scontent-mnl3-2.xx.fbcdn.net.", - "scontent-mrs2-1.xx.fbcdn.net.", - "scontent-mrs2-2.xx.fbcdn.net.", - "scontent-mrs2-3.xx.fbcdn.net.", "scontent-msp1-1.cdninstagram.com.", "scontent-msp1-1.xx.fbcdn.net.", "scontent-mty2-1.cdninstagram.com.", "scontent-mty2-1.xx.fbcdn.net.", "scontent-muc2-1.cdninstagram.com.", "scontent-muc2-1.xx.fbcdn.net.", - "scontent-nrt1-1.xx.fbcdn.net.", - "scontent-nrt1-2.xx.fbcdn.net.", "scontent-ord5-1.cdninstagram.com.", "scontent-ord5-1.xx.fbcdn.net.", "scontent-ord5-2.cdninstagram.com.", @@ -6479,7 +6547,6 @@ var FakeECSFQDNs = container.NewMapSet( "scontent-ord5-3.xx.fbcdn.net.", "scontent-phx1-1.cdninstagram.com.", "scontent-phx1-1.xx.fbcdn.net.", - "scontent-prg1-1.cdninstagram.com.", "scontent-qro1-1.cdninstagram.com.", "scontent-qro1-1.xx.fbcdn.net.", "scontent-qro1-2.cdninstagram.com.", @@ -6506,54 +6573,44 @@ var FakeECSFQDNs = container.NewMapSet( "scontent-sin6-3.xx.fbcdn.net.", "scontent-sjc3-1.cdninstagram.com.", "scontent-sjc3-1.xx.fbcdn.net.", - "scontent-sof1-1.cdninstagram.com.", - "scontent-sof1-1.xx.fbcdn.net.", - "scontent-sof1-2.xx.fbcdn.net.", + "scontent-sjc6-1.cdninstagram.com.", + "scontent-sjc6-1.xx.fbcdn.net.", "scontent-vie1-1.cdninstagram.com.", "scontent-vie1-1.xx.fbcdn.net.", - "scontent-waw2-1.cdninstagram.com.", - "scontent-waw2-1.xx.fbcdn.net.", "scontent-waw2-2.cdninstagram.com.", - "scontent-waw2-2.xx.fbcdn.net.", "scontent-yyz1-1.cdninstagram.com.", "scontent-yyz1-1.xx.fbcdn.net.", - "scot-api.steem-engine.net.", "scraper2.onlineradiobox.com.", - "scripttags.jst.ai.", "scservices.genetec.com.", + "sdaonuo.com.", "sdk.beizi.biz.", "sdk.cqsjd.xyz.", - "sdk.iad-01.braze.com.", - "sdk.qcloud.com.", "sdkgate.pushv3.easebar.com.", "sdks.shopifycdn.com.", "sdktmp.hubcloud.com.cn.", - "seaart.ai.", "seabroadnet.com.", "seagullscientific.com.", "seal-blue.bbb.org.", + "seal-goldengate.bbb.org.", "sealsubscriptions.com.", - "search-proxy-cn.heytapmobi.com.", "search.dnssearch.org.", "search.namequery.com.", "search.us.namequery.com.", + "search22-normal-c-alisg.tiktokv.com.", "searchserverapi.com.", "searchserverapi1.com.", - "seasuncdn.com.", - "seasungamescdn.com.", "seattle.remotepc.com.", - "secapi.netshort.com.", "secure-signals.permutive.app.", - "secure.checkout.visa.com.", + "secure.accurint.com.", + "secure.stratanetwork.com.", "secure.syndetics.com.", - "secure5.arcot.com.", + "secureacceptance.cybersource.com.", "securetheorem.com.", + "security-reports.shopifysvc.com.", "securityapi.d3-pr-tm.com.", "seedtag.com.", - "seguridadinmobiliaria.com.", "semaphore.cocogain.io.", "send.microad.jp.", - "sensei.ruselabs.com.", "sensorsdata.cn.", "sensorsdata.com.", "sensorsgateway.com.", @@ -6561,78 +6618,78 @@ var FakeECSFQDNs = container.NewMapSet( "sentry-webapp.quillbot.com.", "sentry.appodeal.com.", "sentry.cloudlinux.com.", + "sentry.wmt.dev.", "seoul.remotepc.com.", - "serv.xapstream.com.", - "serve-next.olo.com.", - "server.easycounter.com.", + "serraview.com.", + "serve.mongobrain.app.", + "server-v4.shop.app.", "server.shop.app.", - "serverforge.org.", "service2.ultipro.com.", - "servicecdn.ru.", + "servicebus1041.myconnectsecure.com.", + "servicebus1042.myconnectsecure.com.", + "servicebus1043.myconnectsecure.com.", + "servicebus1044.myconnectsecure.com.", "services.adaptiva.cloud.", "services.lego.com.", - "services2.risevision.com.", "servicetitan.com.", "servs.modoro360.com.", + "servt.modoro360.com.", + "servx.modoro360.com.", "servx.opamarketplace.com.", "servx.playstream.media.", "settings.luckyorange.com.", "sevenrooms.com.", + "sewjn80htn-3.algolianet.com.", "sfont.zalopay.com.vn.", "sfu.voip.signal.org.", "sg-o-s3.smartcloudcon.com.", - "sg-trk.bidmatrixdsp.com.", "sg.api.translator.voice.gcloudsdk.com.", "sg.bidder.paddlewaver.com.", "sgfp.tongdun.net.", "sgpcas.ezvizlife.com.", - "sgpjbg.com.", - "sgshort.pay.wechat.com.", - "sgspeed.ino.sgameglobal.com.", - "sh.jd.com.", - "share.connect.aig.", + "sgtm.stevemadden.com.", "share.fcgame.net.", "sharpschool.com.", - "shavers.sbm.pw.", - "shiply-cdn.qq.com.", "shop.app.", "shopcircle.co.", - "shopifp.com.", + "shopify-assets.shopifycdn.com.", "shopify-gtm-suite.getelevar.com.", "shopify.com.", "shopifynetwork.com.", "shortpixel.ai.", + "show-creative1.com.", "shp.ee.", "shrinetheme.com.", "shuzilm.cn.", - "sigma-qdata-h72.proxima.nie.netease.com.", + "signin.ultipro.com.", "simplemdm.com.", - "simpleswap.io.", - "sina.com.", + "simpplr.com.", "sinaimg.cn.", + "sink.archive.org.", "sip-backup.phonepower.com.", "sip-primary.phonepower.com.", "sip.ringcentral.com.", - "sip10.ringcentral.com.", - "sip112-1121.ringcentral.com.", - "sip112-1131.ringcentral.com.", "sip112-1141.ringcentral.com.", - "sip113-1121.ringcentral.com.", - "sip113-1141.ringcentral.com.", + "sip121-1111.ringcentral.com.", "sip121-1121.ringcentral.com.", "sip121-1131.ringcentral.com.", "sip121-1141.ringcentral.com.", + "sip121-1221.ringcentral.com.", + "sip121-1231.ringcentral.com.", "sip121-1241.ringcentral.com.", "sip123-1111.ringcentral.com.", "sip123-1121.ringcentral.com.", "sip123-1131.ringcentral.com.", "sip123-1141.ringcentral.com.", "sip123-1211.ringcentral.com.", + "sip123-1221.ringcentral.com.", "sip123-1231.ringcentral.com.", "sip123-1241.ringcentral.com.", + "sip131-1111.ringcentral.com.", "sip131-1121.ringcentral.com.", "sip131-1131.ringcentral.com.", "sip131-1141.ringcentral.com.", + "sip131-1211.ringcentral.com.", "sip131-1221.ringcentral.com.", "sip131-1241.ringcentral.com.", "sip132-1111.ringcentral.com.", @@ -6640,17 +6697,26 @@ var FakeECSFQDNs = container.NewMapSet( "sip132-1131.ringcentral.com.", "sip132-1141.ringcentral.com.", "sip132-1211.ringcentral.com.", + "sip132-1221.ringcentral.com.", + "sip132-1231.ringcentral.com.", + "sip132-1241.ringcentral.com.", "sip421-121.ringcentral.biz.", "sirsi.net.", + "site-assets.fontawesome.com.", "site-config.com.", + "sixpence.ai.", "sjc.zoom.us.", "skeepers.io.", + "skims.com.", "skyapi.policies.live.net.", "skydrivesync.policies.live.net.", - "smartarget-sp-cache.fra1.digitaloceanspaces.com.", + "skyward-ocprod.iscorp.com.", + "skyward.iscorp.com.", + "sm-tc.cn.", + "sm.cn.", "smartcloudcon.com.", + "smartcommunications.cloud.", "smarthome.ctdevice.ott4china.com.", - "smarthome.iot.heytapmobi.com.", "smoot-api-safari-aapse1c.v.aaplimg.com.", "smoot-api-safari-aeun1a.v.aaplimg.com.", "smoot-api-safari-aeun1b.v.aaplimg.com.", @@ -6680,15 +6746,17 @@ var FakeECSFQDNs = container.NewMapSet( "smoot-searchv2-ausw2b.v.aaplimg.com.", "smoot-searchv2-ausw2c.v.aaplimg.com.", "sms.ads.heytapmobi.com.", - "snap-storage-cdn.l.google.com.", "snippet.affilimatejs.com.", "snmp-device-na.toshiba-solutions.com.", + "so1506.ci.managedwhitelisting.com.", "sobot.com.", "socialchain.app.", "sofia.remotepc.com.", + "sogoucdn.com.", "sohu.com.", "solid.preyproject.com.", "sonar-akl1-1.xx.fbcdn.net.", + "sonar-amd1-1.xx.fbcdn.net.", "sonar-ams2-1.xx.fbcdn.net.", "sonar-ams4-1.xx.fbcdn.net.", "sonar-arn2-1.xx.fbcdn.net.", @@ -6703,7 +6771,9 @@ var FakeECSFQDNs = container.NewMapSet( "sonar-blr1-2.xx.fbcdn.net.", "sonar-bog2-1.xx.fbcdn.net.", "sonar-bog2-2.xx.fbcdn.net.", - "sonar-bom2-1.xx.fbcdn.net.", + "sonar-bom1-1.xx.fbcdn.net.", + "sonar-bom2-2.xx.fbcdn.net.", + "sonar-bom2-3.xx.fbcdn.net.", "sonar-bos5-1.xx.fbcdn.net.", "sonar-bru2-1.xx.fbcdn.net.", "sonar-bsb1-1.xx.fbcdn.net.", @@ -6720,11 +6790,6 @@ var FakeECSFQDNs = container.NewMapSet( "sonar-cgk2-2.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-del2-3.xx.fbcdn.net.", "sonar-den2-1.xx.fbcdn.net.", "sonar-det1-1.xx.fbcdn.net.", "sonar-dfw5-1.xx.fbcdn.net.", @@ -6785,7 +6850,6 @@ var FakeECSFQDNs = container.NewMapSet( "sonar-lim1-1.xx.fbcdn.net.", "sonar-lis1-1.xx.fbcdn.net.", "sonar-los2-1.xx.fbcdn.net.", - "sonar-maa3-1.xx.fbcdn.net.", "sonar-mad1-1.xx.fbcdn.net.", "sonar-mad2-1.xx.fbcdn.net.", "sonar-man2-1.xx.fbcdn.net.", @@ -6835,12 +6899,11 @@ var FakeECSFQDNs = container.NewMapSet( "sonar-sin6-2.xx.fbcdn.net.", "sonar-sin6-3.xx.fbcdn.net.", "sonar-sjc3-1.xx.fbcdn.net.", + "sonar-sjc6-1.xx.fbcdn.net.", "sonar-sof1-1.xx.fbcdn.net.", "sonar-sof1-2.xx.fbcdn.net.", "sonar-ssn1-1.xx.fbcdn.net.", "sonar-syd2-1.xx.fbcdn.net.", - "sonar-tir3-2.xx.fbcdn.net.", - "sonar-tir3-3.xx.fbcdn.net.", "sonar-tpe1-1.xx.fbcdn.net.", "sonar-vie1-1.xx.fbcdn.net.", "sonar-waw2-1.xx.fbcdn.net.", @@ -6848,52 +6911,45 @@ var FakeECSFQDNs = container.NewMapSet( "sonar-yyz1-1.xx.fbcdn.net.", "sonar-zrh1-1.xx.fbcdn.net.", "sonar.viously.com.", - "songswave.com.", - "souluojie.com.", - "sound-machine.com.", "southafricanorth.api.cognitive.microsoft.com.", "southcarolina.remotepc.com.", "southeastasia.api.cognitive.microsoft.com.", "southindia.api.cognitive.microsoft.com.", + "southwest.quantummetric.com.", "spadsync.com.", "sparteo.com.", - "spifc.ssl.fun.", + "speedtest.cn.", + "spiceworks.com.cdn.cloudflare.net.", "spiny.ai.", "spion.savvy.security.", - "split-tool.com.", + "splunk.atlassian.net.", "src.ebay-us.com.", "srv.datacygnal.io.", "srv00.com.", - "ss.zolnm.com.", + "ssctech.com.", + "ssl.geoplugin.net.", "sso.services.box.net.", "ssp.hbrd.io.", "ssp.hybrid.ai.", - "sss.pk-live.cn.", - "ssxd.mediav.com.", + "st-ok-pts.cdn-vk.ru.", "st-ok.cdn-vk.ru.", - "st-sysupgrade.vivo.com.cn.", - "st.p.360.cn.", + "st-premium-v3-univ-srs-win-3744-g3.api.splashtop.com.", "stable.dl2.discordapp.net.", - "staffbase.com.", - "stape.nz.", - "stardust-tv.com.", + "stape.ai.", + "stappupgrade.vivo.com.cn.", "stardustgod.com.", - "starhalo.mobi.", "starrydyn.com.", - "startpage.foxitsoftware.com.cdn.cloudflare.net.", "startssl.com.", - "stat.360safe.com.", - "stat.cnbcindonesia.com.", - "stat.cnnindonesia.com.", - "stat.playfamily.ru.", - "stat2.okko.tv.", + "stat.lianmeng.360.cn.", "statad.ru.", "static-atl3-1.xx.fbcdn.net.", "static-atl3-2.xx.fbcdn.net.", + "static-atl3-3.xx.fbcdn.net.", "static-det1-1.xx.fbcdn.net.", "static-dfw5-1.xx.fbcdn.net.", "static-dfw5-2.xx.fbcdn.net.", "static-dfw5-3.xx.fbcdn.net.", + "static-forms.clink-lists.com.", "static-hou1-1.xx.fbcdn.net.", "static-iad3-1.xx.fbcdn.net.", "static-iad3-2.xx.fbcdn.net.", @@ -6902,58 +6958,63 @@ var FakeECSFQDNs = container.NewMapSet( "static-lga3-1.xx.fbcdn.net.", "static-lga3-2.xx.fbcdn.net.", "static-lga3-3.xx.fbcdn.net.", - "static-lhr6-1.xx.fbcdn.net.", "static-lhr8-1.xx.fbcdn.net.", "static-lhr8-2.xx.fbcdn.net.", "static-mia3-3.xx.fbcdn.net.", + "static-mia5-1.xx.fbcdn.net.", "static-msp1-1.xx.fbcdn.net.", "static-ord5-1.xx.fbcdn.net.", "static-ord5-2.xx.fbcdn.net.", "static-ord5-3.xx.fbcdn.net.", "static-sea1-1.xx.fbcdn.net.", + "static-sea5-1.xx.fbcdn.net.", "static-sjc3-1.xx.fbcdn.net.", + "static-sjc6-1.xx.fbcdn.net.", "static.avito.ru.", - "static.fd-api.com.", - "static.hnonline.sk.", + "static.galaksion.com.", "static.linkr.com.", "static.rustore.ru.", - "static.rutubelist.ru.", - "staticontent.com.", - "statis.ekatox-ru.com.", + "stats.norton.com.trafficmanager.net.", "stats.transitapp.com.", + "stats.vidalytics.com.", "statsig.anthropic.com.", - "steem-engine.net.", "stemchristie.rome2rio.com.", + "stevemadden.com.", "stg-data-in.ads.heytapmobile.com.", "stg-data.ads.heytapmobi.com.", - "stjohnsblackheath.org.uk.", "stockholm.remotepc.com.", "stocks-analytics-events.apple.com.", "store-cfg-ru.heytapmobile.com.", "store.vsco.co.", + "stp-live.inside-graph.com.", "str-vcode-tracker-fenghuang-prd-bj.vivo.com.cn.", "streamhub.tech.", "streaming.forexpros.com.", - "streaming.humix.com.", "streetviewpixels-pa.googleapis.com.", + "stripst.com.", + "sts.eccmp.com.", + "stse02.ultipro.com.", + "stsew02.ultipro.com.", "stsn02.ultipro.com.", + "student.atitesting.com.", "studyquicks.com.", "stun.acrobits.cz.", "stun.cloudflare.com.", "stun.l.google.com.", "stun1.l.google.com.", + "stun1.ringcentral.com.", "stun2.l.google.com.", + "stun2.ringcentral.com.", "stun3.l.google.com.", "stun4.l.google.com.", - "stylitics.com.cdn.cloudflare.net.", "su6786.ci.managedwhitelisting.com.", - "sudingtech.com.", "sumologic.com.", "sunmi.com.", "supabase.com.", + "superacme.com.", + "superantispyware.com.", "supl.qxwz.com.", - "sur.ly.", - "sushi.ruselabs.com.", + "support.powerschool.com.", "sv8.cyberhaven.io.", "sve.cc.", "svk-native.ru.", @@ -6966,40 +7027,43 @@ var FakeECSFQDNs = container.NewMapSet( "sync.bidence.net.", "sync.driftpixel.live.", "sync.omnifytv.live.", - "sync.oraki.io.", - "sync.saideao.com.", - "sync.techdsp.ru.", + "sync.rbstsystems.live.", + "sync.spoutroserve.com.", "sync.videowalldirect.com.", "syndetics.com.", "systemreportservices.genetec.com.", - "t-nation.com.", + "szmyccm.com.", "t-odx.geo2.op-mobile.opera.com.", "t-odx.op-mobile.opera.com.", "t.marketingcloudfx.com.", "t.mookie1.com.", "t.nit.ro.", "t.poki.io.", - "t.rentcafe.com.", + "t.rtbwave.com.", "t1.nhentai.net.", "t1.tacdn.com.", "t2.nhentai.net.", - "t3.nhentai.net.", + "t22431.adipolo.live.", "t3.xiaohongshu.com.", + "t3125.adipolo.live.", + "t315.adipolo.live.", "t4.nhentai.net.", "t9.nhentai.net.", + "t9674.adipolo.live.", + "t99193.adipolo.live.", + "tag.winister.app.", "tags.natwest.com.", - "talleyn.com.", - "talosintelligence.com.", + "taipei.remotepc.com.", + "tako22-normal-useast1a.tiktokv.com.", + "talkingpts.org.", + "tamosplayer.com.", "tampa.remotepc.com.", - "tange365.com.", "tanjingpaas.com.", "tantanapp.com.", - "taodocs.com.", - "tap.avads.live.", - "tapecontent.net.", - "tapestry-app.quantummetric.com.", + "tanx.com.", "tapsell.ir.", "tasks.office.com.", + "tasks.zoom.us.", "tbgku.wx3vze.com.", "tccprod01.honeywell.com.", "tccprod01.resideo.com.", @@ -7008,43 +7072,32 @@ var FakeECSFQDNs = container.NewMapSet( "tccprod03.honeywell.com.", "tccprod03.resideo.com.", "tcdnlive.com.", - "tcdnos.com.", - "tch.quora.com.", - "tclai.top.", "tclclouds.com.", "tdcservices.tandemdiabetes.com.", "tdm.qq.com.", - "tdos.vip.", "teamviewer.com.", "techcrunch.com.", "teddymobile.cn.", "telemetry.savvy.security.", "teleparty.com.", "telephony.goog.", - "teleport.media.", "tencent-cloud.com.", "tencent-cloud.net.", "tencentcos.cn.", "tencentmusic.com.", "tenda.com.cn.", - "tenpay.com.", - "terms3.hicloud.com.", + "test.resolver.perfops.net.", "tgdms.filewave.net.", "tgp.qq.com.", "tgpa.qq.com.", - "thebabycotshop.com.", - "theeducationpeople.org.", - "theliquorshop.com.sg.", - "themarketer.com.", + "themes.shopify.com.", + "thenew.money.", "theoks.net.", - "thetracker.org.", - "think-client.wejoysg.com.", + "thetvapp.to.", "thinkific.com.", - "thirdgen.org.", "thm.visa.com.", "thm12.visa.com.", "tianwenca.com.", - "tienphong.vn.", "time-a-b.nist.gov.", "time-a-g.nist.gov.", "time-a.nist.gov.", @@ -7057,11 +7110,8 @@ var FakeECSFQDNs = container.NewMapSet( "time-c-g.nist.gov.", "time-c.timefreq.bldrdoc.gov.", "time-nw.nist.gov.", - "time.ecansol.net.", - "time.lmtlabs.com.", "time.nest.com.", "time.tritan-bb.net.", - "time.walb.tech.", "time1.aliyun.com.", "time1.google.com.", "time2.aliyun.com.", @@ -7074,25 +7124,27 @@ var FakeECSFQDNs = container.NewMapSet( "timi-esports.qq.com.", "tk.mosspf.com.", "tk.mosspf.net.", - "tkda.mosspf.net.", "tkx.mp.lura.live.", "tlivecdn.com.", "tlivesource.com.", "tls12.eu01.nr-data.net.cdn.cloudflare.net.", "tls12.newrelic.com.cdn.cloudflare.net.", - "tlsext.com.", "tm.barclays.co.uk.", + "tm.bdc-cdn.com.", "tm.cybersource.com.", + "tm.regions.com.", + "tmfp.klarna.com.", "tmfsdktcp.m.qq.com.", "tmfsdktcpv4.m.qq.com.", "tmga.qq.com.", "tmge.alicdn.com.", + "tmx.bestbuy.com.", + "tmx.esfp.c1.vanguard.com.", "tmx.tdbank.com.", "tmx.uptodate.com.", "tngdigital.com.my.", "tokyo.remotepc.com.", "tongdun.net.", - "tools.vssl.com.", "toronto.remotepc.com.", "toshiba-solutions.com.", "touch-us.xiaoyi.com.", @@ -7100,155 +7152,87 @@ var FakeECSFQDNs = container.NewMapSet( "tpns.sgp.tencent.com.", "tpns.sh.tencent.com.", "tpns.tencent.com.", - "tra-ac-ae.apktorrents.com.", - "tra-ac-ae.best82.com.", - "tra-ac-ae2.apktorrents.com.", - "tra-ac-ae2.best82.com.", - "tra-ac-id.apktorrents.com.", - "tra-ac-id.best82.com.", - "tra-ac-id2.apktorrents.com.", - "tra-ac-id2.best82.com.", - "tra-ac-ind.apktorrents.com.", - "tra-ac-ind.best82.com.", - "tra-ac-mas.apktorrents.com.", - "tra-ac-mas.best82.com.", - "tra-ard-id.apktorrents.com.", - "tra-ard-id.best82.com.", - "tra-bd-dac.best61.com.", - "tra-bd-dac.hyper-torrent.com.", - "tra-co-bog.best61.com.", - "tra-co-bog.hyper-torrent.com.", - "tra-eg-cai.best61.com.", - "tra-eg-cai.hyper-torrent.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-iq-bgd.best61.com.", - "tra-iq-bgd.hyper-torrent.com.", - "tra-kh-pnh.best61.com.", - "tra-kh-pnh.hyper-torrent.com.", - "tra-lwb-sg.best61.com.", - "tra-mm-rgn.best61.com.", - "tra-mm-rgn.hyper-torrent.com.", - "tra-my-kl.best61.com.", - "tra-my-kl.hyper-torrent.com.", - "tra-ph-mnl.best61.com.", - "tra-ph-mnl.hyper-torrent.com.", - "tra-pk-khi.best61.com.", - "tra-pk-khi.hyper-torrent.com.", - "tra-s4-us.best61.com.", - "tra-sa-jnb.best61.com.", - "tra-sa-jnb.hyper-torrent.com.", - "tra-tc-ind.apktorrents.com.", - "tra-tc-ind.best82.com.", - "tra-th-bkk.best61.com.", - "tra-th-bkk.hyper-torrent.com.", - "tra-the-br.apktorrents.com.", - "tra-the-br.best82.com.", - "tra-the-tr.apktorrents.com.", - "tra-the-tr.best82.com.", - "tra-ved-br.apktorrents.com.", - "tra-ved-br.best82.com.", - "tra-ved-in.apktorrents.com.", - "tra-ved-in.best82.com.", - "tra-ved-ru.apktorrents.com.", - "tra-ved-ru.best82.com.", + "tpsservice-files-inner.cn-hangzhou.oss-cdn.aliyun-inc.com.", "trace.qq.com.", - "trace.tgp.qq.com.", "track-eu1.hubspot.com.", - "track-gateway.y5kfpt.com.", - "track.easeus.com.", "track.mindtos.com.", "track.sendlane.com.", "trackedlink.net.", "tracker-udp.gbitt.info.", - "tracker.auctor.tv.", - "tracker.best61.com.", - "tracker.darkness.services.", - "tracker.dsp.os.medproad.com.", "tracker.filemail.com.", - "tracker.files.fm.", "tracker.grepler.com.", "tracker.hownetwork.xyz.", - "tracker.hyper-torrent.com.", - "tracker.mywaifu.best.", + "tracker.linvk.com.", "tracker.newtvcdn.com.", "tracker.srv00.com.", "tracker.theoks.net.", - "tracker.therarbg.com.", "tracker1.bt.moack.co.kr.", - "tracker1.myporn.club.", - "tracker3.itzmx.com.", + "tracking.brandzero.org.", "tracking.eu.flamtyr.com.", + "tracking.mygaru.com.", + "tradovateapi.com.", "tradplusad.com.", "transaccional.saludtotal.com.co.", "translate.brave.com.", - "transparency.peer-39.com.", + "transmitdrs.schwab.com.", "traversal.syncromsp.com.", + "treas.gov.", "treasury.gov.", "tri.media.", "tribalfusion.com.", + "trk-keingent.com.", "trk.bid-algorix.com.", - "truecable.com.", - "truney.com.", + "trpcdn.net.", + "truemed.com.", "tse1.explicit.bing.net.", "tsms-dra.security.dbankcloud.com.", "tsms-dre.security.dbankcloud.com.", "tt.browser.360.cn.", "ttcache.com.", - "ttshp.online.", + "ttk2.nbaonlineservice.com.", "ttuhscep.cyberhaven.io.", "tubecup.net.", "tunnel.googlezip.net.", "turn.cloudflare.com.", - "tutubh.com.", - "tvmaze.com.", + "tusd1.sharepoint.com.", "tw.ntp.org.cn.", "tx-cfg-u1.ubixioe.com.", - "tx-data-u1.ubixioe.com.", "tydevice.com.", "u-ams.4dex.io.", "u-las.4dex.io.", "u.4dex.io.", "uaenorth.api.cognitive.microsoft.com.", "uapi.mp.360.cn.", - "uatext66ap.com.", - "ubiabox-us.oss-us-west-1.aliyuncs.com.", - "ubs.sf-express.com.", + "uc.asusappnw.com.", "uc.cn.", "ucloud.com.cn.", "ucweb.com.", "udemycdn.com.", - "udsp.io.", + "udms.zoom.us.", "uhabo.com.", "uk-api.asm.skype.com.", "uk-prod.asyncgw.teams.microsoft.com.", - "ukc-word-view.officeapps.live.com.", - "ukdevilz.com.", + "ukc-excel-collab.officeapps.live.com.", "ulikecam.com.", "ulinq.asia.", "ultipro.com.", "ultiprotime.com.", + "ultiproworkplace.com.", "umeng.com.", - "un228.xyz.", "unipay.qq.com.", "unisoc.com.", "united.quantummetric.com.", "unity.cn.", - "unityads.unitychina.cn.", "unls.mep.go.cr.", - "uodoo.com.", - "up.railway.app.", "update.huorong.cn.", "update.kingsoftstore.com.", "update.vivaldi.com.", + "update.yealink.com.", "updateapp.av380.net.", "updatechannel.sharegate.com.", "updaterservices.genetec.com.", "updatesnl.macrium.com.", + "uphealth.sharepoint.com.", "upravel.com.", "upremium.asia.", "urekamedia.com.", @@ -7258,104 +7242,67 @@ var FakeECSFQDNs = container.NewMapSet( "us-04.ws-api.ringcentral.com.", "us-05.ws-api.ringcentral.com.", "us-06.ws-api.ringcentral.com.", + "us-adx-tracking.tradplusad.com.", "us-api.asm.skype.com.", - "us-atl-anx-r001.router.teamviewer.com.", - "us-atl-anx-r002.router.teamviewer.com.", - "us-atl-anx-r009.router.teamviewer.com.", - "us-atl-anx-r010.router.teamviewer.com.", "us-central1-addshoppers-data-production.cloudfunctions.net.", - "us-central1-ah-acemarketingteam.cloudfunctions.net.", "us-central1-amp-error-reporting.cloudfunctions.net.", "us-central1-bps-oi-production.cloudfunctions.net.", + "us-central1-darden-main.cloudfunctions.net.", + "us-central1-digitalproducts-gabbo.cloudfunctions.net.", + "us-central1-ds-specials-dev.cloudfunctions.net.", + "us-central1-faro-cloud-proxy-production.cloudfunctions.net.", "us-central1-fsgenergy-shared.cloudfunctions.net.", - "us-central1-gaggle-staging.cloudfunctions.net.", - "us-central1-locket-4252a.cloudfunctions.net.", + "us-central1-genericchatapp-4d046.cloudfunctions.net.", + "us-central1-launchpad-169908.cloudfunctions.net.", + "us-central1-live-prod-1-1.cloudfunctions.net.", + "us-central1-openoracle-de73b.cloudfunctions.net.", "us-central1-royal-match-prod-cce6d.cloudfunctions.net.", "us-central1-safelivealert-5.cloudfunctions.net.", "us-central1-shopify-instrumentat-ff788286.cloudfunctions.net.", + "us-central1-speechifymobile.cloudfunctions.net.", "us-central1-tranquil-petal-272922.cloudfunctions.net.", "us-central1-wise-arch-107501.cloudfunctions.net.", - "us-chi-anx-r005.router.teamviewer.com.", - "us-cmh-gcp-r002.router.teamviewer.com.", - "us-cmh-gcp-r004.router.teamviewer.com.", + "us-chi-anx-r002.router.teamviewer.com.", + "us-dal-anx-r001.router.teamviewer.com.", "us-dal-anx-r002.router.teamviewer.com.", - "us-dal-anx-r003.router.teamviewer.com.", - "us-dal-anx-r004.router.teamviewer.com.", - "us-dal-anx-r006.router.teamviewer.com.", - "us-dal-anx-r008.router.teamviewer.com.", - "us-dal-gcp-r003.router.teamviewer.com.", "us-den-anx-r002.router.teamviewer.com.", - "us-den-anx-r003.router.teamviewer.com.", - "us-den-anx-r004.router.teamviewer.com.", - "us-den-anx-r007.router.teamviewer.com.", - "us-den-anx-r008.router.teamviewer.com.", "us-den-anx-r010.router.teamviewer.com.", "us-device-scheduler.ymcs.yealink.com.", "us-device.ymcs.yealink.com.", "us-east4-chkp-gcp-rnd-threat-hunt-box.cloudfunctions.net.", "us-hnl-anx-r001.router.teamviewer.com.", - "us-iad-gcp-r001.router.teamviewer.com.", - "us-lax-anx-r006.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-gcp-r001.router.teamviewer.com.", - "us-lax-gcp-r005.router.teamviewer.com.", + "us-hnl-anx-r002.router.teamviewer.com.", "us-mia-anx-r003.router.teamviewer.com.", - "us-mia-anx-r006.router.teamviewer.com.", - "us-mia-anx-r008.router.teamviewer.com.", - "us-mia-anx-r010.router.teamviewer.com.", - "us-mia-anx-r011.router.teamviewer.com.", - "us-mia-anx-r012.router.teamviewer.com.", - "us-mia-anx-r013.router.teamviewer.com.", + "us-mia-anx-r014.router.teamviewer.com.", "us-njc-anx-r003.router.teamviewer.com.", "us-njc-anx-r004.router.teamviewer.com.", - "us-njc-anx-r006.router.teamviewer.com.", - "us-njc-anx-r009.router.teamviewer.com.", - "us-njc-anx-r011.router.teamviewer.com.", - "us-njc-anx-r012.router.teamviewer.com.", - "us-njc-anx-r013.router.teamviewer.com.", - "us-njc-anx-r014.router.teamviewer.com.", + "us-njc-anx-r007.router.teamviewer.com.", + "us-njc-anx-r010.router.teamviewer.com.", "us-njc-anx-r015.router.teamviewer.com.", + "us-njc-anx-r017.router.teamviewer.com.", "us-njc-anx-r018.router.teamviewer.com.", "us-njc-anx-r019.router.teamviewer.com.", - "us-njc-anx-r020.router.teamviewer.com.", - "us-oma-gcp-r002.router.teamviewer.com.", - "us-pdx-gcp-r004.router.teamviewer.com.", - "us-pdx-gcp-r005.router.teamviewer.com.", + "us-pdx-gcp-r001.router.teamviewer.com.", "us-prod.asyncgw.teams.microsoft.com.", - "us-sea-anx-r001.router.teamviewer.com.", - "us-sea-anx-r002.router.teamviewer.com.", - "us-sea-anx-r004.router.teamviewer.com.", - "us-sea-anx-r006.router.teamviewer.com.", - "us-slc-gcp-r003.router.teamviewer.com.", - "us-slc-gcp-r005.router.teamviewer.com.", "us-spectrum.rcs.telephony.goog.", - "us-was-anx-r001.router.teamviewer.com.", - "us-was-anx-r002.router.teamviewer.com.", - "us-was-anx-r004.router.teamviewer.com.", - "us-was-anx-r005.router.teamviewer.com.", - "us-was-anx-r007.router.teamviewer.com.", - "us-was-anx-r008.router.teamviewer.com.", - "us-was-anx-r009.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-r014.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.dell.com.", + "us-was-anx-r016.router.teamviewer.com.", + "us.a.iteleserve.com.", + "us.a.qwadro.com.", "us.galleryapi.micloud.xiaomi.net.", "us.inspi-dsp.com.", - "us.iot.dreame.tech.", + "us.keyapi.micloud.xiaomi.net.", + "us.production.appliedcloudplatform.com.", "us.tracfone.rcs.telephony.goog.", "us.ubianet.com.", "us.uscc.rcs.telephony.goog.", + "us.xfinity.rcs.telephony.goog.", "us01docs.zoom.us.", "us02log.zoom.us.", "us02nws-platform.zoom.us.", "us02nws.zoom.us.", + "us02polling.zoom.us.", + "us02st1.zoom.us.", + "us02tasks.zoom.us.", "us02web.zoom.us.", "us02www3.zoom.us.", "us04asyncim.zoom.us.", @@ -7375,43 +7322,39 @@ var FakeECSFQDNs = container.NewMapSet( "us06tasks.zoom.us.", "us06web.zoom.us.", "us06www3.zoom.us.", + "us3-cdn.inside-graph.com.", + "us4-cloud.acronis.com.", "usbank.quantummetric.com.", + "usc-collabrtc-geo.rtc.trafficmanager.net.", "usc.edu.", "usc1.s.seedtag.com.", "use4.s.seedtag.com.", - "userlike.com.", + "user-profile.api.speechify.com.", + "usercentrics.eu.", + "userstat.net.", "userx.pro.", "usgs.gov.", "ussav.cynet.com.", - "usserver.serverapi.org.", "usslb.cynet.com.", + "usv.stape.io.", "usw-aiwit-file-push.oss-us-west-1.aliyuncs.com.", + "usw.stape.io.", "usw2.s.seedtag.com.", - "uswest-beacon.deepintent.com.", "ut.hzshudian.com.", - "uta-net.com.", "ute-tech.com.cn.", - "uu3mfpevy.com.", "uuidksinc.net.", "uxfeedback.ru.", "v-key.com.", "v.shopify.com.", - "v.streaming.qq.com.", "v.vivintsky.com.", - "v1.bundlecdn.com.", - "v231jfce5hgkxkp9s14upngl7b8fynz3w.probe.tbcache.com.", "v39-ca.tiktokcdn.com.", - "v39-row.gts.byteoversea.net.", - "v39e-us.tiktokcdn.com.", + "v4.ai.ingka.ikea.net.", "v58pq.mpvflv.com.", "v6-gdvod.kwaicdn.com.", - "va.justanswer.com.", - "vak345.com.", - "vandemoorteleprofessional.com.", + "va9265.ci.managedwhitelisting.com.", "varify.io.", - "vcdn.cloud.", "vcmdiawe.com.", - "veh-dms.na.ultifi.gm.com.", + "vdo.ai.", "velaw.cyberhaven.io.", "venafi.com.", "verification.fda.gov.ph.", @@ -7419,16 +7362,17 @@ var FakeECSFQDNs = container.NewMapSet( "verticals.wix.com.", "vfa.hpplay.cn.", "vg-tcp-gateway.detailroi.com.", - "vg-tcp-receiver.detailroi.com.", "vg-tcp-rule.detailroi.com.", "vgorigin.hakunaymatata.com.", "vhx.com.", "vibe.co.", "vibeaconstr.onezapp.com.", "vicoo.tech.", + "vidctf--assets.ro.co.", "video-atl3-1.xx.fbcdn.net.", "video-atl3-2.xx.fbcdn.net.", "video-atl3-3.xx.fbcdn.net.", + "video-bos5-1.xx.fbcdn.net.", "video-den2-1.xx.fbcdn.net.", "video-det1-1.xx.fbcdn.net.", "video-dfw5-1.xx.fbcdn.net.", @@ -7442,6 +7386,10 @@ var FakeECSFQDNs = container.NewMapSet( "video-lga3-1.xx.fbcdn.net.", "video-lga3-2.xx.fbcdn.net.", "video-lga3-3.xx.fbcdn.net.", + "video-lhr6-1.xx.fbcdn.net.", + "video-lhr6-2.xx.fbcdn.net.", + "video-lhr8-1.xx.fbcdn.net.", + "video-lhr8-2.xx.fbcdn.net.", "video-mia3-1.xx.fbcdn.net.", "video-mia3-2.xx.fbcdn.net.", "video-mia3-3.xx.fbcdn.net.", @@ -7451,18 +7399,23 @@ var FakeECSFQDNs = container.NewMapSet( "video-ord5-1.xx.fbcdn.net.", "video-ord5-2.xx.fbcdn.net.", "video-ord5-3.xx.fbcdn.net.", + "video-phx1-1.xx.fbcdn.net.", "video-sea1-1.xx.fbcdn.net.", "video-sea5-1.xx.fbcdn.net.", "video-sjc3-1.xx.fbcdn.net.", + "video-sjc6-1.xx.fbcdn.net.", "video.rainberrytv.com.", + "video.turncdn.com.", + "video.twimg.com.cdn.cloudflare.net.", "videocontent-dra.himovie.dbankcloud.com.", + "videos.typing.com.", "vidmate.net.", "vieon.vn.", + "viircypg.com.", "vik-ca.moonactive.net.", "viki.com.", "viously.com.", "vip.qq.com.", - "vipads.live.", "virgul.com.", "visaforchina.cn.", "visitor.fiftyt.com.", @@ -7471,6 +7424,7 @@ var FakeECSFQDNs = container.NewMapSet( "vivoglobal.com.", "vlscppe.microsoft.com.", "vmaas.abacusgroupllc.com.", + "vnet-report-7.21cn.com.", "vnt.a-m-p.xyz.", "vnt.woxh.world.", "vod-pro.com.", @@ -7484,211 +7438,203 @@ var FakeECSFQDNs = container.NewMapSet( "voe.sx.", "voice.gcloudcs.com.", "voice.telephony.goog.", - "voicetube.com.", + "voya.com.", + "vpn1.ocso.com.", + "vpushort-stsdk.vivo.com.cn.", "vsco.co.", - "vssl.com.", - "vswenku.com.", "vx9dle.n0qq3z.com.", - "vyvr9aimsfmecivs.seed.packetsdk.io.", - "vyvr9aimsfmecivs.seed.packetsdk.net.", - "vyvr9aimsfmecivs.seed.packetsdk.xyz.", "vzuu.com.", "w-lb.deepl.com.", "w.deepl.com.", - "w25.cf.2ksports.com.", "w3.mp.lura.live.", - "wa9.3cp2h7v3eg.net.", "wallet-order-service.shopifyapps.com.", - "wallstreet-online.de.", - "wanmei.com.", - "wap.cmpassport.com.", + "wangyiyun-js.oss-cn-shanghai.aliyuncs.com.", + "waophoto.com.", "warsaw.remotepc.com.", + "washoenv.infinitecampus.org.", + "wayfair-us.attn.tv.", "wayfinding-hub-gateway-atl.ultipro.com.", "wayfinding-hub-gateway-plas1.ultipro.com.", "wc.shopify.com.", "weather-analytics-events.apple.com.", - "weather-forecasts.ru.", "weather-server-sg.allawnos.com.", "weather-server.allawntech.com.", "weather-widget-events.apple.com.", "weathercn.com.", "weatherlive.world.", "web-static.mindbox.ru.", - "web.archive.org.", "web.voice.telephony.goog.", "web1.remotepc.com.", "webapi.teamviewer.com.", - "webcf.waybackmachine.org.", - "weblate.org.", + "webapi.tresorit.com.", "webmd.com.", - "websocket-centrifugo-sp-v5.stripchat.com.", + "webserver.chatcora.natwest.com.", + "websocket-sp-v6.stripchat.com.", "websocket.app.pdq.com.", "websocket.org.", "wechatos.net.", - "wegame.com.cn.", "weibocdn.com.", + "welcome.ultipro.com.", "wemuslim.com.", - "wenkuxiazai.com.", - "wepartytt.com.", + "wendys.com.", + "wenke99.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.", - "whbdyx.com.", "whirlpool.com.", - "whisk.mxsec.pro.", - "whitehouse.gov.", - "widdimo.com.", - "widget-api.stylitics.com.", + "whitingturner.sharepoint.com.", + "whizzco.com.", + "whoop-status-worker-prod.whoop.workers.dev.", "widget-api.uxfeedback.ru.", - "wiki.clicklaw.bc.ca.", "windows.policies.live.net.", + "windstream.net.", "winscp.net.", "wireless-social.com.", + "wkhpe.com.", + "wl.hpyrdr.com.", + "wmt.dev.", + "wondershare.com.", "wordreference.com.", + "workable.com.", + "workdaycdn.com.cn.", "worldnic.com.", "worldtimeserver.com.", + "wosign.com.", "woxh.world.", "wpk-auth.ucweb.com.", + "wpk-sdkv-intl.effirst.com.", "wr.moyoung.com.", "wr.pvp.net.", - "ws.accessacloud.com.", + "ws-acc-hwp.gamedistribution.com.", + "ws-prod-1.optisigns.com.", + "ws-prod-2.optisigns.com.", + "ws-prod-3.optisigns.com.", "ws.gleap.io.", + "ws.sixpence.ai.", + "ws.smartpass.app.", + "ws.thales.monumetric.com.", "ws.tildacdn.com.", "wsms.haplat.net.", + "wsod.com.", "wsoversea.com.", - "wtzw.com.", - "wuhuyaohua.com.", - "wujinjian.net.", - "wujisite.com.", - "www.3xbz.com.", - "www.allelcoelec.jp.", - "www.aminer.cn.", - "www.art.com.", + "ww2.justanswer.com.", + "www.1024sj.com.", + "www.artfut.com.", "www.atlassian.com.", + "www.audio-digest.org.", "www.automizely-analytics.com.", - "www.bb.com.br.", - "www.bizcommunity.com.", + "www.bistro-la-nature.com.", "www.breitbart.com.", - "www.btc-europe.com.", + "www.canva.com.", "www.cdn-tinkoff.ru.", "www.chrome.com.", "www.claudeusercontent.com.", "www.cmpassport.com.", + "www.cnhtcsales.com.", "www.ctmail.com.", - "www.cz88.net.", - "www.daydreamingland.com.", - "www.easycounter.com.", - "www.ecb.europa.eu.", - "www.ecclesiasticalsewing.com.", - "www.ecosia.org.cdn.cloudflare.net.", - "www.exoticindiaart.com.", - "www.fortnite.com.", + "www.czhuaqiang.com.", + "www.fourth.com.", "www.geoplugin.net.", "www.google.org.", - "www.ietf.org.", - "www.insulationsuperstore.co.uk.", - "www.investopedia.com.", + "www.husunward.com.", + "www.ibegin.com.", + "www.indiamart.com.", "www.jimmyjohns.com.", - "www.jpost.com.", + "www.johnmuirhealth.com.", "www.jrustonapps.net.", - "www.jsnflowmeter.com.", - "www.knoxconnect.net.", - "www.linguee.com.", - "www.mercadolivre.com.br.", - "www.mycharismashop.com.", - "www.net.cn.", - "www.npttech.com.", + "www.nagumosan.com.", + "www.newegg.com.", + "www.nmpa.gov.cn.", + "www.notion.so.", "www.opposhop.cn.", + "www.orchidworld.jp.", + "www.otsu-jva.com.", "www.overleaf.com.", + "www.oxsquare.net.", "www.pingler.com.", + "www.quyou8.com.", + "www.regions.com.", "www.samsungsds.com.", - "www.seaart.ai.", + "www.sdaonuo.com.", "www.sevenrooms.com.", - "www.simonandschuster.net.", - "www.sobot.com.", - "www.speedtest.net.cdn.cloudflare.net.", - "www.thebabycotshop.com.", - "www.tvmaze.com.", - "www.users.storage.live.com.", - "www.vipads.live.", + "www.static-src.com.", + "www.stdlibrary.com.", + "www.stevemadden.com.", + "www.thenew.money.", + "www.time.gov.", + "www.ute-tech.com.cn.", "www.visaforchina.cn.", - "www.vswenku.com.", + "www.wenke99.com.", + "www.whitehouse.gov.", "www.wix.com.", "www.worldtimeserver.com.", - "www.yixuelunwen.com.", - "www.zixin.com.cn.", + "www.yext-pixel.com.", + "www.ymtech-sh.com.", + "www.zmtzlt.com.", "www.zoom.com.", "www.zoom.us.", + "www.ztautoparts.com.", "www1.remotepc.com.", + "www19.pointclickcare.com.", "www2-lb.deepl.com.", "www2.deepl.com.", - "www2.hkej.com.", "www28.pointclickcare.com.", "www3.zoom.us.", "www30.pointclickcare.com.", "www31.pointclickcare.com.", "www5.cbox.ws.", + "wx.huion.cn.", "wxqcloud.qq.com.", "wxqcloud.qq.com.cn.", "wyze.com.", "x-flow.app.", - "x-mol.com.", - "x.cnt.my.", "x.script.ac.", - "xbeibeix.com.", - "xenforo.com.", + "xactlycorp.com.", + "xai.chronosphere.io.", "xftg.msgny.xyz.", "xhpingcdn.com.", "xhscdn.com.cdn.dnsv1.com.", - "xiaopang.sto.ipidea.online.", "xiaoyi.com.", "xingyouc.com.", "xinqiucc.com.", "xintaicz.cn.", - "xjishu.com.", "xmcsrv.net.", "xml.ezmob.com.", - "xoyo.com.", - "xpreg.itsupport247.net.", + "xml.kunvertads.com.", + "xmpp.displaynote.com.", + "xmt.paze.com.", + "xn--pckh8d8a2c4a3j4c.com.", + "xsystewideusync.com.", + "xtools.info.", "yalla.live.", "ydmob.com.", "yealink.com.", - "yhachina.com.", - "yingyan.com.", - "yixuelunwen.com.", "ymmobi.com.", + "ymtech-sh.com.", "yomedia.vn.", "yomeno.xyz.", "yosmart.com.", - "yougreentube.com.", "youlesp.com.", - "yp.ringcentral.biz.", - "yunxindns.com.", + "yp.cdnstream1.com.", "yunxinfw.com.", - "yximgs.com.cdn.dnsv1.com.", - "z-m-scontent-mnl1-1.xx.fbcdn.net.", - "z-m-scontent-mnl1-2.xx.fbcdn.net.", "z.cdn.adtarget.market.", "z.cdn.ftd.agency.", "z.cdn.trafficbass.com.", - "zemanta.com.", "zenno.services.", - "zhaobiao.cn.", + "zennolab.com.", "zhaosw.com.", - "zhuanlichaxun.net.", - "zhulong.com.", - "zixin.com.cn.", "zjcdn.com.yangyi19.com.", - "zlisc.com.", - "zn5.cdn.net.ru.", + "zmtzlt.com.", "zog.link.", + "zoho.uk.", "zoom.us.", + "ztautoparts.com.", "zui.com.", "zuiqiangyingyu.net.", "zurich.remotepc.com.", + "zvuk.com.", + "zxid-m.mobileservice.cn.", "zztfly.com.", ) diff --git a/internal/ecscache/ecscache.go b/internal/ecscache/ecscache.go index 7ecd3b2..3d8cef5 100644 --- a/internal/ecscache/ecscache.go +++ b/internal/ecscache/ecscache.go @@ -18,6 +18,7 @@ import ( "github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/syncutil" + "github.com/AdguardTeam/golibs/timeutil" "github.com/miekg/dns" ) @@ -27,6 +28,9 @@ type MiddlewareConfig struct { // statistics. It must not be nil. Metrics Metrics + // Clock is used for getting current time. It must not be nil. + Clock timeutil.Clock + // Cloner is used to clone messages taken from cache. It must not be nil. Cloner *dnsmsg.Cloner @@ -58,6 +62,9 @@ type MiddlewareConfig struct { // Middleware is a dnsserver.Middleware with ECS-aware caching. type Middleware struct { + // clock is used to get current time for cache expiration. + clock timeutil.Clock + // metrics is used for the collection of the ECS cache statistics. metrics Metrics @@ -71,10 +78,10 @@ type Middleware struct { logger *slog.Logger // cache is the LRU cache for results indicating no support for ECS. - cache agdcache.Interface[uint64, *cacheItem] + cache agdcache.Interface[cacheKey, *cacheItem] // ecsCache is the LRU cache for results indicating ECS support. - ecsCache agdcache.Interface[uint64, *cacheItem] + ecsCache agdcache.Interface[cacheKey, *cacheItem] // geoIP is used to get subnets for countries. geoIP geoip.Interface @@ -97,17 +104,20 @@ const ( // adds the caches with IDs [CacheIDNoECS] and [CacheIDWithECS] to the cache // manager. c must not be nil. func NewMiddleware(c *MiddlewareConfig) (m *Middleware) { - cache := agdcache.NewLRU[uint64, *cacheItem](&agdcache.LRUConfig{ + cache := errors.Must(agdcache.New[cacheKey, *cacheItem](&agdcache.Config{ + Clock: c.Clock, Count: c.NoECSCount, - }) - ecsCache := agdcache.NewLRU[uint64, *cacheItem](&agdcache.LRUConfig{ + })) + ecsCache := errors.Must(agdcache.New[cacheKey, *cacheItem](&agdcache.Config{ + Clock: c.Clock, Count: c.ECSCount, - }) + })) c.CacheManager.Add(cacheIDNoECS, cache) c.CacheManager.Add(cacheIDWithECS, ecsCache) return &Middleware{ + clock: c.Clock, metrics: c.Metrics, cloner: c.Cloner, logger: c.Logger, @@ -235,7 +245,7 @@ func (mw *Middleware) writeUpstreamResponse( respIsECS := respIsECSDependent(scope, req.Question[0].Name) - var cache agdcache.Interface[uint64, *cacheItem] + var cache agdcache.Interface[cacheKey, *cacheItem] if respIsECS { cache = mw.ecsCache } else { diff --git a/internal/ecscache/ecscache_internal_test.go b/internal/ecscache/ecscache_internal_test.go index b8651fa..a869ea4 100644 --- a/internal/ecscache/ecscache_internal_test.go +++ b/internal/ecscache/ecscache_internal_test.go @@ -40,5 +40,3 @@ func TestRoundDiv(t *testing.T) { MaxCount: 100_000, })) } - -// TODO(a.garipov): Add benchmarks for the new ECS cache key packing. diff --git a/internal/ecscache/ecscache_test.go b/internal/ecscache/ecscache_test.go index 154bc34..d57f019 100644 --- a/internal/ecscache/ecscache_test.go +++ b/internal/ecscache/ecscache_test.go @@ -19,6 +19,7 @@ import ( "github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/testutil" + "github.com/AdguardTeam/golibs/timeutil" "github.com/miekg/dns" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -680,7 +681,9 @@ func newWithCache( return dnsserver.WithMiddlewares( h, ecscache.NewMiddleware(&ecscache.MiddlewareConfig{ - Metrics: ecscache.EmptyMetrics{}, + Metrics: ecscache.EmptyMetrics{}, + // TODO(d.kolyshev): Use fake clock and test expiration. + Clock: timeutil.SystemClock{}, Cloner: agdtest.NewCloner(), Logger: slogutil.NewDiscardLogger(), CacheManager: agdcache.EmptyManager{}, diff --git a/internal/filter/config.go b/internal/filter/config.go index f62b2fe..ad19a3a 100644 --- a/internal/filter/config.go +++ b/internal/filter/config.go @@ -2,9 +2,7 @@ package filter import ( "context" - "net/netip" - "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "github.com/AdguardTeam/urlfilter" ) @@ -56,20 +54,18 @@ type ConfigCustom struct { // Custom is a custom filter for a client. type Custom interface { - // DNSResult returns the result of applying the urlfilter DNS filtering - // engine. If the request is not filtered, DNSResult returns nil. - DNSResult( - ctx context.Context, - clientIP netip.Addr, - clientName string, - host string, - rrType dnsmsg.RRType, - isAns bool, - ) (res *urlfilter.DNSResult) - // Rules returns the rules used to create the filter. rules must not be // modified. Rules() (rules []RuleText) + + // SetURLFilterResult applies the DNS filtering engine and sets the values + // in res if any have matched. ok must be true if there is a match. req + // and res must not be nil. + SetURLFilterResult( + ctx context.Context, + req *urlfilter.DNSRequest, + res *urlfilter.DNSResult, + ) (ok bool) } // ConfigParental is the configuration for parental-control filtering. diff --git a/internal/filter/custom/custom.go b/internal/filter/custom/custom.go index bceaddd..4c0fc9e 100644 --- a/internal/filter/custom/custom.go +++ b/internal/filter/custom/custom.go @@ -4,14 +4,11 @@ package custom import ( "context" "log/slog" - "net/netip" - "strings" "sync" - "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" + "github.com/AdguardTeam/AdGuardDNS/internal/agdurlflt" "github.com/AdguardTeam/AdGuardDNS/internal/filter" "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist" - "github.com/AdguardTeam/golibs/stringutil" "github.com/AdguardTeam/urlfilter" ) @@ -45,46 +42,33 @@ func New(c *Config) (f *Filter) { // init initializes f.immutable. func (f *Filter) init(ctx context.Context) { - // TODO(a.garipov): Consider making a copy of [strings.Join] for - // [filter.RuleText]. - textLen := 0 - for _, r := range f.rules { - textLen += len(r) + len("\n") - } - - b := &strings.Builder{} - b.Grow(textLen) - - for _, r := range f.rules { - stringutil.WriteToBuilder(b, string(r), "\n") - } - // Don't use cache for users' custom filters, because [rulelist.ResultCache] // doesn't take $client rules into account. // // TODO(a.garipov): Consider adding client names to the result-cache keys. - cache := rulelist.EmptyResultCache{} - - f.immutable = rulelist.NewImmutable(b.String(), filter.IDCustom, "", cache) + f.immutable = rulelist.NewImmutable( + agdurlflt.RulesToBytes(f.rules), + filter.IDCustom, + "", + rulelist.EmptyResultCache{}, + ) f.logger.DebugContext(ctx, "engine compiled", "num_rules", f.immutable.RulesCount()) } -// DNSResult returns the result of applying the custom filter to the query with -// the given parameters. -func (f *Filter) DNSResult( +// SetURLFilterResult applies the DNS filtering engine and sets the values in +// res if any have matched. ok is true if there is a match. req and res must +// not be nil. +func (f *Filter) SetURLFilterResult( ctx context.Context, - clientIP netip.Addr, - clientName string, - host string, - rrType dnsmsg.RRType, - isAns bool, -) (r *urlfilter.DNSResult) { + req *urlfilter.DNSRequest, + res *urlfilter.DNSResult, +) (ok bool) { f.initOnce.Do(func() { f.init(ctx) }) - return f.immutable.DNSResult(clientIP, clientName, host, rrType, isAns) + return f.immutable.SetURLFilterResult(ctx, req, res) } // Rules implements the [filter.Custom] interface for *Filter. diff --git a/internal/filter/custom/custom_test.go b/internal/filter/custom/custom_test.go index 415e33c..4903ce7 100644 --- a/internal/filter/custom/custom_test.go +++ b/internal/filter/custom/custom_test.go @@ -8,6 +8,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/filter/custom" "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/filtertest" "github.com/AdguardTeam/golibs/logutil/slogutil" + "github.com/AdguardTeam/urlfilter" "github.com/miekg/dns" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -29,7 +30,7 @@ func TestFilter(t *testing.T) { require.NotNil(t, f) require.Equal(t, rules, f.Rules()) - ip := filtertest.IPv4Client + ctx := context.Background() testCases := []struct { name string @@ -57,12 +58,20 @@ func TestFilter(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - dr := f.DNSResult(context.Background(), ip, tc.cliName, tc.host, dns.TypeA, false) + req := &urlfilter.DNSRequest{ + ClientIP: filtertest.IPv4Client, + ClientName: tc.cliName, + Hostname: tc.host, + DNSType: dns.TypeA, + } + res := &urlfilter.DNSResult{} - require.NotNil(t, dr) - require.NotNil(t, dr.NetworkRule) + ok := f.SetURLFilterResult(ctx, req, res) - assert.Equal(t, tc.wantRuleStr, dr.NetworkRule.RuleText) + require.True(t, ok) + require.NotNil(t, res.NetworkRule) + + assert.Equal(t, tc.wantRuleStr, res.NetworkRule.RuleText) }) } } diff --git a/internal/filter/filterstorage/config.go b/internal/filter/filterstorage/config.go index cff4bc1..70e2ed0 100644 --- a/internal/filter/filterstorage/config.go +++ b/internal/filter/filterstorage/config.go @@ -25,27 +25,31 @@ type Config struct { // BlockedServices is the configuration of a blocked-service filter for a // default filter storage. It must not be nil - BlockedServices *ConfigBlockedServices + BlockedServices *BlockedServicesConfig // Custom is the configuration of a custom filters storage for a default // filter storage. It must not be nil - Custom *ConfigCustom + Custom *CustomConfig // HashPrefix is the hashprefix-filter configuration for a default filter // storage. It must not be nil - HashPrefix *ConfigHashPrefix + HashPrefix *HashPrefixConfig // RuleLists is the rule-list configuration for a default filter storage. // It must not be nil. - RuleLists *ConfigRuleLists + RuleLists *RuleListsConfig // SafeSearchGeneral is the general safe-search configuration for a default // filter storage. It must not be nil. - SafeSearchGeneral *ConfigSafeSearch + SafeSearchGeneral *SafeSearchConfig // SafeSearchYouTube is the YouTube safe-search configuration for a default // filter storage. It must not be nil. - SafeSearchYouTube *ConfigSafeSearch + SafeSearchYouTube *SafeSearchConfig + + // StandardAccess is the standard access configuration for a default filter + // storage. It must not be nil. + StandardAccess *StandardAccessConfig // CacheManager is the global cache manager. It must not be nil. CacheManager agdcache.Manager @@ -66,9 +70,9 @@ type Config struct { CacheDir string } -// ConfigBlockedServices is the blocked-service filter configuration for a +// BlockedServicesConfig is the blocked-service filter configuration for a // default filter storage. -type ConfigBlockedServices struct { +type BlockedServicesConfig struct { // IndexURL is the URL of the blocked-service filter index. It must not be // modified after calling [New]. It must not be nil. It is ignored if // [ConfigBlockedServices.Enabled] is false. @@ -102,17 +106,17 @@ type ConfigBlockedServices struct { Enabled bool } -// ConfigCustom is the configuration of a custom filters storage for a default +// CustomConfig is the configuration of a custom filters storage for a default // filter storage. -type ConfigCustom struct { +type CustomConfig struct { // CacheCount is the count of items to keep in the LRU cache of custom // filters. It must be greater than zero. CacheCount int } -// ConfigHashPrefix is the hashprefix-filter configuration for a default filter +// HashPrefixConfig is the hashprefix-filter configuration for a default filter // storage. -type ConfigHashPrefix struct { +type HashPrefixConfig struct { // Adult is the optional hashprefix filter for adult content. If nil, no // adult-content filtering is performed. Adult *hashprefix.Filter @@ -126,8 +130,8 @@ type ConfigHashPrefix struct { NewlyRegistered *hashprefix.Filter } -// ConfigRuleLists is the rule-list configuration for a default filter storage. -type ConfigRuleLists struct { +// RuleListsConfig is the rule-list configuration for a default filter storage. +type RuleListsConfig struct { // IndexURL is the URL of the rule-list filter index. It must not be // modified after calling [New]. It must not be nil. IndexURL *url.URL @@ -164,9 +168,9 @@ type ConfigRuleLists struct { ResultCacheEnabled bool } -// ConfigSafeSearch is the single safe-search configuration for a default filter +// SafeSearchConfig is the single safe-search configuration for a default filter // storage. -type ConfigSafeSearch struct { +type SafeSearchConfig struct { // URL is the HTTP(S) URL of the safe-search rules list. It must not be // modified after calling [New]. It must not be nil. It is ignored if // [ConfigSafeSearch.Enabled] is false. diff --git a/internal/filter/filterstorage/default.go b/internal/filter/filterstorage/default.go index 30b4cc8..5c9ded5 100644 --- a/internal/filter/filterstorage/default.go +++ b/internal/filter/filterstorage/default.go @@ -21,6 +21,7 @@ import ( "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/timeutil" + "github.com/AdguardTeam/urlfilter" "github.com/c2h5oh/datasize" ) @@ -150,7 +151,7 @@ func (s *Default) init(c *Config) (err error) { // initBlockedServices initializes the blocked-service filter in s. c must not // be nil. -func (s *Default) initBlockedServices(c *ConfigBlockedServices) (err error) { +func (s *Default) initBlockedServices(c *BlockedServicesConfig) (err error) { if !c.Enabled { return nil } @@ -181,7 +182,7 @@ func (s *Default) initBlockedServices(c *ConfigBlockedServices) (err error) { // initSafeSearch initializes the safe-search filters in s. gen and yt must not // be nil. -func (s *Default) initSafeSearch(gen, yt *ConfigSafeSearch) (err error) { +func (s *Default) initSafeSearch(gen, yt *SafeSearchConfig) (err error) { s.safeSearchGeneral, err = newSafeSearch(s.baseLogger, gen, s.cacheManager, s.cacheDir) if err != nil { return fmt.Errorf("general safe search: %w", err) @@ -199,7 +200,7 @@ func (s *Default) initSafeSearch(gen, yt *ConfigSafeSearch) (err error) { // arguments must not be empty. func newSafeSearch( baseLogger *slog.Logger, - c *ConfigSafeSearch, + c *SafeSearchConfig, cacheMgr agdcache.Manager, cacheDir string, ) (f *safesearch.Filter, err error) { @@ -230,7 +231,7 @@ func newSafeSearch( // initRuleListRefr initializes the rule-list refresher in s. c must not be // nil. -func (s *Default) initRuleListRefr(c *ConfigRuleLists) (err error) { +func (s *Default) initRuleListRefr(c *RuleListsConfig) (err error) { s.ruleListIdxRefr, err = refreshable.New(&refreshable.Config{ Logger: s.baseLogger.With( slogutil.KeyPrefix, path.Join("filters", string(FilterIDRuleListIndex)), @@ -269,7 +270,12 @@ func (s *Default) ForConfig(ctx context.Context, c filter.Config) (f filter.Inte // forClient returns a new filter based on a client configuration. c must not // be nil. func (s *Default) forClient(ctx context.Context, c *filter.ConfigClient) (f filter.Interface) { - compConf := &composite.Config{} + compConf := &composite.Config{ + // TODO(a.garipov): Find ways of reusing these. Perhaps add Close to + // [filter.Interface]? + URLFilterRequest: &urlfilter.DNSRequest{}, + URLFilterResult: &urlfilter.DNSResult{}, + } s.setParental(ctx, compConf, c.Parental) s.setRuleLists(compConf, c.RuleList) @@ -364,7 +370,12 @@ func (s *Default) setSafeBrowsing(compConf *composite.Config, c *filter.ConfigSa // forGroup returns a new filter based on a group configuration. c must not be // nil. func (s *Default) forGroup(ctx context.Context, c *filter.ConfigGroup) (f filter.Interface) { - compConf := &composite.Config{} + compConf := &composite.Config{ + // TODO(a.garipov): Find ways of reusing these. Perhaps add Close to + // [filter.Interface]? + URLFilterRequest: &urlfilter.DNSRequest{}, + URLFilterResult: &urlfilter.DNSResult{}, + } s.setParental(ctx, compConf, c.Parental) s.setRuleLists(compConf, c.RuleList) diff --git a/internal/filter/filterstorage/default_test.go b/internal/filter/filterstorage/default_test.go index 3cb5216..abb6d3d 100644 --- a/internal/filter/filterstorage/default_test.go +++ b/internal/filter/filterstorage/default_test.go @@ -26,24 +26,24 @@ func TestNew(t *testing.T) { Host: "index.example", } - servicesDisabled := &filterstorage.ConfigBlockedServices{ + servicesDisabled := &filterstorage.BlockedServicesConfig{ Enabled: false, } - safeSearchGeneralDisabled := &filterstorage.ConfigSafeSearch{ + safeSearchGeneralDisabled := &filterstorage.SafeSearchConfig{ ID: filter.IDGeneralSafeSearch, Enabled: false, } - safeSearchYouTubeDisabled := &filterstorage.ConfigSafeSearch{ + safeSearchYouTubeDisabled := &filterstorage.SafeSearchConfig{ ID: filter.IDYoutubeSafeSearch, Enabled: false, } testCases := []struct { - services *filterstorage.ConfigBlockedServices - safeSearchGen *filterstorage.ConfigSafeSearch - safeSearchYT *filterstorage.ConfigSafeSearch + services *filterstorage.BlockedServicesConfig + safeSearchGen *filterstorage.SafeSearchConfig + safeSearchYT *filterstorage.SafeSearchConfig name string }{{ services: servicesDisabled, diff --git a/internal/filter/filterstorage/filterstorage.go b/internal/filter/filterstorage/filterstorage.go index 0d057fe..51a414e 100644 --- a/internal/filter/filterstorage/filterstorage.go +++ b/internal/filter/filterstorage/filterstorage.go @@ -10,14 +10,16 @@ import ( // // TODO(a.garipov): Consider using a separate type. const ( - FilterIDBlockedServiceIndex filter.ID = "blocked_service_index" - FilterIDRuleListIndex filter.ID = "rule_list_index" + FilterIDBlockedServiceIndex filter.ID = "blocked_service_index" + FilterIDRuleListIndex filter.ID = "rule_list_index" + FilterIDStandardProfileAccess filter.ID = "standard_profile_access" ) // Filenames for filter indexes. const ( - indexFileNameBlockedServices = "services.json" - indexFileNameRuleLists = "filters.json" + indexFileNameBlockedServices = "services.json" + indexFileNameRuleLists = "filters.json" + indexFileNameStandardProfileAccess = "standard_profile_access.json" ) // Constants that define cache identifiers for the cache manager. diff --git a/internal/filter/filterstorage/filterstorage_test.go b/internal/filter/filterstorage/filterstorage_test.go index 3191c54..c5fc433 100644 --- a/internal/filter/filterstorage/filterstorage_test.go +++ b/internal/filter/filterstorage/filterstorage_test.go @@ -109,7 +109,7 @@ func newDefault(tb testing.TB) (s *filterstorage.Default) { c := newDisabledConfig(tb, newConfigRuleLists(ruleListIdxURL)) c.BlockedServices = newConfigBlockedServices(svcIdxURL) - c.HashPrefix = &filterstorage.ConfigHashPrefix{ + c.HashPrefix = &filterstorage.HashPrefixConfig{ Adult: filtertest.NewHashprefixFilter(tb, filter.IDAdultBlocking), Dangerous: filtertest.NewHashprefixFilter(tb, filter.IDSafeBrowsing), NewlyRegistered: filtertest.NewHashprefixFilter(tb, filter.IDNewRegDomains), @@ -143,26 +143,26 @@ func newDefault(tb testing.TB) (s *filterstorage.Default) { // entities. func newDisabledConfig( tb testing.TB, - rlConf *filterstorage.ConfigRuleLists, + rlConf *filterstorage.RuleListsConfig, ) (c *filterstorage.Config) { tb.Helper() return &filterstorage.Config{ BaseLogger: slogutil.NewDiscardLogger(), Logger: slogutil.NewDiscardLogger(), - BlockedServices: &filterstorage.ConfigBlockedServices{ + BlockedServices: &filterstorage.BlockedServicesConfig{ Enabled: false, }, - Custom: &filterstorage.ConfigCustom{ + Custom: &filterstorage.CustomConfig{ CacheCount: filtertest.CacheCount, }, - HashPrefix: &filterstorage.ConfigHashPrefix{}, + HashPrefix: &filterstorage.HashPrefixConfig{}, RuleLists: rlConf, - SafeSearchGeneral: &filterstorage.ConfigSafeSearch{ + SafeSearchGeneral: &filterstorage.SafeSearchConfig{ ID: filter.IDGeneralSafeSearch, Enabled: false, }, - SafeSearchYouTube: &filterstorage.ConfigSafeSearch{ + SafeSearchYouTube: &filterstorage.SafeSearchConfig{ ID: filter.IDYoutubeSafeSearch, Enabled: false, }, @@ -177,8 +177,8 @@ func newDisabledConfig( // newConfigBlockedServices is a test helper that returns a new enabled // *ConfigBlockedServices with the given index URL. The rest of the fields are // set to the corresponding [filtertest] values. -func newConfigBlockedServices(indexURL *url.URL) (c *filterstorage.ConfigBlockedServices) { - return &filterstorage.ConfigBlockedServices{ +func newConfigBlockedServices(indexURL *url.URL) (c *filterstorage.BlockedServicesConfig) { + return &filterstorage.BlockedServicesConfig{ IndexURL: indexURL, IndexMaxSize: filtertest.FilterMaxSize, IndexRefreshTimeout: filtertest.Timeout, @@ -192,8 +192,8 @@ func newConfigBlockedServices(indexURL *url.URL) (c *filterstorage.ConfigBlocked // newConfigRuleLists is a test helper that returns a new *ConfigRuleLists with // the given index URL. The rest of the fields are set to the corresponding // [filtertest] values. -func newConfigRuleLists(indexURL *url.URL) (c *filterstorage.ConfigRuleLists) { - return &filterstorage.ConfigRuleLists{ +func newConfigRuleLists(indexURL *url.URL) (c *filterstorage.RuleListsConfig) { + return &filterstorage.RuleListsConfig{ IndexURL: indexURL, IndexMaxSize: filtertest.FilterMaxSize, MaxSize: filtertest.FilterMaxSize, @@ -209,8 +209,8 @@ func newConfigRuleLists(indexURL *url.URL) (c *filterstorage.ConfigRuleLists) { // newConfigSafeSearch is a test helper that returns a new enabled // *ConfigSafeSearch with the given filter URL and ID. The rest of the fields // are set to the corresponding [filtertest] values. -func newConfigSafeSearch(u *url.URL, id filter.ID) (c *filterstorage.ConfigSafeSearch) { - return &filterstorage.ConfigSafeSearch{ +func newConfigSafeSearch(u *url.URL, id filter.ID) (c *filterstorage.SafeSearchConfig) { + return &filterstorage.SafeSearchConfig{ URL: u, ID: id, MaxSize: filtertest.FilterMaxSize, diff --git a/internal/filter/filterstorage/refresh.go b/internal/filter/filterstorage/refresh.go index ebc2c18..5fa8fb3 100644 --- a/internal/filter/filterstorage/refresh.go +++ b/internal/filter/filterstorage/refresh.go @@ -8,7 +8,6 @@ import ( "path" "path/filepath" "slices" - "strings" "github.com/AdguardTeam/AdGuardDNS/internal/errcoll" "github.com/AdguardTeam/AdGuardDNS/internal/filter" @@ -81,13 +80,13 @@ func (s *Default) loadIndex( ctx context.Context, acceptStale bool, ) (resp *indexResp, err error) { - text, err := s.ruleListIdxRefr.Refresh(ctx, acceptStale) + b, err := s.ruleListIdxRefr.Refresh(ctx, acceptStale) if err != nil { return nil, fmt.Errorf("loading index: %w", err) } resp = &indexResp{} - err = json.NewDecoder(strings.NewReader(text)).Decode(resp) + err = json.Unmarshal(b, resp) if err != nil { return nil, fmt.Errorf("decoding: %w", err) } diff --git a/internal/filter/filterstorage/standardaccess.go b/internal/filter/filterstorage/standardaccess.go new file mode 100644 index 0000000..38e05c0 --- /dev/null +++ b/internal/filter/filterstorage/standardaccess.go @@ -0,0 +1,252 @@ +package filterstorage + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "log/slog" + "net/url" + "path/filepath" + + "github.com/AdguardTeam/AdGuardDNS/internal/access" + "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/refreshable" + "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/netutil/urlutil" + "github.com/AdguardTeam/golibs/service" + "github.com/AdguardTeam/golibs/validate" + "github.com/google/renameio/v2" +) + +// StandardAccessStorage is the interface for a storage of standard access +// settings. +type StandardAccessStorage interface { + // Config returns the standard access settings. conf must not be modified + // after calling this method. + Config(ctx context.Context) (conf *access.StandardBlockerConfig, err error) +} + +// EmptyStandardAccessStorage is the empty implementation of the +// [StandardAccessStorage] interface. +type EmptyStandardAccessStorage struct{} + +// type check +var _ StandardAccessStorage = EmptyStandardAccessStorage{} + +// Config implements the [StandardAccessStorage] interface for +// EmptyStandardAccessStorage. It always returns nil. +func (EmptyStandardAccessStorage) Config( + _ context.Context, +) (conf *access.StandardBlockerConfig, err error) { + return nil, nil +} + +// StandardAccessConfig is the configuration of a standard access storage for a +// default filter storage. +// +// TODO(e.burkov): Move to another package, when internal/refreshable is moved +// to golibs. +type StandardAccessConfig struct { + // BaseLogger is used to log cache loading. + BaseLogger *slog.Logger + + // Logger is used to log refresh operations. + Logger *slog.Logger + + // Getter is the storage of standard access settings. It must not be nil. + Getter StandardAccessStorage + + // Setter is the standard access to refresh from storage. It must not be + // nil. + Setter access.StandardSetter + + // CacheDir is the directory where the cache files are stored. + CacheDir string +} + +// StandardAccess updates the standard access settings from storage, caching +// them in the cache directory. +type StandardAccess struct { + getter StandardAccessStorage + logger *slog.Logger + setter access.StandardSetter + latest *access.StandardBlockerConfig + cache *refreshable.Refreshable + cachePath string +} + +// NewStandardAccess creates a new properly initialized standard access. c must +// be valid. It uses the latest cached settings if available, use the +// [StandardAccess.Refresh] method to update them. +func NewStandardAccess(ctx context.Context, c *StandardAccessConfig) (s *StandardAccess, err error) { + cachePath := filepath.Join(c.CacheDir, indexFileNameStandardProfileAccess) + + refr, err := refreshable.New(&refreshable.Config{ + Logger: c.BaseLogger.With(slogutil.KeyPrefix, "standard_access_cache"), + URL: &url.URL{ + Scheme: urlutil.SchemeFile, + Path: cachePath, + }, + ID: FilterIDStandardProfileAccess, + // Don't set CachePath, Timeout and MaxSize, since this refreshable is + // used in file-only mode. Also don't set Staleness, since it always + // accepts stale. + }) + if err != nil { + return nil, fmt.Errorf("creating refreshable: %w", err) + } + + s = &StandardAccess{ + getter: c.Getter, + logger: c.Logger, + setter: c.Setter, + cache: refr, + cachePath: cachePath, + } + + err = s.loadFromCache(ctx) + if err != nil { + if !errors.Is(err, errors.ErrNoValue) { + // Don't wrap the error, since it's informative enough as is. + return nil, err + } + + s.logger.WarnContext(ctx, "cache is empty") + s.latest = &access.StandardBlockerConfig{} + } + + s.setter.SetConfig(s.latest) + + return s, nil +} + +// type check +var _ service.Refresher = (*StandardAccess)(nil) + +// Refresh implements the [service.Refresher] interface for *StandardAccess. +func (s *StandardAccess) Refresh(ctx context.Context) (err error) { + s.logger.InfoContext(ctx, "refresh started") + defer s.logger.InfoContext(ctx, "refresh finished") + + conf, err := s.getter.Config(ctx) + if err != nil { + return err + } + + if conf == nil { + conf = &access.StandardBlockerConfig{} + } + + if conf.Equal(s.latest) { + s.logger.DebugContext(ctx, "no changes") + + return nil + } + + s.latest = conf + s.setter.SetConfig(s.latest) + + err = s.writeCache() + if err != nil { + return fmt.Errorf("writing cache: %w", err) + } + + return nil +} + +// StandardAccessVersion is the current schema version of the standard access +// settings cache. +// +// NOTE: Increment this value on every change in [access.StandardBlockerConfig] +// that requires a change in the JSON representation. +const StandardAccessVersion uint = 1 + +// jsonStandardAccessSettings is the JSON representation of +// [access.StandardBlockerConfig]. +type jsonStandardAccessSettings struct { + AllowedNets []netutil.Prefix `json:"allowed_nets"` + BlockedNets []netutil.Prefix `json:"blocked_nets"` + AllowedASN []geoip.ASN `json:"allowed_asns"` + BlockedASN []geoip.ASN `json:"blocked_asns"` + BlocklistDomainRules []string `json:"rules"` + SchemaVersion uint `json:"schema_version"` +} + +// standardAccessConfigToJSON converts the standard access settings to the JSON +// representation. +func standardAccessConfigToJSON(conf *access.StandardBlockerConfig) (s *jsonStandardAccessSettings) { + s = &jsonStandardAccessSettings{ + AllowedNets: make([]netutil.Prefix, 0, len(conf.AllowedNets)), + BlockedNets: make([]netutil.Prefix, 0, len(conf.BlockedNets)), + AllowedASN: conf.AllowedASN, + BlockedASN: conf.BlockedASN, + BlocklistDomainRules: conf.BlocklistDomainRules, + SchemaVersion: StandardAccessVersion, + } + for _, p := range conf.AllowedNets { + s.AllowedNets = append(s.AllowedNets, netutil.Prefix{Prefix: p}) + } + for _, p := range conf.BlockedNets { + s.BlockedNets = append(s.BlockedNets, netutil.Prefix{Prefix: p}) + } + + return s +} + +// toInternal converts the JSON representation of the standard access settings +// to the internal one. +func (s *jsonStandardAccessSettings) toInternal() (conf *access.StandardBlockerConfig) { + return &access.StandardBlockerConfig{ + AllowedNets: netutil.UnembedPrefixes(s.AllowedNets), + BlockedNets: netutil.UnembedPrefixes(s.BlockedNets), + AllowedASN: s.AllowedASN, + BlockedASN: s.BlockedASN, + BlocklistDomainRules: s.BlocklistDomainRules, + } +} + +// loadFromCache loads the standard access settings from the cache. +func (s *StandardAccess) loadFromCache(ctx context.Context) (err error) { + raw, err := s.cache.Refresh(ctx, true) + if err != nil { + return fmt.Errorf("loading cache: %w", err) + } + + err = validate.NotEmptySlice("cache", raw) + if err != nil { + // Don't wrap the error, since it's informative enough as is. + return err + } + + settings := &jsonStandardAccessSettings{} + err = json.Unmarshal(raw, settings) + if err != nil { + return fmt.Errorf("decoding cache: %w", err) + } + + v := settings.SchemaVersion + err = validate.InRange("schema_version", v, StandardAccessVersion, StandardAccessVersion) + if err != nil { + return fmt.Errorf("malformed cache: %w", err) + } + + s.latest = settings.toInternal() + + return nil +} + +// writeCache writes the latest standard access settings to the cache. +func (s *StandardAccess) writeCache() (err error) { + settings := standardAccessConfigToJSON(s.latest) + + b := &bytes.Buffer{} + err = json.NewEncoder(b).Encode(settings) + if err != nil { + return fmt.Errorf("encoding cache: %w", err) + } + + return renameio.WriteFile(s.cachePath, b.Bytes(), 0o600) +} diff --git a/internal/filter/filterstorage/standardaccess_test.go b/internal/filter/filterstorage/standardaccess_test.go new file mode 100644 index 0000000..ef143a9 --- /dev/null +++ b/internal/filter/filterstorage/standardaccess_test.go @@ -0,0 +1,210 @@ +package filterstorage_test + +import ( + "context" + "net/netip" + "path/filepath" + "testing" + "time" + + "github.com/AdguardTeam/AdGuardDNS/internal/access" + "github.com/AdguardTeam/AdGuardDNS/internal/filter/filterstorage" + "github.com/AdguardTeam/AdGuardDNS/internal/geoip" + "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 + +// testLogger is the common logger for tests. +var testLogger = slogutil.NewDiscardLogger() + +// testStandardAccessStorage is the mock implementation of the +// [filterstorage.StandardAccessStorage] interface for tests. +// +// TODO(e.burkov): Move to agdtest. +type testStandardAccessStorage struct { + OnConfig func(ctx context.Context) (conf *access.StandardBlockerConfig, err error) +} + +// type check +var _ filterstorage.StandardAccessStorage = (*testStandardAccessStorage)(nil) + +// Config implements the [filterstorage.StandardAccessStorage] interface for +// *testStandardAccessStorage. +func (s *testStandardAccessStorage) Config( + ctx context.Context, +) (conf *access.StandardBlockerConfig, err error) { + return s.OnConfig(ctx) +} + +// testStandardSetter is the mock implementation of the [access.StandardSetter] +// interface for tests. +// +// TODO(e.burkov): Move to agdtest. +type testStandardSetter struct { + OnSetConfig func(conf *access.StandardBlockerConfig) +} + +// type check +var _ access.StandardSetter = (*testStandardSetter)(nil) + +// SetConfig implements the [access.StandardSetter] interface for +// *testStandardSetter. +func (s *testStandardSetter) SetConfig(conf *access.StandardBlockerConfig) { + s.OnSetConfig(conf) +} + +// panicSetter is the mock implementation of the [access.StandardSetter] +// interface for tests that panics on any call. +var panicSetter = &testStandardSetter{ + OnSetConfig: func(conf *access.StandardBlockerConfig) { panic("should not be called") }, +} + +func TestStandardAccess(t *testing.T) { + t.Parallel() + + testConf := &access.StandardBlockerConfig{ + AllowedNets: []netip.Prefix{netip.MustParsePrefix("192.0.2.1/32")}, + BlockedNets: []netip.Prefix{netip.MustParsePrefix("192.0.2.2/32")}, + AllowedASN: []geoip.ASN{10}, + BlockedASN: []geoip.ASN{20}, + BlocklistDomainRules: []string{ + "blocked.std.test", + "@@allowed.std.test", + }, + } + + errStorage := &testStandardAccessStorage{ + OnConfig: func(_ context.Context) (conf *access.StandardBlockerConfig, err error) { + return nil, assert.AnError + }, + } + okStorage := &testStandardAccessStorage{ + OnConfig: func(_ context.Context) (conf *access.StandardBlockerConfig, err error) { + return testConf, nil + }, + } + + pt := testutil.PanicT{} + emptySetter := &testStandardSetter{ + OnSetConfig: func(conf *access.StandardBlockerConfig) { require.Empty(pt, conf) }, + } + testSetter := &testStandardSetter{ + OnSetConfig: func(conf *access.StandardBlockerConfig) { require.Equal(pt, testConf, conf) }, + } + + testCases := []struct { + storage filterstorage.StandardAccessStorage + setter access.StandardSetter + wantRefrErr error + name string + }{{ + storage: okStorage, + setter: testSetter, + wantRefrErr: nil, + name: "success", + }, { + storage: filterstorage.EmptyStandardAccessStorage{}, + setter: emptySetter, + wantRefrErr: nil, + name: "empty", + }, { + storage: errStorage, + setter: panicSetter, + wantRefrErr: assert.AnError, + name: "error", + }} + + for _, tc := range testCases { + setter := &testStandardSetter{ + // Use empty setter to ensure that nothing stored in cache. + OnSetConfig: emptySetter.OnSetConfig, + } + + newCtx := testutil.ContextWithTimeout(t, testTimeout) + sa, newErr := filterstorage.NewStandardAccess(newCtx, &filterstorage.StandardAccessConfig{ + Logger: testLogger, + BaseLogger: testLogger, + Getter: tc.storage, + Setter: setter, + CacheDir: t.TempDir(), + }) + require.NoError(t, newErr) + + setter.OnSetConfig = tc.setter.SetConfig + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + ctx := testutil.ContextWithTimeout(t, testTimeout) + err := sa.Refresh(ctx) + require.ErrorIs(t, err, tc.wantRefrErr) + }) + } +} + +func TestStandardAccess_cache(t *testing.T) { + t.Parallel() + + testConf := &access.StandardBlockerConfig{ + AllowedNets: []netip.Prefix{netip.MustParsePrefix("192.0.2.1/32")}, + BlockedNets: []netip.Prefix{netip.MustParsePrefix("192.0.2.2/32")}, + AllowedASN: []geoip.ASN{10}, + BlockedASN: []geoip.ASN{20}, + BlocklistDomainRules: []string{ + "blocked.std.test", + "@@allowed.std.test", + }, + } + + pt := testutil.PanicT{} + testSetter := &testStandardSetter{ + OnSetConfig: func(conf *access.StandardBlockerConfig) { + require.Equal(pt, testConf, conf) + }, + } + emptySetter := &testStandardSetter{ + OnSetConfig: func(conf *access.StandardBlockerConfig) { + require.Empty(pt, conf) + }, + } + + testCases := []struct { + setter access.StandardSetter + wantErrMsg string + name string + }{{ + setter: testSetter, + wantErrMsg: "", + name: "success", + }, { + setter: panicSetter, + wantErrMsg: "malformed cache: schema_version: out of range: " + + "must be no less than 1, got 0", + name: "bad_version", + }, { + setter: emptySetter, + wantErrMsg: "", + name: "non-existent", + }} + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + ctx := testutil.ContextWithTimeout(t, testTimeout) + _, err := filterstorage.NewStandardAccess(ctx, &filterstorage.StandardAccessConfig{ + Logger: testLogger, + BaseLogger: testLogger, + Getter: filterstorage.EmptyStandardAccessStorage{}, + Setter: tc.setter, + CacheDir: filepath.Join("./testdata", t.Name()), + }) + testutil.AssertErrorMsg(t, tc.wantErrMsg, err) + }) + } +} diff --git a/internal/filter/filterstorage/testdata/TestStandardAccess_cache/bad_version/standard_profile_access.json b/internal/filter/filterstorage/testdata/TestStandardAccess_cache/bad_version/standard_profile_access.json new file mode 100644 index 0000000..06a8918 --- /dev/null +++ b/internal/filter/filterstorage/testdata/TestStandardAccess_cache/bad_version/standard_profile_access.json @@ -0,0 +1,4 @@ +{ + "unknown_field": "value", + "schema_version": 0 +} diff --git a/internal/filter/filterstorage/testdata/TestStandardAccess_cache/success/standard_profile_access.json b/internal/filter/filterstorage/testdata/TestStandardAccess_cache/success/standard_profile_access.json new file mode 100644 index 0000000..570d42b --- /dev/null +++ b/internal/filter/filterstorage/testdata/TestStandardAccess_cache/success/standard_profile_access.json @@ -0,0 +1,19 @@ +{ + "allowed_nets": [ + "192.0.2.1/32" + ], + "blocked_nets": [ + "192.0.2.2/32" + ], + "allowed_asns": [ + 10 + ], + "blocked_asns": [ + 20 + ], + "rules": [ + "blocked.std.test", + "@@allowed.std.test" + ], + "schema_version": 1 +} diff --git a/internal/filter/hashprefix/filter.go b/internal/filter/hashprefix/filter.go index 0d41696..b490a6f 100644 --- a/internal/filter/hashprefix/filter.go +++ b/internal/filter/hashprefix/filter.go @@ -81,16 +81,6 @@ type FilterConfig struct { MaxSize datasize.ByteSize } -// cacheItem represents an item that we will store in the cache. -type cacheItem struct { - // res is the filtering result. - res filter.Result - - // host is the cached normalized hostname for later cache key collision - // checks. - host string -} - // Filter is a filter that matches hosts by their hashes based on a hash-prefix // table. It should be initially refreshed with [Filter.RefreshInitial]. type Filter struct { @@ -101,7 +91,7 @@ type Filter struct { errColl errcoll.Interface hashprefixMtcs Metrics metrics filter.Metrics - resCache agdcache.Interface[rulelist.CacheKey, *cacheItem] + resCache agdcache.Interface[rulelist.CacheKey, filter.Result] id filter.ID repIP netip.Addr repFQDN string @@ -119,7 +109,7 @@ const IDPrefix = "filters/hashprefix" func NewFilter(c *FilterConfig) (f *Filter, err error) { id := c.ID - resCache := agdcache.NewLRU[rulelist.CacheKey, *cacheItem](&agdcache.LRUConfig{ + resCache := agdcache.NewLRU[rulelist.CacheKey, filter.Result](&agdcache.LRUConfig{ Count: c.CacheCount, }) @@ -174,10 +164,10 @@ func (f *Filter) FilterRequest( host, qt, cl := req.Host, req.QType, req.QClass cacheKey := rulelist.NewCacheKey(host, qt, cl, false) - item, ok := f.itemFromCache(ctx, cacheKey, host) + item, ok := f.resCache.Get(cacheKey) f.hashprefixMtcs.IncrementLookups(ctx, ok) if ok { - return f.clonedResult(req.DNS, item.res), nil + return f.clonedResult(req.DNS, item), nil } fam, ok := isFilterable(qt) @@ -196,10 +186,7 @@ func (f *Filter) FilterRequest( } if matched == "" { - f.resCache.Set(cacheKey, &cacheItem{ - res: nil, - host: host, - }) + f.resCache.Set(cacheKey, nil) return nil, nil } @@ -210,35 +197,13 @@ func (f *Filter) FilterRequest( return nil, err } - f.setInCache(cacheKey, r, host) + f.setInCache(cacheKey, r) f.hashprefixMtcs.UpdateCacheSize(ctx, f.resCache.Len()) return r, nil } -// itemFromCache retrieves a cache item for the given key. host is used to -// detect key collisions. If there is a key collision, it returns nil and -// false. -func (f *Filter) itemFromCache( - ctx context.Context, - key rulelist.CacheKey, - host string, -) (item *cacheItem, ok bool) { - item, ok = f.resCache.Get(key) - if !ok { - return nil, false - } - - if item.host != host { - f.logger.WarnContext(ctx, "collision: bad cache item", "item", item, "host", host) - - return nil, false - } - - return item, true -} - // isFilterable returns true if the question type is filterable. If the type is // filterable with a blocked page, fam is the address family for the IP // addresses of the blocked page; otherwise fam is [netutil.AddrFamilyNone]. @@ -335,18 +300,12 @@ func (f *Filter) respForFamily( // [*filter.ResultModifiedResponse]. // // See AGDNS-359. -func (f *Filter) setInCache(k rulelist.CacheKey, r filter.Result, host string) { +func (f *Filter) setInCache(k rulelist.CacheKey, r filter.Result) { switch r := r.(type) { case *filter.ResultModifiedRequest: - f.resCache.Set(k, &cacheItem{ - res: r.Clone(f.cloner), - host: host, - }) + f.resCache.Set(k, r.Clone(f.cloner)) case *filter.ResultModifiedResponse: - f.resCache.Set(k, &cacheItem{ - res: r.Clone(f.cloner), - host: host, - }) + f.resCache.Set(k, r.Clone(f.cloner)) default: panic(fmt.Errorf("hashprefix: unexpected type for result: %T(%[1]v)", r)) } @@ -392,13 +351,13 @@ func (f *Filter) refresh(ctx context.Context, acceptStale bool) (err error) { f.metrics.SetFilterStatus(ctx, string(f.id), time.Now(), count, err) }() - text, err := f.refr.Refresh(ctx, acceptStale) + b, err := f.refr.Refresh(ctx, acceptStale) if err != nil { // Don't wrap the error, because it's informative enough as is. return err } - count, err = f.hashes.Reset(text) + count, err = f.hashes.Reset(b) if err != nil { return fmt.Errorf("%s: resetting: %w", f.id, err) } diff --git a/internal/filter/hashprefix/filter_test.go b/internal/filter/hashprefix/filter_test.go index 10bb274..3a32267 100644 --- a/internal/filter/hashprefix/filter_test.go +++ b/internal/filter/hashprefix/filter_test.go @@ -241,7 +241,7 @@ func TestFilter_Refresh(t *testing.T) { refrCh := make(chan struct{}, 1) cachePath, srvURL := filtertest.PrepareRefreshable(t, refrCh, testHashes, http.StatusOK) - strg, err := hashprefix.NewStorage("") + strg, err := hashprefix.NewStorage(nil) require.NoError(t, err) f, err := hashprefix.NewFilter(&hashprefix.FilterConfig{ @@ -292,7 +292,7 @@ func TestFilter_FilterRequest_staleCache(t *testing.T) { // Create the filter. - strg, err := hashprefix.NewStorage("") + strg, err := hashprefix.NewStorage(nil) require.NoError(t, err) cloner := agdtest.NewCloner() diff --git a/internal/filter/hashprefix/hashprefix_test.go b/internal/filter/hashprefix/hashprefix_test.go index 15c1f01..7db9c11 100644 --- a/internal/filter/hashprefix/hashprefix_test.go +++ b/internal/filter/hashprefix/hashprefix_test.go @@ -6,3 +6,6 @@ import ( // testHashes is the host data for tests. const testHashes = filtertest.HostAdultContent + "\n" + +// testHashesData is the host data for tests. +var testHashesData = []byte(testHashes) diff --git a/internal/filter/hashprefix/matcher_test.go b/internal/filter/hashprefix/matcher_test.go index 3eed512..187ba7b 100644 --- a/internal/filter/hashprefix/matcher_test.go +++ b/internal/filter/hashprefix/matcher_test.go @@ -4,9 +4,9 @@ import ( "context" "crypto/sha256" "encoding/hex" - "strings" "testing" + "github.com/AdguardTeam/AdGuardDNS/internal/agdurlflt" "github.com/AdguardTeam/AdGuardDNS/internal/filter" "github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix" "github.com/stretchr/testify/assert" @@ -43,7 +43,7 @@ func TestMatcher(t *testing.T) { hashStrs[i] = hex.EncodeToString(sum[:]) } - hashes, err := hashprefix.NewStorage(strings.Join(hosts, "\n")) + hashes, err := hashprefix.NewStorage(agdurlflt.RulesToBytes(hosts)) require.NoError(t, err) ctx := context.Background() diff --git a/internal/filter/hashprefix/storage.go b/internal/filter/hashprefix/storage.go index eaba656..e6c03fa 100644 --- a/internal/filter/hashprefix/storage.go +++ b/internal/filter/hashprefix/storage.go @@ -2,6 +2,7 @@ package hashprefix import ( "bufio" + "bytes" "crypto/sha256" "encoding/hex" "fmt" @@ -15,8 +16,7 @@ import ( // // TODO(a.garipov): See if we could unexport this. type Storage struct { - // resetMu makes sure that only one reset is taking place at a time. It - // also protects prev. + // resetMu makes sure that only one reset is taking place at a time. resetMu *sync.Mutex // hashSuffixes contains the hashSuffixes map. It is an atomic pointer to @@ -29,9 +29,11 @@ type Storage struct { type suffixMap = map[Prefix][]suffix // NewStorage returns a new hash storage containing hashes of the domain names -// listed in hostnames, one domain name per line, requirements are described in -// [Storage.Reset]. Empty string causes no errors. -func NewStorage(hostnames string) (s *Storage, err error) { +// listed in hostnameData (see [Storage.Reset]). hostnameData may be nil. +// +// TODO(a.garipov): Consider moving the version with the initial data into +// tests. +func NewStorage(hostnameData []byte) (s *Storage, err error) { s = &Storage{ resetMu: &sync.Mutex{}, hashSuffixes: &atomic.Pointer[suffixMap]{}, @@ -39,8 +41,8 @@ func NewStorage(hostnames string) (s *Storage, err error) { s.hashSuffixes.Store(&suffixMap{}) - if hostnames != "" { - _, err = s.Reset(hostnames) + if hostnameData != nil { + _, err = s.Reset(hostnameData) if err != nil { return nil, err } @@ -130,23 +132,23 @@ func (s *Storage) loadHashSuffixes(pref Prefix) (sufs []suffix, ok bool) { } // Reset resets the hosts in the index using the domain names listed in -// hostnames and returns the total number of processed rules. hostnames should -// be a list of valid, lowercased domain names, one per line, and may include +// hostnameData and returns the total number of processed rules. hostnameData +// should contain valid, lowercased domain names, one per line, and may include // empty lines and comments ('#' at the first position). -func (s *Storage) Reset(hostnames string) (n int, err error) { +func (s *Storage) Reset(hostnameData []byte) (n int, err error) { s.resetMu.Lock() defer s.resetMu.Unlock() next := make(suffixMap, len(*s.hashSuffixes.Load())) - sc := bufio.NewScanner(strings.NewReader(hostnames)) + sc := bufio.NewScanner(bytes.NewReader(hostnameData)) for sc.Scan() { - host := sc.Text() + host := sc.Bytes() if len(host) == 0 || host[0] == '#' { continue } - sum := sha256.Sum256([]byte(host)) + sum := sha256.Sum256(host) pref := Prefix(sum[:PrefixLen]) suf := suffix(sum[PrefixLen:]) next[pref] = append(next[pref], suf) diff --git a/internal/filter/hashprefix/storage_test.go b/internal/filter/hashprefix/storage_test.go index b87f456..5dfc385 100644 --- a/internal/filter/hashprefix/storage_test.go +++ b/internal/filter/hashprefix/storage_test.go @@ -5,9 +5,9 @@ import ( "encoding/hex" "fmt" "strconv" - "strings" "testing" + "github.com/AdguardTeam/AdGuardDNS/internal/agdurlflt" "github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix" "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/filtertest" "github.com/stretchr/testify/assert" @@ -15,7 +15,7 @@ import ( ) func TestStorage_Hashes(t *testing.T) { - s, err := hashprefix.NewStorage(testHashes) + s, err := hashprefix.NewStorage(testHashesData) require.NoError(t, err) h := sha256.Sum256([]byte(filtertest.HostAdultContent)) @@ -30,7 +30,7 @@ func TestStorage_Hashes(t *testing.T) { } func TestStorage_Matches(t *testing.T) { - s, err := hashprefix.NewStorage(testHashes) + s, err := hashprefix.NewStorage(testHashesData) require.NoError(t, err) got := s.Matches(filtertest.HostAdultContent) @@ -41,12 +41,12 @@ func TestStorage_Matches(t *testing.T) { } func TestStorage_Reset(t *testing.T) { - s, err := hashprefix.NewStorage(testHashes) + s, err := hashprefix.NewStorage(testHashesData) require.NoError(t, err) assert.True(t, s.Matches(filtertest.HostAdultContent)) - const newHashes = filtertest.Host + "\n" + newHashes := []byte(filtertest.Host + "\n") n, err := s.Reset(newHashes) require.NoError(t, err) @@ -83,7 +83,7 @@ func BenchmarkStorage_Hashes(b *testing.B) { hosts = append(hosts, fmt.Sprintf("%d."+filtertest.HostAdultContent, i)) } - s, err := hashprefix.NewStorage(strings.Join(hosts, "\n")) + s, err := hashprefix.NewStorage(agdurlflt.RulesToBytes(hosts)) require.NoError(b, err) var hashPrefixes []hashprefix.Prefix @@ -105,16 +105,16 @@ func BenchmarkStorage_Hashes(b *testing.B) { }) } - // Most recent results, on a ThinkPad X13 with a Ryzen Pro 7 CPU: + // Most recent results: // - // goos: darwin - // goarch: amd64 - // pkg: github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix - // cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz - // BenchmarkStorage_Hashes/1-12 7134991 173.2 ns/op 80 B/op 2 allocs/op - // BenchmarkStorage_Hashes/2-12 6062851 200.0 ns/op 80 B/op 2 allocs/op - // BenchmarkStorage_Hashes/3-12 5138690 233.9 ns/op 80 B/op 2 allocs/op - // BenchmarkStorage_Hashes/4-12 4361190 271.8 ns/op 80 B/op 2 allocs/op + // goos: darwin + // goarch: arm64 + // pkg: github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix + // cpu: Apple M1 Pro + // BenchmarkStorage_Hashes/1-8 10519970 102.7 ns/op 80 B/op 2 allocs/op + // BenchmarkStorage_Hashes/2-8 10045784 118.1 ns/op 80 B/op 2 allocs/op + // BenchmarkStorage_Hashes/3-8 9088449 129.2 ns/op 80 B/op 2 allocs/op + // BenchmarkStorage_Hashes/4-8 8577764 139.4 ns/op 80 B/op 2 allocs/op } func BenchmarkStorage_ResetHosts(b *testing.B) { @@ -125,22 +125,22 @@ func BenchmarkStorage_ResetHosts(b *testing.B) { hosts = append(hosts, fmt.Sprintf("%d."+filtertest.HostAdultContent, i)) } - hostnames := strings.Join(hosts, "\n") - s, err := hashprefix.NewStorage(hostnames) + hostnameData := agdurlflt.RulesToBytes(hosts) + s, err := hashprefix.NewStorage(hostnameData) require.NoError(b, err) b.ReportAllocs() for b.Loop() { - _, err = s.Reset(hostnames) + _, err = s.Reset(hostnameData) } require.NoError(b, err) // Most recent results: // - // goos: darwin - // goarch: amd64 - // pkg: github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix - // cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz - // BenchmarkStorage_ResetHosts-12 3814 313231 ns/op 118385 B/op 1009 allocs/op + // goos: darwin + // goarch: arm64 + // pkg: github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix + // cpu: Apple M1 Pro + // BenchmarkStorage_ResetHosts-8 8610 128756 ns/op 118380 B/op 1009 allocs/op } diff --git a/internal/filter/internal/composite/composite.go b/internal/filter/internal/composite/composite.go index 0b5be03..91191a3 100644 --- a/internal/filter/internal/composite/composite.go +++ b/internal/filter/internal/composite/composite.go @@ -9,12 +9,19 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "github.com/AdguardTeam/AdGuardDNS/internal/filter" "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist" + "github.com/AdguardTeam/urlfilter" "github.com/miekg/dns" ) // Filter is a composite filter based on several types of safe-search and // rule-list filters. type Filter struct { + // ufReq is the URLFilter request data to use and reuse during filtering. + ufReq *urlfilter.DNSRequest + + // ufRes is the URLFilter result data to use and reuse during filtering. + ufRes *urlfilter.DNSResult + // custom is the custom rule-list filter of the profile, if any. custom filter.Custom @@ -26,13 +33,20 @@ type Filter struct { // services, if any. svcLists []*rulelist.Immutable - // reqFilters are the safe-browsing and safe-search request filters in the - // composite filter. + // reqFilters are the safe-browsing request filters in the composite filter. reqFilters []RequestFilter } // Config is the configuration structure for the composite filter. type Config struct { + // URLFilterRequest is the request data to use and reuse during filtering. + // It must not be nil. + URLFilterRequest *urlfilter.DNSRequest + + // URLFilterResult is the result data to use and reuse during filtering. It + // must not be nil. + URLFilterResult *urlfilter.DNSResult + // SafeBrowsing is the safe-browsing filter to apply, if any. SafeBrowsing RequestFilter @@ -44,10 +58,10 @@ type Config struct { NewRegisteredDomains RequestFilter // GeneralSafeSearch is the general safe-search filter to apply, if any. - GeneralSafeSearch RequestFilter + GeneralSafeSearch RequestFilterUF // YouTubeSafeSearch is the youtube safe-search filter to apply, if any. - YouTubeSafeSearch RequestFilter + YouTubeSafeSearch RequestFilterUF // Custom is the custom rule-list filter of the profile, if any. Custom filter.Custom @@ -61,16 +75,14 @@ type Config struct { ServiceLists []*rulelist.Immutable } -// RequestFilter can filter a request based on the request info. -type RequestFilter interface { - // FilterRequest filters a DNS request based on the information provided - // about the request. req must be valid. - FilterRequest(ctx context.Context, req *filter.Request) (r filter.Result, err error) -} - // New returns a new composite filter. c must not be nil. +// +// TODO(a.garipov): Consider reusing composite filters and adding function Set +// and method Reset. func New(c *Config) (f *Filter) { f = &Filter{ + ufReq: c.URLFilterRequest, + ufRes: c.URLFilterResult, custom: c.Custom, ruleLists: c.RuleLists, svcLists: c.ServiceLists, @@ -79,15 +91,17 @@ func New(c *Config) (f *Filter) { // DO NOT change the order of request filters without necessity. f.reqFilters = appendIfNotNil(f.reqFilters, c.SafeBrowsing) f.reqFilters = appendIfNotNil(f.reqFilters, c.AdultBlocking) - f.reqFilters = appendIfNotNil(f.reqFilters, c.GeneralSafeSearch) - f.reqFilters = appendIfNotNil(f.reqFilters, c.YouTubeSafeSearch) + f.reqFilters = appendIfNotNilUF(f.reqFilters, c.GeneralSafeSearch, f.ufReq, f.ufRes) + f.reqFilters = appendIfNotNilUF(f.reqFilters, c.YouTubeSafeSearch, f.ufReq, f.ufRes) f.reqFilters = appendIfNotNil(f.reqFilters, c.NewRegisteredDomains) return f } -// appendIfNotNil appends flt to flts if flt is not nil. -func appendIfNotNil(flts []RequestFilter, flt RequestFilter) (res []RequestFilter) { +// appendIfNotNil appends flt to orig if flt is not nil. +func appendIfNotNil(orig []RequestFilter, flt RequestFilter) (flts []RequestFilter) { + flts = orig + if flt != nil { flts = append(flts, flt) } @@ -95,6 +109,27 @@ func appendIfNotNil(flts []RequestFilter, flt RequestFilter) (res []RequestFilte return flts } +// appendIfNotNilUF wraps flt and appends it to orig if flt is not nil. +func appendIfNotNilUF( + orig []RequestFilter, + flt RequestFilterUF, + req *urlfilter.DNSRequest, + res *urlfilter.DNSResult, +) (flts []RequestFilter) { + flts = orig + + if flt != nil { + // TODO(a.garipov): Consider reusing wrapper structures. + flts = append(flts, &ufRequestFilter{ + flt: flt, + req: req, + res: res, + }) + } + + return flts +} + // type check var _ filter.Interface = (*Filter)(nil) @@ -137,6 +172,7 @@ func (f *Filter) FilterRequest( // Go on. } + // Secondly, check the safe-browsing and safe-search filters. for _, rf := range f.reqFilters { r, err = rf.FilterRequest(ctx, req) if err != nil { @@ -156,45 +192,87 @@ func (f *Filter) filterReqWithRuleLists( ctx context.Context, req *filter.Request, ) (r filter.Result) { - ip, host, qt := req.RemoteIP, req.Host, req.QType + f.ufReq.Reset() - // TODO(a.garipov): Consider adding a pool of results to the default - // storage and use it here. - ufRes := newURLFilterResult() - if f.custom != nil { - id := filter.IDCustom + f.ufReq.ClientIP = req.RemoteIP + f.ufReq.ClientName = req.ClientName + f.ufReq.DNSType = req.QType + f.ufReq.Hostname = req.Host - // Only use the device name for custom filters of profiles with devices. - dr := f.custom.DNSResult(ctx, ip, req.ClientName, host, qt, false) - mod := rulelist.ProcessDNSRewrites(req, dr.DNSRewrites(), id) - if mod != nil { - // Process the DNS rewrites of the custom list and return them - // first, because custom rules have priority over other rules. - return mod - } - - ufRes.add(id, "", dr) + c := newURLFilterResultCollector() + mod := f.filterReqWithCustom(ctx, req, c, f.ufReq, f.ufRes) + if mod != nil { + // Custom DNS rewrites have priority over other rules. + return mod } - for _, rl := range f.ruleLists { - id, _ := rl.ID() - dr := rl.DNSResult(ip, "", host, qt, false) - mod := rulelist.ProcessDNSRewrites(req, dr.DNSRewrites(), id) - if mod != nil { - // DNS rewrites have higher priority, so a modified request must be - // returned immediately. - return mod - } + // Don't use the device name for non-custom filters. + f.ufReq.ClientName = "" - ufRes.add(id, "", dr) + for _, rl := range f.ruleLists { + f.ufRes.Reset() + ok := rl.SetURLFilterResult(ctx, f.ufReq, f.ufRes) + if ok { + id, _ := rl.ID() + + mod = rulelist.ProcessDNSRewrites(req, f.ufRes.DNSRewrites(), id) + if mod != nil { + // DNS rewrites have higher priority, so a modified request must + // be returned immediately. + return mod + } + + c.add(id, "", f.ufRes) + } } for _, rl := range f.svcLists { id, svcID := rl.ID() - ufRes.add(id, svcID, rl.DNSResult(ip, "", host, qt, false)) + + f.ufRes.Reset() + ok := rl.SetURLFilterResult(ctx, f.ufReq, f.ufRes) + if ok { + c.add(id, svcID, f.ufRes) + } } - return ufRes.toInternal(qt) + return c.toInternal(req.QType) +} + +// filterReqWithCustom filters one question's information through the custom +// rule-list filter of the composite filter, if there is one. All arguments +// must not be nil. +func (f *Filter) filterReqWithCustom( + ctx context.Context, + req *filter.Request, + c *urlFilterResultCollector, + ufReq *urlfilter.DNSRequest, + ufRes *urlfilter.DNSResult, +) (res filter.Result) { + if f.custom == nil { + return nil + } + + // Only use the device name for custom filters of profiles with devices. + ufReq.ClientName = req.ClientName + + ufRes.Reset() + + ok := f.custom.SetURLFilterResult(ctx, ufReq, ufRes) + if !ok { + return nil + } + + id := filter.IDCustom + + mod := rulelist.ProcessDNSRewrites(req, ufRes.DNSRewrites(), id) + if mod != nil { + return mod + } + + c.add(id, "", ufRes) + + return nil } // FilterResponse implements the [filter.Interface] interface for *Filter. It @@ -242,23 +320,49 @@ func (f *Filter) filterRespWithRuleLists( host string, rrType dnsmsg.RRType, ) (r filter.Result) { - ufRes := newURLFilterResult() + f.ufReq.Reset() + + f.ufReq.Answer = true + f.ufReq.ClientIP = resp.RemoteIP + f.ufReq.DNSType = rrType + f.ufReq.Hostname = host + + c := newURLFilterResultCollector() for _, rl := range f.ruleLists { id, _ := rl.ID() - ufRes.add(id, "", rl.DNSResult(resp.RemoteIP, "", host, rrType, true)) + + f.ufRes.Reset() + + ok := rl.SetURLFilterResult(ctx, f.ufReq, f.ufRes) + if ok { + c.add(id, "", f.ufRes) + } } if f.custom != nil { - dr := f.custom.DNSResult(ctx, resp.RemoteIP, resp.ClientName, host, rrType, true) - ufRes.add(filter.IDCustom, "", dr) + f.ufReq.ClientName = resp.ClientName + + f.ufRes.Reset() + + ok := f.custom.SetURLFilterResult(ctx, f.ufReq, f.ufRes) + if ok { + c.add(filter.IDCustom, "", f.ufRes) + } } + f.ufReq.ClientName = "" + for _, rl := range f.svcLists { id, svcID := rl.ID() - ufRes.add(id, svcID, rl.DNSResult(resp.RemoteIP, "", host, rrType, true)) + + f.ufRes.Reset() + ok := rl.SetURLFilterResult(ctx, f.ufReq, f.ufRes) + if ok { + c.add(id, svcID, f.ufRes) + } } - return ufRes.toInternal(rrType) + return c.toInternal(rrType) } // filterHTTPSAnswer filters HTTPS answers information through all rule list @@ -290,7 +394,7 @@ func (f *Filter) filterSVCBHint( hint string, resp *filter.Response, ) (r filter.Result) { - for _, s := range strings.Split(hint, ",") { + for s := range strings.SplitSeq(hint, ",") { r = f.filterRespWithRuleLists(ctx, resp, s, dns.TypeHTTPS) if r != nil { return r diff --git a/internal/filter/internal/composite/composite_internal_test.go b/internal/filter/internal/composite/composite_internal_test.go index ae060bb..b17068d 100644 --- a/internal/filter/internal/composite/composite_internal_test.go +++ b/internal/filter/internal/composite/composite_internal_test.go @@ -7,6 +7,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/filter" "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/filtertest" "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist" + "github.com/AdguardTeam/urlfilter" "github.com/miekg/dns" "github.com/stretchr/testify/assert" ) @@ -20,7 +21,9 @@ func BenchmarkFilter_FilterReqWithRuleLists(b *testing.B) { ) f := New(&Config{ - RuleLists: []*rulelist.Refreshable{blockingRL}, + URLFilterRequest: &urlfilter.DNSRequest{}, + URLFilterResult: &urlfilter.DNSResult{}, + RuleLists: []*rulelist.Refreshable{blockingRL}, }) ctx := context.Background() @@ -37,9 +40,9 @@ func BenchmarkFilter_FilterReqWithRuleLists(b *testing.B) { // Most recent results: // - // goos: darwin - // goarch: amd64 - // pkg: github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/composite - // cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz - // BenchmarkFilter_FilterReqWithRuleLists-12 760046 1336 ns/op 592 B/op 12 allocs/op + // goos: linux + // goarch: amd64 + // pkg: github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/composite + // cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics + // BenchmarkFilter_FilterReqWithRuleLists-16 807964 1904 ns/op 469 B/op 8 allocs/op } diff --git a/internal/filter/internal/composite/composite_test.go b/internal/filter/internal/composite/composite_test.go index da57a31..cd709f1 100644 --- a/internal/filter/internal/composite/composite_test.go +++ b/internal/filter/internal/composite/composite_test.go @@ -1,6 +1,7 @@ package composite_test import ( + "cmp" "context" "net/http" "net/netip" @@ -20,13 +21,14 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/safesearch" "github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/testutil" + "github.com/AdguardTeam/urlfilter" "github.com/miekg/dns" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // newReqData returns data for calling FilterRequest. The context uses -// [filtertest.Timeout] and [tb.Cleanup] is used for its cancelation. req uses +// [filtertest.Timeout] and [tb.Cleanup] is used for its cancellation. req uses // [filtertest.FQDNBlocked], [dns.TypeA], and [dns.ClassINET] for the request // data. func newReqData(tb testing.TB) (ctx context.Context, req *filter.Request) { @@ -52,8 +54,20 @@ func newReqDataWithFQDN(tb testing.TB, fqdn string) (ctx context.Context, req *f return ctx, req } +// newComposite is a helper for creating composite filters tests. c may be nil, +// and all zero-value fields in c are replaced with defaults for tests. +func newComposite(tb testing.TB, c *composite.Config) (f *composite.Filter) { + tb.Helper() + + c = cmp.Or(c, &composite.Config{}) + c.URLFilterRequest = cmp.Or(c.URLFilterRequest, &urlfilter.DNSRequest{}) + c.URLFilterResult = cmp.Or(c.URLFilterResult, &urlfilter.DNSResult{}) + + return composite.New(c) +} + func TestFilter_FilterRequest_customWithClientName(t *testing.T) { - f := composite.New(&composite.Config{ + f := newComposite(t, &composite.Config{ Custom: custom.New(&custom.Config{ Logger: slogutil.NewDiscardLogger(), Rules: []filter.RuleText{ @@ -113,7 +127,7 @@ func TestFilter_FilterRequest_badfilter(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - f := composite.New(&composite.Config{ + f := newComposite(t, &composite.Config{ RuleLists: tc.ruleLists, }) @@ -143,7 +157,7 @@ func TestFilter_FilterRequest_customAllow(t *testing.T) { Rules: []filter.RuleText{allowRule}, }) - f := composite.New(&composite.Config{ + f := newComposite(t, &composite.Config{ Custom: customRL, RuleLists: []*rulelist.Refreshable{blockingRL}, }) @@ -287,12 +301,10 @@ func TestFilter_FilterRequest_dnsrewrite(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - c := &composite.Config{ + f := newComposite(t, &composite.Config{ Custom: tc.custom, RuleLists: tc.ruleLists, - } - - f := composite.New(c) + }) ctx := context.Background() res, fltErr := f.FilterRequest(ctx, &filter.Request{ @@ -309,7 +321,7 @@ func TestFilter_FilterRequest_dnsrewrite(t *testing.T) { } } -// newCustom is a helper to create a cusotm filter from a rule text. +// newCustom is a helper to create a custom filter from a rule text. func newCustom(tb testing.TB, text string) (f *custom.Filter) { tb.Helper() @@ -334,7 +346,7 @@ func TestFilter_FilterRequest_hostsRules(t *testing.T) { ) rl := newFromStr(t, rules, filtertest.RuleListID1) - f := composite.New(&composite.Config{ + f := newComposite(t, &composite.Config{ RuleLists: []*rulelist.Refreshable{rl}, }) @@ -433,7 +445,7 @@ func TestFilter_FilterRequest_safeSearch(t *testing.T) { err = gen.Refresh(testutil.ContextWithTimeout(t, filtertest.Timeout), false) require.NoError(t, err) - f := composite.New(&composite.Config{ + f := newComposite(t, &composite.Config{ GeneralSafeSearch: gen, }) @@ -458,13 +470,13 @@ func TestFilter_FilterRequest_safeSearch(t *testing.T) { func TestFilter_FilterRequest_services(t *testing.T) { svcRL := rulelist.NewImmutable( - filtertest.RuleBlockStr, + []byte(filtertest.RuleBlockStr), filter.IDBlockedService, filtertest.BlockedServiceID1, rulelist.EmptyResultCache{}, ) - f := composite.New(&composite.Config{ + f := newComposite(t, &composite.Config{ ServiceLists: []*rulelist.Immutable{svcRL}, }) @@ -498,7 +510,7 @@ func TestFilter_FilterResponse(t *testing.T) { ) blockingRL := newFromStr(t, blockRules, filtertest.RuleListID1) - f := composite.New(&composite.Config{ + f := newComposite(t, &composite.Config{ RuleLists: []*rulelist.Refreshable{blockingRL}, }) diff --git a/internal/filter/internal/composite/request.go b/internal/filter/internal/composite/request.go new file mode 100644 index 0000000..b9f80ef --- /dev/null +++ b/internal/filter/internal/composite/request.go @@ -0,0 +1,51 @@ +package composite + +import ( + "context" + + "github.com/AdguardTeam/AdGuardDNS/internal/filter" + "github.com/AdguardTeam/urlfilter" +) + +// RequestFilter can filter a request based on the request info. +type RequestFilter interface { + // FilterRequest filters a DNS request based on the information provided + // about the request. req must be valid. + FilterRequest(ctx context.Context, req *filter.Request) (r filter.Result, err error) +} + +// RequestFilterUF can filter a request based on the request info and using +// URLFilter data to optimize allocations. +type RequestFilterUF interface { + // FilterRequestUF filters a DNS request based on the information provided + // about the request and using URLFilter data to optimize allocations. req + // must be valid. ufReq and ufRes must not be nil and must be reset. + FilterRequestUF( + ctx context.Context, + req *filter.Request, + ufReq *urlfilter.DNSRequest, + ufRes *urlfilter.DNSResult, + ) (r filter.Result, err error) +} + +// ufRequestFilter is a wrapper around a [RequestFilterUF] that uses the +// provided URLFilter data. +type ufRequestFilter struct { + flt RequestFilterUF + req *urlfilter.DNSRequest + res *urlfilter.DNSResult +} + +// type check +var _ RequestFilter = (*ufRequestFilter)(nil) + +// FilterRequest implements the [RequestFilter] interface for *ufRequestFilter. +func (f *ufRequestFilter) FilterRequest( + ctx context.Context, + req *filter.Request, +) (r filter.Result, err error) { + f.req.Reset() + f.res.Reset() + + return f.flt.FilterRequestUF(ctx, req, f.req, f.res) +} diff --git a/internal/filter/internal/composite/result.go b/internal/filter/internal/composite/result.go index 72e185f..85bc697 100644 --- a/internal/filter/internal/composite/result.go +++ b/internal/filter/internal/composite/result.go @@ -10,10 +10,10 @@ import ( "github.com/miekg/dns" ) -// urlFilterResult is an entity simplifying the collection and compilation of -// urlfilter results. It contains per-pointer indexes of the IDs of filters -// producing network and host rules. -type urlFilterResult struct { +// urlFilterResultCollector is an entity simplifying the collection and +// compilation of urlfilter results. It contains per-pointer indexes of the IDs +// of filters producing network and host rules. +type urlFilterResultCollector struct { netRuleIDs map[*rules.NetworkRule]filter.ID hostRuleIDs map[*rules.HostRule]filter.ID @@ -25,9 +25,9 @@ type urlFilterResult struct { hostRules6 []*rules.HostRule } -// newURLFilterResult returns a properly initialized *urlFilterResult. -func newURLFilterResult() (r *urlFilterResult) { - return &urlFilterResult{ +// newURLFilterResultCollector returns a properly initialized *urlFilterResult. +func newURLFilterResultCollector() (r *urlFilterResultCollector) { + return &urlFilterResultCollector{ netRuleIDs: map[*rules.NetworkRule]filter.ID{}, hostRuleIDs: map[*rules.HostRule]filter.ID{}, @@ -36,66 +36,61 @@ func newURLFilterResult() (r *urlFilterResult) { } } -// add appends the rules from dr to the slices within r. If dr is nil, add does -// nothing. -func (r *urlFilterResult) add( +// add appends the rules from dr to the slices within c. dr must not be nil. +func (c *urlFilterResultCollector) add( id filter.ID, svcID filter.BlockedServiceID, dr *urlfilter.DNSResult, ) { - if dr == nil { - return - } - for _, nr := range dr.NetworkRules { - r.networkRules = append(r.networkRules, nr) - r.netRuleIDs[nr] = id + c.networkRules = append(c.networkRules, nr) + c.netRuleIDs[nr] = id if svcID != "" { - r.netRuleSvcIDs[nr] = svcID + c.netRuleSvcIDs[nr] = svcID } } - r.addHostRules(id, svcID, dr.HostRulesV4, dr.HostRulesV6) + c.addHostRules(id, svcID, dr.HostRulesV4, dr.HostRulesV6) } // addHostRules adds the host rules to the result. -func (r *urlFilterResult) addHostRules( +func (c *urlFilterResultCollector) addHostRules( id filter.ID, svcID filter.BlockedServiceID, hostRules4 []*rules.HostRule, hostRules6 []*rules.HostRule, ) { for _, hr4 := range hostRules4 { - r.hostRules4 = append(r.hostRules4, hr4) - r.hostRuleIDs[hr4] = id + c.hostRules4 = append(c.hostRules4, hr4) + c.hostRuleIDs[hr4] = id if svcID != "" { - r.hostRuleSvcIDs[hr4] = svcID + c.hostRuleSvcIDs[hr4] = svcID } } for _, hr6 := range hostRules6 { - r.hostRules6 = append(r.hostRules6, hr6) - r.hostRuleIDs[hr6] = id + c.hostRules6 = append(c.hostRules6, hr6) + c.hostRuleIDs[hr6] = id if svcID != "" { - r.hostRuleSvcIDs[hr6] = svcID + c.hostRuleSvcIDs[hr6] = svcID } } } // toInternal converts a result of using several urlfilter rulelists into a // filter.Result. -func (r *urlFilterResult) toInternal(rrType dnsmsg.RRType) (res filter.Result) { - if nr := rules.GetDNSBasicRule(r.networkRules); nr != nil { - return r.netRuleDataToResult(nr) +func (c *urlFilterResultCollector) toInternal(rrType dnsmsg.RRType) (res filter.Result) { + if nr := rules.GetDNSBasicRule(c.networkRules); nr != nil { + return c.netRuleDataToResult(nr) } - return r.hostsRulesToResult(rrType) + return c.hostsRulesToResult(rrType) } // netRuleDataToResult converts a urlfilter network rule into a filtering // result. -func (r *urlFilterResult) netRuleDataToResult(nr *rules.NetworkRule) (res filter.Result) { - fltID, ok := r.netRuleIDs[nr] +func (c *urlFilterResultCollector) netRuleDataToResult(nr *rules.NetworkRule) (res filter.Result) { + fltID, ok := c.netRuleIDs[nr] if !ok { // Shouldn't happen, since fltID is supposed to be among the filters // added to the result. @@ -105,7 +100,7 @@ func (r *urlFilterResult) netRuleDataToResult(nr *rules.NetworkRule) (res filter var rule filter.RuleText if fltID == filter.IDBlockedService { var svcID filter.BlockedServiceID - svcID, ok = r.netRuleSvcIDs[nr] + svcID, ok = c.netRuleSvcIDs[nr] if !ok { // Shouldn't happen, since svcID is supposed to be among the filters // added to the result. @@ -131,8 +126,8 @@ func (r *urlFilterResult) netRuleDataToResult(nr *rules.NetworkRule) (res filter } // hostsRulesToResult converts /etc/hosts-style rules into a filtering result. -func (r *urlFilterResult) hostsRulesToResult(rrType dnsmsg.RRType) (res filter.Result) { - if len(r.hostRules4) == 0 && len(r.hostRules6) == 0 { +func (c *urlFilterResultCollector) hostsRulesToResult(rrType dnsmsg.RRType) (res filter.Result) { + if len(c.hostRules4) == 0 && len(c.hostRules6) == 0 { return nil } @@ -143,24 +138,24 @@ func (r *urlFilterResult) hostsRulesToResult(rrType dnsmsg.RRType) (res filter.R // // See also AGDNS-591. var resHostRule *rules.HostRule - if rrType == dns.TypeA && len(r.hostRules4) > 0 { - resHostRule = r.hostRules4[0] - } else if rrType == dns.TypeAAAA && len(r.hostRules6) > 0 { - resHostRule = r.hostRules6[0] + if rrType == dns.TypeA && len(c.hostRules4) > 0 { + resHostRule = c.hostRules4[0] + } else if rrType == dns.TypeAAAA && len(c.hostRules6) > 0 { + resHostRule = c.hostRules6[0] } else { - if len(r.hostRules4) > 0 { - resHostRule = r.hostRules4[0] + if len(c.hostRules4) > 0 { + resHostRule = c.hostRules4[0] } else { - resHostRule = r.hostRules6[0] + resHostRule = c.hostRules6[0] } } - return r.hostRuleDataToResult(resHostRule) + return c.hostRuleDataToResult(resHostRule) } // hostRuleDataToResult converts a urlfilter host rule into a filtering result. -func (r *urlFilterResult) hostRuleDataToResult(hr *rules.HostRule) (res filter.Result) { - fltID, ok := r.hostRuleIDs[hr] +func (c *urlFilterResultCollector) hostRuleDataToResult(hr *rules.HostRule) (res filter.Result) { + fltID, ok := c.hostRuleIDs[hr] if !ok { // Shouldn't happen, since fltID is supposed to be among the filters // added to the result. @@ -170,7 +165,7 @@ func (r *urlFilterResult) hostRuleDataToResult(hr *rules.HostRule) (res filter.R var rule filter.RuleText if fltID == filter.IDBlockedService { var svcID filter.BlockedServiceID - svcID, ok = r.hostRuleSvcIDs[hr] + svcID, ok = c.hostRuleSvcIDs[hr] if !ok { // Shouldn't happen, since svcID is supposed to be among the filters // added to the result. diff --git a/internal/filter/internal/filtertest/hashprefix.go b/internal/filter/internal/filtertest/hashprefix.go index 0ee1052..242e523 100644 --- a/internal/filter/internal/filtertest/hashprefix.go +++ b/internal/filter/internal/filtertest/hashprefix.go @@ -58,7 +58,7 @@ func NewHashprefixFilterWithRepl( cachePath, srvURL := PrepareRefreshable(tb, nil, data, http.StatusOK) - strg, err := hashprefix.NewStorage("") + strg, err := hashprefix.NewStorage(nil) require.NoError(tb, err) f, err = hashprefix.NewFilter(&hashprefix.FilterConfig{ diff --git a/internal/filter/internal/filtertest/refresh.go b/internal/filter/internal/filtertest/refresh.go index ddc9eb1..b76e3d0 100644 --- a/internal/filter/internal/filtertest/refresh.go +++ b/internal/filter/internal/filtertest/refresh.go @@ -19,6 +19,8 @@ import ( // as well as creates a cache file. If reqCh not nil, a signal is sent every // time the server is called. The server uses [ServerName] as the value of the // Server header. +// +// TODO(a.garipov): Rewrite to use []byte for text. func PrepareRefreshable( tb testing.TB, reqCh chan<- struct{}, diff --git a/internal/filter/internal/refreshable/refreshable.go b/internal/filter/internal/refreshable/refreshable.go index a728630..3be9f57 100644 --- a/internal/filter/internal/refreshable/refreshable.go +++ b/internal/filter/internal/refreshable/refreshable.go @@ -2,6 +2,7 @@ package refreshable import ( + "bytes" "context" "fmt" "io" @@ -26,6 +27,8 @@ import ( // Refreshable contains the logic common to filters and indexes that can refresh // themselves from a file and a URL. +// +// TODO(a.garipov, e.burkov): Move to golibs. type Refreshable struct { logger *slog.Logger http *agdhttp.Client @@ -48,20 +51,26 @@ type Config struct { // ID is the filter list ID for this filter. ID filter.ID - // CachePath is the path to the file containing the cached data. + // CachePath is the path to the file containing the cached data. It only + // used for non-file URLs. CachePath string - // Staleness is the time after which a file is considered stale. + // Staleness is the time after which a file is considered stale. It should + // be positive, otherwise any cache will be discarded. Staleness time.Duration - // Timeout is the timeout for the HTTP client used by this refreshable. + // Timeout is the timeout for the HTTP client used by this refreshable. It + // must be positive, if the non-file URL is used. Timeout time.Duration - // MaxSize is the maximum size of the downloadable data. + // MaxSize is the maximum size of the downloadable data. It must be + // positive, if the non-file URL is used. MaxSize datasize.ByteSize } // New returns a new refreshable. c must not be nil. +// +// TODO(e.burkov): Consider validating c more thoroughly. func New(c *Config) (f *Refreshable, err error) { if c.URL == nil { return nil, fmt.Errorf("refreshable.New: nil url for refreshable with ID %q", c.ID) @@ -87,31 +96,31 @@ func New(c *Config) (f *Refreshable, err error) { // load the data from its URL when there is already a file in the cache // directory, regardless of its staleness. // -// TODO(a.garipov): Consider making refresh return a reader instead of a string. -func (f *Refreshable) Refresh(ctx context.Context, acceptStale bool) (text string, err error) { +// TODO(a.garipov): Consider making refresh return a reader instead of bytes. +func (f *Refreshable) Refresh(ctx context.Context, acceptStale bool) (b []byte, err error) { defer func() { err = errors.Annotate(err, "%s: %w", f.id) }() if strings.EqualFold(f.url.Scheme, urlutil.SchemeFile) { - text, err = f.refreshFromFileOnly(ctx) + b, err = f.refreshFromFileOnly(ctx) } else { - text, err = f.useCachedOrRefreshFromURL(ctx, acceptStale) + b, err = f.useCachedOrRefreshFromURL(ctx, acceptStale) } - return text, err + return b, err } // refreshFromFileOnly refreshes from the file in the URL. It must only be // called when the URL of this refreshable is a file URI. -func (f *Refreshable) refreshFromFileOnly(ctx context.Context) (text string, err error) { +func (f *Refreshable) refreshFromFileOnly(ctx context.Context) (b []byte, err error) { filePath := f.url.Path f.logger.InfoContext(ctx, "using data from file", "path", filePath) - text, err = f.refreshFromFile(true, filePath, time.Time{}) + b, err = f.refreshFromFile(true, filePath, time.Time{}) if err != nil { - return "", fmt.Errorf("refreshing from file %q: %w", filePath, err) + return nil, fmt.Errorf("refreshing from file %q: %w", filePath, err) } - return text, nil + return b, nil } // useCachedOrRefreshFromURL reloads the data from the cache file or the http @@ -122,46 +131,47 @@ func (f *Refreshable) refreshFromFileOnly(ctx context.Context) (text string, err func (f *Refreshable) useCachedOrRefreshFromURL( ctx context.Context, acceptStale bool, -) (text string, err error) { +) (b []byte, err error) { + // TODO(e.burkov): Add [timeutil.Clock]. now := time.Now() - text, err = f.refreshFromFile(acceptStale, f.cachePath, now) + b, err = f.refreshFromFile(acceptStale, f.cachePath, now) if err != nil { - return "", fmt.Errorf("refreshing from cache file %q: %w", f.cachePath, err) + return nil, fmt.Errorf("refreshing from cache file %q: %w", f.cachePath, err) } - if text == "" { + if len(b) == 0 { ru := urlutil.RedactUserinfo(f.url) f.logger.InfoContext(ctx, "refreshing from url", "url", ru) - text, err = f.refreshFromURL(ctx, now) + b, err = f.refreshFromURL(ctx, now) if err != nil { - return "", fmt.Errorf("refreshing from url %q: %w", ru, err) + return nil, fmt.Errorf("refreshing from url %q: %w", ru, err) } } else { f.logger.InfoContext(ctx, "using cached data from file", "path", f.cachePath) } - return text, nil + return b, nil } // refreshFromFile loads data from filePath if the file's mtime shows that it's // still fresh relative to updTime. If acceptStale is true, and the file -// exists, the data is read from there regardless of its staleness. If err is -// nil and text is empty, a refresh from a URL is required. +// exists, the data is read from there regardless of its staleness. If both b +// and err are nil, a refresh from a URL is required. func (f *Refreshable) refreshFromFile( acceptStale bool, filePath string, updTime time.Time, -) (text string, err error) { +) (b []byte, err error) { // #nosec G304 -- Assume that filePath is always either cacheDir + a valid, // no-slash ID or a path from the index env. file, err := os.Open(filePath) if errors.Is(err, os.ErrNotExist) { // File does not exist. Refresh from the URL. - return "", nil + return nil, nil } else if err != nil { - return "", fmt.Errorf("opening refreshable file: %w", err) + return nil, fmt.Errorf("opening refreshable file: %w", err) } defer func() { err = errors.WithDeferred(err, file.Close()) }() @@ -169,21 +179,21 @@ func (f *Refreshable) refreshFromFile( var fi fs.FileInfo fi, err = file.Stat() if err != nil { - return "", fmt.Errorf("reading refreshable file stat: %w", err) + return nil, fmt.Errorf("reading refreshable file stat: %w", err) } if mtime := fi.ModTime(); !mtime.Add(f.staleness).After(updTime) { - return "", nil + return nil, nil } } - b := &strings.Builder{} - _, err = io.Copy(b, file) + // Consider cache files to be of a prevalidated size. + b, err = io.ReadAll(file) if err != nil { - return "", fmt.Errorf("reading refreshable file: %w", err) + return nil, fmt.Errorf("reading refreshable file: %w", err) } - return b.String(), nil + return b, nil } // refreshFromURL loads the data from u, puts it into the file specified by @@ -191,18 +201,18 @@ func (f *Refreshable) refreshFromFile( func (f *Refreshable) refreshFromURL( ctx context.Context, updTime time.Time, -) (text string, err error) { +) (b []byte, err error) { // TODO(a.garipov): Cache these like renameio recommends. tmpDir := renameio.TempDir(filepath.Dir(f.cachePath)) tmpFile, err := renameio.TempFile(tmpDir, f.cachePath) if err != nil { - return "", fmt.Errorf("creating temporary refreshable file: %w", err) + return nil, fmt.Errorf("creating temporary refreshable file: %w", err) } defer func() { err = f.withDeferredTmpCleanup(err, tmpFile, updTime) }() resp, err := f.http.Get(ctx, f.url) if err != nil { - return "", fmt.Errorf("requesting: %w", err) + return nil, fmt.Errorf("requesting: %w", err) } defer func() { err = errors.WithDeferred(err, resp.Body.Close()) }() @@ -218,24 +228,24 @@ func (f *Refreshable) refreshFromURL( err = agdhttp.CheckStatus(resp, http.StatusOK) if err != nil { // Don't wrap the error, because it's informative enough as is. - return "", err + return nil, err } - b := &strings.Builder{} - mw := io.MultiWriter(b, tmpFile) + buf := &bytes.Buffer{} + mw := io.MultiWriter(buf, tmpFile) _, err = io.Copy(mw, ioutil.LimitReader(resp.Body, f.maxSize.Bytes())) if err != nil { - return "", agdhttp.WrapServerError(fmt.Errorf("reading into file: %w", err), resp) + return nil, agdhttp.WrapServerError(fmt.Errorf("reading into file: %w", err), resp) } // TODO(a.garipov): Make a more sophisticated data size ratio check. // // See AGDNS-598. - if b.Len() == 0 { - return "", agdhttp.WrapServerError(errors.Error("empty text, not resetting"), resp) + if buf.Len() == 0 { + return nil, agdhttp.WrapServerError(errors.Error("empty text, not resetting"), resp) } - return b.String(), nil + return buf.Bytes(), nil } // withDeferredTmpCleanup is a helper that performs the necessary cleanups and diff --git a/internal/filter/internal/refreshable/refreshable_test.go b/internal/filter/internal/refreshable/refreshable_test.go index 0717912..fb8e8d1 100644 --- a/internal/filter/internal/refreshable/refreshable_test.go +++ b/internal/filter/internal/refreshable/refreshable_test.go @@ -23,16 +23,22 @@ const refrID = "test_id" // Default texts for tests. const ( - testFileText = "||filefilter.example\n" - testURLText = "||urlfilter.example\n" + testTextFile = "||filefilter.example\n" + testTextURL = "||urlfilter.example\n" +) + +// Byte versions of default texts for tests. +var ( + testTextFileData = []byte(testTextFile) + testTextURLData = []byte(testTextURL) ) func TestRefreshable_Refresh(t *testing.T) { testCases := []struct { name string - wantText string wantErrMsg string srvText string + wantData []byte staleness time.Duration srvCode int acceptStale bool @@ -40,32 +46,32 @@ func TestRefreshable_Refresh(t *testing.T) { useCacheFile bool }{{ name: "no_file", - wantText: testURLText, wantErrMsg: "", - srvText: testURLText, + srvText: testTextURL, + wantData: testTextURLData, staleness: 0, srvCode: http.StatusOK, acceptStale: true, expectReq: true, useCacheFile: false, }, { - name: "no_file_http_empty", - wantText: "", + name: "no_file_http_empty", wantErrMsg: refrID + `: refreshing from url "URL": ` + `server "` + filtertest.ServerName + `": empty text, not resetting`, srvText: "", + wantData: nil, staleness: 0, srvCode: http.StatusOK, acceptStale: true, expectReq: true, useCacheFile: false, }, { - name: "no_file_http_error", - wantText: "", + name: "no_file_http_error", wantErrMsg: refrID + `: refreshing from url "URL": ` + `server "` + filtertest.ServerName + `": ` + `status code error: expected 200, got 500`, srvText: "internal server error", + wantData: nil, staleness: 0, srvCode: http.StatusInternalServerError, acceptStale: true, @@ -73,9 +79,9 @@ func TestRefreshable_Refresh(t *testing.T) { useCacheFile: false, }, { name: "file", - wantText: testFileText, wantErrMsg: "", srvText: "", + wantData: testTextFileData, staleness: filtertest.Staleness, srvCode: http.StatusOK, acceptStale: true, @@ -83,9 +89,9 @@ func TestRefreshable_Refresh(t *testing.T) { useCacheFile: true, }, { name: "file_stale", - wantText: testURLText, wantErrMsg: "", - srvText: testURLText, + srvText: testTextURL, + wantData: testTextURLData, staleness: -1 * time.Hour, srvCode: http.StatusOK, acceptStale: false, @@ -93,9 +99,9 @@ func TestRefreshable_Refresh(t *testing.T) { useCacheFile: true, }, { name: "file_stale_accept", - wantText: testFileText, wantErrMsg: "", srvText: "", + wantData: testTextFileData, staleness: -1 * time.Hour, srvCode: http.StatusOK, acceptStale: true, @@ -123,7 +129,7 @@ func TestRefreshable_Refresh(t *testing.T) { require.NoError(t, err) ctx := testutil.ContextWithTimeout(t, filtertest.Timeout) - gotText, err := f.Refresh(ctx, tc.acceptStale) + gotData, err := f.Refresh(ctx, tc.acceptStale) if tc.expectReq { testutil.RequireReceive(t, reqCh, filtertest.Timeout) } @@ -135,7 +141,7 @@ func TestRefreshable_Refresh(t *testing.T) { } testutil.AssertErrorMsg(t, tc.wantErrMsg, err) - assert.Equal(t, tc.wantText, gotText) + assert.Equal(t, tc.wantData, gotData) }) } } @@ -150,7 +156,7 @@ func prepareCachePath(t *testing.T, realCachePath string, useCacheFile bool) (ca return filepath.Join(t.TempDir(), "does_not_exist") } - err := os.WriteFile(realCachePath, []byte(testFileText), 0o600) + err := os.WriteFile(realCachePath, testTextFileData, 0o600) require.NoError(t, err) return realCachePath @@ -218,7 +224,7 @@ func TestRefreshable_Refresh_fileURL(t *testing.T) { fltFile, err := os.CreateTemp(dir, filepath.Base(t.Name())) require.NoError(t, err) - _, err = fltFile.WriteString(testFileText) + _, err = fltFile.Write(testTextFileData) require.NoError(t, err) require.NoError(t, fltFile.Close()) @@ -240,8 +246,8 @@ func TestRefreshable_Refresh_fileURL(t *testing.T) { require.NoError(t, err) ctx := testutil.ContextWithTimeout(t, filtertest.Timeout) - text, err := f.Refresh(ctx, true) + b, err := f.Refresh(ctx, true) require.NoError(t, err) - assert.Equal(t, testFileText, text) + assert.Equal(t, testTextFileData, b) } diff --git a/internal/filter/internal/rulelist/cache.go b/internal/filter/internal/rulelist/cache.go index 55acc4c..d45fb8c 100644 --- a/internal/filter/internal/rulelist/cache.go +++ b/internal/filter/internal/rulelist/cache.go @@ -1,65 +1,38 @@ package rulelist import ( - "encoding/binary" - "hash/maphash" - "github.com/AdguardTeam/AdGuardDNS/internal/agdcache" "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" - "github.com/AdguardTeam/golibs/mathutil" + "github.com/AdguardTeam/golibs/errors" + "github.com/AdguardTeam/golibs/timeutil" "github.com/AdguardTeam/urlfilter" ) -// CacheKey is the cache key type for [NewCacheKey]. -type CacheKey uint64 - -// hashSeed is the seed used by all hashes to create hash keys. -var hashSeed = maphash.MakeSeed() - -// NewCacheKey produces a cache key based on the arguments using default -// algorithm. -func NewCacheKey(host string, qt dnsmsg.RRType, cl dnsmsg.Class, isAns bool) (k CacheKey) { - // Use maphash explicitly instead of using a key structure to reduce - // allocations and optimize interface conversion up the stack. - h := &maphash.Hash{} - h.SetSeed(hashSeed) - - _, _ = h.WriteString(host) - - // Save on allocations by reusing a buffer. - var buf [5]byte - binary.LittleEndian.PutUint16(buf[:2], qt) - binary.LittleEndian.PutUint16(buf[2:4], cl) - buf[4] = mathutil.BoolToNumber[byte](isAns) - - _, _ = h.Write(buf[:]) - - return CacheKey(h.Sum64()) -} - type ( // ResultCache is a convenient alias for cache to keep types in check. - ResultCache = agdcache.Interface[CacheKey, *CacheItem] + ResultCache = agdcache.Interface[CacheKey, *urlfilter.DNSResult] // EmptyResultCache is a convenient alias for empty cache to keep types in // check. See [filter.DNSResult]. - EmptyResultCache = agdcache.Empty[CacheKey, *CacheItem] + EmptyResultCache = agdcache.Empty[CacheKey, *urlfilter.DNSResult] ) // NewResultCache returns a new initialized cache with the given element count. -// If useCache is false, it returns a cache implementation that does nothing. +// If useCache is true, count must be positive. If useCache is false, it +// returns a cache implementation that does nothing. func NewResultCache(count int, useCache bool) (cache ResultCache) { if !useCache { return EmptyResultCache{} } - return agdcache.NewLRU[CacheKey, *CacheItem](&agdcache.LRUConfig{ + return errors.Must(agdcache.New[CacheKey, *urlfilter.DNSResult](&agdcache.Config{ + Clock: timeutil.SystemClock{}, Count: count, - }) + })) } // NewManagedResultCache is like [NewResultCache] but it also adds a newly -// created cache to the cache manager by id. +// created cache to the cache manager by id. count must be positive. func NewManagedResultCache( m agdcache.Manager, id string, @@ -72,29 +45,29 @@ func NewManagedResultCache( return cache } -// CacheItem is an item stored in a [ResultCache]. -type CacheItem struct { - // res is the DNS filtering result. - res *urlfilter.DNSResult - - // host is the cached normalized hostname for later cache key collision - // checks. +// CacheKey represents a key used in the cache. +type CacheKey struct { + // host is a non-FQDN version of a cached hostname. host string + + // qType is the question type of the DNS request. + qType uint16 + + // qClass is the class of the DNS request. + qClass uint16 + + // isAns is true if the request is an answer. + isAns bool } -// itemFromCache retrieves a cache item for the given key. host is used to -// detect key collisions. If there is a key collision, it returns nil and -// false. -func itemFromCache(cache ResultCache, key CacheKey, host string) (item *CacheItem, ok bool) { - item, ok = cache.Get(key) - if !ok { - return nil, false +// NewCacheKey creates a new cache key for the given parameters. +func NewCacheKey(host string, qt dnsmsg.RRType, cl dnsmsg.Class, isAns bool) (key CacheKey) { + key = CacheKey{ + host: host, + qType: qt, + qClass: cl, + isAns: isAns, } - if item.host != host { - // Cache collision. - return nil, false - } - - return item, true + return key } diff --git a/internal/filter/internal/rulelist/dnsrewrite.go b/internal/filter/internal/rulelist/dnsrewrite.go index 6b5acfe..bfc6bec 100644 --- a/internal/filter/internal/rulelist/dnsrewrite.go +++ b/internal/filter/internal/rulelist/dnsrewrite.go @@ -81,6 +81,8 @@ type dnsRewriteResultResponse map[rules.RRType][]rules.RRValue // processDNSRewriteRules processes DNS rewrite rules in dnsr. The result will // have either CanonName or RCode or Response set. +// +// TODO(a.garipov): Reuse dnsRewriteResult structures. func processDNSRewriteRules(dnsr []*rules.NetworkRule) (res *dnsRewriteResult) { dnsrr := &dnsRewriteResult{ Response: dnsRewriteResultResponse{}, diff --git a/internal/filter/internal/rulelist/immutable.go b/internal/filter/internal/rulelist/immutable.go index f8c3f1b..052135a 100644 --- a/internal/filter/internal/rulelist/immutable.go +++ b/internal/filter/internal/rulelist/immutable.go @@ -21,12 +21,12 @@ type Immutable struct { // NewImmutable returns a new immutable DNS request and response filter using // the provided rule text and IDs. func NewImmutable( - text string, + rulesData []byte, id filter.ID, svcID filter.BlockedServiceID, cache ResultCache, ) (f *Immutable) { return &Immutable{ - baseFilter: newBaseFilter(text, id, svcID, cache), + baseFilter: newBaseFilter(rulesData, id, svcID, cache), } } diff --git a/internal/filter/internal/rulelist/refreshable.go b/internal/filter/internal/rulelist/refreshable.go index c7768d6..0b152f5 100644 --- a/internal/filter/internal/rulelist/refreshable.go +++ b/internal/filter/internal/rulelist/refreshable.go @@ -4,11 +4,9 @@ import ( "context" "fmt" "log/slog" - "net/netip" "strings" "sync" - "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "github.com/AdguardTeam/AdGuardDNS/internal/filter" "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/refreshable" "github.com/AdguardTeam/golibs/netutil/urlutil" @@ -42,7 +40,7 @@ type Refreshable struct { // HTTP(S) URL. The initial refresh should be called explicitly if necessary. func NewRefreshable(c *refreshable.Config, cache ResultCache) (f *Refreshable, err error) { f = &Refreshable{ - baseFilter: newBaseFilter("", c.ID, "", cache), + baseFilter: newBaseFilter(nil, c.ID, "", cache), logger: c.Logger, mu: &sync.RWMutex{}, } @@ -72,49 +70,49 @@ func NewRefreshable(c *refreshable.Config, cache ResultCache) (f *Refreshable, e // // TODO(a.garipov): Only used in tests. Consider removing later. func NewFromString( - text string, + rulesData string, id filter.ID, svcID filter.BlockedServiceID, cache ResultCache, ) (f *Refreshable) { return &Refreshable{ mu: &sync.RWMutex{}, - baseFilter: newBaseFilter(text, id, svcID, cache), + baseFilter: newBaseFilter([]byte(rulesData), id, svcID, cache), } } -// DNSResult returns the result of applying the urlfilter DNS filtering engine. -// If the request is not filtered, DNSResult returns nil. -func (f *Refreshable) DNSResult( - clientIP netip.Addr, - clientName string, - host string, - rrType dnsmsg.RRType, - isAns bool, -) (res *urlfilter.DNSResult) { +// SetURLFilterResult applies the DNS filtering engine and sets the values in +// res if any have matched. ok is true if there is a match. req and res must +// not be nil. +func (f *Refreshable) SetURLFilterResult( + ctx context.Context, + req *urlfilter.DNSRequest, + res *urlfilter.DNSResult, +) (ok bool) { f.mu.RLock() defer f.mu.RUnlock() - return f.baseFilter.DNSResult(clientIP, clientName, host, rrType, isAns) + return f.baseFilter.SetURLFilterResult(ctx, req, res) } // Refresh reloads the rule list data. If acceptStale is true, do not try to // load the list from its URL when there is already a file in the cache // directory, regardless of its staleness. func (f *Refreshable) Refresh(ctx context.Context, acceptStale bool) (err error) { - text, err := f.refr.Refresh(ctx, acceptStale) + rulesData, err := f.refr.Refresh(ctx, acceptStale) if err != nil { // Don't wrap the error, because it's informative enough as is. return err } - // TODO(a.garipov): Add filterlist.BytesRuleList. - strList := &filterlist.StringRuleList{ - RulesText: text, - IgnoreCosmetic: true, + lists := []filterlist.Interface{ + filterlist.NewBytes(&filterlist.BytesConfig{ + RulesText: rulesData, + IgnoreCosmetic: true, + }), } - s, err := filterlist.NewRuleStorage([]filterlist.RuleList{strList}) + s, err := filterlist.NewRuleStorage(lists) if err != nil { return fmt.Errorf("%s: creating rule storage: %w", f.id, err) } diff --git a/internal/filter/internal/rulelist/refreshable_test.go b/internal/filter/internal/rulelist/refreshable_test.go index 4f80c83..219c772 100644 --- a/internal/filter/internal/rulelist/refreshable_test.go +++ b/internal/filter/internal/rulelist/refreshable_test.go @@ -2,83 +2,99 @@ package rulelist_test import ( "net/http" - "net/netip" "testing" - "github.com/AdguardTeam/AdGuardDNS/internal/filter" "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/filtertest" "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/refreshable" "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist" "github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/testutil" + "github.com/AdguardTeam/urlfilter" "github.com/miekg/dns" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -// testReqHost is the request host for tests. -const testReqHost = "blocked.example" - -// testRemoteIP is the client IP for tests -var testRemoteIP = netip.MustParseAddr("1.2.3.4") - -// testFltListID is the common filter list IDs for tests. -const testFltListID filter.ID = "fl1" - -// testBlockRule is the common blocking rule for tests. -const testBlockRule = "||" + testReqHost + "\n" - func TestRefreshable_RulesCount(t *testing.T) { - rl := rulelist.NewFromString(testBlockRule, testFltListID, "", rulelist.EmptyResultCache{}) + rl := rulelist.NewFromString( + filtertest.RuleBlockStr, + filtertest.RuleListID1, + "", + rulelist.EmptyResultCache{}, + ) assert.Equal(t, 1, rl.RulesCount()) } -func TestRefreshable_DNSResult_cache(t *testing.T) { +func TestRefreshable_SetURLFilterResult_cache(t *testing.T) { cache := rulelist.NewResultCache(filtertest.CacheCount, true) - rl := rulelist.NewFromString(testBlockRule, testFltListID, "", cache) + rl := rulelist.NewFromString(filtertest.RuleBlockStr, filtertest.RuleListID1, "", cache) - const qt = dns.TypeA + require.True(t, t.Run("blocked", func(t *testing.T) { + ctx := testutil.ContextWithTimeout(t, filtertest.Timeout) + req := &urlfilter.DNSRequest{ + ClientIP: filtertest.IPv4Client, + Hostname: filtertest.HostBlocked, + DNSType: dns.TypeA, + } + res := &urlfilter.DNSResult{} - t.Run("blocked", func(t *testing.T) { - dr := rl.DNSResult(testRemoteIP, "", testReqHost, qt, false) - require.NotNil(t, dr) + ok := rl.SetURLFilterResult(ctx, req, res) + require.True(t, ok) - assert.Len(t, dr.NetworkRules, 1) + assert.Len(t, res.NetworkRules, 1) + r := res.NetworkRules[0] - cachedDR := rl.DNSResult(testRemoteIP, "", testReqHost, qt, false) - require.NotNil(t, cachedDR) + res.Reset() + ok = rl.SetURLFilterResult(ctx, req, res) + require.True(t, ok) - assert.Same(t, dr, cachedDR) - }) + assert.Same(t, r, res.NetworkRules[0]) + })) - t.Run("none", func(t *testing.T) { - const otherHost = "other.example" + require.True(t, t.Run("none", func(t *testing.T) { + ctx := testutil.ContextWithTimeout(t, filtertest.Timeout) + req := &urlfilter.DNSRequest{ + ClientIP: filtertest.IPv4Client, + Hostname: filtertest.Host, + DNSType: dns.TypeA, + } + res := &urlfilter.DNSResult{} - dr := rl.DNSResult(testRemoteIP, "", otherHost, qt, false) - assert.Nil(t, dr) + ok := rl.SetURLFilterResult(ctx, req, res) + assert.False(t, ok) - cachedDR := rl.DNSResult(testRemoteIP, "", otherHost, dns.TypeA, false) - assert.Nil(t, cachedDR) - }) + res.Reset() + ok = rl.SetURLFilterResult(ctx, req, res) + assert.False(t, ok) + })) } func TestRefreshable_ID(t *testing.T) { - const svcID = filter.BlockedServiceID("test_service") - rl := rulelist.NewFromString(testBlockRule, testFltListID, svcID, rulelist.EmptyResultCache{}) + rl := rulelist.NewFromString( + filtertest.RuleBlockStr, + filtertest.RuleListID1, + filtertest.BlockedServiceID1Str, + rulelist.EmptyResultCache{}, + ) gotID, gotSvcID := rl.ID() - assert.Equal(t, testFltListID, gotID) - assert.Equal(t, svcID, gotSvcID) + assert.Equal(t, filtertest.RuleListID1, gotID) + assert.Equal(t, filtertest.BlockedServiceID1, gotSvcID) } func TestRefreshable_Refresh(t *testing.T) { - cachePath, srvURL := filtertest.PrepareRefreshable(t, nil, testBlockRule, http.StatusOK) + cachePath, srvURL := filtertest.PrepareRefreshable( + t, + nil, + filtertest.RuleBlockStr, + http.StatusOK, + ) rl, err := rulelist.NewRefreshable( &refreshable.Config{ Logger: slogutil.NewDiscardLogger(), URL: srvURL, - ID: testFltListID, + ID: filtertest.RuleListID1, CachePath: cachePath, Staleness: filtertest.Staleness, MaxSize: filtertest.FilterMaxSize, @@ -93,8 +109,71 @@ func TestRefreshable_Refresh(t *testing.T) { assert.Equal(t, 1, rl.RulesCount()) - dr := rl.DNSResult(testRemoteIP, "", testReqHost, dns.TypeA, false) - require.NotNil(t, dr) + ctx = testutil.ContextWithTimeout(t, filtertest.Timeout) + req := &urlfilter.DNSRequest{ + ClientIP: filtertest.IPv4Client, + Hostname: filtertest.HostBlocked, + DNSType: dns.TypeA, + } + res := &urlfilter.DNSResult{} - assert.Len(t, dr.NetworkRules, 1) + ok := rl.SetURLFilterResult(ctx, req, res) + require.True(t, ok) + + assert.Len(t, res.NetworkRules, 1) +} + +func BenchmarkRefreshable_SetURLFilterResult(b *testing.B) { + rl := rulelist.NewFromString( + filtertest.RuleBlockStr, + filtertest.RuleListID1, + "", + rulelist.EmptyResultCache{}, + ) + + ctx := testutil.ContextWithTimeout(b, filtertest.Timeout) + res := &urlfilter.DNSResult{} + + b.Run("blocked", func(b *testing.B) { + req := &urlfilter.DNSRequest{ + ClientIP: filtertest.IPv4Client, + Hostname: filtertest.HostBlocked, + DNSType: dns.TypeA, + } + + var ok bool + b.ReportAllocs() + for b.Loop() { + res.Reset() + ok = rl.SetURLFilterResult(ctx, req, res) + } + + require.True(b, ok) + }) + + b.Run("other", func(b *testing.B) { + req := &urlfilter.DNSRequest{ + ClientIP: filtertest.IPv4Client, + Hostname: filtertest.Host, + DNSType: dns.TypeA, + } + + var ok bool + b.ReportAllocs() + for b.Loop() { + res.Reset() + ok = rl.SetURLFilterResult(ctx, req, res) + } + + require.False(b, ok) + }) + + // Most recent results: + // + // goos: linux + // goarch: amd64 + // pkg: github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist + // cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics + // BenchmarkRefreshable_SetURLFilterResult/blocked-16 1340384 918.6 ns/op 24 B/op 1 allocs/op + // BenchmarkRefreshable_SetURLFilterResult/other-16 2127038 589.3 ns/op 24 B/op 1 allocs/op } diff --git a/internal/filter/internal/rulelist/rulelist.go b/internal/filter/internal/rulelist/rulelist.go index 009fa97..d4740d3 100644 --- a/internal/filter/internal/rulelist/rulelist.go +++ b/internal/filter/internal/rulelist/rulelist.go @@ -3,10 +3,9 @@ package rulelist import ( + "context" "fmt" - "net/netip" - "github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg" "github.com/AdguardTeam/AdGuardDNS/internal/filter" "github.com/AdguardTeam/urlfilter" "github.com/AdguardTeam/urlfilter/filterlist" @@ -18,8 +17,8 @@ import ( type baseFilter struct { // engine is the DNS filtering engine. // - // NOTE: Do not save the [filterlist.RuleList] used to create the engine to - // close it, because filter exclusively uses [filterlist.StringRuleList], + // NOTE: Do not save the [filterlist.Interface] used to create the engine + // to close it, because filter exclusively uses [filterlist.StringRuleList], // which doesn't require closing. engine *urlfilter.DNSEngine @@ -38,7 +37,7 @@ type baseFilter struct { // newBaseFilter returns a new base DNS request and response filter using the // provided rule text and IDs. func newBaseFilter( - text string, + rulesData []byte, id filter.ID, svcID filter.BlockedServiceID, cache ResultCache, @@ -49,13 +48,14 @@ func newBaseFilter( svcID: svcID, } - // TODO(a.garipov): Add filterlist.BytesRuleList. - strList := &filterlist.StringRuleList{ - RulesText: text, - IgnoreCosmetic: true, + lists := []filterlist.Interface{ + filterlist.NewBytes(&filterlist.BytesConfig{ + RulesText: rulesData, + IgnoreCosmetic: true, + }), } - s, err := filterlist.NewRuleStorage([]filterlist.RuleList{strList}) + s, err := filterlist.NewRuleStorage(lists) if err != nil { // Should never happen, there is only one filter list, and the only // error that is currently returned from [filterlist.NewRuleStorage] is @@ -73,50 +73,61 @@ func newBaseFilter( return f } -// DNSResult returns the result of applying the urlfilter DNS filtering engine. -// If the request is not filtered, DNSResult returns nil. -func (f *baseFilter) DNSResult( - clientIP netip.Addr, - clientName string, - host string, - rrType dnsmsg.RRType, - isAns bool, -) (res *urlfilter.DNSResult) { - var ok bool +// SetURLFilterResult applies the DNS filtering engine and sets the values in +// res if any have matched. ok is true if there is a match. req and res must +// not be nil. +func (f *baseFilter) SetURLFilterResult( + _ context.Context, + req *urlfilter.DNSRequest, + res *urlfilter.DNSResult, +) (ok bool) { var cacheKey CacheKey - var item *CacheItem + var cachedRes *urlfilter.DNSResult // Don't waste resources on computing the cache key if the cache is not // enabled. - _, emptyCache := f.cache.(EmptyResultCache) - if !emptyCache { + _, noCache := f.cache.(EmptyResultCache) + if !noCache { // TODO(a.garipov): Add real class here. - cacheKey = NewCacheKey(host, rrType, dns.ClassINET, isAns) - item, ok = itemFromCache(f.cache, cacheKey, host) + cacheKey = NewCacheKey(req.Hostname, req.DNSType, dns.ClassINET, req.Answer) + cachedRes, ok = f.cache.Get(cacheKey) if ok { - return item.res + if cachedRes == nil { + return false + } + + shallowCloneInto(res, cachedRes) + + return true } } - dnsReq := &urlfilter.DNSRequest{ - Hostname: host, - ClientIP: clientIP, - ClientName: clientName, - DNSType: rrType, - Answer: isAns, + ok = f.engine.MatchRequestInto(req, res) + ok = ok || len(res.NetworkRules) > 0 + + if noCache { + return ok } - res, ok = f.engine.MatchRequest(dnsReq) - if !ok && len(res.NetworkRules) == 0 { - res = nil + if ok { + cachedRes = &urlfilter.DNSResult{} + shallowCloneInto(cachedRes, res) } - f.cache.Set(cacheKey, &CacheItem{ - res: res, - host: host, - }) + f.cache.Set(cacheKey, cachedRes) - return res + return ok +} + +// shallowCloneInto sets properties in other, as if making a shallow clone. +// other must not be nil and should be empty or reset using [DNSResult.Reset]. +// +// TODO(a.garipov): Add to urlfilter. +func shallowCloneInto(other, res *urlfilter.DNSResult) { + other.NetworkRule = res.NetworkRule + other.HostRulesV4 = append(other.HostRulesV4, res.HostRulesV4...) + other.HostRulesV6 = append(other.HostRulesV6, res.HostRulesV6...) + other.NetworkRules = append(other.NetworkRules, res.NetworkRules...) } // ID returns the filter list ID of this rule list filter, as well as the ID of diff --git a/internal/filter/internal/rulelist/rulelist_internal_test.go b/internal/filter/internal/rulelist/rulelist_internal_test.go new file mode 100644 index 0000000..df5460b --- /dev/null +++ b/internal/filter/internal/rulelist/rulelist_internal_test.go @@ -0,0 +1,88 @@ +package rulelist + +import ( + "context" + "net/netip" + "testing" + + "github.com/AdguardTeam/AdGuardDNS/internal/filter" + "github.com/AdguardTeam/urlfilter" + "github.com/miekg/dns" + "github.com/stretchr/testify/require" +) + +// TODO(d.kolyshev): Use constants from filtertest package. +const ( + // testHostBlocked is the blocked request host for tests. + testHostBlocked = "blocked.example" + + // testHostOther is the other request host for tests. + testHostOther = "other.example" +) + +// testRemoteIP is the client IP for tests +var testRemoteIP = netip.MustParseAddr("1.2.3.4") + +// testFltListID is the common filter list IDs for tests. +const testFltListID filter.ID = "fl1" + +// testBlockRule is the common blocking rule for tests. +const testBlockRule = "||" + testHostBlocked + "\n" + +// TODO(a.garipov): Add benchmarks with a cache. +func BenchmarkBaseFilter_SetURLFilterResult(b *testing.B) { + f := newBaseFilter( + []byte(testBlockRule), + testFltListID, + "", + EmptyResultCache{}, + ) + + const qt = dns.TypeA + + ctx := context.Background() + res := &urlfilter.DNSResult{} + + b.Run("blocked", func(b *testing.B) { + req := &urlfilter.DNSRequest{ + ClientIP: testRemoteIP, + Hostname: testHostBlocked, + DNSType: qt, + } + + var ok bool + b.ReportAllocs() + for b.Loop() { + res.Reset() + ok = f.SetURLFilterResult(ctx, req, res) + } + + require.True(b, ok) + }) + + b.Run("other", func(b *testing.B) { + req := &urlfilter.DNSRequest{ + ClientIP: testRemoteIP, + Hostname: testHostOther, + DNSType: qt, + } + + var ok bool + b.ReportAllocs() + for b.Loop() { + res.Reset() + ok = f.SetURLFilterResult(ctx, req, res) + } + + require.False(b, ok) + }) + + // Most recent results: + // + // goos: linux + // goarch: amd64 + // pkg: github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist + // cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics + // BenchmarkBaseFilter_SetURLFilterResult/blocked-16 906486 1372 ns/op 24 B/op 1 allocs/op + // BenchmarkBaseFilter_SetURLFilterResult/other-16 2203561 609.1 ns/op 24 B/op 1 allocs/op +} diff --git a/internal/filter/internal/safesearch/safesearch.go b/internal/filter/internal/safesearch/safesearch.go index 7d211be..0b0bcfc 100644 --- a/internal/filter/internal/safesearch/safesearch.go +++ b/internal/filter/internal/safesearch/safesearch.go @@ -11,6 +11,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/composite" "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/refreshable" "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist" + "github.com/AdguardTeam/urlfilter" "github.com/miekg/dns" ) @@ -46,13 +47,15 @@ func New(c *Config, cache rulelist.ResultCache) (f *Filter, err error) { } // type check -var _ composite.RequestFilter = (*Filter)(nil) +var _ composite.RequestFilterUF = (*Filter)(nil) -// FilterRequest implements the [composite.RequestFilter] interface for *Filter. -// It modifies the response if host matches f. -func (f *Filter) FilterRequest( +// FilterRequestUF implements the [composite.RequestFilterUF] interface for +// *Filter. It modifies the response if host matches f. +func (f *Filter) FilterRequestUF( ctx context.Context, req *filter.Request, + ufReq *urlfilter.DNSRequest, + ufRes *urlfilter.DNSResult, ) (r filter.Result, err error) { qt := req.QType switch qt { @@ -62,13 +65,19 @@ func (f *Filter) FilterRequest( return nil, nil } - host := req.Host - dr := f.flt.DNSResult(req.RemoteIP, "", host, qt, false) + ufReq.Hostname = req.Host + ufReq.DNSType = req.QType + + ok := f.flt.SetURLFilterResult(ctx, ufReq, ufRes) + if !ok { + return nil, nil + } + id, _ := f.flt.ID() - r = rulelist.ProcessDNSRewrites(req, dr.DNSRewrites(), id) + r = rulelist.ProcessDNSRewrites(req, ufRes.DNSRewrites(), id) - replaceRule(r, host) + replaceRule(r, req.Host) return r, nil } diff --git a/internal/filter/internal/safesearch/safesearch_test.go b/internal/filter/internal/safesearch/safesearch_test.go index a341a1c..bcdb7bf 100644 --- a/internal/filter/internal/safesearch/safesearch_test.go +++ b/internal/filter/internal/safesearch/safesearch_test.go @@ -17,6 +17,7 @@ import ( "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/safesearch" "github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/testutil" + "github.com/AdguardTeam/urlfilter" "github.com/miekg/dns" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -43,42 +44,16 @@ const testFilterRules = `|` + testEngineWithIP + `^$dnsrewrite=NOERROR;A;` + tes `|` + testEngineWithDomain + `^$dnsrewrite=NOERROR;CNAME;` + testSafeDomain func TestFilter(t *testing.T) { - reqCh := make(chan struct{}, 1) - cachePath, srvURL := filtertest.PrepareRefreshable(t, reqCh, testFilterRules, http.StatusOK) - - f, newErr := safesearch.New( - &safesearch.Config{ - Refreshable: &refreshable.Config{ - Logger: slogutil.NewDiscardLogger(), - ID: filter.IDGeneralSafeSearch, - URL: srvURL, - CachePath: cachePath, - Staleness: filtertest.Staleness, - Timeout: filtertest.Timeout, - MaxSize: filtertest.FilterMaxSize, - }, - CacheTTL: 1 * time.Minute, - }, - rulelist.NewResultCache(filtertest.CacheCount, true), - ) - require.NoError(t, newErr) - - refrErr := f.Refresh(testutil.ContextWithTimeout(t, filtertest.Timeout), true) - require.NoError(t, refrErr) - - testutil.RequireReceive(t, reqCh, filtertest.Timeout) + f := newTestFilter(t) require.True(t, t.Run("no_match", func(t *testing.T) { - ctx := testutil.ContextWithTimeout(t, filtertest.Timeout) - req := newReq(t, testOther, dns.TypeA) - res, err := f.FilterRequest(ctx, req) - require.NoError(t, err) + req := newRequest(t, testOther, dns.TypeA) + res := filterRequest(t, f, req) assert.Nil(t, res) require.True(t, t.Run("cached", func(t *testing.T) { - res, err = f.FilterRequest(ctx, req) - require.NoError(t, err) + res = filterRequest(t, f, req) // TODO(a.garipov): Find a way to make caches more inspectable. assert.Nil(t, res) @@ -86,19 +61,15 @@ func TestFilter(t *testing.T) { })) require.True(t, t.Run("txt", func(t *testing.T) { - ctx := testutil.ContextWithTimeout(t, filtertest.Timeout) - req := newReq(t, testEngineWithIP, dns.TypeTXT) - res, err := f.FilterRequest(ctx, req) - require.NoError(t, err) + req := newRequest(t, testEngineWithIP, dns.TypeTXT) + res := filterRequest(t, f, req) assert.Nil(t, res) })) require.True(t, t.Run("ip", func(t *testing.T) { - ctx := testutil.ContextWithTimeout(t, filtertest.Timeout) - req := newReq(t, testEngineWithIP, dns.TypeA) - res, err := f.FilterRequest(ctx, req) - require.NoError(t, err) + req := newRequest(t, testEngineWithIP, dns.TypeA) + res := filterRequest(t, f, req) rm := testutil.RequireTypeAssert[*filter.ResultModifiedResponse](t, res) require.Len(t, rm.Msg.Answer, 1) @@ -109,11 +80,9 @@ func TestFilter(t *testing.T) { assert.Equal(t, net.IP(testIPOfEngineWithIP.AsSlice()), a.A) t.Run("cached", func(t *testing.T) { - newReq := newReq(t, testEngineWithIP, dns.TypeA) + newReq := newRequest(t, testEngineWithIP, dns.TypeA) - var cachedRes filter.Result - cachedRes, err = f.FilterRequest(ctx, newReq) - require.NoError(t, err) + cachedRes := filterRequest(t, f, newReq) // Do not assert that the results are the same, since a modified // result of a safe search is always cloned. But assert that the @@ -128,10 +97,8 @@ func TestFilter(t *testing.T) { })) require.True(t, t.Run("domain", func(t *testing.T) { - ctx := testutil.ContextWithTimeout(t, filtertest.Timeout) - req := newReq(t, testEngineWithDomain, dns.TypeA) - res, err := f.FilterRequest(ctx, req) - require.NoError(t, err) + req := newRequest(t, testEngineWithDomain, dns.TypeA) + res := filterRequest(t, f, req) rm := testutil.RequireTypeAssert[*filter.ResultModifiedRequest](t, res) require.NotNil(t, rm.Msg) @@ -146,10 +113,8 @@ func TestFilter(t *testing.T) { })) require.True(t, t.Run("https", func(t *testing.T) { - ctx := testutil.ContextWithTimeout(t, filtertest.Timeout) - req := newReq(t, testEngineWithDomain, dns.TypeHTTPS) - res, err := f.FilterRequest(ctx, req) - require.NoError(t, err) + req := newRequest(t, testEngineWithDomain, dns.TypeHTTPS) + res := filterRequest(t, f, req) rm := testutil.RequireTypeAssert[*filter.ResultModifiedRequest](t, res) require.NotNil(t, rm.Msg) @@ -164,9 +129,141 @@ func TestFilter(t *testing.T) { })) } -// newReq is a test helper that returns the filtering request with the given +// filterRequest is a helper that calls [safesearch.Filter.FilterRequestUF]. +func filterRequest(tb testing.TB, f *safesearch.Filter, req *filter.Request) (res filter.Result) { + tb.Helper() + + ctx := testutil.ContextWithTimeout(tb, filtertest.Timeout) + res, err := f.FilterRequestUF(ctx, req, &urlfilter.DNSRequest{}, &urlfilter.DNSResult{}) + require.NoError(tb, err) + + return res +} + +func BenchmarkFilter_FilterRequestUF(b *testing.B) { + const qt = dns.TypeA + + f := newTestFilter(b) + + b.Run("no_match", func(b *testing.B) { + var res filter.Result + var err error + + ctx := testutil.ContextWithTimeout(b, filtertest.Timeout) + req := newRequest(b, testOther, qt) + + ufReq := &urlfilter.DNSRequest{ + Hostname: req.Host, + DNSType: qt, + } + + ufRes := &urlfilter.DNSResult{} + + b.ReportAllocs() + for b.Loop() { + ufRes.Reset() + res, err = f.FilterRequestUF(ctx, req, ufReq, ufRes) + } + + assert.NoError(b, err) + assert.Nil(b, res) + }) + + b.Run("ip", func(b *testing.B) { + var res filter.Result + var err error + + ctx := testutil.ContextWithTimeout(b, filtertest.Timeout) + req := newRequest(b, testEngineWithIP, qt) + + ufReq := &urlfilter.DNSRequest{ + Hostname: req.Host, + DNSType: qt, + } + + ufRes := &urlfilter.DNSResult{} + + b.ReportAllocs() + for b.Loop() { + ufRes.Reset() + res, err = f.FilterRequestUF(ctx, req, ufReq, ufRes) + } + + assert.NoError(b, err) + assert.NotNil(b, res) + }) + + b.Run("domain", func(b *testing.B) { + var res filter.Result + var err error + + ctx := testutil.ContextWithTimeout(b, filtertest.Timeout) + req := newRequest(b, testEngineWithDomain, qt) + + ufReq := &urlfilter.DNSRequest{ + Hostname: req.Host, + DNSType: qt, + } + + ufRes := &urlfilter.DNSResult{} + + b.ReportAllocs() + for b.Loop() { + ufRes.Reset() + res, err = f.FilterRequestUF(ctx, req, ufReq, ufRes) + } + + assert.NoError(b, err) + assert.NotNil(b, res) + }) + + // Most recent results: + // + // goos: darwin + // goarch: arm64 + // pkg: github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/safesearch + // cpu: Apple M1 Pro + // BenchmarkFilter_FilterRequestUF/no_match-8 27804783 43.23 ns/op 0 B/op 0 allocs/op + // BenchmarkFilter_FilterRequestUF/ip-8 3136018 382.3 ns/op 672 B/op 11 allocs/op + // BenchmarkFilter_FilterRequestUF/domain-8 4929343 237.8 ns/op 400 B/op 7 allocs/op +} + +// newTestFilter creates a new [*safesearch.Filter] for testing, and refreshes +// it immediately. +func newTestFilter(tb testing.TB) (f *safesearch.Filter) { + tb.Helper() + + reqCh := make(chan struct{}, 1) + cachePath, srvURL := filtertest.PrepareRefreshable(tb, reqCh, testFilterRules, http.StatusOK) + + f, err := safesearch.New( + &safesearch.Config{ + Refreshable: &refreshable.Config{ + Logger: slogutil.NewDiscardLogger(), + ID: filter.IDGeneralSafeSearch, + URL: srvURL, + CachePath: cachePath, + Staleness: filtertest.Staleness, + Timeout: filtertest.Timeout, + MaxSize: filtertest.FilterMaxSize, + }, + CacheTTL: 1 * time.Minute, + }, + rulelist.NewResultCache(filtertest.CacheCount, true), + ) + require.NoError(tb, err) + + err = f.Refresh(testutil.ContextWithTimeout(tb, filtertest.Timeout), true) + require.NoError(tb, err) + + testutil.RequireReceive(tb, reqCh, filtertest.Timeout) + + return f +} + +// newRequest is a test helper that returns the filtering request with the given // data. -func newReq(tb testing.TB, host string, qt dnsmsg.RRType) (req *filter.Request) { +func newRequest(tb testing.TB, host string, qt dnsmsg.RRType) (req *filter.Request) { tb.Helper() return &filter.Request{ diff --git a/internal/filter/internal/serviceblock/index.go b/internal/filter/internal/serviceblock/index.go index 040964a..91c964a 100644 --- a/internal/filter/internal/serviceblock/index.go +++ b/internal/filter/internal/serviceblock/index.go @@ -5,9 +5,9 @@ import ( "fmt" "log/slog" "path" - "strings" "github.com/AdguardTeam/AdGuardDNS/internal/agdcache" + "github.com/AdguardTeam/AdGuardDNS/internal/agdurlflt" "github.com/AdguardTeam/AdGuardDNS/internal/errcoll" "github.com/AdguardTeam/AdGuardDNS/internal/filter" "github.com/AdguardTeam/AdGuardDNS/internal/filter/internal/rulelist" @@ -93,7 +93,8 @@ func (svc *indexRespService) toInternal( fltIDStr := path.Join(cachePrefix, string(filter.IDBlockedService), string(svcID)) cache := rulelist.NewManagedResultCache(cacheManager, fltIDStr, cacheCount, useCache) - rl = rulelist.NewImmutable(strings.Join(svc.Rules, "\n"), filter.IDBlockedService, svcID, cache) + rulesData := agdurlflt.RulesToBytes(svc.Rules) + rl = rulelist.NewImmutable(rulesData, filter.IDBlockedService, svcID, cache) logger.InfoContext(ctx, "converted service", "svc_id", svcID, "num_rules", rl.RulesCount()) diff --git a/internal/filter/internal/serviceblock/serviceblock.go b/internal/filter/internal/serviceblock/serviceblock.go index c99b760..ddb3a9b 100644 --- a/internal/filter/internal/serviceblock/serviceblock.go +++ b/internal/filter/internal/serviceblock/serviceblock.go @@ -8,7 +8,6 @@ import ( "encoding/json" "fmt" "log/slog" - "strings" "sync" "time" @@ -134,13 +133,13 @@ func (f *Filter) Refresh( // loadIndex fetches, decodes, and returns the blocked service index data. func (f *Filter) loadIndex(ctx context.Context, acceptStale bool) (resp *indexResp, err error) { - text, err := f.refr.Refresh(ctx, acceptStale) + b, err := f.refr.Refresh(ctx, acceptStale) if err != nil { return nil, fmt.Errorf("loading index: %w", err) } resp = &indexResp{} - err = json.NewDecoder(strings.NewReader(text)).Decode(resp) + err = json.Unmarshal(b, resp) if err != nil { return nil, fmt.Errorf("decoding index: %w", err) } diff --git a/internal/filter/internal/serviceblock/serviceblock_test.go b/internal/filter/internal/serviceblock/serviceblock_test.go index e174b99..1a20c68 100644 --- a/internal/filter/internal/serviceblock/serviceblock_test.go +++ b/internal/filter/internal/serviceblock/serviceblock_test.go @@ -1,7 +1,6 @@ package serviceblock_test import ( - "context" "net/http" "testing" @@ -42,7 +41,7 @@ func TestFilter(t *testing.T) { require.NoError(t, err) - ctx := context.Background() + ctx := testutil.ContextWithTimeout(t, filtertest.Timeout) err = f.Refresh(ctx, agdcache.EmptyManager{}, 0, false, false) require.NoError(t, err) diff --git a/internal/metrics/standardaccess.go b/internal/metrics/standardaccess.go new file mode 100644 index 0000000..31ffac1 --- /dev/null +++ b/internal/metrics/standardaccess.go @@ -0,0 +1,81 @@ +package metrics + +import ( + "context" + "fmt" + "time" + + "github.com/AdguardTeam/golibs/container" + "github.com/AdguardTeam/golibs/errors" + "github.com/prometheus/client_golang/prometheus" +) + +// BackendStandardAccess is a metrics collector for standard access updates. +type BackendStandardAccess struct { + // updateDuration is the histogram with the duration of the last TLS session + // tickets update. + updateDuration prometheus.Histogram + + // errorsTotal is the counter with the total number of errors occurred + // during TLS session ticket updates. + errorsTotal prometheus.Counter +} + +// NewBackendStandardAccess returns a new StandardAccessStorage that collects +// metrics about standard access updates. +func NewBackendStandardAccess( + namespace string, + reg prometheus.Registerer, +) (m *BackendStandardAccess, err error) { + const ( + updateDuration = "standard_access_update_duration" + errorsTotal = "standard_access_update_errors_total" + ) + + m = &BackendStandardAccess{ + updateDuration: prometheus.NewHistogram(prometheus.HistogramOpts{ + Name: updateDuration, + Subsystem: subsystemBackend, + Namespace: namespace, + Help: "Duration of the last standard access update in seconds.", + Buckets: []float64{0.001, 0.01, 0.1, 1}, + }), + errorsTotal: prometheus.NewCounter(prometheus.CounterOpts{ + Name: errorsTotal, + Subsystem: subsystemBackend, + Namespace: namespace, + Help: "The total number of errors occurred during standard access updates.", + }), + } + + var errs []error + collectors := container.KeyValues[string, prometheus.Collector]{{ + Key: updateDuration, + Value: m.updateDuration, + }, { + Key: errorsTotal, + Value: m.errorsTotal, + }} + + 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 +} + +// ObserveUpdate implements the [backendpb.StandardAccessMetrics] interface for +// *BackendStandardAccess. +func (m *BackendStandardAccess) ObserveUpdate(_ context.Context, d time.Duration, err error) { + m.updateDuration.Observe(d.Seconds()) + if err != nil { + m.errorsTotal.Inc() + } +} diff --git a/internal/metrics/tlstickets.go b/internal/metrics/tlstickets.go index e71bc89..19bdf17 100644 --- a/internal/metrics/tlstickets.go +++ b/internal/metrics/tlstickets.go @@ -40,11 +40,11 @@ func NewBackendTicketStorage( reg prometheus.Registerer, ) (m *BackendTicketStorage, err error) { const ( - ticketsState = "tickets_state" - updateStatus = "update_status" - updatedTime = "update_time" - updateDuration = "update_duration" - errorsTotal = "update_errors_total" + ticketsState = "tickets_state" + ticketsUpdateStatus = "tickets_update_status" + ticketsUpdatedTime = "tickets_update_time" + ticketsUpdateDuration = "tickets_update_duration" + ticketsErrorsTotal = "tickets_update_errors_total" ) m = &BackendTicketStorage{ @@ -55,26 +55,26 @@ func NewBackendTicketStorage( Help: "State number code of the last updated TLS session tickets.", }), updateStatus: prometheus.NewGaugeVec(prometheus.GaugeOpts{ - Name: updateStatus, + Name: ticketsUpdateStatus, Subsystem: subsystemTLS, Namespace: namespace, Help: "Status of the TLS session ticket update. 1 means success.", }, []string{"name"}), updatedTime: prometheus.NewGaugeVec(prometheus.GaugeOpts{ - Name: updatedTime, + Name: ticketsUpdatedTime, Subsystem: subsystemTLS, Namespace: namespace, Help: "Time when the TLS session ticket was last time updated.", }, []string{"name"}), updateDuration: prometheus.NewHistogram(prometheus.HistogramOpts{ - Name: "update_duration_seconds", + Name: ticketsUpdateDuration, Subsystem: subsystemTLS, Namespace: namespace, Help: "Duration of the last TLS session ticket update in seconds.", Buckets: []float64{0.001, 0.01, 0.1, 1}, }), errorsTotal: prometheus.NewCounter(prometheus.CounterOpts{ - Name: "update_errors_total", + Name: ticketsErrorsTotal, Subsystem: subsystemTLS, Namespace: namespace, Help: "The total number of errors occurred during TLS session ticket updates.", @@ -86,16 +86,16 @@ func NewBackendTicketStorage( Key: ticketsState, Value: m.ticketsState, }, { - Key: updateStatus, + Key: ticketsUpdateStatus, Value: m.updateStatus, }, { - Key: updatedTime, + Key: ticketsUpdatedTime, Value: m.updatedTime, }, { - Key: updateDuration, + Key: ticketsUpdateDuration, Value: m.updateDuration, }, { - Key: errorsTotal, + Key: ticketsErrorsTotal, Value: m.errorsTotal, }} diff --git a/internal/profiledb/default_test.go b/internal/profiledb/default_test.go index 73708fd..6ef1683 100644 --- a/internal/profiledb/default_test.go +++ b/internal/profiledb/default_test.go @@ -375,8 +375,9 @@ func TestDefault_fileCache_success(t *testing.T) { p, d, err := db.ProfileByDeviceID(context.Background(), dev.ID) require.NoError(t, err) + assert.Equal(t, dev, d) - assert.Equal(t, prof, p) + agdtest.AssertEqualProfile(t, prof, p) } func TestDefault_fileCache_badVersion(t *testing.T) { diff --git a/internal/profiledb/internal/filecachepb/filecache.pb.go b/internal/profiledb/internal/filecachepb/filecache.pb.go index 21989be..441e84e 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.36.6 -// protoc v6.31.0 +// protoc-gen-go v1.36.8 +// protoc v6.32.0 // source: filecache.proto package filecachepb @@ -854,6 +854,7 @@ type Access struct { BlocklistAsn []uint32 `protobuf:"varint,5,rep,packed,name=blocklist_asn,json=blocklistAsn,proto3" json:"blocklist_asn,omitempty"` BlocklistCidr []*CidrRange `protobuf:"bytes,2,rep,name=blocklist_cidr,json=blocklistCidr,proto3" json:"blocklist_cidr,omitempty"` BlocklistDomainRules []string `protobuf:"bytes,3,rep,name=blocklist_domain_rules,json=blocklistDomainRules,proto3" json:"blocklist_domain_rules,omitempty"` + StandardEnabled bool `protobuf:"varint,6,opt,name=standard_enabled,json=standardEnabled,proto3" json:"standard_enabled,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -923,6 +924,13 @@ func (x *Access) GetBlocklistDomainRules() []string { return nil } +func (x *Access) GetStandardEnabled() bool { + if x != nil { + return x.StandardEnabled + } + return false +} + type CidrRange struct { state protoimpl.MessageState `protogen:"open.v1"` Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` @@ -1725,13 +1733,14 @@ const file_filecache_proto_rawDesc = "" + "\x0ehuman_id_lower\x18\a \x01(\tR\fhumanIdLower\x12\x1b\n" + "\tlinked_ip\x18\x02 \x01(\fR\blinkedIp\x12#\n" + "\rdedicated_ips\x18\x04 \x03(\fR\fdedicatedIps\x12+\n" + - "\x11filtering_enabled\x18\x05 \x01(\bR\x10filteringEnabled\"\x82\x02\n" + + "\x11filtering_enabled\x18\x05 \x01(\bR\x10filteringEnabled\"\xad\x02\n" + "\x06Access\x12#\n" + "\rallowlist_asn\x18\x04 \x03(\rR\fallowlistAsn\x12;\n" + "\x0eallowlist_cidr\x18\x01 \x03(\v2\x14.profiledb.CidrRangeR\rallowlistCidr\x12#\n" + "\rblocklist_asn\x18\x05 \x03(\rR\fblocklistAsn\x12;\n" + "\x0eblocklist_cidr\x18\x02 \x03(\v2\x14.profiledb.CidrRangeR\rblocklistCidr\x124\n" + - "\x16blocklist_domain_rules\x18\x03 \x03(\tR\x14blocklistDomainRules\"=\n" + + "\x16blocklist_domain_rules\x18\x03 \x03(\tR\x14blocklistDomainRules\x12)\n" + + "\x10standard_enabled\x18\x06 \x01(\bR\x0fstandardEnabled\"=\n" + "\tCidrRange\x12\x18\n" + "\aaddress\x18\x01 \x01(\fR\aaddress\x12\x16\n" + "\x06prefix\x18\x02 \x01(\rR\x06prefix\"\x85\x01\n" + diff --git a/internal/profiledb/internal/filecachepb/filecache.proto b/internal/profiledb/internal/filecachepb/filecache.proto index 564e77d..cb95e97 100644 --- a/internal/profiledb/internal/filecachepb/filecache.proto +++ b/internal/profiledb/internal/filecachepb/filecache.proto @@ -152,6 +152,7 @@ message Access { repeated uint32 blocklist_asn = 5; repeated CidrRange blocklist_cidr = 2; repeated string blocklist_domain_rules = 3; + bool standard_enabled = 6; } message CidrRange { diff --git a/internal/profiledb/internal/filecachepb/filecachepb.go b/internal/profiledb/internal/filecachepb/filecachepb.go index 2fdfdc8..189d8fa 100644 --- a/internal/profiledb/internal/filecachepb/filecachepb.go +++ b/internal/profiledb/internal/filecachepb/filecachepb.go @@ -420,6 +420,7 @@ func (x *Access) toInternal(cons *access.ProfileConstructor) (a access.Profile) AllowedASN: asnToInternal(x.AllowlistAsn), BlockedASN: asnToInternal(x.BlocklistAsn), BlocklistDomainRules: x.BlocklistDomainRules, + StandardEnabled: x.StandardEnabled, }) } @@ -474,14 +475,16 @@ func profileToProtobuf(p *agd.Profile) (pbProf *Profile) { }() return &Profile{ - CustomDomains: customDomainsToProtobuf(p.CustomDomains), - FilterConfig: filterConfigToProtobuf(p.FilterConfig), - Access: accessToProtobuf(p.Access.Config()), - BlockingMode: blockingModeToProtobuf(p.BlockingMode), - Ratelimiter: ratelimiterToProtobuf(p.Ratelimiter.Config()), - AccountId: string(p.AccountID), - ProfileId: string(p.ID), - DeviceIds: unsafelyConvertStrSlice[agd.DeviceID, string](p.DeviceIDs.Values()), + CustomDomains: customDomainsToProtobuf(p.CustomDomains), + FilterConfig: filterConfigToProtobuf(p.FilterConfig), + Access: accessToProtobuf(p.Access.Config()), + BlockingMode: blockingModeToProtobuf(p.BlockingMode), + Ratelimiter: ratelimiterToProtobuf(p.Ratelimiter.Config()), + AccountId: string(p.AccountID), + ProfileId: string(p.ID), + DeviceIds: unsafelyConvertStrSlice[agd.DeviceID, string]( + p.DeviceIDs.Values(), + ), FilteredResponseTtl: durationpb.New(p.FilteredResponseTTL), AutoDevicesEnabled: p.AutoDevicesEnabled, BlockChromePrefetch: p.BlockChromePrefetch, @@ -645,6 +648,7 @@ func accessToProtobuf(c *access.ProfileConfig) (ac *Access) { BlocklistAsn: blockedASNs, BlocklistCidr: prefixesToProtobuf(c.BlockedNets), BlocklistDomainRules: c.BlocklistDomainRules, + StandardEnabled: c.StandardEnabled, } } diff --git a/internal/profiledb/internal/filecachepb/storage_test.go b/internal/profiledb/internal/filecachepb/storage_test.go index 5189d20..11c6ba7 100644 --- a/internal/profiledb/internal/filecachepb/storage_test.go +++ b/internal/profiledb/internal/filecachepb/storage_test.go @@ -6,6 +6,7 @@ import ( "time" "github.com/AdguardTeam/AdGuardDNS/internal/agd" + "github.com/AdguardTeam/AdGuardDNS/internal/agdtest" "github.com/AdguardTeam/AdGuardDNS/internal/profiledb/internal" "github.com/AdguardTeam/AdGuardDNS/internal/profiledb/internal/filecachepb" "github.com/AdguardTeam/AdGuardDNS/internal/profiledb/internal/profiledbtest" @@ -41,7 +42,7 @@ func TestStorage(t *testing.T) { require.NotNil(t, gotFC) require.NotEmpty(t, *gotFC) - assert.Equal(t, fc, gotFC) + agdtest.AssertEqualProfile(t, fc, gotFC) } func TestStorage_Load_noFile(t *testing.T) { diff --git a/internal/profiledb/internal/internal.go b/internal/profiledb/internal/internal.go index 8308e77..c6efc16 100644 --- a/internal/profiledb/internal/internal.go +++ b/internal/profiledb/internal/internal.go @@ -13,7 +13,7 @@ import ( // FileCacheVersion is the version of cached data structure. It must be // manually incremented on every change in [agd.Device], [agd.Profile], and any // file-cache structures. -const FileCacheVersion = 17 +const FileCacheVersion = 18 // CacheVersionError is returned from [FileCacheStorage.Load] method if the // stored cache version doesn't match current [FileCacheVersion]. diff --git a/internal/profiledb/internal/profiledbtest/profiledbtest.go b/internal/profiledb/internal/profiledbtest/profiledbtest.go index 9f63f69..6f8404f 100644 --- a/internal/profiledb/internal/profiledbtest/profiledbtest.go +++ b/internal/profiledb/internal/profiledbtest/profiledbtest.go @@ -54,7 +54,10 @@ var Logger = slogutil.NewDiscardLogger() // ProfileAccessConstructor is the common constructor of profile access managers // for tests. -var ProfileAccessConstructor = access.NewProfileConstructor(access.EmptyProfileMetrics{}) +var ProfileAccessConstructor = access.NewProfileConstructor(&access.ProfileConstructorConfig{ + Metrics: access.EmptyProfileMetrics{}, + Standard: access.EmptyProfile{}, +}) // ContextWithTimeout is a helper that returns a context with [Timeout]. func ContextWithTimeout(tb testing.TB) (ctx context.Context) { @@ -157,6 +160,7 @@ func NewProfile(tb testing.TB) (p *agd.Profile, d *agd.Device) { AllowedASN: []geoip.ASN{1}, BlockedASN: []geoip.ASN{2}, BlocklistDomainRules: []string{"block.test"}, + StandardEnabled: true, }), BlockingMode: &dnsmsg.BlockingModeNullIP{}, Ratelimiter: agd.NewDefaultRatelimiter(&agd.RatelimitConfig{ diff --git a/internal/tlsconfig/customdomaindb_test.go b/internal/tlsconfig/customdomaindb_test.go index 8248f6c..70c623b 100644 --- a/internal/tlsconfig/customdomaindb_test.go +++ b/internal/tlsconfig/customdomaindb_test.go @@ -6,7 +6,6 @@ import ( "crypto/tls" "crypto/x509" "encoding/pem" - "fmt" "net/http" "net/http/httptest" "os" @@ -524,21 +523,16 @@ func (m *testManager) Remove(ctx context.Context, certPath, keyPath string, isCu func newTestManager() (m *testManager) { return &testManager{ onAdd: func(ctx context.Context, certPath, keyPath string, isCustom bool) (err error) { - panic(fmt.Errorf("unexpected call to testManager.Add(%v, %v, %v)", certPath, keyPath, isCustom)) + panic(testutil.UnexpectedCall(ctx, certPath, keyPath, isCustom)) }, onClone: func() (c *tls.Config) { - panic(fmt.Errorf("unexpected call to testManager.Clone()")) + panic(testutil.UnexpectedCall()) }, onCloneWithMetrics: func(proto, srvName string, deviceDomains []string) (c *tls.Config) { - panic(fmt.Errorf( - "unexpected call to testManager.CloneWithMetrics(%v, %v, %v)", - proto, - srvName, - deviceDomains, - )) + panic(testutil.UnexpectedCall(proto, srvName, deviceDomains)) }, onRemove: func(ctx context.Context, certPath, keyPath string, isCustom bool) (err error) { - panic(fmt.Errorf("unexpected call to testManager.Remove(%v, %v, %v)", certPath, keyPath, isCustom)) + panic(testutil.UnexpectedCall(ctx, certPath, keyPath, isCustom)) }, } } @@ -702,7 +696,7 @@ func TestCustomDomainDB_Refresh_retry(t *testing.T) { certName string, ) (cert, key []byte, err error) { if !shouldCall { - panic(fmt.Errorf("unexpected call to strg.OnCertificateData(%s)", certName)) + panic(testutil.UnexpectedCall(ctx, certName)) } if strgErr != nil { diff --git a/main.go b/main.go index 3f7c7c4..5e634df 100644 --- a/main.go +++ b/main.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022-2024 AdGuard Software Ltd. +// Copyright (C) 2022-2025 AdGuard Software Ltd. // // This program is free software: you can redistribute it and/or modify it under // the terms of the GNU Affero General Public License as published by the Free diff --git a/scripts/backend/dns.go b/scripts/backend/dns.go index cf56cd8..866c2e4 100644 --- a/scripts/backend/dns.go +++ b/scripts/backend/dns.go @@ -271,8 +271,9 @@ func (s *mockDNSServiceServer) newDNSProfile(isFullSync bool) (dp *backendpb.DNS Rps: 100, Enabled: true, }, - CustomDomain: customDomain, - AccountId: "acc1234", - DeviceChanges: deviceChanges, + CustomDomain: customDomain, + AccountId: "acc1234", + DeviceChanges: deviceChanges, + StandardAccessSettingsEnabled: true, } } diff --git a/scripts/backend/ratelimiter.go b/scripts/backend/ratelimit.go similarity index 68% rename from scripts/backend/ratelimiter.go rename to scripts/backend/ratelimit.go index 9eef5bd..a8d5e8d 100644 --- a/scripts/backend/ratelimiter.go +++ b/scripts/backend/ratelimit.go @@ -2,8 +2,8 @@ package main import ( "context" - "fmt" "log/slog" + "net/netip" "github.com/AdguardTeam/AdGuardDNS/internal/backendpb" "github.com/AdguardTeam/golibs/httphdr" @@ -56,8 +56,32 @@ func (s *mockRateLimitServiceServer) GetRateLimitSettings( // // TODO(a.garipov): Implement this method. func (s *mockRateLimitServiceServer) GetGlobalAccessSettings( - _ context.Context, - _ *backendpb.GlobalAccessSettingsRequest, + ctx context.Context, + req *backendpb.GlobalAccessSettingsRequest, ) (_ *backendpb.GlobalAccessSettingsResponse, _ error) { - panic(fmt.Errorf("unexpected call to GetGlobalAccessSettings")) + md, _ := metadata.FromIncomingContext(ctx) + + s.log.InfoContext( + ctx, + "getting", + "auth", md.Get(httphdr.Authorization), + "req", req, + ) + + return &backendpb.GlobalAccessSettingsResponse{ + Standard: &backendpb.AccessSettings{ + AllowlistCidr: []*backendpb.CidrRange{{ + Address: netip.MustParseAddr("10.10.10.0").AsSlice(), + Prefix: 24, + }}, + BlocklistCidr: []*backendpb.CidrRange{{ + Address: netip.MustParseAddr("20.20.20.0").AsSlice(), + Prefix: 24, + }}, + AllowlistAsn: []uint32{10}, + BlocklistAsn: []uint32{20}, + BlocklistDomainRules: []string{"block.std.test"}, + Enabled: true, + }, + }, nil } diff --git a/scripts/make/go-fuzz.sh b/scripts/make/go-fuzz.sh index 35cfb6d..bc94fc9 100644 --- a/scripts/make/go-fuzz.sh +++ b/scripts/make/go-fuzz.sh @@ -64,3 +64,15 @@ readonly go count_flags shuffle_flags timeout_flags fuzztime_flags --fuzz='FuzzHumanIDParser_ParseNormalized' \ ./internal/agd/ \ ; + +"$go" test \ + "$count_flags" \ + "$shuffle_flags" \ + "$race_flags" \ + "$timeout_flags" \ + "$x_flags" \ + "$v_flags" \ + "$fuzztime_flags" \ + --fuzz='FuzzDefault' \ + ./internal/agdcache/ \ + ; diff --git a/scripts/make/go-lint.sh b/scripts/make/go-lint.sh index 5324f0f..811e29d 100644 --- a/scripts/make/go-lint.sh +++ b/scripts/make/go-lint.sh @@ -64,17 +64,20 @@ set -f -u # # NOTE: For AdGuard DNS, there are the following exceptions: # +# * internal/agdtest/profile.go: a test helper requiring the use of +# reflect.Type. # * internal/profiledb/internal/filecachepb/unsafe.go: a “safe” unsafe helper # to prevent excessive allocations. blocklist_imports() { import_or_tab="$(printf '^\\(import \\|\t\\)')" readonly import_or_tab - find . \ + find_with_ignore \ -type 'f' \ '(' \ -name '*.go' \ '!' -name '*.pb.go' \ + '!' -path './internal/agdtest/profile.go' \ '!' -path './internal/profiledb/internal/filecachepb/unsafe.go' \ ')' \ -exec \ @@ -104,7 +107,7 @@ blocklist_imports() { method_const() { # NOTE: File ./internal/remotekv/rediskv/rediskv.go is excluded, since it # uses "GET" as a Redis command. - find . \ + find_with_ignore \ -type 'f' \ '(' -name '*.go' '!' -path './internal/remotekv/rediskv/rediskv.go' ')' \ -exec \ @@ -125,10 +128,11 @@ method_const() { # use of filenames like client_manager.go. underscores() { underscore_files="$( - find . \ + find_with_ignore \ -type 'f' \ -name '*_*.go' \ - '!' '(' -name '*_darwin.go' \ + '!' '(' \ + -name '*_darwin.go' \ -o -name '*_generate.go' \ -o -name '*_grpc.pb.go' \ -o -name '*_linux.go' \ @@ -180,7 +184,7 @@ run_linter ineffassign ./... "$dnssrvmod" run_linter unparam ./... "$dnssrvmod" -find . \ +find_with_ignore \ -type 'f' \ '(' \ -name 'Makefile' \ diff --git a/scripts/make/go-test.sh b/scripts/make/go-test.sh index 3441416..a4e24c6 100644 --- a/scripts/make/go-test.sh +++ b/scripts/make/go-test.sh @@ -3,7 +3,7 @@ # This comment is used to simplify checking local copies of the script. Bump # this number every time a significant change is made to this script. # -# AdGuard-Project-Version: 5 +# AdGuard-Project-Version: 6 verbose="${VERBOSE:-0}" readonly verbose @@ -36,7 +36,7 @@ else fi readonly race_flags -count_flags='--count=1' +count_flags='--count=2' cover_flags='--coverprofile=./cover.out' go="${GO:-go}" shuffle_flags='--shuffle=on' diff --git a/scripts/make/helper.sh b/scripts/make/helper.sh index 8caa047..6c8a457 100644 --- a/scripts/make/helper.sh +++ b/scripts/make/helper.sh @@ -6,15 +6,15 @@ # right after the initial environment processing. # This comment is used to simplify checking local copies of the script. Bump -# this number every time a remarkable change is made to this script. +# this number every time a significant change is made to this script. # -# AdGuard-Project-Version: 4 +# AdGuard-Project-Version: 5 # Deferred helpers not_found_msg=' looks like a binary not found error. -make sure you have installed the linter binaries using: +make sure you have installed the linter binaries, including using: $ make go-tools ' @@ -73,3 +73,33 @@ run_linter() ( return "$exitcode" ) + +# find_with_ignore is a wrapper around find that does not descend into ignored +# directories, such as ./tmp/. +# +# NOTE: The arguments must contain on of -exec, -ok, or -print; see +# https://pubs.opengroup.org/onlinepubs/9799919799/utilities/find.html. +# +# TODO(a.garipov): Find a way to integrate the entire gitignore, including the +# global one, without using git, as .git is not copied into the build container. +# +# Keep in sync with .gitignore. +find_with_ignore() { + find . \ + '(' \ + -type 'd' \ + '(' \ + -name '.git' \ + -o -name 'bin' \ + -o -name 'filters' \ + -o -name 'github-mirror' \ + -o -name 'test' \ + -o -name 'test-reports' \ + -o -name 'tmp' \ + ')' \ + -prune \ + ')' \ + -o \ + "$@" \ + ; +} diff --git a/scripts/make/md-lint.sh b/scripts/make/md-lint.sh index e9ee7a5..9b50e2c 100644 --- a/scripts/make/md-lint.sh +++ b/scripts/make/md-lint.sh @@ -1,7 +1,7 @@ #!/bin/sh # This comment is used to simplify checking local copies of the script. Bump -# this number every time a remarkable change is made to this script. +# this number every time a significant change is made to this script. # # AdGuard-Project-Version: 3 diff --git a/scripts/make/sh-lint.sh b/scripts/make/sh-lint.sh index ca748c5..39196ef 100644 --- a/scripts/make/sh-lint.sh +++ b/scripts/make/sh-lint.sh @@ -1,7 +1,7 @@ #!/bin/sh # This comment is used to simplify checking local copies of the script. Bump -# this number every time a remarkable change is made to this script. +# this number every time a significant change is made to this script. # # AdGuard-Project-Version: 3 diff --git a/scripts/make/txt-lint.sh b/scripts/make/txt-lint.sh index 2479a95..6f9129e 100644 --- a/scripts/make/txt-lint.sh +++ b/scripts/make/txt-lint.sh @@ -1,9 +1,9 @@ #!/bin/sh # This comment is used to simplify checking local copies of the script. Bump -# this number every time a remarkable change is made to this script. +# this number every time a significant change is made to this script. # -# AdGuard-Project-Version: 7 +# AdGuard-Project-Version: 9 verbose="${VERBOSE:-0}" readonly verbose @@ -33,19 +33,18 @@ trailing_newlines() ( nl="$(printf '\n')" readonly nl - find . \ + find_with_ignore \ -type 'f' \ '!' '(' \ - -name '*.mmdb' \ + -name '*.exe' \ + -o -name '*.mmdb' \ -o -name '*.out' \ -o -name '*.pb' \ + -o -name '*.test' \ -o -name 'AdGuardDNS' \ -o -name 'agdns' \ - -o -path './.git/*' \ - -o -path './bin/*' \ - -o -path './filters/*' \ - -o -path './test/*' \ ')' \ + -print \ | while read -r f; do final_byte="$(tail -c -1 "$f")" if [ "$final_byte" != "$nl" ]; then @@ -57,19 +56,18 @@ trailing_newlines() ( # trailing_whitespace is a simple check that makes sure that there are no # trailing whitespace in plain-text files. trailing_whitespace() { - find . \ + find_with_ignore \ -type 'f' \ '!' '(' \ - -name '*.mmdb' \ + -name '*.exe' \ + -o -name '*.mmdb' \ -o -name '*.out' \ -o -name '*.pb' \ + -o -name '*.test' \ -o -name 'AdGuardDNS' \ -o -name 'agdns' \ - -o -path './.git/*' \ - -o -path './bin/*' \ - -o -path './filters/*' \ - -o -path './test/*' \ ')' \ + -print \ | while read -r f; do grep -e '[[:space:]]$' -n -- "$f" \ | sed -e "s:^:${f}\::" -e 's/ \+$/>>>&<<